123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698346993470034701347023470334704347053470634707347083470934710347113471234713347143471534716347173471834719347203472134722347233472434725347263472734728347293473034731347323473334734347353473634737347383473934740347413474234743347443474534746347473474834749347503475134752347533475434755347563475734758347593476034761347623476334764347653476634767347683476934770347713477234773347743477534776347773477834779347803478134782347833478434785347863478734788347893479034791347923479334794347953479634797347983479934800348013480234803348043480534806348073480834809348103481134812348133481434815348163481734818348193482034821348223482334824348253482634827348283482934830348313483234833348343483534836348373483834839348403484134842348433484434845348463484734848348493485034851348523485334854348553485634857348583485934860348613486234863348643486534866348673486834869348703487134872348733487434875348763487734878348793488034881348823488334884348853488634887348883488934890348913489234893348943489534896348973489834899349003490134902349033490434905349063490734908349093491034911349123491334914349153491634917349183491934920349213492234923349243492534926349273492834929349303493134932349333493434935349363493734938349393494034941349423494334944349453494634947349483494934950349513495234953349543495534956349573495834959349603496134962349633496434965349663496734968349693497034971349723497334974349753497634977349783497934980349813498234983349843498534986349873498834989349903499134992349933499434995349963499734998349993500035001350023500335004350053500635007350083500935010350113501235013350143501535016350173501835019350203502135022350233502435025350263502735028350293503035031350323503335034350353503635037350383503935040350413504235043350443504535046350473504835049350503505135052350533505435055350563505735058350593506035061350623506335064350653506635067350683506935070350713507235073350743507535076350773507835079350803508135082350833508435085350863508735088350893509035091350923509335094350953509635097350983509935100351013510235103351043510535106351073510835109351103511135112351133511435115351163511735118351193512035121351223512335124351253512635127351283512935130351313513235133351343513535136351373513835139351403514135142351433514435145351463514735148351493515035151351523515335154351553515635157351583515935160351613516235163351643516535166351673516835169351703517135172351733517435175351763517735178351793518035181351823518335184351853518635187351883518935190351913519235193351943519535196351973519835199352003520135202352033520435205352063520735208352093521035211352123521335214352153521635217352183521935220352213522235223352243522535226352273522835229352303523135232352333523435235352363523735238352393524035241352423524335244352453524635247352483524935250352513525235253352543525535256352573525835259352603526135262352633526435265352663526735268352693527035271352723527335274352753527635277352783527935280352813528235283352843528535286352873528835289352903529135292352933529435295352963529735298352993530035301353023530335304353053530635307353083530935310353113531235313353143531535316353173531835319353203532135322353233532435325353263532735328353293533035331353323533335334353353533635337353383533935340353413534235343353443534535346353473534835349353503535135352353533535435355353563535735358353593536035361353623536335364353653536635367353683536935370353713537235373353743537535376353773537835379353803538135382353833538435385353863538735388353893539035391353923539335394353953539635397353983539935400354013540235403354043540535406354073540835409354103541135412354133541435415354163541735418354193542035421354223542335424354253542635427354283542935430354313543235433354343543535436354373543835439354403544135442354433544435445354463544735448354493545035451354523545335454354553545635457354583545935460354613546235463354643546535466354673546835469354703547135472354733547435475354763547735478354793548035481354823548335484354853548635487354883548935490354913549235493354943549535496354973549835499355003550135502355033550435505355063550735508355093551035511355123551335514355153551635517 |
- #include "qcustomplot.h"
- QCPVector2D::QCPVector2D() :
- mX(0),
- mY(0)
- {
- }
- QCPVector2D::QCPVector2D(double x, double y) :
- mX(x),
- mY(y)
- {
- }
- QCPVector2D::QCPVector2D(const QPoint &point) :
- mX(point.x()),
- mY(point.y())
- {
- }
- QCPVector2D::QCPVector2D(const QPointF &point) :
- mX(point.x()),
- mY(point.y())
- {
- }
- void QCPVector2D::normalize()
- {
- if (mX == 0.0 && mY == 0.0) return;
- const double lenInv = 1.0/length();
- mX *= lenInv;
- mY *= lenInv;
- }
- QCPVector2D QCPVector2D::normalized() const
- {
- if (mX == 0.0 && mY == 0.0) return *this;
- const double lenInv = 1.0/length();
- return QCPVector2D(mX*lenInv, mY*lenInv);
- }
- double QCPVector2D::distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const
- {
- const QCPVector2D v(end-start);
- const double vLengthSqr = v.lengthSquared();
- if (!qFuzzyIsNull(vLengthSqr))
- {
- const double mu = v.dot(*this-start)/vLengthSqr;
- if (mu < 0)
- return (*this-start).lengthSquared();
- else if (mu > 1)
- return (*this-end).lengthSquared();
- else
- return ((start + mu*v)-*this).lengthSquared();
- } else
- return (*this-start).lengthSquared();
- }
- double QCPVector2D::distanceSquaredToLine(const QLineF &line) const
- {
- return distanceSquaredToLine(QCPVector2D(line.p1()), QCPVector2D(line.p2()));
- }
- double QCPVector2D::distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const
- {
- return qAbs((*this-base).dot(direction.perpendicular()))/direction.length();
- }
- QCPVector2D &QCPVector2D::operator*=(double factor)
- {
- mX *= factor;
- mY *= factor;
- return *this;
- }
- QCPVector2D &QCPVector2D::operator/=(double divisor)
- {
- mX /= divisor;
- mY /= divisor;
- return *this;
- }
- QCPVector2D &QCPVector2D::operator+=(const QCPVector2D &vector)
- {
- mX += vector.mX;
- mY += vector.mY;
- return *this;
- }
- QCPVector2D &QCPVector2D::operator-=(const QCPVector2D &vector)
- {
- mX -= vector.mX;
- mY -= vector.mY;
- return *this;
- }
- QCPPainter::QCPPainter() :
- mModes(pmDefault),
- mIsAntialiasing(false)
- {
-
-
- }
- QCPPainter::QCPPainter(QPaintDevice *device) :
- QPainter(device),
- mModes(pmDefault),
- mIsAntialiasing(false)
- {
- #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
- if (isActive())
- setRenderHint(QPainter::NonCosmeticDefaultPen);
- #endif
- }
- void QCPPainter::setPen(const QPen &pen)
- {
- QPainter::setPen(pen);
- if (mModes.testFlag(pmNonCosmetic))
- makeNonCosmetic();
- }
- void QCPPainter::setPen(const QColor &color)
- {
- QPainter::setPen(color);
- if (mModes.testFlag(pmNonCosmetic))
- makeNonCosmetic();
- }
- void QCPPainter::setPen(Qt::PenStyle penStyle)
- {
- QPainter::setPen(penStyle);
- if (mModes.testFlag(pmNonCosmetic))
- makeNonCosmetic();
- }
- void QCPPainter::drawLine(const QLineF &line)
- {
- if (mIsAntialiasing || mModes.testFlag(pmVectorized))
- QPainter::drawLine(line);
- else
- QPainter::drawLine(line.toLine());
- }
- void QCPPainter::setAntialiasing(bool enabled)
- {
- setRenderHint(QPainter::Antialiasing, enabled);
- if (mIsAntialiasing != enabled)
- {
- mIsAntialiasing = enabled;
- if (!mModes.testFlag(pmVectorized))
- {
- if (mIsAntialiasing)
- translate(0.5, 0.5);
- else
- translate(-0.5, -0.5);
- }
- }
- }
- void QCPPainter::setModes(QCPPainter::PainterModes modes)
- {
- mModes = modes;
- }
- bool QCPPainter::begin(QPaintDevice *device)
- {
- bool result = QPainter::begin(device);
- #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
- if (result)
- setRenderHint(QPainter::NonCosmeticDefaultPen);
- #endif
- return result;
- }
- void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled)
- {
- if (!enabled && mModes.testFlag(mode))
- mModes &= ~mode;
- else if (enabled && !mModes.testFlag(mode))
- mModes |= mode;
- }
- void QCPPainter::save()
- {
- mAntialiasingStack.push(mIsAntialiasing);
- QPainter::save();
- }
- void QCPPainter::restore()
- {
- if (!mAntialiasingStack.isEmpty())
- mIsAntialiasing = mAntialiasingStack.pop();
- else
- qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
- QPainter::restore();
- }
- void QCPPainter::makeNonCosmetic()
- {
- if (qFuzzyIsNull(pen().widthF()))
- {
- QPen p = pen();
- p.setWidth(1);
- QPainter::setPen(p);
- }
- }
- QCPAbstractPaintBuffer::QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio) :
- mSize(size),
- mDevicePixelRatio(devicePixelRatio),
- mInvalidated(true)
- {
- }
- QCPAbstractPaintBuffer::~QCPAbstractPaintBuffer()
- {
- }
- void QCPAbstractPaintBuffer::setSize(const QSize &size)
- {
- if (mSize != size)
- {
- mSize = size;
- reallocateBuffer();
- }
- }
- void QCPAbstractPaintBuffer::setInvalidated(bool invalidated)
- {
- mInvalidated = invalidated;
- }
- void QCPAbstractPaintBuffer::setDevicePixelRatio(double ratio)
- {
- if (!qFuzzyCompare(ratio, mDevicePixelRatio))
- {
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- mDevicePixelRatio = ratio;
- reallocateBuffer();
- #else
- qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
- mDevicePixelRatio = 1.0;
- #endif
- }
- }
- QCPPaintBufferPixmap::QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio) :
- QCPAbstractPaintBuffer(size, devicePixelRatio)
- {
- QCPPaintBufferPixmap::reallocateBuffer();
- }
- QCPPaintBufferPixmap::~QCPPaintBufferPixmap()
- {
- }
- QCPPainter *QCPPaintBufferPixmap::startPainting()
- {
- QCPPainter *result = new QCPPainter(&mBuffer);
- #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- result->setRenderHint(QPainter::HighQualityAntialiasing);
- #endif
- return result;
- }
- void QCPPaintBufferPixmap::draw(QCPPainter *painter) const
- {
- if (painter && painter->isActive())
- painter->drawPixmap(0, 0, mBuffer);
- else
- qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
- }
- void QCPPaintBufferPixmap::clear(const QColor &color)
- {
- mBuffer.fill(color);
- }
- void QCPPaintBufferPixmap::reallocateBuffer()
- {
- setInvalidated();
- if (!qFuzzyCompare(1.0, mDevicePixelRatio))
- {
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- mBuffer = QPixmap(mSize*mDevicePixelRatio);
- mBuffer.setDevicePixelRatio(mDevicePixelRatio);
- #else
- qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
- mDevicePixelRatio = 1.0;
- mBuffer = QPixmap(mSize);
- #endif
- } else
- {
- mBuffer = QPixmap(mSize);
- }
- }
- #ifdef QCP_OPENGL_PBUFFER
- QCPPaintBufferGlPbuffer::QCPPaintBufferGlPbuffer(const QSize &size, double devicePixelRatio, int multisamples) :
- QCPAbstractPaintBuffer(size, devicePixelRatio),
- mGlPBuffer(0),
- mMultisamples(qMax(0, multisamples))
- {
- QCPPaintBufferGlPbuffer::reallocateBuffer();
- }
- QCPPaintBufferGlPbuffer::~QCPPaintBufferGlPbuffer()
- {
- if (mGlPBuffer)
- delete mGlPBuffer;
- }
- QCPPainter *QCPPaintBufferGlPbuffer::startPainting()
- {
- if (!mGlPBuffer->isValid())
- {
- qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
- return 0;
- }
- QCPPainter *result = new QCPPainter(mGlPBuffer);
- result->setRenderHint(QPainter::HighQualityAntialiasing);
- return result;
- }
- void QCPPaintBufferGlPbuffer::draw(QCPPainter *painter) const
- {
- if (!painter || !painter->isActive())
- {
- qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
- return;
- }
- if (!mGlPBuffer->isValid())
- {
- qDebug() << Q_FUNC_INFO << "OpenGL pbuffer isn't valid, reallocateBuffer was not called?";
- return;
- }
- painter->drawImage(0, 0, mGlPBuffer->toImage());
- }
- void QCPPaintBufferGlPbuffer::clear(const QColor &color)
- {
- if (mGlPBuffer->isValid())
- {
- mGlPBuffer->makeCurrent();
- glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- mGlPBuffer->doneCurrent();
- } else
- qDebug() << Q_FUNC_INFO << "OpenGL pbuffer invalid or context not current";
- }
- void QCPPaintBufferGlPbuffer::reallocateBuffer()
- {
- if (mGlPBuffer)
- delete mGlPBuffer;
- QGLFormat format;
- format.setAlpha(true);
- format.setSamples(mMultisamples);
- mGlPBuffer = new QGLPixelBuffer(mSize, format);
- }
- #endif
- #ifdef QCP_OPENGL_FBO
- QCPPaintBufferGlFbo::QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer<QOpenGLContext> glContext, QWeakPointer<QOpenGLPaintDevice> glPaintDevice) :
- QCPAbstractPaintBuffer(size, devicePixelRatio),
- mGlContext(glContext),
- mGlPaintDevice(glPaintDevice),
- mGlFrameBuffer(0)
- {
- QCPPaintBufferGlFbo::reallocateBuffer();
- }
- QCPPaintBufferGlFbo::~QCPPaintBufferGlFbo()
- {
- if (mGlFrameBuffer)
- delete mGlFrameBuffer;
- }
- QCPPainter *QCPPaintBufferGlFbo::startPainting()
- {
- QSharedPointer<QOpenGLPaintDevice> paintDevice = mGlPaintDevice.toStrongRef();
- QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();
- if (!paintDevice)
- {
- qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
- return 0;
- }
- if (!context)
- {
- qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
- return 0;
- }
- if (!mGlFrameBuffer)
- {
- qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
- return 0;
- }
- if (QOpenGLContext::currentContext() != context.data())
- context->makeCurrent(context->surface());
- mGlFrameBuffer->bind();
- QCPPainter *result = new QCPPainter(paintDevice.data());
- #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- result->setRenderHint(QPainter::HighQualityAntialiasing);
- #endif
- return result;
- }
- void QCPPaintBufferGlFbo::donePainting()
- {
- if (mGlFrameBuffer && mGlFrameBuffer->isBound())
- mGlFrameBuffer->release();
- else
- qDebug() << Q_FUNC_INFO << "Either OpenGL frame buffer not valid or was not bound";
- }
- void QCPPaintBufferGlFbo::draw(QCPPainter *painter) const
- {
- if (!painter || !painter->isActive())
- {
- qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
- return;
- }
- if (!mGlFrameBuffer)
- {
- qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
- return;
- }
- painter->drawImage(0, 0, mGlFrameBuffer->toImage());
- }
- void QCPPaintBufferGlFbo::clear(const QColor &color)
- {
- QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();
- if (!context)
- {
- qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
- return;
- }
- if (!mGlFrameBuffer)
- {
- qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
- return;
- }
- if (QOpenGLContext::currentContext() != context.data())
- context->makeCurrent(context->surface());
- mGlFrameBuffer->bind();
- glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- mGlFrameBuffer->release();
- }
- void QCPPaintBufferGlFbo::reallocateBuffer()
- {
-
- if (mGlFrameBuffer)
- {
- if (mGlFrameBuffer->isBound())
- mGlFrameBuffer->release();
- delete mGlFrameBuffer;
- mGlFrameBuffer = 0;
- }
- QSharedPointer<QOpenGLPaintDevice> paintDevice = mGlPaintDevice.toStrongRef();
- QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();
- if (!paintDevice)
- {
- qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
- return;
- }
- if (!context)
- {
- qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
- return;
- }
-
- context->makeCurrent(context->surface());
- QOpenGLFramebufferObjectFormat frameBufferFormat;
- frameBufferFormat.setSamples(context->format().samples());
- frameBufferFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
- mGlFrameBuffer = new QOpenGLFramebufferObject(mSize*mDevicePixelRatio, frameBufferFormat);
- if (paintDevice->size() != mSize*mDevicePixelRatio)
- paintDevice->setSize(mSize*mDevicePixelRatio);
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- paintDevice->setDevicePixelRatio(mDevicePixelRatio);
- #endif
- }
- #endif
- QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
- QObject(parentPlot),
- mParentPlot(parentPlot),
- mName(layerName),
- mIndex(-1),
- mVisible(true),
- mMode(lmLogical)
- {
-
-
- }
- QCPLayer::~QCPLayer()
- {
-
-
-
-
- while (!mChildren.isEmpty())
- mChildren.last()->setLayer(nullptr);
- if (mParentPlot->currentLayer() == this)
- qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or nullptr beforehand.";
- }
- void QCPLayer::setVisible(bool visible)
- {
- mVisible = visible;
- }
- void QCPLayer::setMode(QCPLayer::LayerMode mode)
- {
- if (mMode != mode)
- {
- mMode = mode;
- if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())
- pb->setInvalidated();
- }
- }
- void QCPLayer::draw(QCPPainter *painter)
- {
- foreach (QCPLayerable *child, mChildren)
- {
- if (child->realVisibility())
- {
- painter->save();
-
- painter->setClipRect(child->clipRect().adjusted(0,0,1,1));
- child->applyDefaultAntialiasingHint(painter);
- child->draw(painter);
- painter->restore();
- }
- }
- }
- void QCPLayer::drawToPaintBuffer()
- {
- if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())
- {
- if (QCPPainter *painter = pb->startPainting())
- {
- if (painter->isActive())
- draw(painter);
- else
- qDebug() << Q_FUNC_INFO << "paint buffer returned inactive painter";
- delete painter;
- pb->donePainting();
- } else
- qDebug() << Q_FUNC_INFO << "paint buffer returned nullptr painter";
- } else
- qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer";
- }
- void QCPLayer::replot()
- {
- if (mMode == lmBuffered && !mParentPlot->hasInvalidatedPaintBuffers())
- {
- if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())
- {
- pb->clear(Qt::transparent);
- drawToPaintBuffer();
- pb->setInvalidated(false);
- mParentPlot->update();
- } else
- qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer";
- } else {
- mParentPlot->replot();
- }
- }
- void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
- {
- if (!mChildren.contains(layerable))
- {
- if (prepend)
- mChildren.prepend(layerable);
- else
- mChildren.append(layerable);
- if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())
- pb->setInvalidated();
- } else
- qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
- }
- void QCPLayer::removeChild(QCPLayerable *layerable)
- {
- if (mChildren.removeOne(layerable))
- {
- if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())
- pb->setInvalidated();
- } else
- qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
- }
- QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
- QObject(plot),
- mVisible(true),
- mParentPlot(plot),
- mParentLayerable(parentLayerable),
- mLayer(nullptr),
- mAntialiased(true)
- {
- if (mParentPlot)
- {
- if (targetLayer.isEmpty())
- setLayer(mParentPlot->currentLayer());
- else if (!setLayer(targetLayer))
- qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
- }
- }
- QCPLayerable::~QCPLayerable()
- {
- if (mLayer)
- {
- mLayer->removeChild(this);
- mLayer = nullptr;
- }
- }
- void QCPLayerable::setVisible(bool on)
- {
- mVisible = on;
- }
- bool QCPLayerable::setLayer(QCPLayer *layer)
- {
- return moveToLayer(layer, false);
- }
- bool QCPLayerable::setLayer(const QString &layerName)
- {
- if (!mParentPlot)
- {
- qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
- return false;
- }
- if (QCPLayer *layer = mParentPlot->layer(layerName))
- {
- return setLayer(layer);
- } else
- {
- qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
- return false;
- }
- }
- void QCPLayerable::setAntialiased(bool enabled)
- {
- mAntialiased = enabled;
- }
- bool QCPLayerable::realVisibility() const
- {
- return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
- }
- double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(pos)
- Q_UNUSED(onlySelectable)
- Q_UNUSED(details)
- return -1.0;
- }
- void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot)
- {
- if (mParentPlot)
- {
- qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
- return;
- }
- if (!parentPlot)
- qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
- mParentPlot = parentPlot;
- parentPlotInitialized(mParentPlot);
- }
- void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable)
- {
- mParentLayerable = parentLayerable;
- }
- bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
- {
- if (layer && !mParentPlot)
- {
- qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
- return false;
- }
- if (layer && layer->parentPlot() != mParentPlot)
- {
- qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
- return false;
- }
- QCPLayer *oldLayer = mLayer;
- if (mLayer)
- mLayer->removeChild(this);
- mLayer = layer;
- if (mLayer)
- mLayer->addChild(this, prepend);
- if (mLayer != oldLayer)
- emit layerChanged(mLayer);
- return true;
- }
- void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
- {
- if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
- painter->setAntialiasing(false);
- else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
- painter->setAntialiasing(true);
- else
- painter->setAntialiasing(localAntialiased);
- }
- void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot)
- {
- Q_UNUSED(parentPlot)
- }
- QCP::Interaction QCPLayerable::selectionCategory() const
- {
- return QCP::iSelectOther;
- }
- QRectF QCPLayerable::clipRect() const
- {
- if (mParentPlot)
- return mParentPlot->viewport();
- else
- return {};
- }
- void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
- {
- Q_UNUSED(event)
- Q_UNUSED(additive)
- Q_UNUSED(details)
- Q_UNUSED(selectionStateChanged)
- }
- void QCPLayerable::deselectEvent(bool *selectionStateChanged)
- {
- Q_UNUSED(selectionStateChanged)
- }
- void QCPLayerable::mousePressEvent(QMouseEvent *event, const QVariant &details)
- {
- Q_UNUSED(details)
- event->ignore();
- }
- void QCPLayerable::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
- {
- Q_UNUSED(startPos)
- event->ignore();
- }
- void QCPLayerable::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
- {
- Q_UNUSED(startPos)
- event->ignore();
- }
- void QCPLayerable::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
- {
- Q_UNUSED(details)
- event->ignore();
- }
- void QCPLayerable::wheelEvent(QWheelEvent *event)
- {
- event->ignore();
- }
- const double QCPRange::minRange = 1e-280;
- const double QCPRange::maxRange = 1e250;
- QCPRange::QCPRange() :
- lower(0),
- upper(0)
- {
- }
- QCPRange::QCPRange(double lower, double upper) :
- lower(lower),
- upper(upper)
- {
- normalize();
- }
- void QCPRange::expand(const QCPRange &otherRange)
- {
- if (lower > otherRange.lower || qIsNaN(lower))
- lower = otherRange.lower;
- if (upper < otherRange.upper || qIsNaN(upper))
- upper = otherRange.upper;
- }
- void QCPRange::expand(double includeCoord)
- {
- if (lower > includeCoord || qIsNaN(lower))
- lower = includeCoord;
- if (upper < includeCoord || qIsNaN(upper))
- upper = includeCoord;
- }
- QCPRange QCPRange::expanded(const QCPRange &otherRange) const
- {
- QCPRange result = *this;
- result.expand(otherRange);
- return result;
- }
- QCPRange QCPRange::expanded(double includeCoord) const
- {
- QCPRange result = *this;
- result.expand(includeCoord);
- return result;
- }
- QCPRange QCPRange::bounded(double lowerBound, double upperBound) const
- {
- if (lowerBound > upperBound)
- qSwap(lowerBound, upperBound);
- QCPRange result(lower, upper);
- if (result.lower < lowerBound)
- {
- result.lower = lowerBound;
- result.upper = lowerBound + size();
- if (result.upper > upperBound || qFuzzyCompare(size(), upperBound-lowerBound))
- result.upper = upperBound;
- } else if (result.upper > upperBound)
- {
- result.upper = upperBound;
- result.lower = upperBound - size();
- if (result.lower < lowerBound || qFuzzyCompare(size(), upperBound-lowerBound))
- result.lower = lowerBound;
- }
- return result;
- }
- QCPRange QCPRange::sanitizedForLogScale() const
- {
- double rangeFac = 1e-3;
- QCPRange sanitizedRange(lower, upper);
- sanitizedRange.normalize();
-
-
- if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
- {
-
- if (rangeFac < sanitizedRange.upper*rangeFac)
- sanitizedRange.lower = rangeFac;
- else
- sanitizedRange.lower = sanitizedRange.upper*rangeFac;
- }
- else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
- {
-
- if (-rangeFac > sanitizedRange.lower*rangeFac)
- sanitizedRange.upper = -rangeFac;
- else
- sanitizedRange.upper = sanitizedRange.lower*rangeFac;
- } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
- {
-
- if (-sanitizedRange.lower > sanitizedRange.upper)
- {
-
- if (-rangeFac > sanitizedRange.lower*rangeFac)
- sanitizedRange.upper = -rangeFac;
- else
- sanitizedRange.upper = sanitizedRange.lower*rangeFac;
- } else
- {
-
- if (rangeFac < sanitizedRange.upper*rangeFac)
- sanitizedRange.lower = rangeFac;
- else
- sanitizedRange.lower = sanitizedRange.upper*rangeFac;
- }
- }
-
- return sanitizedRange;
- }
- QCPRange QCPRange::sanitizedForLinScale() const
- {
- QCPRange sanitizedRange(lower, upper);
- sanitizedRange.normalize();
- return sanitizedRange;
- }
- bool QCPRange::validRange(double lower, double upper)
- {
- return (lower > -maxRange &&
- upper < maxRange &&
- qAbs(lower-upper) > minRange &&
- qAbs(lower-upper) < maxRange &&
- !(lower > 0 && qIsInf(upper/lower)) &&
- !(upper < 0 && qIsInf(lower/upper)));
- }
- bool QCPRange::validRange(const QCPRange &range)
- {
- return (range.lower > -maxRange &&
- range.upper < maxRange &&
- qAbs(range.lower-range.upper) > minRange &&
- qAbs(range.lower-range.upper) < maxRange &&
- !(range.lower > 0 && qIsInf(range.upper/range.lower)) &&
- !(range.upper < 0 && qIsInf(range.lower/range.upper)));
- }
- QCPDataRange::QCPDataRange() :
- mBegin(0),
- mEnd(0)
- {
- }
- QCPDataRange::QCPDataRange(int begin, int end) :
- mBegin(begin),
- mEnd(end)
- {
- }
- QCPDataRange QCPDataRange::bounded(const QCPDataRange &other) const
- {
- QCPDataRange result(intersection(other));
- if (result.isEmpty())
- {
- if (mEnd <= other.mBegin)
- result = QCPDataRange(other.mBegin, other.mBegin);
- else
- result = QCPDataRange(other.mEnd, other.mEnd);
- }
- return result;
- }
- QCPDataRange QCPDataRange::expanded(const QCPDataRange &other) const
- {
- return {qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd)};
- }
- QCPDataRange QCPDataRange::intersection(const QCPDataRange &other) const
- {
- QCPDataRange result(qMax(mBegin, other.mBegin), qMin(mEnd, other.mEnd));
- if (result.isValid())
- return result;
- else
- return {};
- }
- bool QCPDataRange::intersects(const QCPDataRange &other) const
- {
- return !( (mBegin > other.mBegin && mBegin >= other.mEnd) ||
- (mEnd <= other.mBegin && mEnd < other.mEnd) );
- }
- bool QCPDataRange::contains(const QCPDataRange &other) const
- {
- return mBegin <= other.mBegin && mEnd >= other.mEnd;
- }
- QCPDataSelection::QCPDataSelection()
- {
- }
- QCPDataSelection::QCPDataSelection(const QCPDataRange &range)
- {
- mDataRanges.append(range);
- }
- bool QCPDataSelection::operator==(const QCPDataSelection &other) const
- {
- if (mDataRanges.size() != other.mDataRanges.size())
- return false;
- for (int i=0; i<mDataRanges.size(); ++i)
- {
- if (mDataRanges.at(i) != other.mDataRanges.at(i))
- return false;
- }
- return true;
- }
- QCPDataSelection &QCPDataSelection::operator+=(const QCPDataSelection &other)
- {
- mDataRanges << other.mDataRanges;
- simplify();
- return *this;
- }
- QCPDataSelection &QCPDataSelection::operator+=(const QCPDataRange &other)
- {
- addDataRange(other);
- return *this;
- }
- QCPDataSelection &QCPDataSelection::operator-=(const QCPDataSelection &other)
- {
- for (int i=0; i<other.dataRangeCount(); ++i)
- *this -= other.dataRange(i);
- return *this;
- }
- QCPDataSelection &QCPDataSelection::operator-=(const QCPDataRange &other)
- {
- if (other.isEmpty() || isEmpty())
- return *this;
- simplify();
- int i=0;
- while (i < mDataRanges.size())
- {
- const int thisBegin = mDataRanges.at(i).begin();
- const int thisEnd = mDataRanges.at(i).end();
- if (thisBegin >= other.end())
- break;
- if (thisEnd > other.begin())
- {
- if (thisBegin >= other.begin())
- {
- if (thisEnd <= other.end())
- {
- mDataRanges.removeAt(i);
- continue;
- } else
- mDataRanges[i].setBegin(other.end());
- } else
- {
- if (thisEnd <= other.end())
- {
- mDataRanges[i].setEnd(other.begin());
- } else
- {
- mDataRanges[i].setEnd(other.begin());
- mDataRanges.insert(i+1, QCPDataRange(other.end(), thisEnd));
- break;
- }
- }
- }
- ++i;
- }
- return *this;
- }
- int QCPDataSelection::dataPointCount() const
- {
- int result = 0;
- foreach (QCPDataRange dataRange, mDataRanges)
- result += dataRange.length();
- return result;
- }
- QCPDataRange QCPDataSelection::dataRange(int index) const
- {
- if (index >= 0 && index < mDataRanges.size())
- {
- return mDataRanges.at(index);
- } else
- {
- qDebug() << Q_FUNC_INFO << "index out of range:" << index;
- return {};
- }
- }
- QCPDataRange QCPDataSelection::span() const
- {
- if (isEmpty())
- return {};
- else
- return {mDataRanges.first().begin(), mDataRanges.last().end()};
- }
- void QCPDataSelection::addDataRange(const QCPDataRange &dataRange, bool simplify)
- {
- mDataRanges.append(dataRange);
- if (simplify)
- this->simplify();
- }
- void QCPDataSelection::clear()
- {
- mDataRanges.clear();
- }
- void QCPDataSelection::simplify()
- {
-
- for (int i=mDataRanges.size()-1; i>=0; --i)
- {
- if (mDataRanges.at(i).isEmpty())
- mDataRanges.removeAt(i);
- }
- if (mDataRanges.isEmpty())
- return;
-
- std::sort(mDataRanges.begin(), mDataRanges.end(), lessThanDataRangeBegin);
-
- int i = 1;
- while (i < mDataRanges.size())
- {
- if (mDataRanges.at(i-1).end() >= mDataRanges.at(i).begin())
- {
- mDataRanges[i-1].setEnd(qMax(mDataRanges.at(i-1).end(), mDataRanges.at(i).end()));
- mDataRanges.removeAt(i);
- } else
- ++i;
- }
- }
- void QCPDataSelection::enforceType(QCP::SelectionType type)
- {
- simplify();
- switch (type)
- {
- case QCP::stNone:
- {
- mDataRanges.clear();
- break;
- }
- case QCP::stWhole:
- {
-
- break;
- }
- case QCP::stSingleData:
- {
-
- if (!mDataRanges.isEmpty())
- {
- if (mDataRanges.size() > 1)
- mDataRanges = QList<QCPDataRange>() << mDataRanges.first();
- if (mDataRanges.first().length() > 1)
- mDataRanges.first().setEnd(mDataRanges.first().begin()+1);
- }
- break;
- }
- case QCP::stDataRange:
- {
- if (!isEmpty())
- mDataRanges = QList<QCPDataRange>() << span();
- break;
- }
- case QCP::stMultipleDataRanges:
- {
-
- break;
- }
- }
- }
- bool QCPDataSelection::contains(const QCPDataSelection &other) const
- {
- if (other.isEmpty()) return false;
- int otherIndex = 0;
- int thisIndex = 0;
- while (thisIndex < mDataRanges.size() && otherIndex < other.mDataRanges.size())
- {
- if (mDataRanges.at(thisIndex).contains(other.mDataRanges.at(otherIndex)))
- ++otherIndex;
- else
- ++thisIndex;
- }
- return thisIndex < mDataRanges.size();
- }
- QCPDataSelection QCPDataSelection::intersection(const QCPDataRange &other) const
- {
- QCPDataSelection result;
- foreach (QCPDataRange dataRange, mDataRanges)
- result.addDataRange(dataRange.intersection(other), false);
- result.simplify();
- return result;
- }
- QCPDataSelection QCPDataSelection::intersection(const QCPDataSelection &other) const
- {
- QCPDataSelection result;
- for (int i=0; i<other.dataRangeCount(); ++i)
- result += intersection(other.dataRange(i));
- result.simplify();
- return result;
- }
- QCPDataSelection QCPDataSelection::inverse(const QCPDataRange &outerRange) const
- {
- if (isEmpty())
- return QCPDataSelection(outerRange);
- QCPDataRange fullRange = outerRange.expanded(span());
- QCPDataSelection result;
-
- if (mDataRanges.first().begin() != fullRange.begin())
- result.addDataRange(QCPDataRange(fullRange.begin(), mDataRanges.first().begin()), false);
-
- for (int i=1; i<mDataRanges.size(); ++i)
- result.addDataRange(QCPDataRange(mDataRanges.at(i-1).end(), mDataRanges.at(i).begin()), false);
-
- if (mDataRanges.last().end() != fullRange.end())
- result.addDataRange(QCPDataRange(mDataRanges.last().end(), fullRange.end()), false);
- result.simplify();
- return result;
- }
- QCPSelectionRect::QCPSelectionRect(QCustomPlot *parentPlot) :
- QCPLayerable(parentPlot),
- mPen(QBrush(Qt::gray), 0, Qt::DashLine),
- mBrush(Qt::NoBrush),
- mActive(false)
- {
- }
- QCPSelectionRect::~QCPSelectionRect()
- {
- cancel();
- }
- QCPRange QCPSelectionRect::range(const QCPAxis *axis) const
- {
- if (axis)
- {
- if (axis->orientation() == Qt::Horizontal)
- return {axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width())};
- else
- return {axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top())};
- } else
- {
- qDebug() << Q_FUNC_INFO << "called with axis zero";
- return {};
- }
- }
- void QCPSelectionRect::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPSelectionRect::setBrush(const QBrush &brush)
- {
- mBrush = brush;
- }
- void QCPSelectionRect::cancel()
- {
- if (mActive)
- {
- mActive = false;
- emit canceled(mRect, nullptr);
- }
- }
- void QCPSelectionRect::startSelection(QMouseEvent *event)
- {
- mActive = true;
- mRect = QRect(event->pos(), event->pos());
- emit started(event);
- }
- void QCPSelectionRect::moveSelection(QMouseEvent *event)
- {
- mRect.setBottomRight(event->pos());
- emit changed(mRect, event);
- layer()->replot();
- }
- void QCPSelectionRect::endSelection(QMouseEvent *event)
- {
- mRect.setBottomRight(event->pos());
- mActive = false;
- emit accepted(mRect, event);
- }
- void QCPSelectionRect::keyPressEvent(QKeyEvent *event)
- {
- if (event->key() == Qt::Key_Escape && mActive)
- {
- mActive = false;
- emit canceled(mRect, event);
- }
- }
- void QCPSelectionRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aeOther);
- }
- void QCPSelectionRect::draw(QCPPainter *painter)
- {
- if (mActive)
- {
- painter->setPen(mPen);
- painter->setBrush(mBrush);
- painter->drawRect(mRect);
- }
- }
- QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot) :
- QObject(parentPlot),
- mParentPlot(parentPlot)
- {
- mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
- mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
- mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
- mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
- }
- QCPMarginGroup::~QCPMarginGroup()
- {
- clear();
- }
- bool QCPMarginGroup::isEmpty() const
- {
- QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
- while (it.hasNext())
- {
- it.next();
- if (!it.value().isEmpty())
- return false;
- }
- return true;
- }
- void QCPMarginGroup::clear()
- {
-
- QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
- while (it.hasNext())
- {
- it.next();
- const QList<QCPLayoutElement*> elements = it.value();
- for (int i=elements.size()-1; i>=0; --i)
- elements.at(i)->setMarginGroup(it.key(), nullptr);
- }
- }
- double QCPMarginGroup::commonMargin(QCP::MarginSide side) const
- {
-
- double result = 0;
- foreach (QCPLayoutElement *el, mChildren.value(side))
- {
- if (!el->autoMargins().testFlag(side))
- continue;
- double m = qMax(el->calculateAutoMargin(side), QCP::getMarginValue(el->minimumMargins(), side));
- if (m > result)
- result = m;
- }
- return result;
- }
- void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element)
- {
- if (!mChildren[side].contains(element))
- mChildren[side].append(element);
- else
- qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
- }
- void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element)
- {
- if (!mChildren[side].removeOne(element))
- qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
- }
- QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) :
- QCPLayerable(parentPlot),
- mParentLayout(nullptr),
- mMinimumSize(),
- mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
- mSizeConstraintRect(scrInnerRect),
- mRect(0, 0, 0, 0),
- mOuterRect(0, 0, 0, 0),
- mMargins(0, 0, 0, 0),
- mMinimumMargins(0, 0, 0, 0),
- mAutoMargins(QCP::msAll)
- {
- }
- QCPLayoutElement::~QCPLayoutElement()
- {
- setMarginGroup(QCP::msAll, nullptr);
-
- if (qobject_cast<QCPLayout*>(mParentLayout))
- mParentLayout->take(this);
- }
- void QCPLayoutElement::setOuterRect(const QRectF &rect)
- {
- if (mOuterRect != rect)
- {
- mOuterRect = rect;
- mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
- }
- }
- void QCPLayoutElement::setMargins(const QMarginsF &margins)
- {
- if (mMargins != margins)
- {
- mMargins = margins;
- mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
- }
- }
- void QCPLayoutElement::setMinimumMargins(const QMarginsF &margins)
- {
- if (mMinimumMargins != margins)
- {
- mMinimumMargins = margins;
- }
- }
- void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
- {
- mAutoMargins = sides;
- }
- void QCPLayoutElement::setMinimumSize(const QSizeF &size)
- {
- if (mMinimumSize != size)
- {
- mMinimumSize = size;
- if (mParentLayout)
- mParentLayout->sizeConstraintsChanged();
- }
- }
- void QCPLayoutElement::setMinimumSize(double width, double height)
- {
- setMinimumSize(QSizeF(width, height));
- }
- void QCPLayoutElement::setMaximumSize(const QSizeF &size)
- {
- if (mMaximumSize != size)
- {
- mMaximumSize = size;
- if (mParentLayout)
- mParentLayout->sizeConstraintsChanged();
- }
- }
- void QCPLayoutElement::setMaximumSize(double width, double height)
- {
- setMaximumSize(QSizeF(width, height));
- }
- void QCPLayoutElement::setSizeConstraintRect(SizeConstraintRect constraintRect)
- {
- if (mSizeConstraintRect != constraintRect)
- {
- mSizeConstraintRect = constraintRect;
- if (mParentLayout)
- mParentLayout->sizeConstraintsChanged();
- }
- }
- void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
- {
- QVector<QCP::MarginSide> sideVector;
- if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
- if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
- if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
- if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
- foreach (QCP::MarginSide side, sideVector)
- {
- if (marginGroup(side) != group)
- {
- QCPMarginGroup *oldGroup = marginGroup(side);
- if (oldGroup)
- oldGroup->removeChild(side, this);
- if (!group)
- {
- mMarginGroups.remove(side);
- } else
- {
- mMarginGroups[side] = group;
- group->addChild(side, this);
- }
- }
- }
- }
- void QCPLayoutElement::update(UpdatePhase phase)
- {
- if (phase == upMargins)
- {
- if (mAutoMargins != QCP::msNone)
- {
-
- QMarginsF newMargins = mMargins;
- const QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
- foreach (QCP::MarginSide side, allMarginSides)
- {
- if (mAutoMargins.testFlag(side))
- {
- if (mMarginGroups.contains(side))
- QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side));
- else
- QCP::setMarginValue(newMargins, side, calculateAutoMargin(side));
-
- if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
- QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
- }
- }
- setMargins(newMargins);
- }
- }
- }
- QSizeF QCPLayoutElement::minimumOuterSizeHint() const
- {
- return {mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom()};
- }
- QSizeF QCPLayoutElement::maximumOuterSizeHint() const
- {
- return {QWIDGETSIZE_MAX, QWIDGETSIZE_MAX};
- }
- QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
- {
- Q_UNUSED(recursive)
- return QList<QCPLayoutElement*>();
- }
- double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable)
- return -1;
- if (QRectF(mOuterRect).contains(pos))
- {
- if (mParentPlot)
- return mParentPlot->selectionTolerance()*0.99;
- else
- {
- qDebug() << Q_FUNC_INFO << "parent plot not defined";
- return -1;
- }
- } else
- return -1;
- }
- void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
- {
- foreach (QCPLayoutElement* el, elements(false))
- {
- if (!el->parentPlot())
- el->initializeParentPlot(parentPlot);
- }
- }
- double QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side)
- {
- return qMax(QCP::getMarginValue(mMargins, side), QCP::getMarginValue(mMinimumMargins, side));
- }
- void QCPLayoutElement::layoutChanged()
- {
- }
- QCPLayout::QCPLayout()
- {
- }
- void QCPLayout::update(UpdatePhase phase)
- {
- QCPLayoutElement::update(phase);
-
- if (phase == upLayout)
- updateLayout();
-
- const int elCount = elementCount();
- for (int i=0; i<elCount; ++i)
- {
- if (QCPLayoutElement *el = elementAt(i))
- el->update(phase);
- }
- }
- QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
- {
- const int c = elementCount();
- QList<QCPLayoutElement*> result;
- #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
- result.reserve(c);
- #endif
- for (int i=0; i<c; ++i)
- result.append(elementAt(i));
- if (recursive)
- {
- for (int i=0; i<c; ++i)
- {
- if (result.at(i))
- result << result.at(i)->elements(recursive);
- }
- }
- return result;
- }
- void QCPLayout::simplify()
- {
- }
- bool QCPLayout::removeAt(int index)
- {
- if (QCPLayoutElement *el = takeAt(index))
- {
- delete el;
- return true;
- } else
- return false;
- }
- bool QCPLayout::remove(QCPLayoutElement *element)
- {
- if (take(element))
- {
- delete element;
- return true;
- } else
- return false;
- }
- void QCPLayout::clear()
- {
- for (int i=elementCount()-1; i>=0; --i)
- {
- if (elementAt(i))
- removeAt(i);
- }
- simplify();
- }
- void QCPLayout::sizeConstraintsChanged() const
- {
- if (QWidget *w = qobject_cast<QWidget*>(parent()))
- w->updateGeometry();
- else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
- l->sizeConstraintsChanged();
- }
- void QCPLayout::updateLayout()
- {
- }
- void QCPLayout::adoptElement(QCPLayoutElement *el)
- {
- if (el)
- {
- el->mParentLayout = this;
- el->setParentLayerable(this);
- el->setParent(this);
- if (!el->parentPlot())
- el->initializeParentPlot(mParentPlot);
- el->layoutChanged();
- } else
- qDebug() << Q_FUNC_INFO << "Null element passed";
- }
- void QCPLayout::releaseElement(QCPLayoutElement *el)
- {
- if (el)
- {
- el->mParentLayout = nullptr;
- el->setParentLayerable(nullptr);
- el->setParent(mParentPlot);
-
- } else
- qDebug() << Q_FUNC_INFO << "Null element passed";
- }
- QVector<double> QCPLayout::getSectionSizes(QVector<double> maxSizes, QVector<double> minSizes, QVector<double> stretchFactors, double totalSize) const
- {
- if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
- {
- qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
- return QVector<double>();
- }
- if (stretchFactors.isEmpty())
- return QVector<double>();
- int sectionCount = stretchFactors.size();
- QVector<double> sectionSizes(sectionCount);
-
- double minSizeSum = 0;
- for (int i=0; i<sectionCount; ++i)
- minSizeSum += minSizes.at(i);
- if (totalSize < minSizeSum)
- {
-
- for (int i=0; i<sectionCount; ++i)
- {
- stretchFactors[i] = minSizes.at(i);
- minSizes[i] = 0;
- }
- }
- QList<int> minimumLockedSections;
- QList<int> unfinishedSections;
- for (int i=0; i<sectionCount; ++i)
- unfinishedSections.append(i);
- double freeSize = totalSize;
- int outerIterations = 0;
- while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2)
- {
- ++outerIterations;
- int innerIterations = 0;
- while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2)
- {
- ++innerIterations;
-
- int nextId = -1;
- double nextMax = 1e12;
- foreach (int secId, unfinishedSections)
- {
- double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
- if (hitsMaxAt < nextMax)
- {
- nextMax = hitsMaxAt;
- nextId = secId;
- }
- }
-
-
- double stretchFactorSum = 0;
- foreach (int secId, unfinishedSections)
- stretchFactorSum += stretchFactors.at(secId);
- double nextMaxLimit = freeSize/stretchFactorSum;
- if (nextMax < nextMaxLimit)
- {
- foreach (int secId, unfinishedSections)
- {
- sectionSizes[secId] += nextMax*stretchFactors.at(secId);
- freeSize -= nextMax*stretchFactors.at(secId);
- }
- unfinishedSections.removeOne(nextId);
- } else
- {
- foreach (int secId, unfinishedSections)
- sectionSizes[secId] += nextMaxLimit*stretchFactors.at(secId);
- unfinishedSections.clear();
- }
- }
- if (innerIterations == sectionCount*2)
- qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
-
- bool foundMinimumViolation = false;
- for (int i=0; i<sectionSizes.size(); ++i)
- {
- if (minimumLockedSections.contains(i))
- continue;
- if (sectionSizes.at(i) < minSizes.at(i))
- {
- sectionSizes[i] = minSizes.at(i);
- foundMinimumViolation = true;
- minimumLockedSections.append(i);
- }
- }
- if (foundMinimumViolation)
- {
- freeSize = totalSize;
- for (int i=0; i<sectionCount; ++i)
- {
- if (!minimumLockedSections.contains(i))
- unfinishedSections.append(i);
- else
- freeSize -= sectionSizes.at(i);
- }
-
- foreach (int secId, unfinishedSections)
- sectionSizes[secId] = 0;
- }
- }
- if (outerIterations == sectionCount*2)
- qDebug() << Q_FUNC_INFO << "Exceeded maximum expec ted outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
-
-
-
-
- return sectionSizes;
- }
- QSizeF QCPLayout::getFinalMinimumOuterSize(const QCPLayoutElement *el)
- {
- QSizeF minOuterHint = el->minimumOuterSizeHint();
- QSizeF minOuter = el->minimumSize();
- if (minOuter.width() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
- minOuter.rwidth() += el->margins().left() + el->margins().right();
- if (minOuter.height() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
- minOuter.rheight() += el->margins().top() + el->margins().bottom();
- return {minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(),
- minOuter.height() > 0 ? minOuter.height() : minOuterHint.height()};
- }
-
- QSizeF QCPLayout::getFinalMaximumOuterSize(const QCPLayoutElement *el)
- {
- QSizeF maxOuterHint = el->maximumOuterSizeHint();
- QSizeF maxOuter = el->maximumSize();
- if (maxOuter.width() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
- maxOuter.rwidth() += el->margins().left() + el->margins().right();
- if (maxOuter.height() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
- maxOuter.rheight() += el->margins().top() + el->margins().bottom();
- return {maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width() : maxOuterHint.width(),
- maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height() : maxOuterHint.height()};
- }
-
-
-
-
-
-
-
-
-
- QCPLayoutGrid::QCPLayoutGrid() :
- mColumnSpacing(5),
- mRowSpacing(5),
- mWrap(0),
- mFillOrder(foColumnsFirst)
- {
- }
- QCPLayoutGrid::~QCPLayoutGrid()
- {
-
-
- clear();
- }
- QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
- {
- if (row >= 0 && row < mElements.size())
- {
- if (column >= 0 && column < mElements.first().size())
- {
- if (QCPLayoutElement *result = mElements.at(row).at(column))
- return result;
- else
- qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
- } else
- qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
- } else
- qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
- return nullptr;
- }
- bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)
- {
- if (!hasElement(row, column))
- {
- if (element && element->layout())
- element->layout()->take(element);
- expandTo(row+1, column+1);
- mElements[row][column] = element;
- if (element)
- adoptElement(element);
- return true;
- } else
- qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
- return false;
- }
- bool QCPLayoutGrid::addElement(QCPLayoutElement *element)
- {
- int rowIndex = 0;
- int colIndex = 0;
- if (mFillOrder == foColumnsFirst)
- {
- while (hasElement(rowIndex, colIndex))
- {
- ++colIndex;
- if (colIndex >= mWrap && mWrap > 0)
- {
- colIndex = 0;
- ++rowIndex;
- }
- }
- } else
- {
- while (hasElement(rowIndex, colIndex))
- {
- ++rowIndex;
- if (rowIndex >= mWrap && mWrap > 0)
- {
- rowIndex = 0;
- ++colIndex;
- }
- }
- }
- return addElement(rowIndex, colIndex, element);
- }
- bool QCPLayoutGrid::hasElement(int row, int column)
- {
- if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
- return mElements.at(row).at(column);
- else
- return false;
- }
- void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
- {
- if (column >= 0 && column < columnCount())
- {
- if (factor > 0)
- mColumnStretchFactors[column] = factor;
- else
- qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
- } else
- qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
- }
- void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
- {
- if (factors.size() == mColumnStretchFactors.size())
- {
- mColumnStretchFactors = factors;
- for (int i=0; i<mColumnStretchFactors.size(); ++i)
- {
- if (mColumnStretchFactors.at(i) <= 0)
- {
- qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
- mColumnStretchFactors[i] = 1;
- }
- }
- } else
- qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
- }
- void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
- {
- if (row >= 0 && row < rowCount())
- {
- if (factor > 0)
- mRowStretchFactors[row] = factor;
- else
- qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
- } else
- qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
- }
- void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
- {
- if (factors.size() == mRowStretchFactors.size())
- {
- mRowStretchFactors = factors;
- for (int i=0; i<mRowStretchFactors.size(); ++i)
- {
- if (mRowStretchFactors.at(i) <= 0)
- {
- qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
- mRowStretchFactors[i] = 1;
- }
- }
- } else
- qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
- }
- void QCPLayoutGrid::setColumnSpacing(int pixels)
- {
- mColumnSpacing = pixels;
- }
- void QCPLayoutGrid::setRowSpacing(int pixels)
- {
- mRowSpacing = pixels;
- }
- void QCPLayoutGrid::setWrap(int count)
- {
- mWrap = qMax(0, count);
- }
- void QCPLayoutGrid::setFillOrder(FillOrder order, bool rearrange)
- {
-
- const int elCount = elementCount();
- QVector<QCPLayoutElement*> tempElements;
- if (rearrange)
- {
- tempElements.reserve(elCount);
- for (int i=0; i<elCount; ++i)
- {
- if (elementAt(i))
- tempElements.append(takeAt(i));
- }
- simplify();
- }
-
- mFillOrder = order;
-
- if (rearrange)
- {
- foreach (QCPLayoutElement *tempElement, tempElements)
- addElement(tempElement);
- }
- }
- void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
- {
-
- while (rowCount() < newRowCount)
- {
- mElements.append(QList<QCPLayoutElement*>());
- mRowStretchFactors.append(1);
- }
-
- int newColCount = qMax(columnCount(), newColumnCount);
- for (int i=0; i<rowCount(); ++i)
- {
- while (mElements.at(i).size() < newColCount)
- mElements[i].append(nullptr);
- }
- while (mColumnStretchFactors.size() < newColCount)
- mColumnStretchFactors.append(1);
- }
- void QCPLayoutGrid::insertRow(int newIndex)
- {
- if (mElements.isEmpty() || mElements.first().isEmpty())
- {
- expandTo(1, 1);
- return;
- }
- if (newIndex < 0)
- newIndex = 0;
- if (newIndex > rowCount())
- newIndex = rowCount();
- mRowStretchFactors.insert(newIndex, 1);
- QList<QCPLayoutElement*> newRow;
- for (int col=0; col<columnCount(); ++col)
- newRow.append(nullptr);
- mElements.insert(newIndex, newRow);
- }
- void QCPLayoutGrid::insertColumn(int newIndex)
- {
- if (mElements.isEmpty() || mElements.first().isEmpty())
- {
- expandTo(1, 1);
- return;
- }
- if (newIndex < 0)
- newIndex = 0;
- if (newIndex > columnCount())
- newIndex = columnCount();
- mColumnStretchFactors.insert(newIndex, 1);
- for (int row=0; row<rowCount(); ++row)
- mElements[row].insert(newIndex, nullptr);
- }
- int QCPLayoutGrid::rowColToIndex(int row, int column) const
- {
- if (row >= 0 && row < rowCount())
- {
- if (column >= 0 && column < columnCount())
- {
- switch (mFillOrder)
- {
- case foRowsFirst: return column*rowCount() + row;
- case foColumnsFirst: return row*columnCount() + column;
- }
- } else
- qDebug() << Q_FUNC_INFO << "row index out of bounds:" << row;
- } else
- qDebug() << Q_FUNC_INFO << "column index out of bounds:" << column;
- return 0;
- }
- void QCPLayoutGrid::indexToRowCol(int index, int &row, int &column) const
- {
- row = -1;
- column = -1;
- const int nCols = columnCount();
- const int nRows = rowCount();
- if (nCols == 0 || nRows == 0)
- return;
- if (index < 0 || index >= elementCount())
- {
- qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
- return;
- }
- switch (mFillOrder)
- {
- case foRowsFirst:
- {
- column = index / nRows;
- row = index % nRows;
- break;
- }
- case foColumnsFirst:
- {
- row = index / nCols;
- column = index % nCols;
- break;
- }
- }
- }
- void QCPLayoutGrid::updateLayout()
- {
- QVector<double> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
- getMinimumRowColSizes(&minColWidths, &minRowHeights);
- getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
- int totalRowSpacing = (rowCount()-1) * mRowSpacing;
- int totalColSpacing = (columnCount()-1) * mColumnSpacing;
- QVector<double> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
- QVector<double> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
-
- double yOffset = mRect.top();
- for (int row=0; row<rowCount(); ++row)
- {
- if (row > 0)
- yOffset += rowHeights.at(row-1)+mRowSpacing;
- double xOffset = mRect.left();
- for (int col=0; col<columnCount(); ++col)
- {
- if (col > 0)
- xOffset += colWidths.at(col-1)+mColumnSpacing;
- if (mElements.at(row).at(col))
- mElements.at(row).at(col)->setOuterRect(QRectF(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
- }
- }
- }
- QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const
- {
- if (index >= 0 && index < elementCount())
- {
- int row, col;
- indexToRowCol(index, row, col);
- return mElements.at(row).at(col);
- } else
- return nullptr;
- }
- QCPLayoutElement *QCPLayoutGrid::takeAt(int index)
- {
- if (QCPLayoutElement *el = elementAt(index))
- {
- releaseElement(el);
- int row, col;
- indexToRowCol(index, row, col);
- mElements[row][col] = nullptr;
- return el;
- } else
- {
- qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
- return nullptr;
- }
- }
- bool QCPLayoutGrid::take(QCPLayoutElement *element)
- {
- if (element)
- {
- for (int i=0; i<elementCount(); ++i)
- {
- if (elementAt(i) == element)
- {
- takeAt(i);
- return true;
- }
- }
- qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
- } else
- qDebug() << Q_FUNC_INFO << "Can't take nullptr element";
- return false;
- }
- QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
- {
- QList<QCPLayoutElement*> result;
- const int elCount = elementCount();
- #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
- result.reserve(elCount);
- #endif
- for (int i=0; i<elCount; ++i)
- result.append(elementAt(i));
- if (recursive)
- {
- for (int i=0; i<elCount; ++i)
- {
- if (result.at(i))
- result << result.at(i)->elements(recursive);
- }
- }
- return result;
- }
- void QCPLayoutGrid::simplify()
- {
-
- for (int row=rowCount()-1; row>=0; --row)
- {
- bool hasElements = false;
- for (int col=0; col<columnCount(); ++col)
- {
- if (mElements.at(row).at(col))
- {
- hasElements = true;
- break;
- }
- }
- if (!hasElements)
- {
- mRowStretchFactors.removeAt(row);
- mElements.removeAt(row);
- if (mElements.isEmpty())
- mColumnStretchFactors.clear();
- }
- }
-
- for (int col=columnCount()-1; col>=0; --col)
- {
- bool hasElements = false;
- for (int row=0; row<rowCount(); ++row)
- {
- if (mElements.at(row).at(col))
- {
- hasElements = true;
- break;
- }
- }
- if (!hasElements)
- {
- mColumnStretchFactors.removeAt(col);
- for (int row=0; row<rowCount(); ++row)
- mElements[row].removeAt(col);
- }
- }
- }
- QSizeF QCPLayoutGrid::minimumOuterSizeHint() const
- {
- QVector<double> minColWidths, minRowHeights;
- getMinimumRowColSizes(&minColWidths, &minRowHeights);
- QSizeF result(0, 0);
- foreach (double w, minColWidths)
- result.rwidth() += w;
- foreach (double h, minRowHeights)
- result.rheight() += h;
- result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;
- result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;
- result.rwidth() += mMargins.left()+mMargins.right();
- result.rheight() += mMargins.top()+mMargins.bottom();
- return result;
- }
- QSizeF QCPLayoutGrid::maximumOuterSizeHint() const
- {
- QVector<double> maxColWidths, maxRowHeights;
- getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
- QSizeF result(0, 0);
- foreach (double w, maxColWidths)
- result.setWidth(qMin(double(result.width())+w, double(QWIDGETSIZE_MAX)));
- foreach (double h, maxRowHeights)
- result.setHeight(qMin(double(result.height())+h, double(QWIDGETSIZE_MAX)));
- result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;
- result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;
- result.rwidth() += mMargins.left()+mMargins.right();
- result.rheight() += mMargins.top()+mMargins.bottom();
- if (result.height() > QWIDGETSIZE_MAX)
- result.setHeight(QWIDGETSIZE_MAX);
- if (result.width() > QWIDGETSIZE_MAX)
- result.setWidth(QWIDGETSIZE_MAX);
- return result;
- }
- void QCPLayoutGrid::getMinimumRowColSizes(QVector<double> *minColWidths, QVector<double> *minRowHeights) const
- {
- *minColWidths = QVector<double>(columnCount(), 0);
- *minRowHeights = QVector<double>(rowCount(), 0);
- for (int row=0; row<rowCount(); ++row)
- {
- for (int col=0; col<columnCount(); ++col)
- {
- if (QCPLayoutElement *el = mElements.at(row).at(col))
- {
- QSizeF minSize = getFinalMinimumOuterSize(el);
- if (minColWidths->at(col) < minSize.width())
- (*minColWidths)[col] = minSize.width();
- if (minRowHeights->at(row) < minSize.height())
- (*minRowHeights)[row] = minSize.height();
- }
- }
- }
- }
- void QCPLayoutGrid::getMaximumRowColSizes(QVector<double> *maxColWidths, QVector<double> *maxRowHeights) const
- {
- *maxColWidths = QVector<double>(columnCount(), QWIDGETSIZE_MAX);
- *maxRowHeights = QVector<double>(rowCount(), QWIDGETSIZE_MAX);
- for (int row=0; row<rowCount(); ++row)
- {
- for (int col=0; col<columnCount(); ++col)
- {
- if (QCPLayoutElement *el = mElements.at(row).at(col))
- {
- QSizeF maxSize = getFinalMaximumOuterSize(el);
- if (maxColWidths->at(col) > maxSize.width())
- (*maxColWidths)[col] = maxSize.width();
- if (maxRowHeights->at(row) > maxSize.height())
- (*maxRowHeights)[row] = maxSize.height();
- }
- }
- }
- }
- QCPLayoutInset::QCPLayoutInset()
- {
- }
- QCPLayoutInset::~QCPLayoutInset()
- {
-
-
- clear();
- }
- QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const
- {
- if (elementAt(index))
- return mInsetPlacement.at(index);
- else
- {
- qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
- return ipFree;
- }
- }
- Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
- {
- if (elementAt(index))
- return mInsetAlignment.at(index);
- else
- {
- qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
- #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
- return nullptr;
- #else
- return {};
- #endif
- }
- }
- QRectF QCPLayoutInset::insetRect(int index) const
- {
- if (elementAt(index))
- return mInsetRect.at(index);
- else
- {
- qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
- return {};
- }
- }
- void QCPLayoutInset::setInsetPlacement(int index, QCPLayoutInset::InsetPlacement placement)
- {
- if (elementAt(index))
- mInsetPlacement[index] = placement;
- else
- qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
- }
- void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
- {
- if (elementAt(index))
- mInsetAlignment[index] = alignment;
- else
- qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
- }
- void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
- {
- if (elementAt(index))
- mInsetRect[index] = rect;
- else
- qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
- }
- void QCPLayoutInset::updateLayout()
- {
- for (int i=0; i<mElements.size(); ++i)
- {
- QCPLayoutElement *el = mElements.at(i);
- QRectF insetRect;
- QSizeF finalMinSize = getFinalMinimumOuterSize(el);
- QSizeF finalMaxSize = getFinalMaximumOuterSize(el);
- if (mInsetPlacement.at(i) == ipFree)
- {
- insetRect = QRectF(rect().x()+rect().width()*mInsetRect.at(i).x(),
- rect().y()+rect().height()*mInsetRect.at(i).y(),
- rect().width()*mInsetRect.at(i).width(),
- rect().height()*mInsetRect.at(i).height());
- if (insetRect.size().width() < finalMinSize.width())
- insetRect.setWidth(finalMinSize.width());
- if (insetRect.size().height() < finalMinSize.height())
- insetRect.setHeight(finalMinSize.height());
- if (insetRect.size().width() > finalMaxSize.width())
- insetRect.setWidth(finalMaxSize.width());
- if (insetRect.size().height() > finalMaxSize.height())
- insetRect.setHeight(finalMaxSize.height());
- } else if (mInsetPlacement.at(i) == ipBorderAligned)
- {
- insetRect.setSize(finalMinSize);
- Qt::Alignment al = mInsetAlignment.at(i);
- if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
- else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
- else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5);
- if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
- else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
- else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5);
- }
- mElements.at(i)->setOuterRect(insetRect);
- }
- }
- int QCPLayoutInset::elementCount() const
- {
- return mElements.size();
- }
- QCPLayoutElement *QCPLayoutInset::elementAt(int index) const
- {
- if (index >= 0 && index < mElements.size())
- return mElements.at(index);
- else
- return nullptr;
- }
- QCPLayoutElement *QCPLayoutInset::takeAt(int index)
- {
- if (QCPLayoutElement *el = elementAt(index))
- {
- releaseElement(el);
- mElements.removeAt(index);
- mInsetPlacement.removeAt(index);
- mInsetAlignment.removeAt(index);
- mInsetRect.removeAt(index);
- return el;
- } else
- {
- qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
- return nullptr;
- }
- }
- bool QCPLayoutInset::take(QCPLayoutElement *element)
- {
- if (element)
- {
- for (int i=0; i<elementCount(); ++i)
- {
- if (elementAt(i) == element)
- {
- takeAt(i);
- return true;
- }
- }
- qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
- } else
- qDebug() << Q_FUNC_INFO << "Can't take nullptr element";
- return false;
- }
- double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable)
- return -1;
- foreach (QCPLayoutElement *el, mElements)
- {
-
-
- if (el->realVisibility() && el->selectTest(pos, onlySelectable) >= 0)
- return mParentPlot->selectionTolerance()*0.99;
- }
- return -1;
- }
- void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
- {
- if (element)
- {
- if (element->layout())
- element->layout()->take(element);
- mElements.append(element);
- mInsetPlacement.append(ipBorderAligned);
- mInsetAlignment.append(alignment);
- mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
- adoptElement(element);
- } else
- qDebug() << Q_FUNC_INFO << "Can't add nullptr element";
- }
- void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)
- {
- if (element)
- {
- if (element->layout())
- element->layout()->take(element);
- mElements.append(element);
- mInsetPlacement.append(ipFree);
- mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
- mInsetRect.append(rect);
- adoptElement(element);
- } else
- qDebug() << Q_FUNC_INFO << "Can't add nullptr element";
- }
- QCPLineEnding::QCPLineEnding() :
- mStyle(esNone),
- mWidth(8),
- mLength(10),
- mInverted(false)
- {
- }
- QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
- mStyle(style),
- mWidth(width),
- mLength(length),
- mInverted(inverted)
- {
- }
- void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style)
- {
- mStyle = style;
- }
- void QCPLineEnding::setWidth(double width)
- {
- mWidth = width;
- }
- void QCPLineEnding::setLength(double length)
- {
- mLength = length;
- }
- void QCPLineEnding::setInverted(bool inverted)
- {
- mInverted = inverted;
- }
- double QCPLineEnding::boundingDistance() const
- {
- switch (mStyle)
- {
- case esNone:
- return 0;
- case esFlatArrow:
- case esSpikeArrow:
- case esLineArrow:
- case esSkewedBar:
- return qSqrt(mWidth*mWidth+mLength*mLength);
- case esDisc:
- case esSquare:
- case esDiamond:
- case esBar:
- case esHalfBar:
- return mWidth*1.42;
- }
- return 0;
- }
- double QCPLineEnding::realLength() const
- {
- switch (mStyle)
- {
- case esNone:
- case esLineArrow:
- case esSkewedBar:
- case esBar:
- case esHalfBar:
- return 0;
- case esFlatArrow:
- return mLength;
- case esDisc:
- case esSquare:
- case esDiamond:
- return mWidth*0.5;
- case esSpikeArrow:
- return mLength*0.8;
- }
- return 0;
- }
- void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const
- {
- if (mStyle == esNone)
- return;
- QCPVector2D lengthVec = dir.normalized() * mLength*(mInverted ? -1 : 1);
- if (lengthVec.isNull())
- lengthVec = QCPVector2D(1, 0);
- QCPVector2D widthVec = dir.normalized().perpendicular() * mWidth*0.5*(mInverted ? -1 : 1);
- QPen penBackup = painter->pen();
- QBrush brushBackup = painter->brush();
- QPen miterPen = penBackup;
- miterPen.setJoinStyle(Qt::MiterJoin);
- QBrush brush(painter->pen().color(), Qt::SolidPattern);
- switch (mStyle)
- {
- case esNone: break;
- case esFlatArrow:
- {
- QPointF points[3] = {pos.toPointF(),
- (pos-lengthVec+widthVec).toPointF(),
- (pos-lengthVec-widthVec).toPointF()
- };
- painter->setPen(miterPen);
- painter->setBrush(brush);
- painter->drawConvexPolygon(points, 3);
- painter->setBrush(brushBackup);
- painter->setPen(penBackup);
- break;
- }
- case esSpikeArrow:
- {
- QPointF points[4] = {pos.toPointF(),
- (pos-lengthVec+widthVec).toPointF(),
- (pos-lengthVec*0.8).toPointF(),
- (pos-lengthVec-widthVec).toPointF()
- };
- painter->setPen(miterPen);
- painter->setBrush(brush);
- painter->drawConvexPolygon(points, 4);
- painter->setBrush(brushBackup);
- painter->setPen(penBackup);
- break;
- }
- case esLineArrow:
- {
- QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
- pos.toPointF(),
- (pos-lengthVec-widthVec).toPointF()
- };
- painter->setPen(miterPen);
- painter->drawPolyline(points, 3);
- painter->setPen(penBackup);
- break;
- }
- case esDisc:
- {
- painter->setBrush(brush);
- painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
- painter->setBrush(brushBackup);
- break;
- }
- case esSquare:
- {
- QCPVector2D widthVecPerp = widthVec.perpendicular();
- QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
- (pos-widthVecPerp-widthVec).toPointF(),
- (pos+widthVecPerp-widthVec).toPointF(),
- (pos+widthVecPerp+widthVec).toPointF()
- };
- painter->setPen(miterPen);
- painter->setBrush(brush);
- painter->drawConvexPolygon(points, 4);
- painter->setBrush(brushBackup);
- painter->setPen(penBackup);
- break;
- }
- case esDiamond:
- {
- QCPVector2D widthVecPerp = widthVec.perpendicular();
- QPointF points[4] = {(pos-widthVecPerp).toPointF(),
- (pos-widthVec).toPointF(),
- (pos+widthVecPerp).toPointF(),
- (pos+widthVec).toPointF()
- };
- painter->setPen(miterPen);
- painter->setBrush(brush);
- painter->drawConvexPolygon(points, 4);
- painter->setBrush(brushBackup);
- painter->setPen(penBackup);
- break;
- }
- case esBar:
- {
- painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
- break;
- }
- case esHalfBar:
- {
- painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
- break;
- }
- case esSkewedBar:
- {
- QCPVector2D shift;
- if (!qFuzzyIsNull(painter->pen().widthF()) || painter->modes().testFlag(QCPPainter::pmNonCosmetic))
- shift = dir.normalized()*qMax(qreal(1.0), painter->pen().widthF())*qreal(0.5);
-
- painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+shift).toPointF(),
- (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+shift).toPointF());
- break;
- }
- }
- }
- void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double angle) const
- {
- draw(painter, pos, QCPVector2D(qCos(angle), qSin(angle)));
- }
- const QChar QCPLabelPainterPrivate::SymbolDot(183);
- const QChar QCPLabelPainterPrivate::SymbolCross(215);
- QCPLabelPainterPrivate::QCPLabelPainterPrivate(QCustomPlot *parentPlot) :
- mAnchorMode(amRectangular),
- mAnchorSide(asLeft),
- mAnchorReferenceType(artNormal),
- mColor(Qt::black),
- mPadding(0),
- mRotation(0),
- mSubstituteExponent(true),
- mMultiplicationSymbol(QChar(215)),
- mAbbreviateDecimalPowers(false),
- mParentPlot(parentPlot),
- mLabelCache(16)
- {
- analyzeFontMetrics();
- }
- QCPLabelPainterPrivate::~QCPLabelPainterPrivate()
- {
- }
- void QCPLabelPainterPrivate::setAnchorSide(AnchorSide side)
- {
- mAnchorSide = side;
- }
- void QCPLabelPainterPrivate::setAnchorMode(AnchorMode mode)
- {
- mAnchorMode = mode;
- }
- void QCPLabelPainterPrivate::setAnchorReference(const QPointF &pixelPoint)
- {
- mAnchorReference = pixelPoint;
- }
- void QCPLabelPainterPrivate::setAnchorReferenceType(AnchorReferenceType type)
- {
- mAnchorReferenceType = type;
- }
- void QCPLabelPainterPrivate::setFont(const QFont &font)
- {
- if (mFont != font)
- {
- mFont = font;
- analyzeFontMetrics();
- }
- }
- void QCPLabelPainterPrivate::setColor(const QColor &color)
- {
- mColor = color;
- }
- void QCPLabelPainterPrivate::setPadding(int padding)
- {
- mPadding = padding;
- }
- void QCPLabelPainterPrivate::setRotation(double rotation)
- {
- mRotation = qBound(-90.0, rotation, 90.0);
- }
- void QCPLabelPainterPrivate::setSubstituteExponent(bool enabled)
- {
- mSubstituteExponent = enabled;
- }
- void QCPLabelPainterPrivate::setMultiplicationSymbol(QChar symbol)
- {
- mMultiplicationSymbol = symbol;
- }
- void QCPLabelPainterPrivate::setAbbreviateDecimalPowers(bool enabled)
- {
- mAbbreviateDecimalPowers = enabled;
- }
- void QCPLabelPainterPrivate::setCacheSize(int labelCount)
- {
- mLabelCache.setMaxCost(labelCount);
- }
- int QCPLabelPainterPrivate::cacheSize() const
- {
- return mLabelCache.maxCost();
- }
- void QCPLabelPainterPrivate::drawTickLabel(QCPPainter *painter, const QPointF &tickPos, const QString &text)
- {
- double realRotation = mRotation;
- AnchorSide realSide = mAnchorSide;
-
- if (mAnchorMode == amSkewedUpright)
- {
- realSide = skewedAnchorSide(tickPos, 0.2, 0.3);
- } else if (mAnchorMode == amSkewedRotated)
- {
- realSide = skewedAnchorSide(tickPos, 0, 0);
- realRotation += QCPVector2D(tickPos-mAnchorReference).angle()/M_PI*180.0;
- if (realRotation > 90) realRotation -= 180;
- else if (realRotation < -90) realRotation += 180;
- }
- realSide = rotationCorrectedSide(realSide, realRotation);
- drawLabelMaybeCached(painter, mFont, mColor, getAnchorPos(tickPos), realSide, realRotation, text);
- }
- void QCPLabelPainterPrivate::clearCache()
- {
- mLabelCache.clear();
- }
- QByteArray QCPLabelPainterPrivate::generateLabelParameterHash() const
- {
- QByteArray result;
- result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));
- result.append(QByteArray::number(mRotation));
-
- result.append(QByteArray::number(int(mSubstituteExponent)));
- result.append(QString(mMultiplicationSymbol).toUtf8());
- result.append(mColor.name().toLatin1()+QByteArray::number(mColor.alpha(), 16));
- result.append(mFont.toString().toLatin1());
- return result;
- }
- void QCPLabelPainterPrivate::drawLabelMaybeCached(QCPPainter *painter, const QFont &font, const QColor &color, const QPointF &pos, AnchorSide side, double rotation, const QString &text)
- {
-
- if (text.isEmpty()) return;
- QSize finalSize;
- if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching))
- {
- QByteArray key = cacheKey(text, color, rotation, side);
- CachedLabel *cachedLabel = mLabelCache.take(QString::fromUtf8(key));
- if (!cachedLabel)
- {
- LabelData labelData = getTickLabelData(font, color, rotation, side, text);
- cachedLabel = createCachedLabel(labelData);
- }
-
- bool labelClippedByBorder = false;
-
- if (!labelClippedByBorder)
- {
- painter->drawPixmap(pos+cachedLabel->offset, cachedLabel->pixmap);
- finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();
- }
- mLabelCache.insert(QString::fromUtf8(key), cachedLabel);
- } else
- {
- LabelData labelData = getTickLabelData(font, color, rotation, side, text);
-
- bool labelClippedByBorder = false;
-
- if (!labelClippedByBorder)
- {
- drawText(painter, pos, labelData);
- finalSize = labelData.rotatedTotalBounds.size();
- }
- }
-
- }
- QPointF QCPLabelPainterPrivate::getAnchorPos(const QPointF &tickPos)
- {
- switch (mAnchorMode)
- {
- case amRectangular:
- {
- switch (mAnchorSide)
- {
- case asLeft: return tickPos+QPointF(mPadding, 0);
- case asRight: return tickPos+QPointF(-mPadding, 0);
- case asTop: return tickPos+QPointF(0, mPadding);
- case asBottom: return tickPos+QPointF(0, -mPadding);
- case asTopLeft: return tickPos+QPointF(mPadding*M_SQRT1_2, mPadding*M_SQRT1_2);
- case asTopRight: return tickPos+QPointF(-mPadding*M_SQRT1_2, mPadding*M_SQRT1_2);
- case asBottomRight: return tickPos+QPointF(-mPadding*M_SQRT1_2, -mPadding*M_SQRT1_2);
- case asBottomLeft: return tickPos+QPointF(mPadding*M_SQRT1_2, -mPadding*M_SQRT1_2);
- }
- }
- case amSkewedUpright:
- case amSkewedRotated:
- {
- QCPVector2D anchorNormal(tickPos-mAnchorReference);
- if (mAnchorReferenceType == artTangent)
- anchorNormal = anchorNormal.perpendicular();
- anchorNormal.normalize();
- return tickPos+(anchorNormal*mPadding).toPointF();
- }
- }
- return tickPos;
- }
- void QCPLabelPainterPrivate::drawText(QCPPainter *painter, const QPointF &pos, const LabelData &labelData) const
- {
-
- QTransform oldTransform = painter->transform();
- QFont oldFont = painter->font();
- QPen oldPen = painter->pen();
-
- painter->translate(pos);
- painter->setTransform(labelData.transform, true);
-
- painter->setFont(labelData.baseFont);
- painter->setPen(QPen(labelData.color));
- if (!labelData.expPart.isEmpty())
- {
- painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
- if (!labelData.suffixPart.isEmpty())
- painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart);
- painter->setFont(labelData.expFont);
- painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
- } else
- {
- painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
- }
-
-
- painter->setTransform(oldTransform);
- painter->setFont(oldFont);
- painter->setPen(oldPen);
- }
- QCPLabelPainterPrivate::LabelData QCPLabelPainterPrivate::getTickLabelData(const QFont &font, const QColor &color, double rotation, AnchorSide side, const QString &text) const
- {
- LabelData result;
- result.rotation = rotation;
- result.side = side;
- result.color = color;
-
- bool useBeautifulPowers = false;
- int ePos = -1;
- int eLast = -1;
- if (mSubstituteExponent)
- {
- ePos = text.indexOf(QLatin1Char('e'));
- if (ePos > 0 && text.at(ePos-1).isDigit())
- {
- eLast = ePos;
- while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))
- ++eLast;
- if (eLast > ePos)
- useBeautifulPowers = true;
- }
- }
-
- result.baseFont = font;
- if (result.baseFont.pointSizeF() > 0)
- result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05);
- QFontMetrics baseFontMetrics(result.baseFont);
- if (useBeautifulPowers)
- {
-
- result.basePart = text.left(ePos);
- result.suffixPart = text.mid(eLast+1);
-
- if (mAbbreviateDecimalPowers && result.basePart == QLatin1String("1"))
- result.basePart = QLatin1String("10");
- else
- result.basePart += QString(mMultiplicationSymbol) + QLatin1String("10");
- result.expPart = text.mid(ePos+1, eLast-ePos);
-
- while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0'))
- result.expPart.remove(1, 1);
- if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
- result.expPart.remove(0, 1);
-
- result.expFont = font;
- if (result.expFont.pointSize() > 0)
- result.expFont.setPointSizeF(result.expFont.pointSize()*0.75);
- else
- result.expFont.setPixelSize(int(result.expFont.pixelSize()*0.75));
-
- result.baseBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
- result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
- if (!result.suffixPart.isEmpty())
- result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart);
- result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+result.suffixBounds.width()+2, 0);
- } else
- {
- result.basePart = text;
- result.totalBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
- }
- result.totalBounds.moveTopLeft(QPoint(0, 0));
- applyAnchorTransform(result);
- result.rotatedTotalBounds = result.transform.mapRect(result.totalBounds);
- return result;
- }
- void QCPLabelPainterPrivate::applyAnchorTransform(LabelData &labelData) const
- {
- if (!qFuzzyIsNull(labelData.rotation))
- labelData.transform.rotate(labelData.rotation);
-
-
- labelData.transform.translate(0, -labelData.totalBounds.height()+mLetterDescent+mLetterCapHeight);
- if (labelData.side == asLeft || labelData.side == asRight)
- labelData.transform.translate(0, -mLetterCapHeight/2.0);
- else if (labelData.side == asTop || labelData.side == asBottom)
- labelData.transform.translate(-labelData.totalBounds.width()/2.0, 0);
- if (labelData.side == asTopRight || labelData.side == asRight || labelData.side == asBottomRight)
- labelData.transform.translate(-labelData.totalBounds.width(), 0);
- if (labelData.side == asBottomLeft || labelData.side == asBottom || labelData.side == asBottomRight)
- labelData.transform.translate(0, -mLetterCapHeight);
- }
- QCPLabelPainterPrivate::CachedLabel *QCPLabelPainterPrivate::createCachedLabel(const LabelData &labelData) const
- {
- CachedLabel *result = new CachedLabel;
-
- if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio()))
- {
- result->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio());
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- # ifdef QCP_DEVICEPIXELRATIO_FLOAT
- result->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF());
- # else
- result->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio());
- # endif
- #endif
- } else
- result->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
- result->pixmap.fill(Qt::transparent);
-
-
-
- result->offset = labelData.rotatedTotalBounds.topLeft();
- QCPPainter cachePainter(&result->pixmap);
- drawText(&cachePainter, -result->offset, labelData);
- return result;
- }
- QByteArray QCPLabelPainterPrivate::cacheKey(const QString &text, const QColor &color, double rotation, AnchorSide side) const
- {
- return text.toUtf8()+
- QByteArray::number(color.red()+256*color.green()+65536*color.blue(), 36)+
- QByteArray::number(color.alpha()+256*int(side), 36)+
- QByteArray::number(int(rotation*100)%36000, 36);
- }
- QCPLabelPainterPrivate::AnchorSide QCPLabelPainterPrivate::skewedAnchorSide(const QPointF &tickPos, double sideExpandHorz, double sideExpandVert) const
- {
- QCPVector2D anchorNormal = QCPVector2D(tickPos-mAnchorReference);
- if (mAnchorReferenceType == artTangent)
- anchorNormal = anchorNormal.perpendicular();
- const double radius = anchorNormal.length();
- const double sideHorz = sideExpandHorz*radius;
- const double sideVert = sideExpandVert*radius;
- if (anchorNormal.x() > sideHorz)
- {
- if (anchorNormal.y() > sideVert) return asTopLeft;
- else if (anchorNormal.y() < -sideVert) return asBottomLeft;
- else return asLeft;
- } else if (anchorNormal.x() < -sideHorz)
- {
- if (anchorNormal.y() > sideVert) return asTopRight;
- else if (anchorNormal.y() < -sideVert) return asBottomRight;
- else return asRight;
- } else
- {
- if (anchorNormal.y() > 0) return asTop;
- else return asBottom;
- }
- }
- QCPLabelPainterPrivate::AnchorSide QCPLabelPainterPrivate::rotationCorrectedSide(AnchorSide side, double rotation) const
- {
- AnchorSide result = side;
- const bool rotateClockwise = rotation > 0;
- if (!qFuzzyIsNull(rotation))
- {
- if (!qFuzzyCompare(qAbs(rotation), 90))
- {
- if (side == asTop) result = rotateClockwise ? asLeft : asRight;
- else if (side == asBottom) result = rotateClockwise ? asRight : asLeft;
- else if (side == asTopLeft) result = rotateClockwise ? asLeft : asTop;
- else if (side == asTopRight) result = rotateClockwise ? asTop : asRight;
- else if (side == asBottomLeft) result = rotateClockwise ? asBottom : asLeft;
- else if (side == asBottomRight) result = rotateClockwise ? asRight : asBottom;
- } else
- {
- if (side == asLeft) result = rotateClockwise ? asBottom : asTop;
- else if (side == asRight) result = rotateClockwise ? asTop : asBottom;
- else if (side == asTop) result = rotateClockwise ? asLeft : asRight;
- else if (side == asBottom) result = rotateClockwise ? asRight : asLeft;
- else if (side == asTopLeft) result = rotateClockwise ? asBottomLeft : asTopRight;
- else if (side == asTopRight) result = rotateClockwise ? asTopLeft : asBottomRight;
- else if (side == asBottomLeft) result = rotateClockwise ? asBottomRight : asTopLeft;
- else if (side == asBottomRight) result = rotateClockwise ? asTopRight : asBottomLeft;
- }
- }
- return result;
- }
- void QCPLabelPainterPrivate::analyzeFontMetrics()
- {
- const QFontMetrics fm(mFont);
- mLetterCapHeight = fm.tightBoundingRect(QLatin1String("8")).height();
- mLetterDescent = fm.descent();
- }
- QCPAxisTicker::QCPAxisTicker() :
- mTickStepStrategy(tssReadability),
- mTickCount(5),
- mTickOrigin(0)
- {
- }
- QCPAxisTicker::~QCPAxisTicker()
- {
- }
- void QCPAxisTicker::setTickStepStrategy(QCPAxisTicker::TickStepStrategy strategy)
- {
- mTickStepStrategy = strategy;
- }
- void QCPAxisTicker::setTickCount(int count)
- {
- if (count > 0)
- mTickCount = count;
- else
- qDebug() << Q_FUNC_INFO << "tick count must be greater than zero:" << count;
- }
- void QCPAxisTicker::setTickOrigin(double origin)
- {
- mTickOrigin = origin;
- }
- void QCPAxisTicker::generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector<double> &ticks, QVector<double> *subTicks, QVector<QString> *tickLabels)
- {
-
- double tickStep = getTickStep(range);
- ticks = createTickVector(tickStep, range);
- trimTicks(range, ticks, true);
-
- if (subTicks)
- {
- if (!ticks.isEmpty())
- {
- *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks);
- trimTicks(range, *subTicks, false);
- } else
- *subTicks = QVector<double>();
- }
-
- trimTicks(range, ticks, false);
-
- if (tickLabels)
- *tickLabels = createLabelVector(ticks, locale, formatChar, precision);
- }
- double QCPAxisTicker::getTickStep(const QCPRange &range)
- {
- double exactStep = range.size()/double(mTickCount+1e-10);
- return cleanMantissa(exactStep);
- }
- int QCPAxisTicker::getSubTickCount(double tickStep)
- {
- int result = 1;
-
- double epsilon = 0.01;
- double intPartf;
- int intPart;
- double fracPart = modf(getMantissa(tickStep), &intPartf);
- intPart = int(intPartf);
-
- if (fracPart < epsilon || 1.0-fracPart < epsilon)
- {
- if (1.0-fracPart < epsilon)
- ++intPart;
- switch (intPart)
- {
- case 1: result = 4; break;
- case 2: result = 3; break;
- case 3: result = 2; break;
- case 4: result = 3; break;
- case 5: result = 4; break;
- case 6: result = 2; break;
- case 7: result = 6; break;
- case 8: result = 3; break;
- case 9: result = 2; break;
- }
- } else
- {
-
- if (qAbs(fracPart-0.5) < epsilon)
- {
- switch (intPart)
- {
- case 1: result = 2; break;
- case 2: result = 4; break;
- case 3: result = 4; break;
- case 4: result = 2; break;
- case 5: result = 4; break;
- case 6: result = 4; break;
- case 7: result = 2; break;
- case 8: result = 4; break;
- case 9: result = 4; break;
- }
- }
-
- }
- return result;
- }
- QString QCPAxisTicker::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
- {
- return locale.toString(tick, formatChar.toLatin1(), precision);
- }
- QVector<double> QCPAxisTicker::createSubTickVector(int subTickCount, const QVector<double> &ticks)
- {
- QVector<double> result;
- if (subTickCount <= 0 || ticks.size() < 2)
- return result;
- result.reserve((ticks.size()-1)*subTickCount);
- for (int i=1; i<ticks.size(); ++i)
- {
- double subTickStep = (ticks.at(i)-ticks.at(i-1))/double(subTickCount+1);
- for (int k=1; k<=subTickCount; ++k)
- result.append(ticks.at(i-1) + k*subTickStep);
- }
- return result;
- }
- QVector<double> QCPAxisTicker::createTickVector(double tickStep, const QCPRange &range)
- {
- QVector<double> result;
-
- qint64 firstStep = qint64(floor((range.lower-mTickOrigin)/tickStep));
- qint64 lastStep = qint64(ceil((range.upper-mTickOrigin)/tickStep));
- int tickcount = int(lastStep-firstStep+1);
- if (tickcount < 0) tickcount = 0;
- result.resize(tickcount);
- for (int i=0; i<tickcount; ++i)
- result[i] = mTickOrigin + (firstStep+i)*tickStep;
- return result;
- }
- QVector<QString> QCPAxisTicker::createLabelVector(const QVector<double> &ticks, const QLocale &locale, QChar formatChar, int precision)
- {
- QVector<QString> result;
- result.reserve(ticks.size());
- foreach (double tickCoord, ticks)
- result.append(getTickLabel(tickCoord, locale, formatChar, precision));
- return result;
- }
- void QCPAxisTicker::trimTicks(const QCPRange &range, QVector<double> &ticks, bool keepOneOutlier) const
- {
- bool lowFound = false;
- bool highFound = false;
- int lowIndex = 0;
- int highIndex = -1;
- for (int i=0; i < ticks.size(); ++i)
- {
- if (ticks.at(i) >= range.lower)
- {
- lowFound = true;
- lowIndex = i;
- break;
- }
- }
- for (int i=ticks.size()-1; i >= 0; --i)
- {
- if (ticks.at(i) <= range.upper)
- {
- highFound = true;
- highIndex = i;
- break;
- }
- }
- if (highFound && lowFound)
- {
- int trimFront = qMax(0, lowIndex-(keepOneOutlier ? 1 : 0));
- int trimBack = qMax(0, ticks.size()-(keepOneOutlier ? 2 : 1)-highIndex);
- if (trimFront > 0 || trimBack > 0)
- ticks = ticks.mid(trimFront, ticks.size()-trimFront-trimBack);
- } else
- ticks.clear();
- }
- double QCPAxisTicker::pickClosest(double target, const QVector<double> &candidates) const
- {
- if (candidates.size() == 1)
- return candidates.first();
- QVector<double>::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target);
- if (it == candidates.constEnd())
- return *(it-1);
- else if (it == candidates.constBegin())
- return *it;
- else
- return target-*(it-1) < *it-target ? *(it-1) : *it;
- }
- double QCPAxisTicker::getMantissa(double input, double *magnitude) const
- {
- const double mag = qPow(10.0, qFloor(qLn(input)/qLn(10.0)));
- if (magnitude) *magnitude = mag;
- return input/mag;
- }
- double QCPAxisTicker::cleanMantissa(double input) const
- {
- double magnitude;
- const double mantissa = getMantissa(input, &magnitude);
- switch (mTickStepStrategy)
- {
- case tssReadability:
- {
- return pickClosest(mantissa, QVector<double>() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude;
- }
- case tssMeetTickCount:
- {
-
- if (mantissa <= 5.0)
- return int(mantissa*2)/2.0*magnitude;
- else
- return int(mantissa/2.0)*2.0*magnitude;
- }
- }
- return input;
- }
- QCPAxisTickerDateTime::QCPAxisTickerDateTime() :
- mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
- mDateTimeSpec(Qt::LocalTime),
- mDateStrategy(dsNone)
- {
- setTickCount(4);
- }
- void QCPAxisTickerDateTime::setDateTimeFormat(const QString &format)
- {
- mDateTimeFormat = format;
- }
- void QCPAxisTickerDateTime::setDateTimeSpec(Qt::TimeSpec spec)
- {
- mDateTimeSpec = spec;
- }
- # if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
- void QCPAxisTickerDateTime::setTimeZone(const QTimeZone &zone)
- {
- mTimeZone = zone;
- mDateTimeSpec = Qt::TimeZone;
- }
- #endif
- void QCPAxisTickerDateTime::setTickOrigin(double origin)
- {
- QCPAxisTicker::setTickOrigin(origin);
- }
- void QCPAxisTickerDateTime::setTickOrigin(const QDateTime &origin)
- {
- setTickOrigin(dateTimeToKey(origin));
- }
- double QCPAxisTickerDateTime::getTickStep(const QCPRange &range)
- {
- double result = range.size()/double(mTickCount+1e-10);
- mDateStrategy = dsNone;
- if (result < 1)
- {
- result = cleanMantissa(result);
- } else if (result < 86400*30.4375*12)
- {
- result = pickClosest(result, QVector<double>()
- << 1 << 2.5 << 5 << 10 << 15 << 30 << 60 << 2.5*60 << 5*60 << 10*60 << 15*60 << 30*60 << 60*60
- << 3600*2 << 3600*3 << 3600*6 << 3600*12 << 3600*24
- << 86400*2 << 86400*5 << 86400*7 << 86400*14 << 86400*30.4375 << 86400*30.4375*2 << 86400*30.4375*3 << 86400*30.4375*6 << 86400*30.4375*12);
- if (result > 86400*30.4375-1)
- mDateStrategy = dsUniformDayInMonth;
- else if (result > 3600*24-1)
- mDateStrategy = dsUniformTimeInDay;
- } else
- {
- const double secondsPerYear = 86400*30.4375*12;
- result = cleanMantissa(result/secondsPerYear)*secondsPerYear;
- mDateStrategy = dsUniformDayInMonth;
- }
- return result;
- }
- int QCPAxisTickerDateTime::getSubTickCount(double tickStep)
- {
- int result = QCPAxisTicker::getSubTickCount(tickStep);
- switch (qRound(tickStep))
- {
- case 5*60: result = 4; break;
- case 10*60: result = 1; break;
- case 15*60: result = 2; break;
- case 30*60: result = 1; break;
- case 60*60: result = 3; break;
- case 3600*2: result = 3; break;
- case 3600*3: result = 2; break;
- case 3600*6: result = 1; break;
- case 3600*12: result = 3; break;
- case 3600*24: result = 3; break;
- case 86400*2: result = 1; break;
- case 86400*5: result = 4; break;
- case 86400*7: result = 6; break;
- case 86400*14: result = 1; break;
- case int(86400*30.4375+0.5): result = 3; break;
- case int(86400*30.4375*2+0.5): result = 1; break;
- case int(86400*30.4375*3+0.5): result = 2; break;
- case int(86400*30.4375*6+0.5): result = 5; break;
- case int(86400*30.4375*12+0.5): result = 3; break;
- }
- return result;
- }
- QString QCPAxisTickerDateTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
- {
- Q_UNUSED(precision)
- Q_UNUSED(formatChar)
- # if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
- if (mDateTimeSpec == Qt::TimeZone)
- return locale.toString(keyToDateTime(tick).toTimeZone(mTimeZone), mDateTimeFormat);
- else
- return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
- # else
- return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
- # endif
- }
- QVector<double> QCPAxisTickerDateTime::createTickVector(double tickStep, const QCPRange &range)
- {
- QVector<double> result = QCPAxisTicker::createTickVector(tickStep, range);
- if (!result.isEmpty())
- {
- if (mDateStrategy == dsUniformTimeInDay)
- {
- QDateTime uniformDateTime = keyToDateTime(mTickOrigin);
- QDateTime tickDateTime;
- for (int i=0; i<result.size(); ++i)
- {
- tickDateTime = keyToDateTime(result.at(i));
- tickDateTime.setTime(uniformDateTime.time());
- result[i] = dateTimeToKey(tickDateTime);
- }
- } else if (mDateStrategy == dsUniformDayInMonth)
- {
- QDateTime uniformDateTime = keyToDateTime(mTickOrigin);
- QDateTime tickDateTime;
- for (int i=0; i<result.size(); ++i)
- {
- tickDateTime = keyToDateTime(result.at(i));
- tickDateTime.setTime(uniformDateTime.time());
- int thisUniformDay = uniformDateTime.date().day() <= tickDateTime.date().daysInMonth() ? uniformDateTime.date().day() : tickDateTime.date().daysInMonth();
- if (thisUniformDay-tickDateTime.date().day() < -15)
- tickDateTime = tickDateTime.addMonths(1);
- else if (thisUniformDay-tickDateTime.date().day() > 15)
- tickDateTime = tickDateTime.addMonths(-1);
- tickDateTime.setDate(QDate(tickDateTime.date().year(), tickDateTime.date().month(), thisUniformDay));
- result[i] = dateTimeToKey(tickDateTime);
- }
- }
- }
- return result;
- }
- QDateTime QCPAxisTickerDateTime::keyToDateTime(double key)
- {
- # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
- return QDateTime::fromTime_t(key).addMSecs((key-(qint64)key)*1000);
- # else
- return QDateTime::fromMSecsSinceEpoch(qint64(key*1000.0));
- # endif
- }
- double QCPAxisTickerDateTime::dateTimeToKey(const QDateTime &dateTime)
- {
- # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
- return dateTime.toTime_t()+dateTime.time().msec()/1000.0;
- # else
- return dateTime.toMSecsSinceEpoch()/1000.0;
- # endif
- }
- double QCPAxisTickerDateTime::dateTimeToKey(const QDate &date, Qt::TimeSpec timeSpec)
- {
- # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
- return QDateTime(date, QTime(0, 0), timeSpec).toTime_t();
- # elif QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
- return QDateTime(date, QTime(0, 0), timeSpec).toMSecsSinceEpoch()/1000.0;
- # else
- return date.startOfDay(timeSpec).toMSecsSinceEpoch()/1000.0;
- # endif
- }
- QCPAxisTickerTime::QCPAxisTickerTime() :
- mTimeFormat(QLatin1String("%h:%m:%s")),
- mSmallestUnit(tuSeconds),
- mBiggestUnit(tuHours)
- {
- setTickCount(4);
- mFieldWidth[tuMilliseconds] = 3;
- mFieldWidth[tuSeconds] = 2;
- mFieldWidth[tuMinutes] = 2;
- mFieldWidth[tuHours] = 2;
- mFieldWidth[tuDays] = 1;
- mFormatPattern[tuMilliseconds] = QLatin1String("%z");
- mFormatPattern[tuSeconds] = QLatin1String("%s");
- mFormatPattern[tuMinutes] = QLatin1String("%m");
- mFormatPattern[tuHours] = QLatin1String("%h");
- mFormatPattern[tuDays] = QLatin1String("%d");
- }
- void QCPAxisTickerTime::setTimeFormat(const QString &format)
- {
- mTimeFormat = format;
-
-
- mSmallestUnit = tuMilliseconds;
- mBiggestUnit = tuMilliseconds;
- bool hasSmallest = false;
- for (int i = tuMilliseconds; i <= tuDays; ++i)
- {
- TimeUnit unit = static_cast<TimeUnit>(i);
- if (mTimeFormat.contains(mFormatPattern.value(unit)))
- {
- if (!hasSmallest)
- {
- mSmallestUnit = unit;
- hasSmallest = true;
- }
- mBiggestUnit = unit;
- }
- }
- }
- void QCPAxisTickerTime::setFieldWidth(QCPAxisTickerTime::TimeUnit unit, int width)
- {
- mFieldWidth[unit] = qMax(width, 1);
- }
- double QCPAxisTickerTime::getTickStep(const QCPRange &range)
- {
- double result = range.size()/double(mTickCount+1e-10);
- if (result < 1)
- {
- if (mSmallestUnit == tuMilliseconds)
- result = qMax(cleanMantissa(result), 0.001);
- else
- result = 1.0;
- } else if (result < 3600*24)
- {
-
- QVector<double> availableSteps;
-
- if (mSmallestUnit <= tuSeconds)
- availableSteps << 1;
- if (mSmallestUnit == tuMilliseconds)
- availableSteps << 2.5;
- else if (mSmallestUnit == tuSeconds)
- availableSteps << 2;
- if (mSmallestUnit <= tuSeconds)
- availableSteps << 5 << 10 << 15 << 30;
-
- if (mSmallestUnit <= tuMinutes)
- availableSteps << 1*60;
- if (mSmallestUnit <= tuSeconds)
- availableSteps << 2.5*60;
- else if (mSmallestUnit == tuMinutes)
- availableSteps << 2*60;
- if (mSmallestUnit <= tuMinutes)
- availableSteps << 5*60 << 10*60 << 15*60 << 30*60;
-
- if (mSmallestUnit <= tuHours)
- availableSteps << 1*3600 << 2*3600 << 3*3600 << 6*3600 << 12*3600 << 24*3600;
-
- result = pickClosest(result, availableSteps);
- } else
- {
- const double secondsPerDay = 3600*24;
- result = cleanMantissa(result/secondsPerDay)*secondsPerDay;
- }
- return result;
- }
- int QCPAxisTickerTime::getSubTickCount(double tickStep)
- {
- int result = QCPAxisTicker::getSubTickCount(tickStep);
- switch (qRound(tickStep))
- {
- case 5*60: result = 4; break;
- case 10*60: result = 1; break;
- case 15*60: result = 2; break;
- case 30*60: result = 1; break;
- case 60*60: result = 3; break;
- case 3600*2: result = 3; break;
- case 3600*3: result = 2; break;
- case 3600*6: result = 1; break;
- case 3600*12: result = 3; break;
- case 3600*24: result = 3; break;
- }
- return result;
- }
- QString QCPAxisTickerTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
- {
- Q_UNUSED(precision)
- Q_UNUSED(formatChar)
- Q_UNUSED(locale)
- bool negative = tick < 0;
- if (negative) tick *= -1;
- double values[tuDays+1];
- double restValues[tuDays+1];
- restValues[tuMilliseconds] = tick*1000;
- values[tuMilliseconds] = modf(restValues[tuMilliseconds]/1000, &restValues[tuSeconds])*1000;
- values[tuSeconds] = modf(restValues[tuSeconds]/60, &restValues[tuMinutes])*60;
- values[tuMinutes] = modf(restValues[tuMinutes]/60, &restValues[tuHours])*60;
- values[tuHours] = modf(restValues[tuHours]/24, &restValues[tuDays])*24;
-
- QString result = mTimeFormat;
- for (int i = mSmallestUnit; i <= mBiggestUnit; ++i)
- {
- TimeUnit iUnit = static_cast<TimeUnit>(i);
- replaceUnit(result, iUnit, qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit]));
- }
- if (negative)
- result.prepend(QLatin1Char('-'));
- return result;
- }
- void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit unit, int value) const
- {
- QString valueStr = QString::number(value);
- while (valueStr.size() < mFieldWidth.value(unit))
- valueStr.prepend(QLatin1Char('0'));
- text.replace(mFormatPattern.value(unit), valueStr);
- }
- QCPAxisTickerFixed::QCPAxisTickerFixed() :
- mTickStep(1.0),
- mScaleStrategy(ssNone)
- {
- }
- void QCPAxisTickerFixed::setTickStep(double step)
- {
- if (step > 0)
- mTickStep = step;
- else
- qDebug() << Q_FUNC_INFO << "tick step must be greater than zero:" << step;
- }
- void QCPAxisTickerFixed::setScaleStrategy(QCPAxisTickerFixed::ScaleStrategy strategy)
- {
- mScaleStrategy = strategy;
- }
- double QCPAxisTickerFixed::getTickStep(const QCPRange &range)
- {
- switch (mScaleStrategy)
- {
- case ssNone:
- {
- return mTickStep;
- }
- case ssMultiples:
- {
- double exactStep = range.size()/double(mTickCount+1e-10);
- if (exactStep < mTickStep)
- return mTickStep;
- else
- return qint64(cleanMantissa(exactStep/mTickStep)+0.5)*mTickStep;
- }
- case ssPowers:
- {
- double exactStep = range.size()/double(mTickCount+1e-10);
- return qPow(mTickStep, int(qLn(exactStep)/qLn(mTickStep)+0.5));
- }
- }
- return mTickStep;
- }
- QCPAxisTickerText::QCPAxisTickerText() :
- mSubTickCount(0)
- {
- }
- void QCPAxisTickerText::setTicks(const QMap<double, QString> &ticks)
- {
- mTicks = ticks;
- }
- void QCPAxisTickerText::setTicks(const QVector<double> &positions, const QVector<QString> &labels)
- {
- clear();
- addTicks(positions, labels);
- }
- void QCPAxisTickerText::setSubTickCount(int subTicks)
- {
- if (subTicks >= 0)
- mSubTickCount = subTicks;
- else
- qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
- }
- void QCPAxisTickerText::clear()
- {
- mTicks.clear();
- }
- void QCPAxisTickerText::addTick(double position, const QString &label)
- {
- mTicks.insert(position, label);
- }
- void QCPAxisTickerText::addTicks(const QMap<double, QString> &ticks)
- {
- #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
- mTicks.unite(ticks);
- #else
- mTicks.insert(ticks);
- #endif
- }
- void QCPAxisTickerText::addTicks(const QVector<double> &positions, const QVector<QString> &labels)
- {
- if (positions.size() != labels.size())
- qDebug() << Q_FUNC_INFO << "passed unequal length vectors for positions and labels:" << positions.size() << labels.size();
- int n = qMin(positions.size(), labels.size());
- for (int i=0; i<n; ++i)
- mTicks.insert(positions.at(i), labels.at(i));
- }
- double QCPAxisTickerText::getTickStep(const QCPRange &range)
- {
-
- Q_UNUSED(range)
- return 1.0;
- }
- int QCPAxisTickerText::getSubTickCount(double tickStep)
- {
- Q_UNUSED(tickStep)
- return mSubTickCount;
- }
- QString QCPAxisTickerText::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
- {
- Q_UNUSED(locale)
- Q_UNUSED(formatChar)
- Q_UNUSED(precision)
- return mTicks.value(tick);
- }
- QVector<double> QCPAxisTickerText::createTickVector(double tickStep, const QCPRange &range)
- {
- Q_UNUSED(tickStep)
- QVector<double> result;
- if (mTicks.isEmpty())
- return result;
- QMap<double, QString>::const_iterator start = mTicks.lowerBound(range.lower);
- QMap<double, QString>::const_iterator end = mTicks.upperBound(range.upper);
-
- if (start != mTicks.constBegin()) --start;
- if (end != mTicks.constEnd()) ++end;
- for (QMap<double, QString>::const_iterator it = start; it != end; ++it)
- result.append(it.key());
- return result;
- }
- QCPAxisTickerPi::QCPAxisTickerPi() :
- mPiSymbol(QLatin1String(" ")+QChar(0x03C0)),
- mPiValue(M_PI),
- mPeriodicity(0),
- mFractionStyle(fsUnicodeFractions),
- mPiTickStep(0)
- {
- setTickCount(4);
- }
- void QCPAxisTickerPi::setPiSymbol(QString symbol)
- {
- mPiSymbol = symbol;
- }
- void QCPAxisTickerPi::setPiValue(double pi)
- {
- mPiValue = pi;
- }
- void QCPAxisTickerPi::setPeriodicity(int multiplesOfPi)
- {
- mPeriodicity = qAbs(multiplesOfPi);
- }
- void QCPAxisTickerPi::setFractionStyle(QCPAxisTickerPi::FractionStyle style)
- {
- mFractionStyle = style;
- }
- double QCPAxisTickerPi::getTickStep(const QCPRange &range)
- {
- mPiTickStep = range.size()/mPiValue/double(mTickCount+1e-10);
- mPiTickStep = cleanMantissa(mPiTickStep);
- return mPiTickStep*mPiValue;
- }
- int QCPAxisTickerPi::getSubTickCount(double tickStep)
- {
- return QCPAxisTicker::getSubTickCount(tickStep/mPiValue);
- }
- QString QCPAxisTickerPi::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
- {
- double tickInPis = tick/mPiValue;
- if (mPeriodicity > 0)
- tickInPis = fmod(tickInPis, mPeriodicity);
- if (mFractionStyle != fsFloatingPoint && mPiTickStep > 0.09 && mPiTickStep < 50)
- {
-
- int denominator = 1000;
- int numerator = qRound(tickInPis*denominator);
- simplifyFraction(numerator, denominator);
- if (qAbs(numerator) == 1 && denominator == 1)
- return (numerator < 0 ? QLatin1String("-") : QLatin1String("")) + mPiSymbol.trimmed();
- else if (numerator == 0)
- return QLatin1String("0");
- else
- return fractionToString(numerator, denominator) + mPiSymbol;
- } else
- {
- if (qFuzzyIsNull(tickInPis))
- return QLatin1String("0");
- else if (qFuzzyCompare(qAbs(tickInPis), 1.0))
- return (tickInPis < 0 ? QLatin1String("-") : QLatin1String("")) + mPiSymbol.trimmed();
- else
- return QCPAxisTicker::getTickLabel(tickInPis, locale, formatChar, precision) + mPiSymbol;
- }
- }
- void QCPAxisTickerPi::simplifyFraction(int &numerator, int &denominator) const
- {
- if (numerator == 0 || denominator == 0)
- return;
- int num = numerator;
- int denom = denominator;
- while (denom != 0)
- {
- int oldDenom = denom;
- denom = num % denom;
- num = oldDenom;
- }
-
- numerator /= num;
- denominator /= num;
- }
- QString QCPAxisTickerPi::fractionToString(int numerator, int denominator) const
- {
- if (denominator == 0)
- {
- qDebug() << Q_FUNC_INFO << "called with zero denominator";
- return QString();
- }
- if (mFractionStyle == fsFloatingPoint)
- {
- qDebug() << Q_FUNC_INFO << "shouldn't be called with fraction style fsDecimal";
- return QString::number(numerator/double(denominator));
- }
- int sign = numerator*denominator < 0 ? -1 : 1;
- numerator = qAbs(numerator);
- denominator = qAbs(denominator);
- if (denominator == 1)
- {
- return QString::number(sign*numerator);
- } else
- {
- int integerPart = numerator/denominator;
- int remainder = numerator%denominator;
- if (remainder == 0)
- {
- return QString::number(sign*integerPart);
- } else
- {
- if (mFractionStyle == fsAsciiFractions)
- {
- return QString(QLatin1String("%1%2%3/%4"))
- .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
- .arg(integerPart > 0 ? QString::number(integerPart)+QLatin1String(" ") : QString(QLatin1String("")))
- .arg(remainder)
- .arg(denominator);
- } else if (mFractionStyle == fsUnicodeFractions)
- {
- return QString(QLatin1String("%1%2%3"))
- .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
- .arg(integerPart > 0 ? QString::number(integerPart) : QLatin1String(""))
- .arg(unicodeFraction(remainder, denominator));
- }
- }
- }
- return QString();
- }
- QString QCPAxisTickerPi::unicodeFraction(int numerator, int denominator) const
- {
- return unicodeSuperscript(numerator)+QChar(0x2044)+unicodeSubscript(denominator);
- }
- QString QCPAxisTickerPi::unicodeSuperscript(int number) const
- {
- if (number == 0)
- return QString(QChar(0x2070));
- QString result;
- while (number > 0)
- {
- const int digit = number%10;
- switch (digit)
- {
- case 1: { result.prepend(QChar(0x00B9)); break; }
- case 2: { result.prepend(QChar(0x00B2)); break; }
- case 3: { result.prepend(QChar(0x00B3)); break; }
- default: { result.prepend(QChar(0x2070+digit)); break; }
- }
- number /= 10;
- }
- return result;
- }
- QString QCPAxisTickerPi::unicodeSubscript(int number) const
- {
- if (number == 0)
- return QString(QChar(0x2080));
- QString result;
- while (number > 0)
- {
- result.prepend(QChar(0x2080+number%10));
- number /= 10;
- }
- return result;
- }
- QCPAxisTickerLog::QCPAxisTickerLog() :
- mLogBase(10.0),
- mSubTickCount(8),
- mLogBaseLnInv(1.0/qLn(mLogBase))
- {
- }
- void QCPAxisTickerLog::setLogBase(double base)
- {
- if (base > 0)
- {
- mLogBase = base;
- mLogBaseLnInv = 1.0/qLn(mLogBase);
- } else
- qDebug() << Q_FUNC_INFO << "log base has to be greater than zero:" << base;
- }
- void QCPAxisTickerLog::setSubTickCount(int subTicks)
- {
- if (subTicks >= 0)
- mSubTickCount = subTicks;
- else
- qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
- }
- int QCPAxisTickerLog::getSubTickCount(double tickStep)
- {
- Q_UNUSED(tickStep)
- return mSubTickCount;
- }
- QVector<double> QCPAxisTickerLog::createTickVector(double tickStep, const QCPRange &range)
- {
- QVector<double> result;
- if (range.lower > 0 && range.upper > 0)
- {
- const double baseTickCount = qLn(range.upper/range.lower)*mLogBaseLnInv;
- if (baseTickCount < 1.6)
- return QCPAxisTicker::createTickVector(tickStep, range);
- const double exactPowerStep = baseTickCount/double(mTickCount+1e-10);
- const double newLogBase = qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1));
- double currentTick = qPow(newLogBase, qFloor(qLn(range.lower)/qLn(newLogBase)));
- result.append(currentTick);
- while (currentTick < range.upper && currentTick > 0)
- {
- currentTick *= newLogBase;
- result.append(currentTick);
- }
- } else if (range.lower < 0 && range.upper < 0)
- {
- const double baseTickCount = qLn(range.lower/range.upper)*mLogBaseLnInv;
- if (baseTickCount < 1.6)
- return QCPAxisTicker::createTickVector(tickStep, range);
- const double exactPowerStep = baseTickCount/double(mTickCount+1e-10);
- const double newLogBase = qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1));
- double currentTick = -qPow(newLogBase, qCeil(qLn(-range.lower)/qLn(newLogBase)));
- result.append(currentTick);
- while (currentTick < range.upper && currentTick < 0)
- {
- currentTick /= newLogBase;
- result.append(currentTick);
- }
- } else
- {
- qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << range.lower << ".." << range.upper;
- }
- return result;
- }
- QCPGrid::QCPGrid(QCPAxis *parentAxis) :
- QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
- mSubGridVisible{},
- mAntialiasedSubGrid{},
- mAntialiasedZeroLine{},
- mParentAxis(parentAxis)
- {
-
- setParent(parentAxis);
- setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
- setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
- setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
- setSubGridVisible(false);
- setAntialiased(false);
- setAntialiasedSubGrid(false);
- setAntialiasedZeroLine(false);
- }
- void QCPGrid::setSubGridVisible(bool visible)
- {
- mSubGridVisible = visible;
- }
- void QCPGrid::setAntialiasedSubGrid(bool enabled)
- {
- mAntialiasedSubGrid = enabled;
- }
- void QCPGrid::setAntialiasedZeroLine(bool enabled)
- {
- mAntialiasedZeroLine = enabled;
- }
- void QCPGrid::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPGrid::setSubGridPen(const QPen &pen)
- {
- mSubGridPen = pen;
- }
- void QCPGrid::setZeroLinePen(const QPen &pen)
- {
- mZeroLinePen = pen;
- }
- void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
- }
- void QCPGrid::draw(QCPPainter *painter)
- {
- if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
- if (mParentAxis->subTicks() && mSubGridVisible)
- drawSubGridLines(painter);
- drawGridLines(painter);
- }
- void QCPGrid::drawGridLines(QCPPainter *painter) const
- {
- if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
- const int tickCount = mParentAxis->mTickVector.size();
- double t;
- if (mParentAxis->orientation() == Qt::Horizontal)
- {
-
- int zeroLineIndex = -1;
- if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
- {
- applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
- painter->setPen(mZeroLinePen);
- double epsilon = mParentAxis->range().size()*1E-6;
- for (int i=0; i<tickCount; ++i)
- {
- if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
- {
- zeroLineIndex = i;
- t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));
- painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
- break;
- }
- }
- }
-
- applyDefaultAntialiasingHint(painter);
- painter->setPen(mPen);
- for (int i=0; i<tickCount; ++i)
- {
- if (i == zeroLineIndex) continue;
- t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));
- painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
- }
- } else
- {
-
- int zeroLineIndex = -1;
- if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
- {
- applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
- painter->setPen(mZeroLinePen);
- double epsilon = mParentAxis->mRange.size()*1E-6;
- for (int i=0; i<tickCount; ++i)
- {
- if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
- {
- zeroLineIndex = i;
- t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));
- painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
- break;
- }
- }
- }
-
- applyDefaultAntialiasingHint(painter);
- painter->setPen(mPen);
- for (int i=0; i<tickCount; ++i)
- {
- if (i == zeroLineIndex) continue;
- t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i));
- painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
- }
- }
- }
- void QCPGrid::drawSubGridLines(QCPPainter *painter) const
- {
- if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
- applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid);
- double t;
- painter->setPen(mSubGridPen);
- if (mParentAxis->orientation() == Qt::Horizontal)
- {
- foreach (double tickCoord, mParentAxis->mSubTickVector)
- {
- t = mParentAxis->coordToPixel(tickCoord);
- painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
- }
- } else
- {
- foreach (double tickCoord, mParentAxis->mSubTickVector)
- {
- t = mParentAxis->coordToPixel(tickCoord);
- painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
- }
- }
- }
- QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) :
- QCPLayerable(parent->parentPlot(), QString(), parent),
-
- mAxisType(type),
- mAxisRect(parent),
- mPadding(5),
- mOrientation(orientation(type)),
- mSelectableParts(spAxis | spTickLabels | spAxisLabel),
- mSelectedParts(spNone),
- mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- mSelectedBasePen(QPen(Qt::blue, 2)),
-
- mLabel(),
- mLabelFont(mParentPlot->font()),
- mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
- mLabelColor(Qt::black),
- mSelectedLabelColor(Qt::blue),
-
- mTickLabels(true),
- mTickLabelFont(mParentPlot->font()),
- mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
- mTickLabelColor(Qt::black),
- mSelectedTickLabelColor(Qt::blue),
- mNumberPrecision(6),
- mNumberFormatChar('g'),
- mNumberBeautifulPowers(true),
-
- mTicks(true),
- mSubTicks(true),
- mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- mSelectedTickPen(QPen(Qt::blue, 2)),
- mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- mSelectedSubTickPen(QPen(Qt::blue, 2)),
-
- mRange(0, 5),
- mRangeReversed(false),
- mScaleType(stLinear),
-
- mGrid(new QCPGrid(this)),
- mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
- mTicker(new QCPAxisTicker),
- mCachedMarginValid(false),
- mCachedMargin(0),
- mDragging(false)
- {
- setParent(parent);
- mGrid->setVisible(false);
- setAntialiased(false);
- setLayer(mParentPlot->currentLayer());
- if (type == atTop)
- {
- setTickLabelPadding(3);
- setLabelPadding(6);
- } else if (type == atRight)
- {
- setTickLabelPadding(7);
- setLabelPadding(12);
- } else if (type == atBottom)
- {
- setTickLabelPadding(3);
- setLabelPadding(3);
- } else if (type == atLeft)
- {
- setTickLabelPadding(5);
- setLabelPadding(10);
- }
- }
- QCPAxis::~QCPAxis()
- {
- delete mAxisPainter;
- delete mGrid;
- }
- int QCPAxis::tickLabelPadding() const
- {
- return mAxisPainter->tickLabelPadding;
- }
- double QCPAxis::tickLabelRotation() const
- {
- return mAxisPainter->tickLabelRotation;
- }
- QCPAxis::LabelSide QCPAxis::tickLabelSide() const
- {
- return mAxisPainter->tickLabelSide;
- }
- QString QCPAxis::numberFormat() const
- {
- QString result;
- result.append(mNumberFormatChar);
- if (mNumberBeautifulPowers)
- {
- result.append(QLatin1Char('b'));
- if (mAxisPainter->numberMultiplyCross)
- result.append(QLatin1Char('c'));
- }
- return result;
- }
- int QCPAxis::tickLengthIn() const
- {
- return mAxisPainter->tickLengthIn;
- }
- int QCPAxis::tickLengthOut() const
- {
- return mAxisPainter->tickLengthOut;
- }
- int QCPAxis::subTickLengthIn() const
- {
- return mAxisPainter->subTickLengthIn;
- }
- int QCPAxis::subTickLengthOut() const
- {
- return mAxisPainter->subTickLengthOut;
- }
- int QCPAxis::labelPadding() const
- {
- return mAxisPainter->labelPadding;
- }
- int QCPAxis::offset() const
- {
- return int(mAxisPainter->offset);
- }
- QCPLineEnding QCPAxis::lowerEnding() const
- {
- return mAxisPainter->lowerEnding;
- }
- QCPLineEnding QCPAxis::upperEnding() const
- {
- return mAxisPainter->upperEnding;
- }
- void QCPAxis::setScaleType(QCPAxis::ScaleType type)
- {
- if (mScaleType != type)
- {
- mScaleType = type;
- if (mScaleType == stLogarithmic)
- setRange(mRange.sanitizedForLogScale());
- mCachedMarginValid = false;
- emit scaleTypeChanged(mScaleType);
- }
- }
- void QCPAxis::setRange(const QCPRange &range)
- {
- if (qFuzzyCompare(range.lower, mRange.lower) && qFuzzyCompare(range.upper, mRange.upper))
- return;
- if (!QCPRange::validRange(range)) return;
- QCPRange oldRange = mRange;
- if (mScaleType == stLogarithmic)
- {
- mRange = range.sanitizedForLogScale();
- } else
- {
- mRange = range.sanitizedForLinScale();
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPAxis::setSelectableParts(const SelectableParts &selectable)
- {
- if (mSelectableParts != selectable)
- {
- mSelectableParts = selectable;
- emit selectableChanged(mSelectableParts);
- }
- }
- void QCPAxis::setSelectedParts(const SelectableParts &selected)
- {
- if (mSelectedParts != selected)
- {
- mSelectedParts = selected;
- emit selectionChanged(mSelectedParts);
- }
- }
- void QCPAxis::setRange(double lower, double upper)
- {
- if (qFuzzyCompare(lower, mRange.lower) && qFuzzyCompare(upper, mRange.upper))
- return;
- if (!QCPRange::validRange(lower, upper)) return;
- QCPRange oldRange = mRange;
- mRange.lower = lower;
- mRange.upper = upper;
- if (mScaleType == stLogarithmic)
- {
- mRange = mRange.sanitizedForLogScale();
- } else
- {
- mRange = mRange.sanitizedForLinScale();
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
- {
- if (alignment == Qt::AlignLeft)
- setRange(position, position+size);
- else if (alignment == Qt::AlignRight)
- setRange(position-size, position);
- else
- setRange(position-size/2.0, position+size/2.0);
- }
- void QCPAxis::setRangeLower(double lower)
- {
- if (qFuzzyCompare(mRange.lower, lower))
- return;
- QCPRange oldRange = mRange;
- mRange.lower = lower;
- if (mScaleType == stLogarithmic)
- {
- mRange = mRange.sanitizedForLogScale();
- } else
- {
- mRange = mRange.sanitizedForLinScale();
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPAxis::setRangeUpper(double upper)
- {
- if (qFuzzyCompare(mRange.upper, upper))
- return;
- QCPRange oldRange = mRange;
- mRange.upper = upper;
- if (mScaleType == stLogarithmic)
- {
- mRange = mRange.sanitizedForLogScale();
- } else
- {
- mRange = mRange.sanitizedForLinScale();
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPAxis::setRangeReversed(bool reversed)
- {
- mRangeReversed = reversed;
- }
- void QCPAxis::setTicker(QSharedPointer<QCPAxisTicker> ticker)
- {
- if (ticker)
- mTicker = ticker;
- else
- qDebug() << Q_FUNC_INFO << "can not set nullptr as axis ticker";
-
- }
- void QCPAxis::setTicks(bool show)
- {
- if (mTicks != show)
- {
- mTicks = show;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setTickLabels(bool show)
- {
- if (mTickLabels != show)
- {
- mTickLabels = show;
- mCachedMarginValid = false;
- if (!mTickLabels)
- mTickVectorLabels.clear();
- }
- }
- void QCPAxis::setTickLabelPadding(int padding)
- {
- if (mAxisPainter->tickLabelPadding != padding)
- {
- mAxisPainter->tickLabelPadding = padding;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setTickLabelFont(const QFont &font)
- {
- if (font != mTickLabelFont)
- {
- mTickLabelFont = font;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setTickLabelColor(const QColor &color)
- {
- mTickLabelColor = color;
- }
- void QCPAxis::setTickLabelRotation(double degrees)
- {
- if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
- {
- mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setTickLabelSide(LabelSide side)
- {
- mAxisPainter->tickLabelSide = side;
- mCachedMarginValid = false;
- }
- void QCPAxis::setNumberFormat(const QString &formatCode)
- {
- if (formatCode.isEmpty())
- {
- qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
- return;
- }
- mCachedMarginValid = false;
-
- QString allowedFormatChars(QLatin1String("eEfgG"));
- if (allowedFormatChars.contains(formatCode.at(0)))
- {
- mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
- } else
- {
- qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
- return;
- }
- if (formatCode.length() < 2)
- {
- mNumberBeautifulPowers = false;
- mAxisPainter->numberMultiplyCross = false;
- return;
- }
-
- if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
- {
- mNumberBeautifulPowers = true;
- } else
- {
- qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
- return;
- }
- if (formatCode.length() < 3)
- {
- mAxisPainter->numberMultiplyCross = false;
- return;
- }
-
- if (formatCode.at(2) == QLatin1Char('c'))
- {
- mAxisPainter->numberMultiplyCross = true;
- } else if (formatCode.at(2) == QLatin1Char('d'))
- {
- mAxisPainter->numberMultiplyCross = false;
- } else
- {
- qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
- return;
- }
- }
- void QCPAxis::setNumberPrecision(int precision)
- {
- if (mNumberPrecision != precision)
- {
- mNumberPrecision = precision;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setTickLength(int inside, int outside)
- {
- setTickLengthIn(inside);
- setTickLengthOut(outside);
- }
- void QCPAxis::setTickLengthIn(int inside)
- {
- if (mAxisPainter->tickLengthIn != inside)
- {
- mAxisPainter->tickLengthIn = inside;
- }
- }
- void QCPAxis::setTickLengthOut(int outside)
- {
- if (mAxisPainter->tickLengthOut != outside)
- {
- mAxisPainter->tickLengthOut = outside;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setSubTicks(bool show)
- {
- if (mSubTicks != show)
- {
- mSubTicks = show;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setSubTickLength(int inside, int outside)
- {
- setSubTickLengthIn(inside);
- setSubTickLengthOut(outside);
- }
- void QCPAxis::setSubTickLengthIn(int inside)
- {
- if (mAxisPainter->subTickLengthIn != inside)
- {
- mAxisPainter->subTickLengthIn = inside;
- }
- }
- void QCPAxis::setSubTickLengthOut(int outside)
- {
- if (mAxisPainter->subTickLengthOut != outside)
- {
- mAxisPainter->subTickLengthOut = outside;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setBasePen(const QPen &pen)
- {
- mBasePen = pen;
- }
- void QCPAxis::setTickPen(const QPen &pen)
- {
- mTickPen = pen;
- }
- void QCPAxis::setSubTickPen(const QPen &pen)
- {
- mSubTickPen = pen;
- }
- void QCPAxis::setLabelFont(const QFont &font)
- {
- if (mLabelFont != font)
- {
- mLabelFont = font;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setLabelColor(const QColor &color)
- {
- mLabelColor = color;
- }
- void QCPAxis::setLabel(const QString &str)
- {
- if (mLabel != str)
- {
- mLabel = str;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setLabelPadding(int padding)
- {
- if (mAxisPainter->labelPadding != padding)
- {
- mAxisPainter->labelPadding = padding;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setPadding(int padding)
- {
- if (mPadding != padding)
- {
- mPadding = padding;
- mCachedMarginValid = false;
- }
- }
- void QCPAxis::setOffset(int offset)
- {
- mAxisPainter->offset = offset;
- }
- void QCPAxis::setSelectedTickLabelFont(const QFont &font)
- {
- if (font != mSelectedTickLabelFont)
- {
- mSelectedTickLabelFont = font;
-
- }
- }
- void QCPAxis::setSelectedLabelFont(const QFont &font)
- {
- mSelectedLabelFont = font;
-
- }
- void QCPAxis::setSelectedTickLabelColor(const QColor &color)
- {
- if (color != mSelectedTickLabelColor)
- {
- mSelectedTickLabelColor = color;
- }
- }
- void QCPAxis::setSelectedLabelColor(const QColor &color)
- {
- mSelectedLabelColor = color;
- }
- void QCPAxis::setSelectedBasePen(const QPen &pen)
- {
- mSelectedBasePen = pen;
- }
- void QCPAxis::setSelectedTickPen(const QPen &pen)
- {
- mSelectedTickPen = pen;
- }
- void QCPAxis::setSelectedSubTickPen(const QPen &pen)
- {
- mSelectedSubTickPen = pen;
- }
- void QCPAxis::setLowerEnding(const QCPLineEnding &ending)
- {
- mAxisPainter->lowerEnding = ending;
- }
- void QCPAxis::setUpperEnding(const QCPLineEnding &ending)
- {
- mAxisPainter->upperEnding = ending;
- }
- void QCPAxis::moveRange(double diff)
- {
- QCPRange oldRange = mRange;
- if (mScaleType == stLinear)
- {
- mRange.lower += diff;
- mRange.upper += diff;
- } else
- {
- mRange.lower *= diff;
- mRange.upper *= diff;
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPAxis::scaleRange(double factor)
- {
- scaleRange(factor, range().center());
- }
- void QCPAxis::scaleRange(double factor, double center)
- {
- QCPRange oldRange = mRange;
- if (mScaleType == stLinear)
- {
- QCPRange newRange;
- newRange.lower = (mRange.lower-center)*factor + center;
- newRange.upper = (mRange.upper-center)*factor + center;
- if (QCPRange::validRange(newRange))
- mRange = newRange.sanitizedForLinScale();
- } else
- {
- if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0))
- {
- QCPRange newRange;
- newRange.lower = qPow(mRange.lower/center, factor)*center;
- newRange.upper = qPow(mRange.upper/center, factor)*center;
- if (QCPRange::validRange(newRange))
- mRange = newRange.sanitizedForLogScale();
- } else
- qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
- {
- double otherPixelSize, ownPixelSize;
- if (otherAxis->orientation() == Qt::Horizontal)
- otherPixelSize = otherAxis->axisRect()->width();
- else
- otherPixelSize = otherAxis->axisRect()->height();
- if (orientation() == Qt::Horizontal)
- ownPixelSize = axisRect()->width();
- else
- ownPixelSize = axisRect()->height();
- double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/double(otherPixelSize);
- setRange(range().center(), newRangeSize, Qt::AlignCenter);
- }
- void QCPAxis::rescale(bool onlyVisiblePlottables)
- {
- QCPRange newRange;
- bool haveRange = false;
- foreach (QCPAbstractPlottable *plottable, plottables())
- {
- if (!plottable->realVisibility() && onlyVisiblePlottables)
- continue;
- QCPRange plottableRange;
- bool currentFoundRange;
- QCP::SignDomain signDomain = QCP::sdBoth;
- if (mScaleType == stLogarithmic)
- signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);
- if (plottable->keyAxis() == this)
- plottableRange = plottable->getKeyRange(currentFoundRange, signDomain);
- else
- plottableRange = plottable->getValueRange(currentFoundRange, signDomain);
- if (currentFoundRange)
- {
- if (!haveRange)
- newRange = plottableRange;
- else
- newRange.expand(plottableRange);
- haveRange = true;
- }
- }
- if (haveRange)
- {
- if (!QCPRange::validRange(newRange))
- {
- double center = (newRange.lower+newRange.upper)*0.5;
- if (mScaleType == stLinear)
- {
- newRange.lower = center-mRange.size()/2.0;
- newRange.upper = center+mRange.size()/2.0;
- } else
- {
- newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
- newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
- }
- }
- setRange(newRange);
- }
- }
- double QCPAxis::pixelToCoord(double value) const
- {
- if (orientation() == Qt::Horizontal)
- {
- auto width = mAxisRect->right() - mAxisRect->left();
-
- if (mScaleType == stLinear)
- {
- if (!mRangeReversed)
- return (value-mAxisRect->left())/width*mRange.size()+mRange.lower;
- else
- return -(value-mAxisRect->left())/width*mRange.size()+mRange.upper;
- } else
- {
- if (!mRangeReversed)
- return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/width)*mRange.lower;
- else
- return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/width)*mRange.upper;
- }
- } else
- {
- auto height = mAxisRect->bottom() - mAxisRect->top();
-
- if (mScaleType == stLinear)
- {
- if (!mRangeReversed)
- return (mAxisRect->bottom()-value)/height*mRange.size()+mRange.lower;
- else
- return -(mAxisRect->bottom()-value)/height*mRange.size()+mRange.upper;
- } else
- {
- if (!mRangeReversed)
- return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/height)*mRange.lower;
- else
- return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/height)*mRange.upper;
- }
- }
- }
- double QCPAxis::coordToPixel(double value) const
- {
- if (orientation() == Qt::Horizontal)
- {
- auto width = mAxisRect->right() - mAxisRect->left();
-
- if (mScaleType == stLinear)
- {
- if (!mRangeReversed)
- return (value-mRange.lower)/mRange.size()*width+mAxisRect->left();
- else
- return (mRange.upper-value)/mRange.size()*width+mAxisRect->left();
- } else
- {
- if (value >= 0.0 && mRange.upper < 0.0)
- return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
- else if (value <= 0.0 && mRange.upper >= 0.0)
- return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
- else
- {
- if (!mRangeReversed)
- return qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*width+mAxisRect->left();
- else
- return qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*width+mAxisRect->left();
- }
- }
- } else
- {
- auto height = mAxisRect->bottom() - mAxisRect->top();
-
- if (mScaleType == stLinear)
- {
- if (!mRangeReversed)
- return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*height;
- else
- return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*height;
- } else
- {
- if (value >= 0.0 && mRange.upper < 0.0)
- return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
- else if (value <= 0.0 && mRange.upper >= 0.0)
- return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
- else
- {
- if (!mRangeReversed)
- return mAxisRect->bottom()-qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*height;
- else
- return mAxisRect->bottom()-qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*height;
- }
- }
- }
- }
- QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const
- {
- if (!mVisible)
- return spNone;
- if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
- return spAxis;
- else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
- return spTickLabels;
- else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
- return spAxisLabel;
- else
- return spNone;
- }
- double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- if (!mParentPlot) return -1;
- SelectablePart part = getPartAt(pos);
- if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
- return -1;
- if (details)
- details->setValue(part);
- return mParentPlot->selectionTolerance()*0.99;
- }
- QList<QCPAbstractPlottable*> QCPAxis::plottables() const
- {
- QList<QCPAbstractPlottable*> result;
- if (!mParentPlot) return result;
- foreach (QCPAbstractPlottable *plottable, mParentPlot->mPlottables)
- {
- if (plottable->keyAxis() == this || plottable->valueAxis() == this)
- result.append(plottable);
- }
- return result;
- }
- QList<QCPGraph*> QCPAxis::graphs() const
- {
- QList<QCPGraph*> result;
- if (!mParentPlot) return result;
- foreach (QCPGraph *graph, mParentPlot->mGraphs)
- {
- if (graph->keyAxis() == this || graph->valueAxis() == this)
- result.append(graph);
- }
- return result;
- }
- QList<QCPAbstractItem*> QCPAxis::items() const
- {
- QList<QCPAbstractItem*> result;
- if (!mParentPlot) return result;
- foreach (QCPAbstractItem *item, mParentPlot->mItems)
- {
- foreach (QCPItemPosition *position, item->positions())
- {
- if (position->keyAxis() == this || position->valueAxis() == this)
- {
- result.append(item);
- break;
- }
- }
- }
- return result;
- }
- QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side)
- {
- switch (side)
- {
- case QCP::msLeft: return atLeft;
- case QCP::msRight: return atRight;
- case QCP::msTop: return atTop;
- case QCP::msBottom: return atBottom;
- default: break;
- }
- qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << static_cast<int>(side);
- return atLeft;
- }
- QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type)
- {
- switch (type)
- {
- case atLeft: return atRight;
- case atRight: return atLeft;
- case atBottom: return atTop;
- case atTop: return atBottom;
- }
- qDebug() << Q_FUNC_INFO << "invalid axis type";
- return atLeft;
- }
- void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
- {
- Q_UNUSED(event)
- SelectablePart part = details.value<SelectablePart>();
- if (mSelectableParts.testFlag(part))
- {
- SelectableParts selBefore = mSelectedParts;
- setSelectedParts(additive ? mSelectedParts^part : part);
- if (selectionStateChanged)
- *selectionStateChanged = mSelectedParts != selBefore;
- }
- }
- void QCPAxis::deselectEvent(bool *selectionStateChanged)
- {
- SelectableParts selBefore = mSelectedParts;
- setSelectedParts(mSelectedParts & ~mSelectableParts);
- if (selectionStateChanged)
- *selectionStateChanged = mSelectedParts != selBefore;
- }
- void QCPAxis::mousePressEvent(QMouseEvent *event, const QVariant &details)
- {
- Q_UNUSED(details)
- if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag) ||
- !mAxisRect->rangeDrag().testFlag(orientation()) ||
- !mAxisRect->rangeDragAxes(orientation()).contains(this))
- {
- event->ignore();
- return;
- }
- if (event->buttons() & Qt::LeftButton)
- {
- mDragging = true;
-
- if (mParentPlot->noAntialiasingOnDrag())
- {
- mAADragBackup = mParentPlot->antialiasedElements();
- mNotAADragBackup = mParentPlot->notAntialiasedElements();
- }
-
- if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
- mDragStartRange = mRange;
- }
- }
- void QCPAxis::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
- {
- if (mDragging)
- {
- const double startPixel = orientation() == Qt::Horizontal ? startPos.x() : startPos.y();
- const double currentPixel = orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y();
- if (mScaleType == QCPAxis::stLinear)
- {
- const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel);
- setRange(mDragStartRange.lower+diff, mDragStartRange.upper+diff);
- } else if (mScaleType == QCPAxis::stLogarithmic)
- {
- const double diff = pixelToCoord(startPixel) / pixelToCoord(currentPixel);
- setRange(mDragStartRange.lower*diff, mDragStartRange.upper*diff);
- }
- if (mParentPlot->noAntialiasingOnDrag())
- mParentPlot->setNotAntialiasedElements(QCP::aeAll);
- mParentPlot->replot(QCustomPlot::rpQueuedReplot);
- }
- }
- void QCPAxis::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
- {
- Q_UNUSED(event)
- Q_UNUSED(startPos)
- mDragging = false;
- if (mParentPlot->noAntialiasingOnDrag())
- {
- mParentPlot->setAntialiasedElements(mAADragBackup);
- mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
- }
- }
- void QCPAxis::wheelEvent(QWheelEvent *event)
- {
-
- if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom) ||
- !mAxisRect->rangeZoom().testFlag(orientation()) ||
- !mAxisRect->rangeZoomAxes(orientation()).contains(this))
- {
- event->ignore();
- return;
- }
- #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
- const double delta = event->delta();
- #else
- const double delta = event->angleDelta().y();
- #endif
- #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
- const QPointF pos = event->pos();
- #else
- const QPointF pos = event->position();
- #endif
- const double wheelSteps = delta/120.0;
- const double factor = qPow(mAxisRect->rangeZoomFactor(orientation()), wheelSteps);
- scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? pos.x() : pos.y()));
- mParentPlot->replot();
- }
- void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
- }
- void QCPAxis::draw(QCPPainter *painter)
- {
- QVector<double> subTickPositions;
- QVector<double> tickPositions;
- QVector<QString> tickLabels;
- tickPositions.reserve(mTickVector.size());
- tickLabels.reserve(mTickVector.size());
- subTickPositions.reserve(mSubTickVector.size());
- if (mTicks)
- {
- for (int i=0; i<mTickVector.size(); ++i)
- {
- tickPositions.append(coordToPixel(mTickVector.at(i)));
- if (mTickLabels)
- tickLabels.append(mTickVectorLabels.at(i));
- }
- if (mSubTicks)
- {
- const int subTickCount = mSubTickVector.size();
- for (int i=0; i<subTickCount; ++i)
- subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
- }
- }
-
-
- mAxisPainter->type = mAxisType;
- mAxisPainter->basePen = getBasePen();
- mAxisPainter->labelFont = getLabelFont();
- mAxisPainter->labelColor = getLabelColor();
- mAxisPainter->label = mLabel;
- mAxisPainter->substituteExponent = mNumberBeautifulPowers;
- mAxisPainter->tickPen = getTickPen();
- mAxisPainter->subTickPen = getSubTickPen();
- mAxisPainter->tickLabelFont = getTickLabelFont();
- mAxisPainter->tickLabelColor = getTickLabelColor();
- mAxisPainter->axisRect = mAxisRect->rect();
- mAxisPainter->viewportRect = mParentPlot->viewport();
- mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
- mAxisPainter->reversedEndings = mRangeReversed;
- mAxisPainter->tickPositions = tickPositions;
- mAxisPainter->tickLabels = tickLabels;
- mAxisPainter->subTickPositions = subTickPositions;
- mAxisPainter->draw(painter);
- }
- void QCPAxis::setupTickVectors()
- {
- if (!mParentPlot) return;
- if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
- QVector<QString> oldLabels = mTickVectorLabels;
- mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : nullptr, mTickLabels ? &mTickVectorLabels : nullptr);
- mCachedMarginValid &= mTickVectorLabels == oldLabels;
- }
- QPen QCPAxis::getBasePen() const
- {
- return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
- }
- QPen QCPAxis::getTickPen() const
- {
- return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
- }
- QPen QCPAxis::getSubTickPen() const
- {
- return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
- }
- QFont QCPAxis::getTickLabelFont() const
- {
- return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
- }
- QFont QCPAxis::getLabelFont() const
- {
- return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
- }
- QColor QCPAxis::getTickLabelColor() const
- {
- return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
- }
- QColor QCPAxis::getLabelColor() const
- {
- return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
- }
- int QCPAxis::calculateMargin()
- {
- if (!mVisible)
- return 0;
- if (mCachedMarginValid)
- return mCachedMargin;
-
- int margin = 0;
- QVector<double> tickPositions;
- QVector<QString> tickLabels;
- tickPositions.reserve(mTickVector.size());
- tickLabels.reserve(mTickVector.size());
- if (mTicks)
- {
- for (int i=0; i<mTickVector.size(); ++i)
- {
- tickPositions.append(coordToPixel(mTickVector.at(i)));
- if (mTickLabels)
- tickLabels.append(mTickVectorLabels.at(i));
- }
- }
-
-
- mAxisPainter->type = mAxisType;
- mAxisPainter->labelFont = getLabelFont();
- mAxisPainter->label = mLabel;
- mAxisPainter->tickLabelFont = mTickLabelFont;
- mAxisPainter->axisRect = mAxisRect->rect();
- mAxisPainter->viewportRect = mParentPlot->viewport();
- mAxisPainter->tickPositions = tickPositions;
- mAxisPainter->tickLabels = tickLabels;
- margin += mAxisPainter->size();
- margin += mPadding;
- mCachedMargin = margin;
- mCachedMarginValid = true;
- return margin;
- }
- QCP::Interaction QCPAxis::selectionCategory() const
- {
- return QCP::iSelectAxes;
- }
- QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) :
- type(QCPAxis::atLeft),
- basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- lowerEnding(QCPLineEnding::esNone),
- upperEnding(QCPLineEnding::esNone),
- labelPadding(0),
- tickLabelPadding(0),
- tickLabelRotation(0),
- tickLabelSide(QCPAxis::lsOutside),
- substituteExponent(true),
- numberMultiplyCross(false),
- tickLengthIn(5),
- tickLengthOut(0),
- subTickLengthIn(2),
- subTickLengthOut(0),
- tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- offset(0),
- abbreviateDecimalPowers(false),
- reversedEndings(false),
- mParentPlot(parentPlot),
- mLabelCache(16)
- {
- }
- QCPAxisPainterPrivate::~QCPAxisPainterPrivate()
- {
- }
- void QCPAxisPainterPrivate::draw(QCPPainter *painter)
- {
- QByteArray newHash = generateLabelParameterHash();
- if (newHash != mLabelParameterHash)
- {
- mLabelCache.clear();
- mLabelParameterHash = newHash;
- }
- QPointF origin;
- switch (type)
- {
- case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPointF(-offset, 0); break;
- case QCPAxis::atRight: origin = axisRect.bottomRight()+QPointF(+offset, 0); break;
- case QCPAxis::atTop: origin = axisRect.topLeft() +QPointF(0, -offset); break;
- case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPointF(0, +offset); break;
- }
- double xCor = 0, yCor = 0;
- switch (type)
- {
- case QCPAxis::atTop: yCor = -1; break;
- case QCPAxis::atRight: xCor = 1; break;
- default: break;
- }
- int margin = 0;
-
- QLineF baseLine;
- painter->setPen(basePen);
- if (QCPAxis::orientation(type) == Qt::Horizontal)
- baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
- else
- baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
- if (reversedEndings)
- baseLine = QLineF(baseLine.p2(), baseLine.p1());
- painter->drawLine(baseLine);
-
- if (!tickPositions.isEmpty())
- {
- painter->setPen(tickPen);
- int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
- if (QCPAxis::orientation(type) == Qt::Horizontal)
- {
- foreach (double tickPos, tickPositions)
- painter->drawLine(QLineF(tickPos+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPos+xCor, origin.y()+tickLengthIn*tickDir+yCor));
- } else
- {
- foreach (double tickPos, tickPositions)
- painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPos+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPos+yCor));
- }
- }
-
- if (!subTickPositions.isEmpty())
- {
- painter->setPen(subTickPen);
-
- int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
- if (QCPAxis::orientation(type) == Qt::Horizontal)
- {
- foreach (double subTickPos, subTickPositions)
- painter->drawLine(QLineF(subTickPos+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPos+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
- } else
- {
- foreach (double subTickPos, subTickPositions)
- painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPos+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPos+yCor));
- }
- }
- margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
-
- bool antialiasingBackup = painter->antialiasing();
- painter->setAntialiasing(true);
- painter->setBrush(QBrush(basePen.color()));
- QCPVector2D baseLineVector(baseLine.dx(), baseLine.dy());
- if (lowerEnding.style() != QCPLineEnding::esNone)
- lowerEnding.draw(painter, QCPVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
- if (upperEnding.style() != QCPLineEnding::esNone)
- upperEnding.draw(painter, QCPVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
- painter->setAntialiasing(antialiasingBackup);
-
- QRect oldClipRect;
- if (tickLabelSide == QCPAxis::lsInside)
- {
- oldClipRect = painter->clipRegion().boundingRect();
- painter->setClipRect(axisRect);
- }
- QSize tickLabelsSize(0, 0);
- if (!tickLabels.isEmpty())
- {
- if (tickLabelSide == QCPAxis::lsOutside)
- margin += tickLabelPadding;
- painter->setFont(tickLabelFont);
- painter->setPen(QPen(tickLabelColor));
- const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
- int distanceToAxis = margin;
- if (tickLabelSide == QCPAxis::lsInside)
- distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
- for (int i=0; i<maxLabelIndex; ++i)
- placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
- if (tickLabelSide == QCPAxis::lsOutside)
- margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
- }
- if (tickLabelSide == QCPAxis::lsInside)
- painter->setClipRect(oldClipRect);
-
- QRect labelBounds;
- if (!label.isEmpty())
- {
- margin += labelPadding;
- painter->setFont(labelFont);
- painter->setPen(QPen(labelColor));
- labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
- if (type == QCPAxis::atLeft)
- {
- QTransform oldTransform = painter->transform();
- painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
- painter->rotate(-90);
- painter->drawText(0, 0, int(axisRect.height()), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
- painter->setTransform(oldTransform);
- }
- else if (type == QCPAxis::atRight)
- {
- QTransform oldTransform = painter->transform();
- painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
- painter->rotate(90);
- painter->drawText(0, 0, int(axisRect.height()), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
- painter->setTransform(oldTransform);
- }
- else if (type == QCPAxis::atTop)
- painter->drawText(int(origin.x()), int(origin.y())-margin-labelBounds.height(), int(axisRect.width()), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
- else if (type == QCPAxis::atBottom)
- painter->drawText(int(origin.x()), int(origin.y())+margin, int(axisRect.width()), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
- }
-
- int selectionTolerance = 0;
- if (mParentPlot)
- selectionTolerance = mParentPlot->selectionTolerance();
- else
- qDebug() << Q_FUNC_INFO << "mParentPlot is null";
- int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
- int selAxisInSize = selectionTolerance;
- int selTickLabelSize;
- int selTickLabelOffset;
- if (tickLabelSide == QCPAxis::lsOutside)
- {
- selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
- selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
- } else
- {
- selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
- selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
- }
- int selLabelSize = labelBounds.height();
- int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
- if (type == QCPAxis::atLeft)
- {
- mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
- mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
- mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
- } else if (type == QCPAxis::atRight)
- {
- mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
- mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
- mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
- } else if (type == QCPAxis::atTop)
- {
- mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
- mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
- mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
- } else if (type == QCPAxis::atBottom)
- {
- mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
- mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
- mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
- }
- mAxisSelectionBox = mAxisSelectionBox.normalized();
- mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();
- mLabelSelectionBox = mLabelSelectionBox.normalized();
-
-
-
- }
- int QCPAxisPainterPrivate::size()
- {
- int result = 0;
- QByteArray newHash = generateLabelParameterHash();
- if (newHash != mLabelParameterHash)
- {
- mLabelCache.clear();
- mLabelParameterHash = newHash;
- }
-
- if (!tickPositions.isEmpty())
- result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
-
- if (tickLabelSide == QCPAxis::lsOutside)
- {
- QSize tickLabelsSize(0, 0);
- if (!tickLabels.isEmpty())
- {
- foreach (const QString &tickLabel, tickLabels)
- getMaxTickLabelSize(tickLabelFont, tickLabel, &tickLabelsSize);
- result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
- result += tickLabelPadding;
- }
- }
-
- if (!label.isEmpty())
- {
- QFontMetrics fontMetrics(labelFont);
- QRect bounds;
- bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
- result += bounds.height() + labelPadding;
- }
- return result;
- }
- void QCPAxisPainterPrivate::clearCache()
- {
- mLabelCache.clear();
- }
- QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const
- {
- QByteArray result;
- result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));
- result.append(QByteArray::number(tickLabelRotation));
- result.append(QByteArray::number(int(tickLabelSide)));
- result.append(QByteArray::number(int(substituteExponent)));
- result.append(QByteArray::number(int(numberMultiplyCross)));
- result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
- result.append(tickLabelFont.toString().toLatin1());
- return result;
- }
- void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
- {
-
- if (text.isEmpty()) return;
- QSize finalSize;
- QPointF labelAnchor;
- switch (type)
- {
- case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
- case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
- case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
- case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
- }
- if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching))
- {
- CachedLabel *cachedLabel = mLabelCache.take(text);
- if (!cachedLabel)
- {
- cachedLabel = new CachedLabel;
- TickLabelData labelData = getTickLabelData(painter->font(), text);
- cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
- if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio()))
- {
- cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio());
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- # ifdef QCP_DEVICEPIXELRATIO_FLOAT
- cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF());
- # else
- cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio());
- # endif
- #endif
- } else
- cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
- cachedLabel->pixmap.fill(Qt::transparent);
- QCPPainter cachePainter(&cachedLabel->pixmap);
- cachePainter.setPen(painter->pen());
- drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
- }
-
- bool labelClippedByBorder = false;
- if (tickLabelSide == QCPAxis::lsOutside)
- {
- if (QCPAxis::orientation(type) == Qt::Horizontal)
- labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width()/mParentPlot->bufferDevicePixelRatio() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
- else
- labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height()/mParentPlot->bufferDevicePixelRatio() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
- }
- if (!labelClippedByBorder)
- {
- painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
- finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();
- }
- mLabelCache.insert(text, cachedLabel);
- } else
- {
- TickLabelData labelData = getTickLabelData(painter->font(), text);
- QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
-
- bool labelClippedByBorder = false;
- if (tickLabelSide == QCPAxis::lsOutside)
- {
- if (QCPAxis::orientation(type) == Qt::Horizontal)
- labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
- else
- labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
- }
- if (!labelClippedByBorder)
- {
- drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
- finalSize = labelData.rotatedTotalBounds.size();
- }
- }
-
- if (finalSize.width() > tickLabelsSize->width())
- tickLabelsSize->setWidth(finalSize.width());
- if (finalSize.height() > tickLabelsSize->height())
- tickLabelsSize->setHeight(finalSize.height());
- }
- void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
- {
-
- QTransform oldTransform = painter->transform();
- QFont oldFont = painter->font();
-
- painter->translate(x, y);
- if (!qFuzzyIsNull(tickLabelRotation))
- painter->rotate(tickLabelRotation);
-
- if (!labelData.expPart.isEmpty())
- {
- painter->setFont(labelData.baseFont);
- painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
- if (!labelData.suffixPart.isEmpty())
- painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart);
- painter->setFont(labelData.expFont);
- painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
- } else
- {
- painter->setFont(labelData.baseFont);
- painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
- }
-
- painter->setTransform(oldTransform);
- painter->setFont(oldFont);
- }
- QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const
- {
- TickLabelData result;
-
- bool useBeautifulPowers = false;
- int ePos = -1;
- int eLast = -1;
- if (substituteExponent)
- {
- ePos = text.indexOf(QLatin1Char('e'));
- if (ePos > 0 && text.at(ePos-1).isDigit())
- {
- eLast = ePos;
- while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))
- ++eLast;
- if (eLast > ePos)
- useBeautifulPowers = true;
- }
- }
-
- result.baseFont = font;
- if (result.baseFont.pointSizeF() > 0)
- result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05);
- if (useBeautifulPowers)
- {
-
- result.basePart = text.left(ePos);
- result.suffixPart = text.mid(eLast+1);
-
- if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
- result.basePart = QLatin1String("10");
- else
- result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
- result.expPart = text.mid(ePos+1, eLast-ePos);
-
- while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0'))
- result.expPart.remove(1, 1);
- if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
- result.expPart.remove(0, 1);
-
- result.expFont = font;
- if (result.expFont.pointSize() > 0)
- result.expFont.setPointSize(int(result.expFont.pointSize()*0.75));
- else
- result.expFont.setPixelSize(int(result.expFont.pixelSize()*0.75));
-
- result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
- result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
- if (!result.suffixPart.isEmpty())
- result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart);
- result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+result.suffixBounds.width()+2, 0);
- } else
- {
- result.basePart = text;
- result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
- }
- result.totalBounds.moveTopLeft(QPoint(0, 0));
-
- result.rotatedTotalBounds = result.totalBounds;
- if (!qFuzzyIsNull(tickLabelRotation))
- {
- QTransform transform;
- transform.rotate(tickLabelRotation);
- result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
- }
- return result;
- }
- QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const
- {
-
- bool doRotation = !qFuzzyIsNull(tickLabelRotation);
- bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0);
- double radians = tickLabelRotation/180.0*M_PI;
- double x = 0;
- double y = 0;
- if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside))
- {
- if (doRotation)
- {
- if (tickLabelRotation > 0)
- {
- x = -qCos(radians)*labelData.totalBounds.width();
- y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
- } else
- {
- x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
- y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
- }
- } else
- {
- x = -labelData.totalBounds.width();
- y = -labelData.totalBounds.height()/2.0;
- }
- } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside))
- {
- if (doRotation)
- {
- if (tickLabelRotation > 0)
- {
- x = +qSin(radians)*labelData.totalBounds.height();
- y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
- } else
- {
- x = 0;
- y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
- }
- } else
- {
- x = 0;
- y = -labelData.totalBounds.height()/2.0;
- }
- } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside))
- {
- if (doRotation)
- {
- if (tickLabelRotation > 0)
- {
- x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
- y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
- } else
- {
- x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
- y = -qCos(-radians)*labelData.totalBounds.height();
- }
- } else
- {
- x = -labelData.totalBounds.width()/2.0;
- y = -labelData.totalBounds.height();
- }
- } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside))
- {
- if (doRotation)
- {
- if (tickLabelRotation > 0)
- {
- x = +qSin(radians)*labelData.totalBounds.height()/2.0;
- y = 0;
- } else
- {
- x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
- y = +qSin(-radians)*labelData.totalBounds.width();
- }
- } else
- {
- x = -labelData.totalBounds.width()/2.0;
- y = 0;
- }
- }
- return {x, y};
- }
- void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
- {
-
- QSize finalSize;
- if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text))
- {
- const CachedLabel *cachedLabel = mLabelCache.object(text);
- finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();
- } else
- {
- TickLabelData labelData = getTickLabelData(font, text);
- finalSize = labelData.rotatedTotalBounds.size();
- }
-
- if (finalSize.width() > tickLabelsSize->width())
- tickLabelsSize->setWidth(finalSize.width());
- if (finalSize.height() > tickLabelsSize->height())
- tickLabelsSize->setHeight(finalSize.height());
- }
- QCPScatterStyle::QCPScatterStyle() :
- mSize(6),
- mShape(ssNone),
- mPen(Qt::NoPen),
- mBrush(Qt::NoBrush),
- mPenDefined(false)
- {
- }
- QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size) :
- mSize(size),
- mShape(shape),
- mPen(Qt::NoPen),
- mBrush(Qt::NoBrush),
- mPenDefined(false)
- {
- }
- QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) :
- mSize(size),
- mShape(shape),
- mPen(QPen(color)),
- mBrush(Qt::NoBrush),
- mPenDefined(true)
- {
- }
- QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
- mSize(size),
- mShape(shape),
- mPen(QPen(color)),
- mBrush(QBrush(fill)),
- mPenDefined(true)
- {
- }
- QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
- mSize(size),
- mShape(shape),
- mPen(pen),
- mBrush(brush),
- mPenDefined(pen.style() != Qt::NoPen)
- {
- }
- QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) :
- mSize(5),
- mShape(ssPixmap),
- mPen(Qt::NoPen),
- mBrush(Qt::NoBrush),
- mPixmap(pixmap),
- mPenDefined(false)
- {
- }
- QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
- mSize(size),
- mShape(ssCustom),
- mPen(pen),
- mBrush(brush),
- mCustomPath(customPath),
- mPenDefined(pen.style() != Qt::NoPen)
- {
- }
- void QCPScatterStyle::setFromOther(const QCPScatterStyle &other, ScatterProperties properties)
- {
- if (properties.testFlag(spPen))
- {
- setPen(other.pen());
- if (!other.isPenDefined())
- undefinePen();
- }
- if (properties.testFlag(spBrush))
- setBrush(other.brush());
- if (properties.testFlag(spSize))
- setSize(other.size());
- if (properties.testFlag(spShape))
- {
- setShape(other.shape());
- if (other.shape() == ssPixmap)
- setPixmap(other.pixmap());
- else if (other.shape() == ssCustom)
- setCustomPath(other.customPath());
- }
- }
- void QCPScatterStyle::setSize(double size)
- {
- mSize = size;
- }
- void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape)
- {
- mShape = shape;
- }
- void QCPScatterStyle::setPen(const QPen &pen)
- {
- mPenDefined = true;
- mPen = pen;
- }
- void QCPScatterStyle::setBrush(const QBrush &brush)
- {
- mBrush = brush;
- }
- void QCPScatterStyle::setPixmap(const QPixmap &pixmap)
- {
- setShape(ssPixmap);
- mPixmap = pixmap;
- }
- void QCPScatterStyle::setCustomPath(const QPainterPath &customPath)
- {
- setShape(ssCustom);
- mCustomPath = customPath;
- }
- void QCPScatterStyle::undefinePen()
- {
- mPenDefined = false;
- }
- void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
- {
- painter->setPen(mPenDefined ? mPen : defaultPen);
- painter->setBrush(mBrush);
- }
- void QCPScatterStyle::drawShape(QCPPainter *painter, const QPointF &pos) const
- {
- drawShape(painter, pos.x(), pos.y());
- }
- void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
- {
- double w = mSize/2.0;
- switch (mShape)
- {
- case ssNone: break;
- case ssDot:
- {
- painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
- break;
- }
- case ssCross:
- {
- painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
- painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
- break;
- }
- case ssPlus:
- {
- painter->drawLine(QLineF(x-w, y, x+w, y));
- painter->drawLine(QLineF( x, y+w, x, y-w));
- break;
- }
- case ssCircle:
- {
- painter->drawEllipse(QPointF(x , y), w, w);
- break;
- }
- case ssDisc:
- {
- QBrush b = painter->brush();
- painter->setBrush(painter->pen().color());
- painter->drawEllipse(QPointF(x , y), w, w);
- painter->setBrush(b);
- break;
- }
- case ssSquare:
- {
- painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
- break;
- }
- case ssDiamond:
- {
- QPointF lineArray[4] = {QPointF(x-w, y),
- QPointF( x, y-w),
- QPointF(x+w, y),
- QPointF( x, y+w)};
- painter->drawPolygon(lineArray, 4);
- break;
- }
- case ssStar:
- {
- painter->drawLine(QLineF(x-w, y, x+w, y));
- painter->drawLine(QLineF( x, y+w, x, y-w));
- painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
- painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
- break;
- }
- case ssTriangle:
- {
- QPointF lineArray[3] = {QPointF(x-w, y+0.755*w),
- QPointF(x+w, y+0.755*w),
- QPointF( x, y-0.977*w)};
- painter->drawPolygon(lineArray, 3);
- break;
- }
- case ssTriangleInverted:
- {
- QPointF lineArray[3] = {QPointF(x-w, y-0.755*w),
- QPointF(x+w, y-0.755*w),
- QPointF( x, y+0.977*w)};
- painter->drawPolygon(lineArray, 3);
- break;
- }
- case ssCrossSquare:
- {
- painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
- painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
- painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
- break;
- }
- case ssPlusSquare:
- {
- painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
- painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
- painter->drawLine(QLineF( x, y+w, x, y-w));
- break;
- }
- case ssCrossCircle:
- {
- painter->drawEllipse(QPointF(x, y), w, w);
- painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
- painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
- break;
- }
- case ssPlusCircle:
- {
- painter->drawEllipse(QPointF(x, y), w, w);
- painter->drawLine(QLineF(x-w, y, x+w, y));
- painter->drawLine(QLineF( x, y+w, x, y-w));
- break;
- }
- case ssPeace:
- {
- painter->drawEllipse(QPointF(x, y), w, w);
- painter->drawLine(QLineF(x, y-w, x, y+w));
- painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
- painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
- break;
- }
- case ssPixmap:
- {
- const double widthHalf = mPixmap.width()*0.5;
- const double heightHalf = mPixmap.height()*0.5;
- #if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)
- const QRectF clipRect = painter->clipRegion().boundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);
- #else
- const QRectF clipRect = painter->clipBoundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);
- #endif
- if (clipRect.contains(x, y))
- painter->drawPixmap(x-widthHalf, y-heightHalf, mPixmap);
- break;
- }
- case ssCustom:
- {
- QTransform oldTransform = painter->transform();
- painter->translate(x, y);
- painter->scale(mSize/6.0, mSize/6.0);
- painter->drawPath(mCustomPath);
- painter->setTransform(oldTransform);
- break;
- }
- }
- }
- QCPSelectionDecorator::QCPSelectionDecorator() :
- mPen(QColor(80, 80, 255), 2.5),
- mBrush(Qt::NoBrush),
- mUsedScatterProperties(QCPScatterStyle::spNone),
- mPlottable(nullptr)
- {
- }
- QCPSelectionDecorator::~QCPSelectionDecorator()
- {
- }
- void QCPSelectionDecorator::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPSelectionDecorator::setBrush(const QBrush &brush)
- {
- mBrush = brush;
- }
- void QCPSelectionDecorator::setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties)
- {
- mScatterStyle = scatterStyle;
- setUsedScatterProperties(usedProperties);
- }
- void QCPSelectionDecorator::setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties)
- {
- mUsedScatterProperties = properties;
- }
- void QCPSelectionDecorator::applyPen(QCPPainter *painter) const
- {
- painter->setPen(mPen);
- }
- void QCPSelectionDecorator::applyBrush(QCPPainter *painter) const
- {
- painter->setBrush(mBrush);
- }
- QCPScatterStyle QCPSelectionDecorator::getFinalScatterStyle(const QCPScatterStyle &unselectedStyle) const
- {
- QCPScatterStyle result(unselectedStyle);
- result.setFromOther(mScatterStyle, mUsedScatterProperties);
-
-
-
- if (!result.isPenDefined())
- result.setPen(mPen);
- return result;
- }
- void QCPSelectionDecorator::copyFrom(const QCPSelectionDecorator *other)
- {
- setPen(other->pen());
- setBrush(other->brush());
- setScatterStyle(other->scatterStyle(), other->usedScatterProperties());
- }
- void QCPSelectionDecorator::drawDecoration(QCPPainter *painter, QCPDataSelection selection)
- {
- Q_UNUSED(painter)
- Q_UNUSED(selection)
- }
- bool QCPSelectionDecorator::registerWithPlottable(QCPAbstractPlottable *plottable)
- {
- if (!mPlottable)
- {
- mPlottable = plottable;
- return true;
- } else
- {
- qDebug() << Q_FUNC_INFO << "This selection decorator is already registered with plottable:" << reinterpret_cast<quintptr>(mPlottable);
- return false;
- }
- }
- QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) :
- QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
- mName(),
- mAntialiasedFill(true),
- mAntialiasedScatters(true),
- mPen(Qt::black),
- mBrush(Qt::NoBrush),
- mKeyAxis(keyAxis),
- mValueAxis(valueAxis),
- mSelectable(QCP::stWhole),
- mSelectionDecorator(nullptr)
- {
- if (keyAxis->parentPlot() != valueAxis->parentPlot())
- qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
- if (keyAxis->orientation() == valueAxis->orientation())
- qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
- mParentPlot->registerPlottable(this);
- setSelectionDecorator(new QCPSelectionDecorator);
- }
- QCPAbstractPlottable::~QCPAbstractPlottable()
- {
- if (mSelectionDecorator)
- {
- delete mSelectionDecorator;
- mSelectionDecorator = nullptr;
- }
- }
- void QCPAbstractPlottable::setName(const QString &name)
- {
- mName = name;
- }
- void QCPAbstractPlottable::setAntialiasedFill(bool enabled)
- {
- mAntialiasedFill = enabled;
- }
- void QCPAbstractPlottable::setAntialiasedScatters(bool enabled)
- {
- mAntialiasedScatters = enabled;
- }
- void QCPAbstractPlottable::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPAbstractPlottable::setBrush(const QBrush &brush)
- {
- mBrush = brush;
- }
- void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis)
- {
- mKeyAxis = axis;
- }
- void QCPAbstractPlottable::setValueAxis(QCPAxis *axis)
- {
- mValueAxis = axis;
- }
- void QCPAbstractPlottable::setSelection(QCPDataSelection selection)
- {
- selection.enforceType(mSelectable);
- if (mSelection != selection)
- {
- mSelection = selection;
- emit selectionChanged(selected());
- emit selectionChanged(mSelection);
- }
- }
- void QCPAbstractPlottable::setSelectionDecorator(QCPSelectionDecorator *decorator)
- {
- if (decorator)
- {
- if (decorator->registerWithPlottable(this))
- {
- delete mSelectionDecorator;
- mSelectionDecorator = decorator;
- }
- } else if (mSelectionDecorator)
- {
- delete mSelectionDecorator;
- mSelectionDecorator = nullptr;
- }
- }
- void QCPAbstractPlottable::setSelectable(QCP::SelectionType selectable)
- {
- if (mSelectable != selectable)
- {
- mSelectable = selectable;
- QCPDataSelection oldSelection = mSelection;
- mSelection.enforceType(mSelectable);
- emit selectableChanged(mSelectable);
- if (mSelection != oldSelection)
- {
- emit selectionChanged(selected());
- emit selectionChanged(mSelection);
- }
- }
- }
- void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- if (keyAxis->orientation() == Qt::Horizontal)
- {
- x = keyAxis->coordToPixel(key);
- y = valueAxis->coordToPixel(value);
- } else
- {
- y = keyAxis->coordToPixel(key);
- x = valueAxis->coordToPixel(value);
- }
- }
- const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
- if (keyAxis->orientation() == Qt::Horizontal)
- return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
- else
- return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
- }
- void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- if (keyAxis->orientation() == Qt::Horizontal)
- {
- key = keyAxis->pixelToCoord(x);
- value = valueAxis->pixelToCoord(y);
- } else
- {
- key = keyAxis->pixelToCoord(y);
- value = valueAxis->pixelToCoord(x);
- }
- }
- void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
- {
- pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
- }
- void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
- {
- rescaleKeyAxis(onlyEnlarge);
- rescaleValueAxis(onlyEnlarge);
- }
- void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
- QCP::SignDomain signDomain = QCP::sdBoth;
- if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
- signDomain = (keyAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);
- bool foundRange;
- QCPRange newRange = getKeyRange(foundRange, signDomain);
- if (foundRange)
- {
- if (onlyEnlarge)
- newRange.expand(keyAxis->range());
- if (!QCPRange::validRange(newRange))
- {
- double center = (newRange.lower+newRange.upper)*0.5;
- if (keyAxis->scaleType() == QCPAxis::stLinear)
- {
- newRange.lower = center-keyAxis->range().size()/2.0;
- newRange.upper = center+keyAxis->range().size()/2.0;
- } else
- {
- newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
- newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
- }
- }
- keyAxis->setRange(newRange);
- }
- }
- bool QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return false; }
- QCP::SignDomain signDomain = QCP::sdBoth;
- if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
- signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);
- bool foundRange;
- QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange());
- if (foundRange)
- {
- if (onlyEnlarge)
- newRange.expand(valueAxis->range());
- if (!QCPRange::validRange(newRange))
- {
- double center = (newRange.lower+newRange.upper)*0.5;
- if (valueAxis->scaleType() == QCPAxis::stLinear)
- {
- newRange.lower = center-valueAxis->range().size()/2.0;
- newRange.upper = center+valueAxis->range().size()/2.0;
- } else
- {
- newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
- newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
- }
- }
- valueAxis->setRange(newRange);
- }
- return foundRange;
- }
- bool QCPAbstractPlottable::addToLegend(QCPLegend *legend)
- {
- if (!legend)
- {
- qDebug() << Q_FUNC_INFO << "passed legend is null";
- return false;
- }
- if (legend->parentPlot() != mParentPlot)
- {
- qDebug() << Q_FUNC_INFO << "passed legend isn't in the same QCustomPlot as this plottable";
- return false;
- }
- if (!legend->hasItemWithPlottable(this))
- {
- legend->addItem(new QCPPlottableLegendItem(legend, this));
- return true;
- } else
- return false;
- }
- bool QCPAbstractPlottable::addToLegend()
- {
- if (!mParentPlot || !mParentPlot->legend)
- return false;
- else
- return addToLegend(mParentPlot->legend);
- }
- bool QCPAbstractPlottable::removeFromLegend(QCPLegend *legend) const
- {
- if (!legend)
- {
- qDebug() << Q_FUNC_INFO << "passed legend is null";
- return false;
- }
- if (QCPPlottableLegendItem *lip = legend->itemWithPlottable(this))
- return legend->removeItem(lip);
- else
- return false;
- }
- bool QCPAbstractPlottable::removeFromLegend() const
- {
- if (!mParentPlot || !mParentPlot->legend)
- return false;
- else
- return removeFromLegend(mParentPlot->legend);
- }
- QRectF QCPAbstractPlottable::clipRect() const
- {
- if (mKeyAxis && mValueAxis)
- return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
- else
- return {};
- }
- QCP::Interaction QCPAbstractPlottable::selectionCategory() const
- {
- return QCP::iSelectPlottables;
- }
- void QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
- }
- void QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
- }
- void QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
- }
- void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
- {
- Q_UNUSED(event)
- if (mSelectable != QCP::stNone)
- {
- QCPDataSelection newSelection = details.value<QCPDataSelection>();
- QCPDataSelection selectionBefore = mSelection;
- if (additive)
- {
- if (mSelectable == QCP::stWhole)
- {
- if (selected())
- setSelection(QCPDataSelection());
- else
- setSelection(newSelection);
- } else
- {
- if (mSelection.contains(newSelection))
- setSelection(mSelection-newSelection);
- else
- setSelection(mSelection+newSelection);
- }
- } else
- setSelection(newSelection);
- if (selectionStateChanged)
- *selectionStateChanged = mSelection != selectionBefore;
- }
- }
- void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
- {
- if (mSelectable != QCP::stNone)
- {
- QCPDataSelection selectionBefore = mSelection;
- setSelection(QCPDataSelection());
- if (selectionStateChanged)
- *selectionStateChanged = mSelection != selectionBefore;
- }
- }
- QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId) :
- mName(name),
- mParentPlot(parentPlot),
- mParentItem(parentItem),
- mAnchorId(anchorId)
- {
- }
- QCPItemAnchor::~QCPItemAnchor()
- {
-
- foreach (QCPItemPosition *child, mChildrenX.values())
- {
- if (child->parentAnchorX() == this)
- child->setParentAnchorX(nullptr);
- }
- foreach (QCPItemPosition *child, mChildrenY.values())
- {
- if (child->parentAnchorY() == this)
- child->setParentAnchorY(nullptr);
- }
- }
- QPointF QCPItemAnchor::pixelPosition() const
- {
- if (mParentItem)
- {
- if (mAnchorId > -1)
- {
- return mParentItem->anchorPixelPosition(mAnchorId);
- } else
- {
- qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
- return {};
- }
- } else
- {
- qDebug() << Q_FUNC_INFO << "no parent item set";
- return {};
- }
- }
- void QCPItemAnchor::addChildX(QCPItemPosition *pos)
- {
- if (!mChildrenX.contains(pos))
- mChildrenX.insert(pos);
- else
- qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
- }
- void QCPItemAnchor::removeChildX(QCPItemPosition *pos)
- {
- if (!mChildrenX.remove(pos))
- qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
- }
- void QCPItemAnchor::addChildY(QCPItemPosition *pos)
- {
- if (!mChildrenY.contains(pos))
- mChildrenY.insert(pos);
- else
- qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
- }
- void QCPItemAnchor::removeChildY(QCPItemPosition *pos)
- {
- if (!mChildrenY.remove(pos))
- qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
- }
- QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name) :
- QCPItemAnchor(parentPlot, parentItem, name),
- mPositionTypeX(ptAbsolute),
- mPositionTypeY(ptAbsolute),
- mKey(0),
- mValue(0),
- mParentAnchorX(nullptr),
- mParentAnchorY(nullptr)
- {
- }
- QCPItemPosition::~QCPItemPosition()
- {
-
-
-
- foreach (QCPItemPosition *child, mChildrenX.values())
- {
- if (child->parentAnchorX() == this)
- child->setParentAnchorX(nullptr);
- }
- foreach (QCPItemPosition *child, mChildrenY.values())
- {
- if (child->parentAnchorY() == this)
- child->setParentAnchorY(nullptr);
- }
-
- if (mParentAnchorX)
- mParentAnchorX->removeChildX(this);
- if (mParentAnchorY)
- mParentAnchorY->removeChildY(this);
- }
- QCPAxisRect *QCPItemPosition::axisRect() const
- {
- return mAxisRect.data();
- }
- void QCPItemPosition::setType(QCPItemPosition::PositionType type)
- {
- setTypeX(type);
- setTypeY(type);
- }
- void QCPItemPosition::setTypeX(QCPItemPosition::PositionType type)
- {
- if (mPositionTypeX != type)
- {
-
-
- bool retainPixelPosition = true;
- if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
- retainPixelPosition = false;
- if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
- retainPixelPosition = false;
- QPointF pixel;
- if (retainPixelPosition)
- pixel = pixelPosition();
- mPositionTypeX = type;
- if (retainPixelPosition)
- setPixelPosition(pixel);
- }
- }
- void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type)
- {
- if (mPositionTypeY != type)
- {
-
-
- bool retainPixelPosition = true;
- if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
- retainPixelPosition = false;
- if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
- retainPixelPosition = false;
- QPointF pixel;
- if (retainPixelPosition)
- pixel = pixelPosition();
- mPositionTypeY = type;
- if (retainPixelPosition)
- setPixelPosition(pixel);
- }
- }
- bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
- {
- bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
- bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
- return successX && successY;
- }
- bool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
- {
-
- if (parentAnchor == this)
- {
- qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
- return false;
- }
-
- QCPItemAnchor *currentParent = parentAnchor;
- while (currentParent)
- {
- if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
- {
-
- if (currentParentPos == this)
- {
- qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
- return false;
- }
- currentParent = currentParentPos->parentAnchorX();
- } else
- {
-
-
-
- if (currentParent->mParentItem == mParentItem)
- {
- qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
- return false;
- }
- break;
- }
- }
-
- if (!mParentAnchorX && mPositionTypeX == ptPlotCoords)
- setTypeX(ptAbsolute);
-
- QPointF pixelP;
- if (keepPixelPosition)
- pixelP = pixelPosition();
-
- if (mParentAnchorX)
- mParentAnchorX->removeChildX(this);
-
- if (parentAnchor)
- parentAnchor->addChildX(this);
- mParentAnchorX = parentAnchor;
-
- if (keepPixelPosition)
- setPixelPosition(pixelP);
- else
- setCoords(0, coords().y());
- return true;
- }
- bool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
- {
-
- if (parentAnchor == this)
- {
- qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
- return false;
- }
-
- QCPItemAnchor *currentParent = parentAnchor;
- while (currentParent)
- {
- if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
- {
-
- if (currentParentPos == this)
- {
- qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
- return false;
- }
- currentParent = currentParentPos->parentAnchorY();
- } else
- {
-
-
-
- if (currentParent->mParentItem == mParentItem)
- {
- qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
- return false;
- }
- break;
- }
- }
-
- if (!mParentAnchorY && mPositionTypeY == ptPlotCoords)
- setTypeY(ptAbsolute);
-
- QPointF pixelP;
- if (keepPixelPosition)
- pixelP = pixelPosition();
-
- if (mParentAnchorY)
- mParentAnchorY->removeChildY(this);
-
- if (parentAnchor)
- parentAnchor->addChildY(this);
- mParentAnchorY = parentAnchor;
-
- if (keepPixelPosition)
- setPixelPosition(pixelP);
- else
- setCoords(coords().x(), 0);
- return true;
- }
- void QCPItemPosition::setCoords(double key, double value)
- {
- mKey = key;
- mValue = value;
- }
- void QCPItemPosition::setCoords(const QPointF &pos)
- {
- setCoords(pos.x(), pos.y());
- }
- QPointF QCPItemPosition::pixelPosition() const
- {
- QPointF result;
-
- switch (mPositionTypeX)
- {
- case ptAbsolute:
- {
- result.rx() = mKey;
- if (mParentAnchorX)
- result.rx() += mParentAnchorX->pixelPosition().x();
- break;
- }
- case ptViewportRatio:
- {
- result.rx() = mKey*mParentPlot->viewport().width();
- if (mParentAnchorX)
- result.rx() += mParentAnchorX->pixelPosition().x();
- else
- result.rx() += mParentPlot->viewport().left();
- break;
- }
- case ptAxisRectRatio:
- {
- if (mAxisRect)
- {
- result.rx() = mKey*mAxisRect.data()->width();
- if (mParentAnchorX)
- result.rx() += mParentAnchorX->pixelPosition().x();
- else
- result.rx() += mAxisRect.data()->left();
- } else
- qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
- break;
- }
- case ptPlotCoords:
- {
- if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
- result.rx() = mKeyAxis.data()->coordToPixel(mKey);
- else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
- result.rx() = mValueAxis.data()->coordToPixel(mValue);
- else
- qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
- break;
- }
- }
-
- switch (mPositionTypeY)
- {
- case ptAbsolute:
- {
- result.ry() = mValue;
- if (mParentAnchorY)
- result.ry() += mParentAnchorY->pixelPosition().y();
- break;
- }
- case ptViewportRatio:
- {
- result.ry() = mValue*mParentPlot->viewport().height();
- if (mParentAnchorY)
- result.ry() += mParentAnchorY->pixelPosition().y();
- else
- result.ry() += mParentPlot->viewport().top();
- break;
- }
- case ptAxisRectRatio:
- {
- if (mAxisRect)
- {
- result.ry() = mValue*mAxisRect.data()->height();
- if (mParentAnchorY)
- result.ry() += mParentAnchorY->pixelPosition().y();
- else
- result.ry() += mAxisRect.data()->top();
- } else
- qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
- break;
- }
- case ptPlotCoords:
- {
- if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
- result.ry() = mKeyAxis.data()->coordToPixel(mKey);
- else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
- result.ry() = mValueAxis.data()->coordToPixel(mValue);
- else
- qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
- break;
- }
- }
- return result;
- }
- void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
- {
- mKeyAxis = keyAxis;
- mValueAxis = valueAxis;
- }
- void QCPItemPosition::setAxisRect(QCPAxisRect *axisRect)
- {
- mAxisRect = axisRect;
- }
- void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition)
- {
- double x = pixelPosition.x();
- double y = pixelPosition.y();
- switch (mPositionTypeX)
- {
- case ptAbsolute:
- {
- if (mParentAnchorX)
- x -= mParentAnchorX->pixelPosition().x();
- break;
- }
- case ptViewportRatio:
- {
- if (mParentAnchorX)
- x -= mParentAnchorX->pixelPosition().x();
- else
- x -= mParentPlot->viewport().left();
- x /= double(mParentPlot->viewport().width());
- break;
- }
- case ptAxisRectRatio:
- {
- if (mAxisRect)
- {
- if (mParentAnchorX)
- x -= mParentAnchorX->pixelPosition().x();
- else
- x -= mAxisRect.data()->left();
- x /= double(mAxisRect.data()->width());
- } else
- qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
- break;
- }
- case ptPlotCoords:
- {
- if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
- x = mKeyAxis.data()->pixelToCoord(x);
- else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
- y = mValueAxis.data()->pixelToCoord(x);
- else
- qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
- break;
- }
- }
- switch (mPositionTypeY)
- {
- case ptAbsolute:
- {
- if (mParentAnchorY)
- y -= mParentAnchorY->pixelPosition().y();
- break;
- }
- case ptViewportRatio:
- {
- if (mParentAnchorY)
- y -= mParentAnchorY->pixelPosition().y();
- else
- y -= mParentPlot->viewport().top();
- y /= double(mParentPlot->viewport().height());
- break;
- }
- case ptAxisRectRatio:
- {
- if (mAxisRect)
- {
- if (mParentAnchorY)
- y -= mParentAnchorY->pixelPosition().y();
- else
- y -= mAxisRect.data()->top();
- y /= double(mAxisRect.data()->height());
- } else
- qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
- break;
- }
- case ptPlotCoords:
- {
- if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
- x = mKeyAxis.data()->pixelToCoord(y);
- else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
- y = mValueAxis.data()->pixelToCoord(y);
- else
- qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
- break;
- }
- }
- setCoords(x, y);
- }
- QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) :
- QCPLayerable(parentPlot),
- mClipToAxisRect(false),
- mSelectable(true),
- mSelected(false)
- {
- parentPlot->registerItem(this);
- QList<QCPAxisRect*> rects = parentPlot->axisRects();
- if (!rects.isEmpty())
- {
- setClipToAxisRect(true);
- setClipAxisRect(rects.first());
- }
- }
- QCPAbstractItem::~QCPAbstractItem()
- {
-
- qDeleteAll(mAnchors);
- }
- QCPAxisRect *QCPAbstractItem::clipAxisRect() const
- {
- return mClipAxisRect.data();
- }
- void QCPAbstractItem::setClipToAxisRect(bool clip)
- {
- mClipToAxisRect = clip;
- if (mClipToAxisRect)
- setParentLayerable(mClipAxisRect.data());
- }
- void QCPAbstractItem::setClipAxisRect(QCPAxisRect *rect)
- {
- mClipAxisRect = rect;
- if (mClipToAxisRect)
- setParentLayerable(mClipAxisRect.data());
- }
- void QCPAbstractItem::setSelectable(bool selectable)
- {
- if (mSelectable != selectable)
- {
- mSelectable = selectable;
- emit selectableChanged(mSelectable);
- }
- }
- void QCPAbstractItem::setSelected(bool selected)
- {
- if (mSelected != selected)
- {
- mSelected = selected;
- emit selectionChanged(mSelected);
- }
- }
- QCPItemPosition *QCPAbstractItem::position(const QString &name) const
- {
- foreach (QCPItemPosition *position, mPositions)
- {
- if (position->name() == name)
- return position;
- }
- qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
- return nullptr;
- }
- QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
- {
- foreach (QCPItemAnchor *anchor, mAnchors)
- {
- if (anchor->name() == name)
- return anchor;
- }
- qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
- return nullptr;
- }
- bool QCPAbstractItem::hasAnchor(const QString &name) const
- {
- foreach (QCPItemAnchor *anchor, mAnchors)
- {
- if (anchor->name() == name)
- return true;
- }
- return false;
- }
- QRectF QCPAbstractItem::clipRect() const
- {
- if (mClipToAxisRect && mClipAxisRect)
- return mClipAxisRect.data()->rect();
- else
- return mParentPlot->viewport();
- }
- void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aeItems);
- }
- double QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const
- {
- double result = -1;
-
- const QList<QLineF> lines = QList<QLineF>() << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
- << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
- const QCPVector2D posVec(pos);
- double minDistSqr = (std::numeric_limits<double>::max)();
- foreach (const QLineF &line, lines)
- {
- double distSqr = posVec.distanceSquaredToLine(line.p1(), line.p2());
- if (distSqr < minDistSqr)
- minDistSqr = distSqr;
- }
- result = qSqrt(minDistSqr);
-
- if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
- {
- if (rect.contains(pos))
- result = mParentPlot->selectionTolerance()*0.99;
- }
- return result;
- }
- QPointF QCPAbstractItem::anchorPixelPosition(int anchorId) const
- {
- qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
- return {};
- }
- QCPItemPosition *QCPAbstractItem::createPosition(const QString &name)
- {
- if (hasAnchor(name))
- qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
- QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
- mPositions.append(newPosition);
- mAnchors.append(newPosition);
- newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
- newPosition->setType(QCPItemPosition::ptPlotCoords);
- if (mParentPlot->axisRect())
- newPosition->setAxisRect(mParentPlot->axisRect());
- newPosition->setCoords(0, 0);
- return newPosition;
- }
- QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
- {
- if (hasAnchor(name))
- qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
- QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
- mAnchors.append(newAnchor);
- return newAnchor;
- }
- void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
- {
- Q_UNUSED(event)
- Q_UNUSED(details)
- if (mSelectable)
- {
- bool selBefore = mSelected;
- setSelected(additive ? !mSelected : true);
- if (selectionStateChanged)
- *selectionStateChanged = mSelected != selBefore;
- }
- }
- void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
- {
- if (mSelectable)
- {
- bool selBefore = mSelected;
- setSelected(false);
- if (selectionStateChanged)
- *selectionStateChanged = mSelected != selBefore;
- }
- }
- QCP::Interaction QCPAbstractItem::selectionCategory() const
- {
- return QCP::iSelectItems;
- }
- QCustomPlot::QCustomPlot(QWidget *parent) :
- QWidget(parent),
- xAxis(nullptr),
- yAxis(nullptr),
- xAxis2(nullptr),
- yAxis2(nullptr),
- legend(nullptr),
- mBufferDevicePixelRatio(1.0),
- mPlotLayout(nullptr),
- mAutoAddPlottableToLegend(true),
- mAntialiasedElements(QCP::aeNone),
- mNotAntialiasedElements(QCP::aeNone),
- mInteractions(QCP::iNone),
- mSelectionTolerance(8),
- mNoAntialiasingOnDrag(false),
- mBackgroundBrush(Qt::white, Qt::SolidPattern),
- mBackgroundScaled(true),
- mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
- mCurrentLayer(nullptr),
- mPlottingHints(QCP::phCacheLabels|QCP::phImmediateRefresh),
- mMultiSelectModifier(Qt::ControlModifier),
- mSelectionRectMode(QCP::srmNone),
- mSelectionRect(nullptr),
- mOpenGl(false),
- mMouseHasMoved(false),
- mMouseEventLayerable(nullptr),
- mMouseSignalLayerable(nullptr),
- mReplotting(false),
- mReplotQueued(false),
- mReplotTime(0),
- mReplotTimeAverage(0),
- mOpenGlMultisamples(16),
- mOpenGlAntialiasedElementsBackup(QCP::aeNone),
- mOpenGlCacheLabelsBackup(true)
- {
- setAttribute(Qt::WA_NoMousePropagation);
- setAttribute(Qt::WA_OpaquePaintEvent);
- setFocusPolicy(Qt::ClickFocus);
- setMouseTracking(true);
- QLocale currentLocale = locale();
- currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
- setLocale(currentLocale);
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- # ifdef QCP_DEVICEPIXELRATIO_FLOAT
- setBufferDevicePixelRatio(QWidget::devicePixelRatioF());
- # else
- setBufferDevicePixelRatio(QWidget::devicePixelRatio());
- # endif
- #endif
- mOpenGlAntialiasedElementsBackup = mAntialiasedElements;
- mOpenGlCacheLabelsBackup = mPlottingHints.testFlag(QCP::phCacheLabels);
-
- mLayers.append(new QCPLayer(this, QLatin1String("background")));
- mLayers.append(new QCPLayer(this, QLatin1String("grid")));
- mLayers.append(new QCPLayer(this, QLatin1String("main")));
- mLayers.append(new QCPLayer(this, QLatin1String("axes")));
- mLayers.append(new QCPLayer(this, QLatin1String("legend")));
- mLayers.append(new QCPLayer(this, QLatin1String("overlay")));
- updateLayerIndices();
- setCurrentLayer(QLatin1String("main"));
- layer(QLatin1String("overlay"))->setMode(QCPLayer::lmBuffered);
-
- mPlotLayout = new QCPLayoutGrid;
- mPlotLayout->initializeParentPlot(this);
- mPlotLayout->setParent(this);
- mPlotLayout->setLayer(QLatin1String("main"));
- QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
- mPlotLayout->addElement(0, 0, defaultAxisRect);
- xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
- yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
- xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
- yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
- legend = new QCPLegend;
- legend->setVisible(false);
- defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
- defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
- defaultAxisRect->setLayer(QLatin1String("background"));
- xAxis->setLayer(QLatin1String("axes"));
- yAxis->setLayer(QLatin1String("axes"));
- xAxis2->setLayer(QLatin1String("axes"));
- yAxis2->setLayer(QLatin1String("axes"));
- xAxis->grid()->setLayer(QLatin1String("grid"));
- yAxis->grid()->setLayer(QLatin1String("grid"));
- xAxis2->grid()->setLayer(QLatin1String("grid"));
- yAxis2->grid()->setLayer(QLatin1String("grid"));
- legend->setLayer(QLatin1String("legend"));
-
- mSelectionRect = new QCPSelectionRect(this);
- mSelectionRect->setLayer(QLatin1String("overlay"));
- setViewport(rect());
- replot(rpQueuedReplot);
- }
- QCustomPlot::~QCustomPlot()
- {
- clearPlottables();
- clearItems();
- if (mPlotLayout)
- {
- delete mPlotLayout;
- mPlotLayout = nullptr;
- }
- mCurrentLayer = nullptr;
- qDeleteAll(mLayers);
- mLayers.clear();
- }
- void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
- {
- mAntialiasedElements = antialiasedElements;
-
- if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
- mNotAntialiasedElements |= ~mAntialiasedElements;
- }
- void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
- {
- if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
- mAntialiasedElements &= ~antialiasedElement;
- else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
- mAntialiasedElements |= antialiasedElement;
-
- if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
- mNotAntialiasedElements |= ~mAntialiasedElements;
- }
- void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements ¬AntialiasedElements)
- {
- mNotAntialiasedElements = notAntialiasedElements;
-
- if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
- mAntialiasedElements |= ~mNotAntialiasedElements;
- }
- void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
- {
- if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
- mNotAntialiasedElements &= ~notAntialiasedElement;
- else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
- mNotAntialiasedElements |= notAntialiasedElement;
-
- if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
- mAntialiasedElements |= ~mNotAntialiasedElements;
- }
- void QCustomPlot::setAutoAddPlottableToLegend(bool on)
- {
- mAutoAddPlottableToLegend = on;
- }
- void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
- {
- mInteractions = interactions;
- }
- void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
- {
- if (!enabled && mInteractions.testFlag(interaction))
- mInteractions &= ~interaction;
- else if (enabled && !mInteractions.testFlag(interaction))
- mInteractions |= interaction;
- }
- void QCustomPlot::setSelectionTolerance(int pixels)
- {
- mSelectionTolerance = pixels;
- }
- void QCustomPlot::setNoAntialiasingOnDrag(bool enabled)
- {
- mNoAntialiasingOnDrag = enabled;
- }
- void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
- {
- mPlottingHints = hints;
- }
- void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled)
- {
- QCP::PlottingHints newHints = mPlottingHints;
- if (!enabled)
- newHints &= ~hint;
- else
- newHints |= hint;
- if (newHints != mPlottingHints)
- setPlottingHints(newHints);
- }
- void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
- {
- mMultiSelectModifier = modifier;
- }
- void QCustomPlot::setSelectionRectMode(QCP::SelectionRectMode mode)
- {
- if (mSelectionRect)
- {
- if (mode == QCP::srmNone)
- mSelectionRect->cancel();
-
- if (mSelectionRectMode == QCP::srmSelect)
- disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
- else if (mSelectionRectMode == QCP::srmZoom)
- disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
-
- if (mode == QCP::srmSelect)
- connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
- else if (mode == QCP::srmZoom)
- connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
- }
- mSelectionRectMode = mode;
- }
- void QCustomPlot::setSelectionRect(QCPSelectionRect *selectionRect)
- {
- delete mSelectionRect;
- mSelectionRect = selectionRect;
- if (mSelectionRect)
- {
-
- if (mSelectionRectMode == QCP::srmSelect)
- connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
- else if (mSelectionRectMode == QCP::srmZoom)
- connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
- }
- }
- void QCustomPlot::setOpenGl(bool enabled, int multisampling)
- {
- mOpenGlMultisamples = qMax(0, multisampling);
- #ifdef QCUSTOMPLOT_USE_OPENGL
- mOpenGl = enabled;
- if (mOpenGl)
- {
- if (setupOpenGl())
- {
-
- mOpenGlAntialiasedElementsBackup = mAntialiasedElements;
- mOpenGlCacheLabelsBackup = mPlottingHints.testFlag(QCP::phCacheLabels);
-
- setAntialiasedElements(QCP::aeAll);
- setPlottingHint(QCP::phCacheLabels, false);
- } else
- {
- qDebug() << Q_FUNC_INFO << "Failed to enable OpenGL, continuing plotting without hardware acceleration.";
- mOpenGl = false;
- }
- } else
- {
-
- if (mAntialiasedElements == QCP::aeAll)
- setAntialiasedElements(mOpenGlAntialiasedElementsBackup);
- if (!mPlottingHints.testFlag(QCP::phCacheLabels))
- setPlottingHint(QCP::phCacheLabels, mOpenGlCacheLabelsBackup);
- freeOpenGl();
- }
-
- mPaintBuffers.clear();
- setupPaintBuffers();
- #else
- Q_UNUSED(enabled)
- qDebug() << Q_FUNC_INFO << "QCustomPlot can't use OpenGL because QCUSTOMPLOT_USE_OPENGL was not defined during compilation (add 'DEFINES += QCUSTOMPLOT_USE_OPENGL' to your qmake .pro file)";
- #endif
- }
- void QCustomPlot::setViewport(const QRect &rect)
- {
- mViewport = rect;
- if (mPlotLayout)
- mPlotLayout->setOuterRect(mViewport);
- }
- void QCustomPlot::setBufferDevicePixelRatio(double ratio)
- {
- if (!qFuzzyCompare(ratio, mBufferDevicePixelRatio))
- {
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- mBufferDevicePixelRatio = ratio;
- foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)
- buffer->setDevicePixelRatio(mBufferDevicePixelRatio);
-
- #else
- qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
- mBufferDevicePixelRatio = 1.0;
- #endif
- }
- }
- void QCustomPlot::setBackground(const QPixmap &pm)
- {
- mBackgroundPixmap = pm;
- mScaledBackgroundPixmap = QPixmap();
- }
- void QCustomPlot::setBackground(const QBrush &brush)
- {
- mBackgroundBrush = brush;
- }
- void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
- {
- mBackgroundPixmap = pm;
- mScaledBackgroundPixmap = QPixmap();
- mBackgroundScaled = scaled;
- mBackgroundScaledMode = mode;
- }
- void QCustomPlot::setBackgroundScaled(bool scaled)
- {
- mBackgroundScaled = scaled;
- }
- void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
- {
- mBackgroundScaledMode = mode;
- }
- QCPAbstractPlottable *QCustomPlot::plottable(int index)
- {
- if (index >= 0 && index < mPlottables.size())
- {
- return mPlottables.at(index);
- } else
- {
- qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
- return nullptr;
- }
- }
- QCPAbstractPlottable *QCustomPlot::plottable()
- {
- if (!mPlottables.isEmpty())
- {
- return mPlottables.last();
- } else
- return nullptr;
- }
- bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable)
- {
- if (!mPlottables.contains(plottable))
- {
- qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
- return false;
- }
-
- plottable->removeFromLegend();
-
- if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
- mGraphs.removeOne(graph);
-
- delete plottable;
- mPlottables.removeOne(plottable);
- return true;
- }
- bool QCustomPlot::removePlottable(int index)
- {
- if (index >= 0 && index < mPlottables.size())
- return removePlottable(mPlottables[index]);
- else
- {
- qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
- return false;
- }
- }
- int QCustomPlot::clearPlottables()
- {
- int c = mPlottables.size();
- for (int i=c-1; i >= 0; --i)
- removePlottable(mPlottables[i]);
- return c;
- }
- int QCustomPlot::plottableCount() const
- {
- return mPlottables.size();
- }
- QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
- {
- QList<QCPAbstractPlottable*> result;
- foreach (QCPAbstractPlottable *plottable, mPlottables)
- {
- if (plottable->selected())
- result.append(plottable);
- }
- return result;
- }
- QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable, int *dataIndex) const
- {
- return plottableAt<QCPAbstractPlottable>(pos, onlySelectable, dataIndex);
- }
- bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const
- {
- return mPlottables.contains(plottable);
- }
- QCPGraph *QCustomPlot::graph(int index) const
- {
- if (index >= 0 && index < mGraphs.size())
- {
- return mGraphs.at(index);
- } else
- {
- qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
- return nullptr;
- }
- }
- QCPGraph *QCustomPlot::graph() const
- {
- if (!mGraphs.isEmpty())
- {
- return mGraphs.last();
- } else
- return nullptr;
- }
- QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
- {
- if (!keyAxis) keyAxis = xAxis;
- if (!valueAxis) valueAxis = yAxis;
- if (!keyAxis || !valueAxis)
- {
- qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
- return nullptr;
- }
- if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
- {
- qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
- return nullptr;
- }
- QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
- newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
- return newGraph;
- }
- bool QCustomPlot::removeGraph(QCPGraph *graph)
- {
- return removePlottable(graph);
- }
- bool QCustomPlot::removeGraph(int index)
- {
- if (index >= 0 && index < mGraphs.size())
- return removeGraph(mGraphs[index]);
- else
- return false;
- }
- int QCustomPlot::clearGraphs()
- {
- int c = mGraphs.size();
- for (int i=c-1; i >= 0; --i)
- removeGraph(mGraphs[i]);
- return c;
- }
- int QCustomPlot::graphCount() const
- {
- return mGraphs.size();
- }
- QList<QCPGraph*> QCustomPlot::selectedGraphs() const
- {
- QList<QCPGraph*> result;
- foreach (QCPGraph *graph, mGraphs)
- {
- if (graph->selected())
- result.append(graph);
- }
- return result;
- }
- QCPAbstractItem *QCustomPlot::item(int index) const
- {
- if (index >= 0 && index < mItems.size())
- {
- return mItems.at(index);
- } else
- {
- qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
- return nullptr;
- }
- }
- QCPAbstractItem *QCustomPlot::item() const
- {
- if (!mItems.isEmpty())
- {
- return mItems.last();
- } else
- return nullptr;
- }
- bool QCustomPlot::removeItem(QCPAbstractItem *item)
- {
- if (mItems.contains(item))
- {
- delete item;
- mItems.removeOne(item);
- return true;
- } else
- {
- qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
- return false;
- }
- }
- bool QCustomPlot::removeItem(int index)
- {
- if (index >= 0 && index < mItems.size())
- return removeItem(mItems[index]);
- else
- {
- qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
- return false;
- }
- }
- int QCustomPlot::clearItems()
- {
- int c = mItems.size();
- for (int i=c-1; i >= 0; --i)
- removeItem(mItems[i]);
- return c;
- }
- int QCustomPlot::itemCount() const
- {
- return mItems.size();
- }
- QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
- {
- QList<QCPAbstractItem*> result;
- foreach (QCPAbstractItem *item, mItems)
- {
- if (item->selected())
- result.append(item);
- }
- return result;
- }
- QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
- {
- return itemAt<QCPAbstractItem>(pos, onlySelectable);
- }
- bool QCustomPlot::hasItem(QCPAbstractItem *item) const
- {
- return mItems.contains(item);
- }
- QCPLayer *QCustomPlot::layer(const QString &name) const
- {
- foreach (QCPLayer *layer, mLayers)
- {
- if (layer->name() == name)
- return layer;
- }
- return nullptr;
- }
- QCPLayer *QCustomPlot::layer(int index) const
- {
- if (index >= 0 && index < mLayers.size())
- {
- return mLayers.at(index);
- } else
- {
- qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
- return nullptr;
- }
- }
- QCPLayer *QCustomPlot::currentLayer() const
- {
- return mCurrentLayer;
- }
- bool QCustomPlot::setCurrentLayer(const QString &name)
- {
- if (QCPLayer *newCurrentLayer = layer(name))
- {
- return setCurrentLayer(newCurrentLayer);
- } else
- {
- qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
- return false;
- }
- }
- bool QCustomPlot::setCurrentLayer(QCPLayer *layer)
- {
- if (!mLayers.contains(layer))
- {
- qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
- return false;
- }
- mCurrentLayer = layer;
- return true;
- }
- int QCustomPlot::layerCount() const
- {
- return mLayers.size();
- }
- bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
- {
- if (!otherLayer)
- otherLayer = mLayers.last();
- if (!mLayers.contains(otherLayer))
- {
- qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
- return false;
- }
- if (layer(name))
- {
- qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
- return false;
- }
- QCPLayer *newLayer = new QCPLayer(this, name);
- mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
- updateLayerIndices();
- setupPaintBuffers();
- return true;
- }
- bool QCustomPlot::removeLayer(QCPLayer *layer)
- {
- if (!mLayers.contains(layer))
- {
- qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
- return false;
- }
- if (mLayers.size() < 2)
- {
- qDebug() << Q_FUNC_INFO << "can't remove last layer";
- return false;
- }
-
- int removedIndex = layer->index();
- bool isFirstLayer = removedIndex==0;
- QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
- QList<QCPLayerable*> children = layer->children();
- if (isFirstLayer)
- std::reverse(children.begin(), children.end());
- foreach (QCPLayerable *child, children)
- child->moveToLayer(targetLayer, isFirstLayer);
-
- if (layer == mCurrentLayer)
- setCurrentLayer(targetLayer);
-
- if (QSharedPointer<QCPAbstractPaintBuffer> pb = layer->mPaintBuffer.toStrongRef())
- pb->setInvalidated();
-
- delete layer;
- mLayers.removeOne(layer);
- updateLayerIndices();
- return true;
- }
- bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
- {
- if (!mLayers.contains(layer))
- {
- qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
- return false;
- }
- if (!mLayers.contains(otherLayer))
- {
- qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
- return false;
- }
- if (layer->index() > otherLayer->index())
- mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
- else if (layer->index() < otherLayer->index())
- mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1));
-
- if (QSharedPointer<QCPAbstractPaintBuffer> pb = layer->mPaintBuffer.toStrongRef())
- pb->setInvalidated();
- if (QSharedPointer<QCPAbstractPaintBuffer> pb = otherLayer->mPaintBuffer.toStrongRef())
- pb->setInvalidated();
- updateLayerIndices();
- return true;
- }
- int QCustomPlot::axisRectCount() const
- {
- return axisRects().size();
- }
- QCPAxisRect *QCustomPlot::axisRect(int index) const
- {
- const QList<QCPAxisRect*> rectList = axisRects();
- if (index >= 0 && index < rectList.size())
- {
- return rectList.at(index);
- } else
- {
- return nullptr;
- }
- }
- QList<QCPAxisRect*> QCustomPlot::axisRects() const
- {
- QList<QCPAxisRect*> result;
- QStack<QCPLayoutElement*> elementStack;
- if (mPlotLayout)
- elementStack.push(mPlotLayout);
- while (!elementStack.isEmpty())
- {
- foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
- {
- if (element)
- {
- elementStack.push(element);
- if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
- result.append(ar);
- }
- }
- }
- return result;
- }
- QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
- {
- QCPLayoutElement *currentElement = mPlotLayout;
- bool searchSubElements = true;
- while (searchSubElements && currentElement)
- {
- searchSubElements = false;
- foreach (QCPLayoutElement *subElement, currentElement->elements(false))
- {
- if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
- {
- currentElement = subElement;
- searchSubElements = true;
- break;
- }
- }
- }
- return currentElement;
- }
- QCPAxisRect *QCustomPlot::axisRectAt(const QPointF &pos) const
- {
- QCPAxisRect *result = nullptr;
- QCPLayoutElement *currentElement = mPlotLayout;
- bool searchSubElements = true;
- while (searchSubElements && currentElement)
- {
- searchSubElements = false;
- foreach (QCPLayoutElement *subElement, currentElement->elements(false))
- {
- if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
- {
- currentElement = subElement;
- searchSubElements = true;
- if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(currentElement))
- result = ar;
- break;
- }
- }
- }
- return result;
- }
- QList<QCPAxis*> QCustomPlot::selectedAxes() const
- {
- QList<QCPAxis*> result, allAxes;
- foreach (QCPAxisRect *rect, axisRects())
- allAxes << rect->axes();
- foreach (QCPAxis *axis, allAxes)
- {
- if (axis->selectedParts() != QCPAxis::spNone)
- result.append(axis);
- }
- return result;
- }
- QList<QCPLegend*> QCustomPlot::selectedLegends() const
- {
- QList<QCPLegend*> result;
- QStack<QCPLayoutElement*> elementStack;
- if (mPlotLayout)
- elementStack.push(mPlotLayout);
- while (!elementStack.isEmpty())
- {
- foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
- {
- if (subElement)
- {
- elementStack.push(subElement);
- if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
- {
- if (leg->selectedParts() != QCPLegend::spNone)
- result.append(leg);
- }
- }
- }
- }
- return result;
- }
- void QCustomPlot::deselectAll()
- {
- foreach (QCPLayer *layer, mLayers)
- {
- foreach (QCPLayerable *layerable, layer->children())
- layerable->deselectEvent(nullptr);
- }
- }
- void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
- {
- if (refreshPriority == QCustomPlot::rpQueuedReplot)
- {
- if (!mReplotQueued)
- {
- mReplotQueued = true;
- QTimer::singleShot(0, this, SLOT(replot()));
- }
- return;
- }
- if (mReplotting)
- return;
- mReplotting = true;
- mReplotQueued = false;
- emit beforeReplot();
- # if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)
- QTime replotTimer;
- replotTimer.start();
- # else
- QElapsedTimer replotTimer;
- replotTimer.start();
- # endif
- updateLayout();
-
- setupPaintBuffers();
- foreach (QCPLayer *layer, mLayers)
- layer->drawToPaintBuffer();
- foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)
- buffer->setInvalidated(false);
- if ((refreshPriority == rpRefreshHint && mPlottingHints.testFlag(QCP::phImmediateRefresh)) || refreshPriority==rpImmediateRefresh)
- repaint();
- else
- update();
- # if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)
- mReplotTime = replotTimer.elapsed();
- # else
- mReplotTime = replotTimer.nsecsElapsed()*1e-6;
- # endif
- if (!qFuzzyIsNull(mReplotTimeAverage))
- mReplotTimeAverage = mReplotTimeAverage*0.9 + mReplotTime*0.1;
- else
- mReplotTimeAverage = mReplotTime;
- emit afterReplot();
- mReplotting = false;
- }
- double QCustomPlot::replotTime(bool average) const
- {
- return average ? mReplotTimeAverage : mReplotTime;
- }
- void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
- {
- QList<QCPAxis*> allAxes;
- foreach (QCPAxisRect *rect, axisRects())
- allAxes << rect->axes();
- foreach (QCPAxis *axis, allAxes)
- axis->rescale(onlyVisiblePlottables);
- }
- bool QCustomPlot::savePdf(const QString &fileName, int width, int height, QCP::ExportPen exportPen, const QString &pdfCreator, const QString &pdfTitle)
- {
- bool success = false;
- #ifdef QT_NO_PRINTER
- Q_UNUSED(fileName)
- Q_UNUSED(exportPen)
- Q_UNUSED(width)
- Q_UNUSED(height)
- Q_UNUSED(pdfCreator)
- Q_UNUSED(pdfTitle)
- qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
- #else
- int newWidth, newHeight;
- if (width == 0 || height == 0)
- {
- newWidth = this->width();
- newHeight = this->height();
- } else
- {
- newWidth = width;
- newHeight = height;
- }
- QPrinter printer(QPrinter::ScreenResolution);
- printer.setOutputFileName(fileName);
- printer.setOutputFormat(QPrinter::PdfFormat);
- printer.setColorMode(QPrinter::Color);
- printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
- printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
- QRect oldViewport = viewport();
- setViewport(QRect(0, 0, newWidth, newHeight));
- #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
- printer.setFullPage(true);
- printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
- #else
- QPageLayout pageLayout;
- pageLayout.setMode(QPageLayout::FullPageMode);
- pageLayout.setOrientation(QPageLayout::Portrait);
- pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
- pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
- printer.setPageLayout(pageLayout);
- #endif
- QCPPainter printpainter;
- if (printpainter.begin(&printer))
- {
- printpainter.setMode(QCPPainter::pmVectorized);
- printpainter.setMode(QCPPainter::pmNoCaching);
- printpainter.setMode(QCPPainter::pmNonCosmetic, exportPen==QCP::epNoCosmetic);
- printpainter.setWindow(mViewport);
- if (mBackgroundBrush.style() != Qt::NoBrush &&
- mBackgroundBrush.color() != Qt::white &&
- mBackgroundBrush.color() != Qt::transparent &&
- mBackgroundBrush.color().alpha() > 0)
- printpainter.fillRect(viewport(), mBackgroundBrush);
- draw(&printpainter);
- printpainter.end();
- success = true;
- }
- setViewport(oldViewport);
- #endif
- return success;
- }
- bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
- {
- return saveRastered(fileName, width, height, scale, "PNG", quality, resolution, resolutionUnit);
- }
- bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
- {
- return saveRastered(fileName, width, height, scale, "JPG", quality, resolution, resolutionUnit);
- }
- bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale, int resolution, QCP::ResolutionUnit resolutionUnit)
- {
- return saveRastered(fileName, width, height, scale, "BMP", -1, resolution, resolutionUnit);
- }
- QSize QCustomPlot::minimumSizeHint() const
- {
- return mPlotLayout->minimumOuterSizeHint().toSize();
- }
- QSize QCustomPlot::sizeHint() const
- {
- return mPlotLayout->minimumOuterSizeHint().toSize();
- }
- void QCustomPlot::paintEvent(QPaintEvent *event)
- {
- Q_UNUSED(event)
- QCPPainter painter(this);
- if (painter.isActive())
- {
- #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- painter.setRenderHint(QPainter::HighQualityAntialiasing);
- #endif
- if (mBackgroundBrush.style() != Qt::NoBrush)
- painter.fillRect(mViewport, mBackgroundBrush);
- drawBackground(&painter);
- foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)
- buffer->draw(&painter);
- }
- }
- void QCustomPlot::resizeEvent(QResizeEvent *event)
- {
- Q_UNUSED(event)
-
- setViewport(rect());
- replot(rpQueuedRefresh);
- }
- void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
- {
- emit mouseDoubleClick(event);
- mMouseHasMoved = false;
- mMousePressPos = event->pos();
-
- QList<QVariant> details;
- QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);
- for (int i=0; i<candidates.size(); ++i)
- {
- event->accept();
- candidates.at(i)->mouseDoubleClickEvent(event, details.at(i));
- if (event->isAccepted())
- {
- mMouseEventLayerable = candidates.at(i);
- mMouseEventLayerableDetails = details.at(i);
- break;
- }
- }
-
- if (!candidates.isEmpty())
- {
- if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(candidates.first()))
- {
- int dataIndex = 0;
- if (!details.first().value<QCPDataSelection>().isEmpty())
- dataIndex = details.first().value<QCPDataSelection>().dataRange().begin();
- emit plottableDoubleClick(ap, dataIndex, event);
- } else if (QCPAxis *ax = qobject_cast<QCPAxis*>(candidates.first()))
- emit axisDoubleClick(ax, details.first().value<QCPAxis::SelectablePart>(), event);
- else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(candidates.first()))
- emit itemDoubleClick(ai, event);
- else if (QCPLegend *lg = qobject_cast<QCPLegend*>(candidates.first()))
- emit legendDoubleClick(lg, nullptr, event);
- else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(candidates.first()))
- emit legendDoubleClick(li->parentLegend(), li, event);
- }
- event->accept();
- }
- void QCustomPlot::mousePressEvent(QMouseEvent *event)
- {
- emit mousePress(event);
-
- mMouseHasMoved = false;
- mMousePressPos = event->pos();
- if (mSelectionRect && mSelectionRectMode != QCP::srmNone)
- {
- if (mSelectionRectMode != QCP::srmZoom || qobject_cast<QCPAxisRect*>(axisRectAt(mMousePressPos)))
- mSelectionRect->startSelection(event);
- } else
- {
-
- QList<QVariant> details;
- QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);
- if (!candidates.isEmpty())
- {
- mMouseSignalLayerable = candidates.first();
- mMouseSignalLayerableDetails = details.first();
- }
-
- for (int i=0; i<candidates.size(); ++i)
- {
- event->accept();
- candidates.at(i)->mousePressEvent(event, details.at(i));
- if (event->isAccepted())
- {
- mMouseEventLayerable = candidates.at(i);
- mMouseEventLayerableDetails = details.at(i);
- break;
- }
- }
- }
- event->accept();
- }
- void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
- {
- emit mouseMove(event);
- if (!mMouseHasMoved && (mMousePressPos-event->pos()).manhattanLength() > 3)
- mMouseHasMoved = true;
- if (mSelectionRect && mSelectionRect->isActive())
- mSelectionRect->moveSelection(event);
- else if (mMouseEventLayerable)
- mMouseEventLayerable->mouseMoveEvent(event, mMousePressPos);
- event->accept();
- }
- void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
- {
- emit mouseRelease(event);
- if (!mMouseHasMoved)
- {
- emit mouseClick(event);
- if (mSelectionRect && mSelectionRect->isActive())
- mSelectionRect->cancel();
- if (event->button() == Qt::LeftButton)
- processPointSelection(event);
-
- if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(mMouseSignalLayerable))
- {
- int dataIndex = 0;
- if (!mMouseSignalLayerableDetails.value<QCPDataSelection>().isEmpty())
- dataIndex = mMouseSignalLayerableDetails.value<QCPDataSelection>().dataRange().begin();
- emit plottableClick(ap, dataIndex, event);
- } else if (QCPAxis *ax = qobject_cast<QCPAxis*>(mMouseSignalLayerable))
- emit axisClick(ax, mMouseSignalLayerableDetails.value<QCPAxis::SelectablePart>(), event);
- else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(mMouseSignalLayerable))
- emit itemClick(ai, event);
- else if (QCPLegend *lg = qobject_cast<QCPLegend*>(mMouseSignalLayerable))
- emit legendClick(lg, nullptr, event);
- else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(mMouseSignalLayerable))
- emit legendClick(li->parentLegend(), li, event);
- mMouseSignalLayerable = nullptr;
- }
- if (mSelectionRect && mSelectionRect->isActive())
- {
-
- mSelectionRect->endSelection(event);
- } else
- {
-
- if (mMouseEventLayerable)
- {
- mMouseEventLayerable->mouseReleaseEvent(event, mMousePressPos);
- mMouseEventLayerable = nullptr;
- }
- }
- if (noAntialiasingOnDrag())
- replot(rpQueuedReplot);
- event->accept();
- }
- void QCustomPlot::wheelEvent(QWheelEvent *event)
- {
- emit mouseWheel(event);
- #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
- const QPointF pos = event->pos();
- #else
- const QPointF pos = event->position();
- #endif
-
- foreach (QCPLayerable *candidate, layerableListAt(pos, false))
- {
- event->accept();
- candidate->wheelEvent(event);
- if (event->isAccepted())
- break;
- }
- event->accept();
- }
- void QCustomPlot::draw(QCPPainter *painter)
- {
- updateLayout();
-
- drawBackground(painter);
-
- foreach (QCPLayer *layer, mLayers)
- layer->draw(painter);
-
- }
- void QCustomPlot::updateLayout()
- {
-
- mPlotLayout->update(QCPLayoutElement::upPreparation);
- mPlotLayout->update(QCPLayoutElement::upMargins);
- mPlotLayout->update(QCPLayoutElement::upLayout);
- emit afterLayout();
- }
- void QCustomPlot::drawBackground(QCPPainter *painter)
- {
-
-
- if (!mBackgroundPixmap.isNull())
- {
- if (mBackgroundScaled)
- {
-
- QSize scaledSize(mBackgroundPixmap.size());
- scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
- if (mScaledBackgroundPixmap.size() != scaledSize)
- mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
- painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
- } else
- {
- painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
- }
- }
- }
- void QCustomPlot::setupPaintBuffers()
- {
- int bufferIndex = 0;
- if (mPaintBuffers.isEmpty())
- mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
- for (int layerIndex = 0; layerIndex < mLayers.size(); ++layerIndex)
- {
- QCPLayer *layer = mLayers.at(layerIndex);
- if (layer->mode() == QCPLayer::lmLogical)
- {
- layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();
- } else if (layer->mode() == QCPLayer::lmBuffered)
- {
- ++bufferIndex;
- if (bufferIndex >= mPaintBuffers.size())
- mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
- layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();
- if (layerIndex < mLayers.size()-1 && mLayers.at(layerIndex+1)->mode() == QCPLayer::lmLogical)
- {
- ++bufferIndex;
- if (bufferIndex >= mPaintBuffers.size())
- mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
- }
- }
- }
-
- while (mPaintBuffers.size()-1 > bufferIndex)
- mPaintBuffers.removeLast();
-
- foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)
- {
- buffer->setSize(viewport().size());
- buffer->clear(Qt::transparent);
- buffer->setInvalidated();
- }
- }
- QCPAbstractPaintBuffer *QCustomPlot::createPaintBuffer()
- {
- if (mOpenGl)
- {
- #if defined(QCP_OPENGL_FBO)
- return new QCPPaintBufferGlFbo(viewport().size(), mBufferDevicePixelRatio, mGlContext, mGlPaintDevice);
- #elif defined(QCP_OPENGL_PBUFFER)
- return new QCPPaintBufferGlPbuffer(viewport().size(), mBufferDevicePixelRatio, mOpenGlMultisamples);
- #else
- qDebug() << Q_FUNC_INFO << "OpenGL enabled even though no support for it compiled in, this shouldn't have happened. Falling back to pixmap paint buffer.";
- return new QCPPaintBufferPixmap(viewport().size(), mBufferDevicePixelRatio);
- #endif
- } else
- return new QCPPaintBufferPixmap(viewport().size(), mBufferDevicePixelRatio);
- }
- bool QCustomPlot::hasInvalidatedPaintBuffers()
- {
- foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)
- {
- if (buffer->invalidated())
- return true;
- }
- return false;
- }
- bool QCustomPlot::setupOpenGl()
- {
- #ifdef QCP_OPENGL_FBO
- freeOpenGl();
- QSurfaceFormat proposedSurfaceFormat;
- proposedSurfaceFormat.setSamples(mOpenGlMultisamples);
- #ifdef QCP_OPENGL_OFFSCREENSURFACE
- QOffscreenSurface *surface = new QOffscreenSurface;
- #else
- QWindow *surface = new QWindow;
- surface->setSurfaceType(QSurface::OpenGLSurface);
- #endif
- surface->setFormat(proposedSurfaceFormat);
- surface->create();
- mGlSurface = QSharedPointer<QSurface>(surface);
- mGlContext = QSharedPointer<QOpenGLContext>(new QOpenGLContext);
- mGlContext->setFormat(mGlSurface->format());
- if (!mGlContext->create())
- {
- qDebug() << Q_FUNC_INFO << "Failed to create OpenGL context";
- mGlContext.clear();
- mGlSurface.clear();
- return false;
- }
- if (!mGlContext->makeCurrent(mGlSurface.data()))
- {
- qDebug() << Q_FUNC_INFO << "Failed to make opengl context current";
- mGlContext.clear();
- mGlSurface.clear();
- return false;
- }
- if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
- {
- qDebug() << Q_FUNC_INFO << "OpenGL of this system doesn't support frame buffer objects";
- mGlContext.clear();
- mGlSurface.clear();
- return false;
- }
- mGlPaintDevice = QSharedPointer<QOpenGLPaintDevice>(new QOpenGLPaintDevice);
- return true;
- #elif defined(QCP_OPENGL_PBUFFER)
- return QGLFormat::hasOpenGL();
- #else
- return false;
- #endif
- }
- void QCustomPlot::freeOpenGl()
- {
- #ifdef QCP_OPENGL_FBO
- mGlPaintDevice.clear();
- mGlContext.clear();
- mGlSurface.clear();
- #endif
- }
- void QCustomPlot::axisRemoved(QCPAxis *axis)
- {
- if (xAxis == axis)
- xAxis = nullptr;
- if (xAxis2 == axis)
- xAxis2 = nullptr;
- if (yAxis == axis)
- yAxis = nullptr;
- if (yAxis2 == axis)
- yAxis2 = nullptr;
-
- }
- void QCustomPlot::legendRemoved(QCPLegend *legend)
- {
- if (this->legend == legend)
- this->legend = nullptr;
- }
- void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event)
- {
- typedef QPair<QCPAbstractPlottable*, QCPDataSelection> SelectionCandidate;
- typedef QMultiMap<int, SelectionCandidate> SelectionCandidates;
- bool selectionStateChanged = false;
- if (mInteractions.testFlag(QCP::iSelectPlottables))
- {
- SelectionCandidates potentialSelections;
- QRectF rectF(rect.normalized());
- if (QCPAxisRect *affectedAxisRect = axisRectAt(rectF.topLeft()))
- {
-
- foreach (QCPAbstractPlottable *plottable, affectedAxisRect->plottables())
- {
- if (QCPPlottableInterface1D *plottableInterface = plottable->interface1D())
- {
- QCPDataSelection dataSel = plottableInterface->selectTestRect(rectF, true);
- if (!dataSel.isEmpty())
- potentialSelections.insert(dataSel.dataPointCount(), SelectionCandidate(plottable, dataSel));
- }
- }
- if (!mInteractions.testFlag(QCP::iMultiSelect))
- {
-
- if (!potentialSelections.isEmpty())
- {
- SelectionCandidates::iterator it = potentialSelections.begin();
- while (it != std::prev(potentialSelections.end()))
- it = potentialSelections.erase(it);
- }
- }
- bool additive = event->modifiers().testFlag(mMultiSelectModifier);
-
- if (!additive)
- {
-
- foreach (QCPLayer *layer, mLayers)
- {
- foreach (QCPLayerable *layerable, layer->children())
- {
- if ((potentialSelections.isEmpty() || potentialSelections.constBegin()->first != layerable) && mInteractions.testFlag(layerable->selectionCategory()))
- {
- bool selChanged = false;
- layerable->deselectEvent(&selChanged);
- selectionStateChanged |= selChanged;
- }
- }
- }
- }
-
- SelectionCandidates::const_iterator it = potentialSelections.constEnd();
- while (it != potentialSelections.constBegin())
- {
- --it;
- if (mInteractions.testFlag(it.value().first->selectionCategory()))
- {
- bool selChanged = false;
- it.value().first->selectEvent(event, additive, QVariant::fromValue(it.value().second), &selChanged);
- selectionStateChanged |= selChanged;
- }
- }
- }
- }
- if (selectionStateChanged)
- {
- emit selectionChangedByUser();
- replot(rpQueuedReplot);
- } else if (mSelectionRect)
- mSelectionRect->layer()->replot();
- }
- void QCustomPlot::processRectZoom(QRect rect, QMouseEvent *event)
- {
- Q_UNUSED(event)
- if (QCPAxisRect *axisRect = axisRectAt(rect.topLeft()))
- {
- QList<QCPAxis*> affectedAxes = QList<QCPAxis*>() << axisRect->rangeZoomAxes(Qt::Horizontal) << axisRect->rangeZoomAxes(Qt::Vertical);
- affectedAxes.removeAll(static_cast<QCPAxis*>(nullptr));
- axisRect->zoom(QRectF(rect), affectedAxes);
- }
- replot(rpQueuedReplot);
- }
- void QCustomPlot::processPointSelection(QMouseEvent *event)
- {
- QVariant details;
- QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
- bool selectionStateChanged = false;
- bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
-
- if (!additive)
- {
- foreach (QCPLayer *layer, mLayers)
- {
- foreach (QCPLayerable *layerable, layer->children())
- {
- if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
- {
- bool selChanged = false;
- layerable->deselectEvent(&selChanged);
- selectionStateChanged |= selChanged;
- }
- }
- }
- }
- if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
- {
-
- bool selChanged = false;
- clickedLayerable->selectEvent(event, additive, details, &selChanged);
- selectionStateChanged |= selChanged;
- }
- if (selectionStateChanged)
- {
- emit selectionChangedByUser();
- replot(rpQueuedReplot);
- }
- }
- bool QCustomPlot::registerPlottable(QCPAbstractPlottable *plottable)
- {
- if (mPlottables.contains(plottable))
- {
- qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
- return false;
- }
- if (plottable->parentPlot() != this)
- {
- qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
- return false;
- }
- mPlottables.append(plottable);
-
- if (mAutoAddPlottableToLegend)
- plottable->addToLegend();
- if (!plottable->layer())
- plottable->setLayer(currentLayer());
- return true;
- }
- bool QCustomPlot::registerGraph(QCPGraph *graph)
- {
- if (!graph)
- {
- qDebug() << Q_FUNC_INFO << "passed graph is zero";
- return false;
- }
- if (mGraphs.contains(graph))
- {
- qDebug() << Q_FUNC_INFO << "graph already registered with this QCustomPlot";
- return false;
- }
- mGraphs.append(graph);
- return true;
- }
- bool QCustomPlot::registerItem(QCPAbstractItem *item)
- {
- if (mItems.contains(item))
- {
- qDebug() << Q_FUNC_INFO << "item already added to this QCustomPlot:" << reinterpret_cast<quintptr>(item);
- return false;
- }
- if (item->parentPlot() != this)
- {
- qDebug() << Q_FUNC_INFO << "item not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
- return false;
- }
- mItems.append(item);
- if (!item->layer())
- item->setLayer(currentLayer());
- return true;
- }
- void QCustomPlot::updateLayerIndices() const
- {
- for (int i=0; i<mLayers.size(); ++i)
- mLayers.at(i)->mIndex = i;
- }
- QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
- {
- QList<QVariant> details;
- QList<QCPLayerable*> candidates = layerableListAt(pos, onlySelectable, selectionDetails ? &details : nullptr);
- if (selectionDetails && !details.isEmpty())
- *selectionDetails = details.first();
- if (!candidates.isEmpty())
- return candidates.first();
- else
- return nullptr;
- }
- QList<QCPLayerable*> QCustomPlot::layerableListAt(const QPointF &pos, bool onlySelectable, QList<QVariant> *selectionDetails) const
- {
- QList<QCPLayerable*> result;
- for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
- {
- const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
- for (int i=layerables.size()-1; i>=0; --i)
- {
- if (!layerables.at(i)->realVisibility())
- continue;
- QVariant details;
- double dist = layerables.at(i)->selectTest(pos, onlySelectable, selectionDetails ? &details : nullptr);
- if (dist >= 0 && dist < selectionTolerance())
- {
- result.append(layerables.at(i));
- if (selectionDetails)
- selectionDetails->append(details);
- }
- }
- }
- return result;
- }
- bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
- {
- QImage buffer = toPixmap(width, height, scale).toImage();
- int dotsPerMeter = 0;
- switch (resolutionUnit)
- {
- case QCP::ruDotsPerMeter: dotsPerMeter = resolution; break;
- case QCP::ruDotsPerCentimeter: dotsPerMeter = resolution*100; break;
- case QCP::ruDotsPerInch: dotsPerMeter = int(resolution/0.0254); break;
- }
- buffer.setDotsPerMeterX(dotsPerMeter);
- buffer.setDotsPerMeterY(dotsPerMeter);
- if (!buffer.isNull())
- return buffer.save(fileName, format, quality);
- else
- return false;
- }
- QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
- {
-
- int newWidth, newHeight;
- if (width == 0 || height == 0)
- {
- newWidth = this->width();
- newHeight = this->height();
- } else
- {
- newWidth = width;
- newHeight = height;
- }
- int scaledWidth = qRound(scale*newWidth);
- int scaledHeight = qRound(scale*newHeight);
- QPixmap result(scaledWidth, scaledHeight);
- result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
- QCPPainter painter;
- painter.begin(&result);
- if (painter.isActive())
- {
- QRect oldViewport = viewport();
- setViewport(QRect(0, 0, newWidth, newHeight));
- painter.setMode(QCPPainter::pmNoCaching);
- if (!qFuzzyCompare(scale, 1.0))
- {
- if (scale > 1.0)
- painter.setMode(QCPPainter::pmNonCosmetic);
- painter.scale(scale, scale);
- }
- if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
- painter.fillRect(mViewport, mBackgroundBrush);
- draw(&painter);
- setViewport(oldViewport);
- painter.end();
- } else
- {
- qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
- return QPixmap();
- }
- return result;
- }
- void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
- {
-
- int newWidth, newHeight;
- if (width == 0 || height == 0)
- {
- newWidth = this->width();
- newHeight = this->height();
- } else
- {
- newWidth = width;
- newHeight = height;
- }
- if (painter->isActive())
- {
- QRect oldViewport = viewport();
- setViewport(QRect(0, 0, newWidth, newHeight));
- painter->setMode(QCPPainter::pmNoCaching);
- if (mBackgroundBrush.style() != Qt::NoBrush)
- painter->fillRect(mViewport, mBackgroundBrush);
- draw(painter);
- setViewport(oldViewport);
- } else
- qDebug() << Q_FUNC_INFO << "Passed painter is not active";
- }
- QCPColorGradient::QCPColorGradient() :
- mLevelCount(350),
- mColorInterpolation(ciRGB),
- mNanHandling(nhNone),
- mNanColor(Qt::black),
- mPeriodic(false),
- mColorBufferInvalidated(true)
- {
- mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
- }
- QCPColorGradient::QCPColorGradient(GradientPreset preset) :
- mLevelCount(350),
- mColorInterpolation(ciRGB),
- mNanHandling(nhNone),
- mNanColor(Qt::black),
- mPeriodic(false),
- mColorBufferInvalidated(true)
- {
- mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
- loadPreset(preset);
- }
- bool QCPColorGradient::operator==(const QCPColorGradient &other) const
- {
- return ((other.mLevelCount == this->mLevelCount) &&
- (other.mColorInterpolation == this->mColorInterpolation) &&
- (other.mNanHandling == this ->mNanHandling) &&
- (other.mNanColor == this->mNanColor) &&
- (other.mPeriodic == this->mPeriodic) &&
- (other.mColorStops == this->mColorStops));
- }
- void QCPColorGradient::setLevelCount(int n)
- {
- if (n < 2)
- {
- qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
- n = 2;
- }
- if (n != mLevelCount)
- {
- mLevelCount = n;
- mColorBufferInvalidated = true;
- }
- }
- void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
- {
- mColorStops = colorStops;
- mColorBufferInvalidated = true;
- }
- void QCPColorGradient::setColorStopAt(double position, const QColor &color)
- {
- mColorStops.insert(position, color);
- mColorBufferInvalidated = true;
- }
- void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation)
- {
- if (interpolation != mColorInterpolation)
- {
- mColorInterpolation = interpolation;
- mColorBufferInvalidated = true;
- }
- }
- void QCPColorGradient::setNanHandling(QCPColorGradient::NanHandling handling)
- {
- mNanHandling = handling;
- }
- void QCPColorGradient::setNanColor(const QColor &color)
- {
- mNanColor = color;
- }
- void QCPColorGradient::setPeriodic(bool enabled)
- {
- mPeriodic = enabled;
- }
- void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
- {
-
- if (!data)
- {
- qDebug() << Q_FUNC_INFO << "null pointer given as data";
- return;
- }
- if (!scanLine)
- {
- qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
- return;
- }
- if (mColorBufferInvalidated)
- updateColorBuffer();
- const bool skipNanCheck = mNanHandling == nhNone;
- const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower);
- for (int i=0; i<n; ++i)
- {
- const double value = data[dataIndexFactor*i];
- if (skipNanCheck || !std::isnan(value))
- {
- int index = int((!logarithmic ? value-range.lower : qLn(value/range.lower)) * posToIndexFactor);
- if (!mPeriodic)
- {
- index = qBound(0, index, mLevelCount-1);
- } else
- {
- index %= mLevelCount;
- if (index < 0)
- index += mLevelCount;
- }
- scanLine[i] = mColorBuffer.at(index);
- } else
- {
- switch(mNanHandling)
- {
- case nhLowestColor: scanLine[i] = mColorBuffer.first(); break;
- case nhHighestColor: scanLine[i] = mColorBuffer.last(); break;
- case nhTransparent: scanLine[i] = qRgba(0, 0, 0, 0); break;
- case nhNanColor: scanLine[i] = mNanColor.rgba(); break;
- case nhNone: break;
- }
- }
- }
- }
- void QCPColorGradient::colorize(const double *data, const unsigned char *alpha, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
- {
-
- if (!data)
- {
- qDebug() << Q_FUNC_INFO << "null pointer given as data";
- return;
- }
- if (!alpha)
- {
- qDebug() << Q_FUNC_INFO << "null pointer given as alpha";
- return;
- }
- if (!scanLine)
- {
- qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
- return;
- }
- if (mColorBufferInvalidated)
- updateColorBuffer();
- const bool skipNanCheck = mNanHandling == nhNone;
- const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower);
- for (int i=0; i<n; ++i)
- {
- const double value = data[dataIndexFactor*i];
- if (skipNanCheck || !std::isnan(value))
- {
- int index = int((!logarithmic ? value-range.lower : qLn(value/range.lower)) * posToIndexFactor);
- if (!mPeriodic)
- {
- index = qBound(0, index, mLevelCount-1);
- } else
- {
- index %= mLevelCount;
- if (index < 0)
- index += mLevelCount;
- }
- if (alpha[dataIndexFactor*i] == 255)
- {
- scanLine[i] = mColorBuffer.at(index);
- } else
- {
- const QRgb rgb = mColorBuffer.at(index);
- const float alphaF = alpha[dataIndexFactor*i]/255.0f;
- scanLine[i] = qRgba(int(qRed(rgb)*alphaF), int(qGreen(rgb)*alphaF), int(qBlue(rgb)*alphaF), int(qAlpha(rgb)*alphaF));
- }
- } else
- {
- switch(mNanHandling)
- {
- case nhLowestColor: scanLine[i] = mColorBuffer.first(); break;
- case nhHighestColor: scanLine[i] = mColorBuffer.last(); break;
- case nhTransparent: scanLine[i] = qRgba(0, 0, 0, 0); break;
- case nhNanColor: scanLine[i] = mNanColor.rgba(); break;
- case nhNone: break;
- }
- }
- }
- }
- QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
- {
-
- if (mColorBufferInvalidated)
- updateColorBuffer();
- const bool skipNanCheck = mNanHandling == nhNone;
- if (!skipNanCheck && std::isnan(position))
- {
- switch(mNanHandling)
- {
- case nhLowestColor: return mColorBuffer.first();
- case nhHighestColor: return mColorBuffer.last();
- case nhTransparent: return qRgba(0, 0, 0, 0);
- case nhNanColor: return mNanColor.rgba();
- case nhNone: return qRgba(0, 0, 0, 0);
- }
- }
- const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower);
- int index = int((!logarithmic ? position-range.lower : qLn(position/range.lower)) * posToIndexFactor);
- if (!mPeriodic)
- {
- index = qBound(0, index, mLevelCount-1);
- } else
- {
- index %= mLevelCount;
- if (index < 0)
- index += mLevelCount;
- }
- return mColorBuffer.at(index);
- }
- void QCPColorGradient::loadPreset(GradientPreset preset)
- {
- clearColorStops();
- switch (preset)
- {
- case gpGrayscale:
- setColorInterpolation(ciRGB);
- setColorStopAt(0, Qt::black);
- setColorStopAt(1, Qt::white);
- break;
- case gpHot:
- setColorInterpolation(ciRGB);
- setColorStopAt(0, QColor(50, 0, 0));
- setColorStopAt(0.2, QColor(180, 10, 0));
- setColorStopAt(0.4, QColor(245, 50, 0));
- setColorStopAt(0.6, QColor(255, 150, 10));
- setColorStopAt(0.8, QColor(255, 255, 50));
- setColorStopAt(1, QColor(255, 255, 255));
- break;
- case gpCold:
- setColorInterpolation(ciRGB);
- setColorStopAt(0, QColor(0, 0, 50));
- setColorStopAt(0.2, QColor(0, 10, 180));
- setColorStopAt(0.4, QColor(0, 50, 245));
- setColorStopAt(0.6, QColor(10, 150, 255));
- setColorStopAt(0.8, QColor(50, 255, 255));
- setColorStopAt(1, QColor(255, 255, 255));
- break;
- case gpNight:
- setColorInterpolation(ciHSV);
- setColorStopAt(0, QColor(10, 20, 30));
- setColorStopAt(1, QColor(250, 255, 250));
- break;
- case gpCandy:
- setColorInterpolation(ciHSV);
- setColorStopAt(0, QColor(0, 0, 255));
- setColorStopAt(1, QColor(255, 250, 250));
- break;
- case gpGeography:
- setColorInterpolation(ciRGB);
- setColorStopAt(0, QColor(70, 170, 210));
- setColorStopAt(0.20, QColor(90, 160, 180));
- setColorStopAt(0.25, QColor(45, 130, 175));
- setColorStopAt(0.30, QColor(100, 140, 125));
- setColorStopAt(0.5, QColor(100, 140, 100));
- setColorStopAt(0.6, QColor(130, 145, 120));
- setColorStopAt(0.7, QColor(140, 130, 120));
- setColorStopAt(0.9, QColor(180, 190, 190));
- setColorStopAt(1, QColor(210, 210, 230));
- break;
- case gpIon:
- setColorInterpolation(ciHSV);
- setColorStopAt(0, QColor(50, 10, 10));
- setColorStopAt(0.45, QColor(0, 0, 255));
- setColorStopAt(0.8, QColor(0, 255, 255));
- setColorStopAt(1, QColor(0, 255, 0));
- break;
- case gpThermal:
- setColorInterpolation(ciRGB);
- setColorStopAt(0, QColor(0, 0, 50));
- setColorStopAt(0.15, QColor(20, 0, 120));
- setColorStopAt(0.33, QColor(200, 30, 140));
- setColorStopAt(0.6, QColor(255, 100, 0));
- setColorStopAt(0.85, QColor(255, 255, 40));
- setColorStopAt(1, QColor(255, 255, 255));
- break;
- case gpPolar:
- setColorInterpolation(ciRGB);
- setColorStopAt(0, QColor(50, 255, 255));
- setColorStopAt(0.18, QColor(10, 70, 255));
- setColorStopAt(0.28, QColor(10, 10, 190));
- setColorStopAt(0.5, QColor(0, 0, 0));
- setColorStopAt(0.72, QColor(190, 10, 10));
- setColorStopAt(0.82, QColor(255, 70, 10));
- setColorStopAt(1, QColor(255, 255, 50));
- break;
- case gpSpectrum:
- setColorInterpolation(ciHSV);
- setColorStopAt(0, QColor(50, 0, 50));
- setColorStopAt(0.15, QColor(0, 0, 255));
- setColorStopAt(0.35, QColor(0, 255, 255));
- setColorStopAt(0.6, QColor(255, 255, 0));
- setColorStopAt(0.75, QColor(255, 30, 0));
- setColorStopAt(1, QColor(50, 0, 0));
- break;
- case gpJet:
- setColorInterpolation(ciRGB);
- setColorStopAt(0, QColor(0, 0, 100));
- setColorStopAt(0.15, QColor(0, 50, 255));
- setColorStopAt(0.35, QColor(0, 255, 255));
- setColorStopAt(0.65, QColor(255, 255, 0));
- setColorStopAt(0.85, QColor(255, 30, 0));
- setColorStopAt(1, QColor(100, 0, 0));
- break;
- case gpHues:
- setColorInterpolation(ciHSV);
- setColorStopAt(0, QColor(255, 0, 0));
- setColorStopAt(1.0/3.0, QColor(0, 0, 255));
- setColorStopAt(2.0/3.0, QColor(0, 255, 0));
- setColorStopAt(1, QColor(255, 0, 0));
- break;
- }
- }
- void QCPColorGradient::clearColorStops()
- {
- mColorStops.clear();
- mColorBufferInvalidated = true;
- }
- QCPColorGradient QCPColorGradient::inverted() const
- {
- QCPColorGradient result(*this);
- result.clearColorStops();
- for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
- result.setColorStopAt(1.0-it.key(), it.value());
- return result;
- }
- bool QCPColorGradient::stopsUseAlpha() const
- {
- for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
- {
- if (it.value().alpha() < 255)
- return true;
- }
- return false;
- }
- void QCPColorGradient::updateColorBuffer()
- {
- if (mColorBuffer.size() != mLevelCount)
- mColorBuffer.resize(mLevelCount);
- if (mColorStops.size() > 1)
- {
- double indexToPosFactor = 1.0/double(mLevelCount-1);
- const bool useAlpha = stopsUseAlpha();
- for (int i=0; i<mLevelCount; ++i)
- {
- double position = i*indexToPosFactor;
- QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
- if (it == mColorStops.constEnd())
- {
- if (useAlpha)
- {
- const QColor col = std::prev(it).value();
- const double alphaPremultiplier = col.alpha()/255.0;
- mColorBuffer[i] = qRgba(int(col.red()*alphaPremultiplier),
- int(col.green()*alphaPremultiplier),
- int(col.blue()*alphaPremultiplier),
- col.alpha());
- } else
- mColorBuffer[i] = std::prev(it).value().rgba();
- } else if (it == mColorStops.constBegin())
- {
- if (useAlpha)
- {
- const QColor &col = it.value();
- const double alphaPremultiplier = col.alpha()/255.0;
- mColorBuffer[i] = qRgba(int(col.red()*alphaPremultiplier),
- int(col.green()*alphaPremultiplier),
- int(col.blue()*alphaPremultiplier),
- col.alpha());
- } else
- mColorBuffer[i] = it.value().rgba();
- } else
- {
- QMap<double, QColor>::const_iterator high = it;
- QMap<double, QColor>::const_iterator low = std::prev(it);
- double t = (position-low.key())/(high.key()-low.key());
- switch (mColorInterpolation)
- {
- case ciRGB:
- {
- if (useAlpha)
- {
- const int alpha = int((1-t)*low.value().alpha() + t*high.value().alpha());
- const double alphaPremultiplier = alpha/255.0;
- mColorBuffer[i] = qRgba(int( ((1-t)*low.value().red() + t*high.value().red())*alphaPremultiplier ),
- int( ((1-t)*low.value().green() + t*high.value().green())*alphaPremultiplier ),
- int( ((1-t)*low.value().blue() + t*high.value().blue())*alphaPremultiplier ),
- alpha);
- } else
- {
- mColorBuffer[i] = qRgb(int( ((1-t)*low.value().red() + t*high.value().red()) ),
- int( ((1-t)*low.value().green() + t*high.value().green()) ),
- int( ((1-t)*low.value().blue() + t*high.value().blue())) );
- }
- break;
- }
- case ciHSV:
- {
- QColor lowHsv = low.value().toHsv();
- QColor highHsv = high.value().toHsv();
- double hue = 0;
- double hueDiff = highHsv.hueF()-lowHsv.hueF();
- if (hueDiff > 0.5)
- hue = lowHsv.hueF() - t*(1.0-hueDiff);
- else if (hueDiff < -0.5)
- hue = lowHsv.hueF() + t*(1.0+hueDiff);
- else
- hue = lowHsv.hueF() + t*hueDiff;
- if (hue < 0) hue += 1.0;
- else if (hue >= 1.0) hue -= 1.0;
- if (useAlpha)
- {
- const QRgb rgb = QColor::fromHsvF(hue,
- (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),
- (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
- const double alpha = (1-t)*lowHsv.alphaF() + t*highHsv.alphaF();
- mColorBuffer[i] = qRgba(int(qRed(rgb)*alpha), int(qGreen(rgb)*alpha), int(qBlue(rgb)*alpha), int(255*alpha));
- }
- else
- {
- mColorBuffer[i] = QColor::fromHsvF(hue,
- (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),
- (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
- }
- break;
- }
- }
- }
- }
- } else if (mColorStops.size() == 1)
- {
- const QRgb rgb = mColorStops.constBegin().value().rgb();
- const double alpha = mColorStops.constBegin().value().alphaF();
- mColorBuffer.fill(qRgba(int(qRed(rgb)*alpha), int(qGreen(rgb)*alpha), int(qBlue(rgb)*alpha), int(255*alpha)));
- } else
- {
- mColorBuffer.fill(qRgb(0, 0, 0));
- }
- mColorBufferInvalidated = false;
- }
- QCPSelectionDecoratorBracket::QCPSelectionDecoratorBracket() :
- mBracketPen(QPen(Qt::black)),
- mBracketBrush(Qt::NoBrush),
- mBracketWidth(5),
- mBracketHeight(50),
- mBracketStyle(bsSquareBracket),
- mTangentToData(false),
- mTangentAverage(2)
- {
- }
- QCPSelectionDecoratorBracket::~QCPSelectionDecoratorBracket()
- {
- }
- void QCPSelectionDecoratorBracket::setBracketPen(const QPen &pen)
- {
- mBracketPen = pen;
- }
- void QCPSelectionDecoratorBracket::setBracketBrush(const QBrush &brush)
- {
- mBracketBrush = brush;
- }
- void QCPSelectionDecoratorBracket::setBracketWidth(int width)
- {
- mBracketWidth = width;
- }
- void QCPSelectionDecoratorBracket::setBracketHeight(int height)
- {
- mBracketHeight = height;
- }
- void QCPSelectionDecoratorBracket::setBracketStyle(QCPSelectionDecoratorBracket::BracketStyle style)
- {
- mBracketStyle = style;
- }
- void QCPSelectionDecoratorBracket::setTangentToData(bool enabled)
- {
- mTangentToData = enabled;
- }
- void QCPSelectionDecoratorBracket::setTangentAverage(int pointCount)
- {
- mTangentAverage = pointCount;
- if (mTangentAverage < 1)
- mTangentAverage = 1;
- }
- void QCPSelectionDecoratorBracket::drawBracket(QCPPainter *painter, int direction) const
- {
- switch (mBracketStyle)
- {
- case bsSquareBracket:
- {
- painter->drawLine(QLineF(mBracketWidth*direction, -mBracketHeight*0.5, 0, -mBracketHeight*0.5));
- painter->drawLine(QLineF(mBracketWidth*direction, mBracketHeight*0.5, 0, mBracketHeight*0.5));
- painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5));
- break;
- }
- case bsHalfEllipse:
- {
- painter->drawArc(QRectF(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight), -90*16, -180*16*direction);
- break;
- }
- case bsEllipse:
- {
- painter->drawEllipse(QRectF(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight));
- break;
- }
- case bsPlus:
- {
- painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5));
- painter->drawLine(QLineF(-mBracketWidth*0.5, 0, mBracketWidth*0.5, 0));
- break;
- }
- default:
- {
- qDebug() << Q_FUNC_INFO << "unknown/custom bracket style can't be handeld by default implementation:" << static_cast<int>(mBracketStyle);
- break;
- }
- }
- }
- void QCPSelectionDecoratorBracket::drawDecoration(QCPPainter *painter, QCPDataSelection selection)
- {
- if (!mPlottable || selection.isEmpty()) return;
- if (QCPPlottableInterface1D *interface1d = mPlottable->interface1D())
- {
- foreach (const QCPDataRange &dataRange, selection.dataRanges())
- {
-
- int openBracketDir = (mPlottable->keyAxis() && !mPlottable->keyAxis()->rangeReversed()) ? 1 : -1;
- int closeBracketDir = -openBracketDir;
- QPointF openBracketPos = getPixelCoordinates(interface1d, dataRange.begin());
- QPointF closeBracketPos = getPixelCoordinates(interface1d, dataRange.end()-1);
- double openBracketAngle = 0;
- double closeBracketAngle = 0;
- if (mTangentToData)
- {
- openBracketAngle = getTangentAngle(interface1d, dataRange.begin(), openBracketDir);
- closeBracketAngle = getTangentAngle(interface1d, dataRange.end()-1, closeBracketDir);
- }
-
- QTransform oldTransform = painter->transform();
- painter->setPen(mBracketPen);
- painter->setBrush(mBracketBrush);
- painter->translate(openBracketPos);
- painter->rotate(openBracketAngle/M_PI*180.0);
- drawBracket(painter, openBracketDir);
- painter->setTransform(oldTransform);
-
- painter->setPen(mBracketPen);
- painter->setBrush(mBracketBrush);
- painter->translate(closeBracketPos);
- painter->rotate(closeBracketAngle/M_PI*180.0);
- drawBracket(painter, closeBracketDir);
- painter->setTransform(oldTransform);
- }
- }
- }
- double QCPSelectionDecoratorBracket::getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const
- {
- if (!interface1d || dataIndex < 0 || dataIndex >= interface1d->dataCount())
- return 0;
- direction = direction < 0 ? -1 : 1;
-
- int averageCount;
- if (direction < 0)
- averageCount = qMin(mTangentAverage, dataIndex);
- else
- averageCount = qMin(mTangentAverage, interface1d->dataCount()-1-dataIndex);
- qDebug() << averageCount;
-
- QVector<QPointF> points(averageCount);
- QPointF pointsAverage;
- int currentIndex = dataIndex;
- for (int i=0; i<averageCount; ++i)
- {
- points[i] = getPixelCoordinates(interface1d, currentIndex);
- pointsAverage += points[i];
- currentIndex += direction;
- }
- pointsAverage /= double(averageCount);
-
- double numSum = 0;
- double denomSum = 0;
- for (int i=0; i<averageCount; ++i)
- {
- const double dx = points.at(i).x()-pointsAverage.x();
- const double dy = points.at(i).y()-pointsAverage.y();
- numSum += dx*dy;
- denomSum += dx*dx;
- }
- if (!qFuzzyIsNull(denomSum) && !qFuzzyIsNull(numSum))
- {
- return qAtan2(numSum, denomSum);
- } else
- return 0;
- }
- QPointF QCPSelectionDecoratorBracket::getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const
- {
- QCPAxis *keyAxis = mPlottable->keyAxis();
- QCPAxis *valueAxis = mPlottable->valueAxis();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {0, 0}; }
- if (keyAxis->orientation() == Qt::Horizontal)
- return {keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)), valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex))};
- else
- return {valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)), keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex))};
- }
- QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
- QCPLayoutElement(parentPlot),
- mBackgroundBrush(Qt::NoBrush),
- mBackgroundScaled(true),
- mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
- mInsetLayout(new QCPLayoutInset),
- mRangeDrag(Qt::Horizontal|Qt::Vertical),
- mRangeZoom(Qt::Horizontal|Qt::Vertical),
- mRangeZoomFactorHorz(0.85),
- mRangeZoomFactorVert(0.85),
- mDragging(false)
- {
- mInsetLayout->initializeParentPlot(mParentPlot);
- mInsetLayout->setParentLayerable(this);
- mInsetLayout->setParent(this);
- setMinimumSize(50, 50);
- setMinimumMargins(QMargins(15, 15, 15, 15));
- mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
- mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
- mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
- mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
- if (setupDefaultAxes)
- {
- QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
- QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
- QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
- QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
- setRangeDragAxes(xAxis, yAxis);
- setRangeZoomAxes(xAxis, yAxis);
- xAxis2->setVisible(false);
- yAxis2->setVisible(false);
- xAxis->grid()->setVisible(true);
- yAxis->grid()->setVisible(true);
- xAxis2->grid()->setVisible(false);
- yAxis2->grid()->setVisible(false);
- xAxis2->grid()->setZeroLinePen(Qt::NoPen);
- yAxis2->grid()->setZeroLinePen(Qt::NoPen);
- xAxis2->grid()->setVisible(false);
- yAxis2->grid()->setVisible(false);
- }
- }
- QCPAxisRect::~QCPAxisRect()
- {
- delete mInsetLayout;
- mInsetLayout = nullptr;
- foreach (QCPAxis *axis, axes())
- removeAxis(axis);
- }
- int QCPAxisRect::axisCount(QCPAxis::AxisType type) const
- {
- return mAxes.value(type).size();
- }
- QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
- {
- QList<QCPAxis*> ax(mAxes.value(type));
- if (index >= 0 && index < ax.size())
- {
- return ax.at(index);
- } else
- {
- qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
- return nullptr;
- }
- }
- QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
- {
- QList<QCPAxis*> result;
- if (types.testFlag(QCPAxis::atLeft))
- result << mAxes.value(QCPAxis::atLeft);
- if (types.testFlag(QCPAxis::atRight))
- result << mAxes.value(QCPAxis::atRight);
- if (types.testFlag(QCPAxis::atTop))
- result << mAxes.value(QCPAxis::atTop);
- if (types.testFlag(QCPAxis::atBottom))
- result << mAxes.value(QCPAxis::atBottom);
- return result;
- }
- QList<QCPAxis*> QCPAxisRect::axes() const
- {
- QList<QCPAxis*> result;
- QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
- while (it.hasNext())
- {
- it.next();
- result << it.value();
- }
- return result;
- }
- QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis)
- {
- QCPAxis *newAxis = axis;
- if (!newAxis)
- {
- newAxis = new QCPAxis(this, type);
- } else
- {
- if (newAxis->axisType() != type)
- {
- qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
- return nullptr;
- }
- if (newAxis->axisRect() != this)
- {
- qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
- return nullptr;
- }
- if (axes().contains(newAxis))
- {
- qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
- return nullptr;
- }
- }
- if (!mAxes[type].isEmpty())
- {
- bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
- newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
- newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
- }
- mAxes[type].append(newAxis);
-
- if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this)
- {
- switch (type)
- {
- case QCPAxis::atBottom: { if (!mParentPlot->xAxis) mParentPlot->xAxis = newAxis; break; }
- case QCPAxis::atLeft: { if (!mParentPlot->yAxis) mParentPlot->yAxis = newAxis; break; }
- case QCPAxis::atTop: { if (!mParentPlot->xAxis2) mParentPlot->xAxis2 = newAxis; break; }
- case QCPAxis::atRight: { if (!mParentPlot->yAxis2) mParentPlot->yAxis2 = newAxis; break; }
- }
- }
- return newAxis;
- }
- QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
- {
- QList<QCPAxis*> result;
- if (types.testFlag(QCPAxis::atLeft))
- result << addAxis(QCPAxis::atLeft);
- if (types.testFlag(QCPAxis::atRight))
- result << addAxis(QCPAxis::atRight);
- if (types.testFlag(QCPAxis::atTop))
- result << addAxis(QCPAxis::atTop);
- if (types.testFlag(QCPAxis::atBottom))
- result << addAxis(QCPAxis::atBottom);
- return result;
- }
- bool QCPAxisRect::removeAxis(QCPAxis *axis)
- {
-
- QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
- while (it.hasNext())
- {
- it.next();
- if (it.value().contains(axis))
- {
- if (it.value().first() == axis && it.value().size() > 1)
- it.value()[1]->setOffset(axis->offset());
- mAxes[it.key()].removeOne(axis);
- if (qobject_cast<QCustomPlot*>(parentPlot()))
- parentPlot()->axisRemoved(axis);
- delete axis;
- return true;
- }
- }
- qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
- return false;
- }
- void QCPAxisRect::zoom(const QRectF &pixelRect)
- {
- zoom(pixelRect, axes());
- }
- void QCPAxisRect::zoom(const QRectF &pixelRect, const QList<QCPAxis*> &affectedAxes)
- {
- foreach (QCPAxis *axis, affectedAxes)
- {
- if (!axis)
- {
- qDebug() << Q_FUNC_INFO << "a passed axis was zero";
- continue;
- }
- QCPRange pixelRange;
- if (axis->orientation() == Qt::Horizontal)
- pixelRange = QCPRange(pixelRect.left(), pixelRect.right());
- else
- pixelRange = QCPRange(pixelRect.top(), pixelRect.bottom());
- axis->setRange(axis->pixelToCoord(pixelRange.lower), axis->pixelToCoord(pixelRange.upper));
- }
- }
- void QCPAxisRect::setupFullAxesBox(bool connectRanges)
- {
- QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
- if (axisCount(QCPAxis::atBottom) == 0)
- xAxis = addAxis(QCPAxis::atBottom);
- else
- xAxis = axis(QCPAxis::atBottom);
- if (axisCount(QCPAxis::atLeft) == 0)
- yAxis = addAxis(QCPAxis::atLeft);
- else
- yAxis = axis(QCPAxis::atLeft);
- if (axisCount(QCPAxis::atTop) == 0)
- xAxis2 = addAxis(QCPAxis::atTop);
- else
- xAxis2 = axis(QCPAxis::atTop);
- if (axisCount(QCPAxis::atRight) == 0)
- yAxis2 = addAxis(QCPAxis::atRight);
- else
- yAxis2 = axis(QCPAxis::atRight);
- xAxis->setVisible(true);
- yAxis->setVisible(true);
- xAxis2->setVisible(true);
- yAxis2->setVisible(true);
- xAxis2->setTickLabels(false);
- yAxis2->setTickLabels(false);
- xAxis2->setRange(xAxis->range());
- xAxis2->setRangeReversed(xAxis->rangeReversed());
- xAxis2->setScaleType(xAxis->scaleType());
- xAxis2->setTicks(xAxis->ticks());
- xAxis2->setNumberFormat(xAxis->numberFormat());
- xAxis2->setNumberPrecision(xAxis->numberPrecision());
- xAxis2->ticker()->setTickCount(xAxis->ticker()->tickCount());
- xAxis2->ticker()->setTickOrigin(xAxis->ticker()->tickOrigin());
- yAxis2->setRange(yAxis->range());
- yAxis2->setRangeReversed(yAxis->rangeReversed());
- yAxis2->setScaleType(yAxis->scaleType());
- yAxis2->setTicks(yAxis->ticks());
- yAxis2->setNumberFormat(yAxis->numberFormat());
- yAxis2->setNumberPrecision(yAxis->numberPrecision());
- yAxis2->ticker()->setTickCount(yAxis->ticker()->tickCount());
- yAxis2->ticker()->setTickOrigin(yAxis->ticker()->tickOrigin());
- if (connectRanges)
- {
- connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
- connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
- }
- }
- QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
- {
-
- QList<QCPAbstractPlottable*> result;
- foreach (QCPAbstractPlottable *plottable, mParentPlot->mPlottables)
- {
- if (plottable->keyAxis()->axisRect() == this || plottable->valueAxis()->axisRect() == this)
- result.append(plottable);
- }
- return result;
- }
- QList<QCPGraph*> QCPAxisRect::graphs() const
- {
-
- QList<QCPGraph*> result;
- foreach (QCPGraph *graph, mParentPlot->mGraphs)
- {
- if (graph->keyAxis()->axisRect() == this || graph->valueAxis()->axisRect() == this)
- result.append(graph);
- }
- return result;
- }
- QList<QCPAbstractItem *> QCPAxisRect::items() const
- {
-
-
- QList<QCPAbstractItem*> result;
- foreach (QCPAbstractItem *item, mParentPlot->mItems)
- {
- if (item->clipAxisRect() == this)
- {
- result.append(item);
- continue;
- }
- foreach (QCPItemPosition *position, item->positions())
- {
- if (position->axisRect() == this ||
- position->keyAxis()->axisRect() == this ||
- position->valueAxis()->axisRect() == this)
- {
- result.append(item);
- break;
- }
- }
- }
- return result;
- }
- void QCPAxisRect::update(UpdatePhase phase)
- {
- QCPLayoutElement::update(phase);
- switch (phase)
- {
- case upPreparation:
- {
- foreach (QCPAxis *axis, axes())
- axis->setupTickVectors();
- break;
- }
- case upLayout:
- {
- mInsetLayout->setOuterRect(rect());
- break;
- }
- default: break;
- }
-
- mInsetLayout->update(phase);
- }
- QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
- {
- QList<QCPLayoutElement*> result;
- if (mInsetLayout)
- {
- result << mInsetLayout;
- if (recursive)
- result << mInsetLayout->elements(recursive);
- }
- return result;
- }
- void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- painter->setAntialiasing(false);
- }
- void QCPAxisRect::draw(QCPPainter *painter)
- {
- drawBackground(painter);
- }
- void QCPAxisRect::setBackground(const QPixmap &pm)
- {
- mBackgroundPixmap = pm;
- mScaledBackgroundPixmap = QPixmap();
- }
- void QCPAxisRect::setBackground(const QBrush &brush)
- {
- mBackgroundBrush = brush;
- }
- void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
- {
- mBackgroundPixmap = pm;
- mScaledBackgroundPixmap = QPixmap();
- mBackgroundScaled = scaled;
- mBackgroundScaledMode = mode;
- }
- void QCPAxisRect::setBackgroundScaled(bool scaled)
- {
- mBackgroundScaled = scaled;
- }
- void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
- {
- mBackgroundScaledMode = mode;
- }
- QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
- {
- if (orientation == Qt::Horizontal)
- return mRangeDragHorzAxis.isEmpty() ? nullptr : mRangeDragHorzAxis.first().data();
- else
- return mRangeDragVertAxis.isEmpty() ? nullptr : mRangeDragVertAxis.first().data();
- }
- QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
- {
- if (orientation == Qt::Horizontal)
- return mRangeZoomHorzAxis.isEmpty() ? nullptr : mRangeZoomHorzAxis.first().data();
- else
- return mRangeZoomVertAxis.isEmpty() ? nullptr : mRangeZoomVertAxis.first().data();
- }
- QList<QCPAxis*> QCPAxisRect::rangeDragAxes(Qt::Orientation orientation)
- {
- QList<QCPAxis*> result;
- if (orientation == Qt::Horizontal)
- {
- foreach (QPointer<QCPAxis> axis, mRangeDragHorzAxis)
- {
- if (!axis.isNull())
- result.append(axis.data());
- }
- } else
- {
- foreach (QPointer<QCPAxis> axis, mRangeDragVertAxis)
- {
- if (!axis.isNull())
- result.append(axis.data());
- }
- }
- return result;
- }
- QList<QCPAxis*> QCPAxisRect::rangeZoomAxes(Qt::Orientation orientation)
- {
- QList<QCPAxis*> result;
- if (orientation == Qt::Horizontal)
- {
- foreach (QPointer<QCPAxis> axis, mRangeZoomHorzAxis)
- {
- if (!axis.isNull())
- result.append(axis.data());
- }
- } else
- {
- foreach (QPointer<QCPAxis> axis, mRangeZoomVertAxis)
- {
- if (!axis.isNull())
- result.append(axis.data());
- }
- }
- return result;
- }
- double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
- {
- return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
- }
- void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
- {
- mRangeDrag = orientations;
- }
- void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
- {
- mRangeZoom = orientations;
- }
- void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
- {
- QList<QCPAxis*> horz, vert;
- if (horizontal)
- horz.append(horizontal);
- if (vertical)
- vert.append(vertical);
- setRangeDragAxes(horz, vert);
- }
- void QCPAxisRect::setRangeDragAxes(QList<QCPAxis*> axes)
- {
- QList<QCPAxis*> horz, vert;
- foreach (QCPAxis *ax, axes)
- {
- if (ax->orientation() == Qt::Horizontal)
- horz.append(ax);
- else
- vert.append(ax);
- }
- setRangeDragAxes(horz, vert);
- }
- void QCPAxisRect::setRangeDragAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical)
- {
- mRangeDragHorzAxis.clear();
- foreach (QCPAxis *ax, horizontal)
- {
- QPointer<QCPAxis> axPointer(ax);
- if (!axPointer.isNull())
- mRangeDragHorzAxis.append(axPointer);
- else
- qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:" << reinterpret_cast<quintptr>(ax);
- }
- mRangeDragVertAxis.clear();
- foreach (QCPAxis *ax, vertical)
- {
- QPointer<QCPAxis> axPointer(ax);
- if (!axPointer.isNull())
- mRangeDragVertAxis.append(axPointer);
- else
- qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:" << reinterpret_cast<quintptr>(ax);
- }
- }
- void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
- {
- QList<QCPAxis*> horz, vert;
- if (horizontal)
- horz.append(horizontal);
- if (vertical)
- vert.append(vertical);
- setRangeZoomAxes(horz, vert);
- }
- void QCPAxisRect::setRangeZoomAxes(QList<QCPAxis*> axes)
- {
- QList<QCPAxis*> horz, vert;
- foreach (QCPAxis *ax, axes)
- {
- if (ax->orientation() == Qt::Horizontal)
- horz.append(ax);
- else
- vert.append(ax);
- }
- setRangeZoomAxes(horz, vert);
- }
- void QCPAxisRect::setRangeZoomAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical)
- {
- mRangeZoomHorzAxis.clear();
- foreach (QCPAxis *ax, horizontal)
- {
- QPointer<QCPAxis> axPointer(ax);
- if (!axPointer.isNull())
- mRangeZoomHorzAxis.append(axPointer);
- else
- qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:" << reinterpret_cast<quintptr>(ax);
- }
- mRangeZoomVertAxis.clear();
- foreach (QCPAxis *ax, vertical)
- {
- QPointer<QCPAxis> axPointer(ax);
- if (!axPointer.isNull())
- mRangeZoomVertAxis.append(axPointer);
- else
- qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:" << reinterpret_cast<quintptr>(ax);
- }
- }
- void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
- {
- mRangeZoomFactorHorz = horizontalFactor;
- mRangeZoomFactorVert = verticalFactor;
- }
- void QCPAxisRect::setRangeZoomFactor(double factor)
- {
- mRangeZoomFactorHorz = factor;
- mRangeZoomFactorVert = factor;
- }
- void QCPAxisRect::drawBackground(QCPPainter *painter)
- {
-
- if (mBackgroundBrush != Qt::NoBrush)
- painter->fillRect(mRect, mBackgroundBrush);
-
- if (!mBackgroundPixmap.isNull())
- {
- if (mBackgroundScaled)
- {
-
- QSizeF scaledSize(mBackgroundPixmap.size());
- scaledSize.scale(mRect.size(), mBackgroundScaledMode);
- if (mScaledBackgroundPixmap.size() != scaledSize)
- mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size().toSize(), mBackgroundScaledMode, Qt::SmoothTransformation);
- painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
- } else
- {
- painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
- }
- }
- }
- void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)
- {
- const QList<QCPAxis*> axesList = mAxes.value(type);
- if (axesList.isEmpty())
- return;
- bool isFirstVisible = !axesList.first()->visible();
- for (int i=1; i<axesList.size(); ++i)
- {
- int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
- if (axesList.at(i)->visible())
- {
- if (!isFirstVisible)
- offset += axesList.at(i)->tickLengthIn();
- isFirstVisible = false;
- }
- axesList.at(i)->setOffset(offset);
- }
- }
- double QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
- {
- if (!mAutoMargins.testFlag(side))
- qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
- updateAxesOffset(QCPAxis::marginSideToAxisType(side));
-
- const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
- if (!axesList.isEmpty())
- return axesList.last()->offset() + axesList.last()->calculateMargin();
- else
- return 0;
- }
- void QCPAxisRect::layoutChanged()
- {
- if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this)
- {
- if (axisCount(QCPAxis::atBottom) > 0 && !mParentPlot->xAxis)
- mParentPlot->xAxis = axis(QCPAxis::atBottom);
- if (axisCount(QCPAxis::atLeft) > 0 && !mParentPlot->yAxis)
- mParentPlot->yAxis = axis(QCPAxis::atLeft);
- if (axisCount(QCPAxis::atTop) > 0 && !mParentPlot->xAxis2)
- mParentPlot->xAxis2 = axis(QCPAxis::atTop);
- if (axisCount(QCPAxis::atRight) > 0 && !mParentPlot->yAxis2)
- mParentPlot->yAxis2 = axis(QCPAxis::atRight);
- }
- }
- void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)
- {
- Q_UNUSED(details)
- if (event->buttons() & Qt::LeftButton)
- {
- mDragging = true;
-
- if (mParentPlot->noAntialiasingOnDrag())
- {
- mAADragBackup = mParentPlot->antialiasedElements();
- mNotAADragBackup = mParentPlot->notAntialiasedElements();
- }
-
- if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
- {
- mDragStartHorzRange.clear();
- foreach (QPointer<QCPAxis> axis, mRangeDragHorzAxis)
- mDragStartHorzRange.append(axis.isNull() ? QCPRange() : axis->range());
- mDragStartVertRange.clear();
- foreach (QPointer<QCPAxis> axis, mRangeDragVertAxis)
- mDragStartVertRange.append(axis.isNull() ? QCPRange() : axis->range());
- }
- }
- }
- void QCPAxisRect::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
- {
- Q_UNUSED(startPos)
-
- if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
- {
- if (mRangeDrag.testFlag(Qt::Horizontal))
- {
- for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
- {
- QCPAxis *ax = mRangeDragHorzAxis.at(i).data();
- if (!ax)
- continue;
- if (i >= mDragStartHorzRange.size())
- break;
- if (ax->mScaleType == QCPAxis::stLinear)
- {
- double diff = ax->pixelToCoord(startPos.x()) - ax->pixelToCoord(event->pos().x());
- ax->setRange(mDragStartHorzRange.at(i).lower+diff, mDragStartHorzRange.at(i).upper+diff);
- } else if (ax->mScaleType == QCPAxis::stLogarithmic)
- {
- double diff = ax->pixelToCoord(startPos.x()) / ax->pixelToCoord(event->pos().x());
- ax->setRange(mDragStartHorzRange.at(i).lower*diff, mDragStartHorzRange.at(i).upper*diff);
- }
- }
- }
- if (mRangeDrag.testFlag(Qt::Vertical))
- {
- for (int i=0; i<mRangeDragVertAxis.size(); ++i)
- {
- QCPAxis *ax = mRangeDragVertAxis.at(i).data();
- if (!ax)
- continue;
- if (i >= mDragStartVertRange.size())
- break;
- if (ax->mScaleType == QCPAxis::stLinear)
- {
- double diff = ax->pixelToCoord(startPos.y()) - ax->pixelToCoord(event->pos().y());
- ax->setRange(mDragStartVertRange.at(i).lower+diff, mDragStartVertRange.at(i).upper+diff);
- } else if (ax->mScaleType == QCPAxis::stLogarithmic)
- {
- double diff = ax->pixelToCoord(startPos.y()) / ax->pixelToCoord(event->pos().y());
- ax->setRange(mDragStartVertRange.at(i).lower*diff, mDragStartVertRange.at(i).upper*diff);
- }
- }
- }
- if (mRangeDrag != 0)
- {
- if (mParentPlot->noAntialiasingOnDrag())
- mParentPlot->setNotAntialiasedElements(QCP::aeAll);
- mParentPlot->replot(QCustomPlot::rpQueuedReplot);
- }
- }
- }
- void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
- {
- Q_UNUSED(event)
- Q_UNUSED(startPos)
- mDragging = false;
- if (mParentPlot->noAntialiasingOnDrag())
- {
- mParentPlot->setAntialiasedElements(mAADragBackup);
- mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
- }
- }
- void QCPAxisRect::wheelEvent(QWheelEvent *event)
- {
- #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
- const double delta = event->delta();
- #else
- const double delta = event->angleDelta().y();
- #endif
- #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
- const QPointF pos = event->pos();
- #else
- const QPointF pos = event->position();
- #endif
-
- if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
- {
- if (mRangeZoom != 0)
- {
- double factor;
- double wheelSteps = delta/120.0;
- if (mRangeZoom.testFlag(Qt::Horizontal))
- {
- factor = qPow(mRangeZoomFactorHorz, wheelSteps);
- foreach (QPointer<QCPAxis> axis, mRangeZoomHorzAxis)
- {
- if (!axis.isNull())
- axis->scaleRange(factor, axis->pixelToCoord(pos.x()));
- }
- }
- if (mRangeZoom.testFlag(Qt::Vertical))
- {
- factor = qPow(mRangeZoomFactorVert, wheelSteps);
- foreach (QPointer<QCPAxis> axis, mRangeZoomVertAxis)
- {
- if (!axis.isNull())
- axis->scaleRange(factor, axis->pixelToCoord(pos.y()));
- }
- }
- mParentPlot->replot();
- }
- }
- }
- QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
- QCPLayoutElement(parent->parentPlot()),
- mParentLegend(parent),
- mFont(parent->font()),
- mTextColor(parent->textColor()),
- mSelectedFont(parent->selectedFont()),
- mSelectedTextColor(parent->selectedTextColor()),
- mSelectable(true),
- mSelected(false)
- {
- setLayer(QLatin1String("legend"));
- setMargins(QMargins(0, 0, 0, 0));
- }
- void QCPAbstractLegendItem::setFont(const QFont &font)
- {
- mFont = font;
- }
- void QCPAbstractLegendItem::setTextColor(const QColor &color)
- {
- mTextColor = color;
- }
- void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
- {
- mSelectedFont = font;
- }
- void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
- {
- mSelectedTextColor = color;
- }
- void QCPAbstractLegendItem::setSelectable(bool selectable)
- {
- if (mSelectable != selectable)
- {
- mSelectable = selectable;
- emit selectableChanged(mSelectable);
- }
- }
- void QCPAbstractLegendItem::setSelected(bool selected)
- {
- if (mSelected != selected)
- {
- mSelected = selected;
- emit selectionChanged(mSelected);
- }
- }
- double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (!mParentPlot) return -1;
- if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
- return -1;
- if (mRect.contains(pos.toPoint()))
- return mParentPlot->selectionTolerance()*0.99;
- else
- return -1;
- }
- void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
- }
- QRectF QCPAbstractLegendItem::clipRect() const
- {
- return mOuterRect;
- }
- void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
- {
- Q_UNUSED(event)
- Q_UNUSED(details)
- if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
- {
- bool selBefore = mSelected;
- setSelected(additive ? !mSelected : true);
- if (selectionStateChanged)
- *selectionStateChanged = mSelected != selBefore;
- }
- }
- void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
- {
- if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
- {
- bool selBefore = mSelected;
- setSelected(false);
- if (selectionStateChanged)
- *selectionStateChanged = mSelected != selBefore;
- }
- }
- QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
- QCPAbstractLegendItem(parent),
- mPlottable(plottable)
- {
- setAntialiased(false);
- }
- QPen QCPPlottableLegendItem::getIconBorderPen() const
- {
- return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
- }
- QColor QCPPlottableLegendItem::getTextColor() const
- {
- return mSelected ? mSelectedTextColor : mTextColor;
- }
- QFont QCPPlottableLegendItem::getFont() const
- {
- return mSelected ? mSelectedFont : mFont;
- }
- void QCPPlottableLegendItem::draw(QCPPainter *painter)
- {
- if (!mPlottable) return;
- painter->setFont(getFont());
- painter->setPen(QPen(getTextColor()));
- QSizeF iconSize = mParentLegend->iconSize();
- QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
- QRectF iconRect(mRect.topLeft(), iconSize);
- int textHeight = qMax(textRect.height(), iconSize.height());
- painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
-
- painter->save();
- painter->setClipRect(iconRect, Qt::IntersectClip);
- mPlottable->drawLegendIcon(painter, iconRect);
- painter->restore();
-
- if (getIconBorderPen().style() != Qt::NoPen)
- {
- painter->setPen(getIconBorderPen());
- painter->setBrush(Qt::NoBrush);
- int halfPen = qCeil(painter->pen().widthF()*0.5)+1;
- painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen));
- painter->drawRect(iconRect);
- }
- }
- QSizeF QCPPlottableLegendItem::minimumOuterSizeHint() const
- {
- if (!mPlottable) return {};
- QSizeF result(0, 0);
- QRectF textRect;
- QFontMetrics fontMetrics(getFont());
- QSizeF iconSize = mParentLegend->iconSize();
- textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
- result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width());
- result.setHeight(qMax(textRect.height(), iconSize.height()));
- result.rwidth() += mMargins.left()+mMargins.right();
- result.rheight() += mMargins.top()+mMargins.bottom();
- return result;
- }
- QCPLegend::QCPLegend() :
- mIconTextPadding{}
- {
- setFillOrder(QCPLayoutGrid::foRowsFirst);
- setWrap(0);
- setRowSpacing(3);
- setColumnSpacing(8);
- setMargins(QMargins(7, 5, 7, 4));
- setAntialiased(false);
- setIconSize(32, 18);
- setIconTextPadding(7);
- setSelectableParts(spLegendBox | spItems);
- setSelectedParts(spNone);
- setBorderPen(QPen(Qt::black, 0));
- setSelectedBorderPen(QPen(Qt::blue, 2));
- setIconBorderPen(Qt::NoPen);
- setSelectedIconBorderPen(QPen(Qt::blue, 2));
- setBrush(Qt::white);
- setSelectedBrush(Qt::white);
- setTextColor(Qt::black);
- setSelectedTextColor(Qt::blue);
- }
- QCPLegend::~QCPLegend()
- {
- clearItems();
- if (qobject_cast<QCustomPlot*>(mParentPlot))
- mParentPlot->legendRemoved(this);
- }
- QCPLegend::SelectableParts QCPLegend::selectedParts() const
- {
-
- bool hasSelectedItems = false;
- for (int i=0; i<itemCount(); ++i)
- {
- if (item(i) && item(i)->selected())
- {
- hasSelectedItems = true;
- break;
- }
- }
- if (hasSelectedItems)
- return mSelectedParts | spItems;
- else
- return mSelectedParts & ~spItems;
- }
- void QCPLegend::setBorderPen(const QPen &pen)
- {
- mBorderPen = pen;
- }
- void QCPLegend::setBrush(const QBrush &brush)
- {
- mBrush = brush;
- }
- void QCPLegend::setFont(const QFont &font)
- {
- mFont = font;
- for (int i=0; i<itemCount(); ++i)
- {
- if (item(i))
- item(i)->setFont(mFont);
- }
- }
- void QCPLegend::setTextColor(const QColor &color)
- {
- mTextColor = color;
- for (int i=0; i<itemCount(); ++i)
- {
- if (item(i))
- item(i)->setTextColor(color);
- }
- }
- void QCPLegend::setIconSize(const QSize &size)
- {
- mIconSize = size;
- }
- void QCPLegend::setIconSize(int width, int height)
- {
- mIconSize.setWidth(width);
- mIconSize.setHeight(height);
- }
- void QCPLegend::setIconTextPadding(int padding)
- {
- mIconTextPadding = padding;
- }
- void QCPLegend::setIconBorderPen(const QPen &pen)
- {
- mIconBorderPen = pen;
- }
- void QCPLegend::setSelectableParts(const SelectableParts &selectable)
- {
- if (mSelectableParts != selectable)
- {
- mSelectableParts = selectable;
- emit selectableChanged(mSelectableParts);
- }
- }
- void QCPLegend::setSelectedParts(const SelectableParts &selected)
- {
- SelectableParts newSelected = selected;
- mSelectedParts = this->selectedParts();
- if (mSelectedParts != newSelected)
- {
- if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems))
- {
- qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
- newSelected &= ~spItems;
- }
- if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems))
- {
- for (int i=0; i<itemCount(); ++i)
- {
- if (item(i))
- item(i)->setSelected(false);
- }
- }
- mSelectedParts = newSelected;
- emit selectionChanged(mSelectedParts);
- }
- }
- void QCPLegend::setSelectedBorderPen(const QPen &pen)
- {
- mSelectedBorderPen = pen;
- }
- void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
- {
- mSelectedIconBorderPen = pen;
- }
- void QCPLegend::setSelectedBrush(const QBrush &brush)
- {
- mSelectedBrush = brush;
- }
- void QCPLegend::setSelectedFont(const QFont &font)
- {
- mSelectedFont = font;
- for (int i=0; i<itemCount(); ++i)
- {
- if (item(i))
- item(i)->setSelectedFont(font);
- }
- }
- void QCPLegend::setSelectedTextColor(const QColor &color)
- {
- mSelectedTextColor = color;
- for (int i=0; i<itemCount(); ++i)
- {
- if (item(i))
- item(i)->setSelectedTextColor(color);
- }
- }
- QCPAbstractLegendItem *QCPLegend::item(int index) const
- {
- return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
- }
- QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
- {
- for (int i=0; i<itemCount(); ++i)
- {
- if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
- {
- if (pli->plottable() == plottable)
- return pli;
- }
- }
- return nullptr;
- }
- int QCPLegend::itemCount() const
- {
- return elementCount();
- }
- bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
- {
- for (int i=0; i<itemCount(); ++i)
- {
- if (item == this->item(i))
- return true;
- }
- return false;
- }
- bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
- {
- return itemWithPlottable(plottable);
- }
- bool QCPLegend::addItem(QCPAbstractLegendItem *item)
- {
- return addElement(item);
- }
- bool QCPLegend::removeItem(int index)
- {
- if (QCPAbstractLegendItem *ali = item(index))
- {
- bool success = remove(ali);
- if (success)
- setFillOrder(fillOrder(), true);
- return success;
- } else
- return false;
- }
- bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
- {
- bool success = remove(item);
- if (success)
- setFillOrder(fillOrder(), true);
- return success;
- }
- void QCPLegend::clearItems()
- {
- for (int i=elementCount()-1; i>=0; --i)
- {
- if (item(i))
- removeAt(i);
- }
- setFillOrder(fillOrder(), true);
- }
- QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
- {
- QList<QCPAbstractLegendItem*> result;
- for (int i=0; i<itemCount(); ++i)
- {
- if (QCPAbstractLegendItem *ali = item(i))
- {
- if (ali->selected())
- result.append(ali);
- }
- }
- return result;
- }
- void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
- }
- QPen QCPLegend::getBorderPen() const
- {
- return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
- }
- QBrush QCPLegend::getBrush() const
- {
- return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
- }
- void QCPLegend::draw(QCPPainter *painter)
- {
-
- painter->setBrush(getBrush());
- painter->setPen(getBorderPen());
- painter->drawRect(mOuterRect);
- }
- double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- if (!mParentPlot) return -1;
- if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
- return -1;
- if (mOuterRect.contains(pos.toPoint()))
- {
- if (details) details->setValue(spLegendBox);
- return mParentPlot->selectionTolerance()*0.99;
- }
- return -1;
- }
- void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
- {
- Q_UNUSED(event)
- mSelectedParts = selectedParts();
- if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
- {
- SelectableParts selBefore = mSelectedParts;
- setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox);
- if (selectionStateChanged)
- *selectionStateChanged = mSelectedParts != selBefore;
- }
- }
- void QCPLegend::deselectEvent(bool *selectionStateChanged)
- {
- mSelectedParts = selectedParts();
- if (mSelectableParts.testFlag(spLegendBox))
- {
- SelectableParts selBefore = mSelectedParts;
- setSelectedParts(selectedParts() & ~spLegendBox);
- if (selectionStateChanged)
- *selectionStateChanged = mSelectedParts != selBefore;
- }
- }
- QCP::Interaction QCPLegend::selectionCategory() const
- {
- return QCP::iSelectLegend;
- }
- QCP::Interaction QCPAbstractLegendItem::selectionCategory() const
- {
- return QCP::iSelectLegend;
- }
- void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
- {
- if (parentPlot && !parentPlot->legend)
- parentPlot->legend = this;
- }
- QCPTextElement::QCPTextElement(QCustomPlot *parentPlot) :
- QCPLayoutElement(parentPlot),
- mText(),
- mTextFlags(Qt::AlignCenter),
- mFont(QFont(QLatin1String("sans serif"), 12)),
- mTextColor(Qt::black),
- mSelectedFont(QFont(QLatin1String("sans serif"), 12)),
- mSelectedTextColor(Qt::blue),
- mSelectable(false),
- mSelected(false)
- {
- if (parentPlot)
- {
- mFont = parentPlot->font();
- mSelectedFont = parentPlot->font();
- }
- setMargins(QMargins(2, 2, 2, 2));
- }
- QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text) :
- QCPLayoutElement(parentPlot),
- mText(text),
- mTextFlags(Qt::AlignCenter),
- mFont(QFont(QLatin1String("sans serif"), 12)),
- mTextColor(Qt::black),
- mSelectedFont(QFont(QLatin1String("sans serif"), 12)),
- mSelectedTextColor(Qt::blue),
- mSelectable(false),
- mSelected(false)
- {
- if (parentPlot)
- {
- mFont = parentPlot->font();
- mSelectedFont = parentPlot->font();
- }
- setMargins(QMargins(2, 2, 2, 2));
- }
- QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, double pointSize) :
- QCPLayoutElement(parentPlot),
- mText(text),
- mTextFlags(Qt::AlignCenter),
- mFont(QFont(QLatin1String("sans serif"), int(pointSize))),
- mTextColor(Qt::black),
- mSelectedFont(QFont(QLatin1String("sans serif"), int(pointSize))),
- mSelectedTextColor(Qt::blue),
- mSelectable(false),
- mSelected(false)
- {
- mFont.setPointSizeF(pointSize);
- if (parentPlot)
- {
- mFont = parentPlot->font();
- mFont.setPointSizeF(pointSize);
- mSelectedFont = parentPlot->font();
- mSelectedFont.setPointSizeF(pointSize);
- }
- setMargins(QMargins(2, 2, 2, 2));
- }
- QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QString &fontFamily, double pointSize) :
- QCPLayoutElement(parentPlot),
- mText(text),
- mTextFlags(Qt::AlignCenter),
- mFont(QFont(fontFamily, int(pointSize))),
- mTextColor(Qt::black),
- mSelectedFont(QFont(fontFamily, int(pointSize))),
- mSelectedTextColor(Qt::blue),
- mSelectable(false),
- mSelected(false)
- {
- mFont.setPointSizeF(pointSize);
- setMargins(QMargins(2, 2, 2, 2));
- }
- QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QFont &font) :
- QCPLayoutElement(parentPlot),
- mText(text),
- mTextFlags(Qt::AlignCenter),
- mFont(font),
- mTextColor(Qt::black),
- mSelectedFont(font),
- mSelectedTextColor(Qt::blue),
- mSelectable(false),
- mSelected(false)
- {
- setMargins(QMargins(2, 2, 2, 2));
- }
- void QCPTextElement::setText(const QString &text)
- {
- mText = text;
- }
- void QCPTextElement::setTextFlags(int flags)
- {
- mTextFlags = flags;
- }
- void QCPTextElement::setFont(const QFont &font)
- {
- mFont = font;
- }
- void QCPTextElement::setTextColor(const QColor &color)
- {
- mTextColor = color;
- }
- void QCPTextElement::setSelectedFont(const QFont &font)
- {
- mSelectedFont = font;
- }
- void QCPTextElement::setSelectedTextColor(const QColor &color)
- {
- mSelectedTextColor = color;
- }
- void QCPTextElement::setSelectable(bool selectable)
- {
- if (mSelectable != selectable)
- {
- mSelectable = selectable;
- emit selectableChanged(mSelectable);
- }
- }
- void QCPTextElement::setSelected(bool selected)
- {
- if (mSelected != selected)
- {
- mSelected = selected;
- emit selectionChanged(mSelected);
- }
- }
- void QCPTextElement::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aeOther);
- }
- void QCPTextElement::draw(QCPPainter *painter)
- {
- painter->setFont(mainFont());
- painter->setPen(QPen(mainTextColor()));
- painter->drawText(mRect, mTextFlags, mText, &mTextBoundingRect);
- }
- QSizeF QCPTextElement::minimumOuterSizeHint() const
- {
- QFontMetrics metrics(mFont);
- QSizeF result(metrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, mText).size());
- result.rwidth() += mMargins.left()+mMargins.right();
- result.rheight() += mMargins.top()+mMargins.bottom();
- return result;
- }
- QSizeF QCPTextElement::maximumOuterSizeHint() const
- {
- QFontMetrics metrics(mFont);
- QSizeF result(metrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, mText).size());
- result.setWidth(QWIDGETSIZE_MAX);
- result.rheight() += mMargins.top()+mMargins.bottom();
- return result;
- }
- void QCPTextElement::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
- {
- Q_UNUSED(event)
- Q_UNUSED(details)
- if (mSelectable)
- {
- bool selBefore = mSelected;
- setSelected(additive ? !mSelected : true);
- if (selectionStateChanged)
- *selectionStateChanged = mSelected != selBefore;
- }
- }
- void QCPTextElement::deselectEvent(bool *selectionStateChanged)
- {
- if (mSelectable)
- {
- bool selBefore = mSelected;
- setSelected(false);
- if (selectionStateChanged)
- *selectionStateChanged = mSelected != selBefore;
- }
- }
- double QCPTextElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable && !mSelectable)
- return -1;
- if (mTextBoundingRect.contains(pos.toPoint()))
- return mParentPlot->selectionTolerance()*0.99;
- else
- return -1;
- }
- void QCPTextElement::mousePressEvent(QMouseEvent *event, const QVariant &details)
- {
- Q_UNUSED(details)
- event->accept();
- }
- void QCPTextElement::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
- {
- if ((QPointF(event->pos())-startPos).manhattanLength() <= 3)
- emit clicked(event);
- }
- void QCPTextElement::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
- {
- Q_UNUSED(details)
- emit doubleClicked(event);
- }
- QFont QCPTextElement::mainFont() const
- {
- return mSelected ? mSelectedFont : mFont;
- }
- QColor QCPTextElement::mainTextColor() const
- {
- return mSelected ? mSelectedTextColor : mTextColor;
- }
- QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) :
- QCPLayoutElement(parentPlot),
- mType(QCPAxis::atTop),
- mDataScaleType(QCPAxis::stLinear),
- mGradient(QCPColorGradient::gpCold),
- mBarWidth(20),
- mAxisRect(new QCPColorScaleAxisRectPrivate(this))
- {
- setMinimumMargins(QMargins(0, 6, 0, 6));
- setType(QCPAxis::atRight);
- setDataRange(QCPRange(0, 6));
- }
- QCPColorScale::~QCPColorScale()
- {
- delete mAxisRect;
- }
- QString QCPColorScale::label() const
- {
- if (!mColorAxis)
- {
- qDebug() << Q_FUNC_INFO << "internal color axis undefined";
- return QString();
- }
- return mColorAxis.data()->label();
- }
- bool QCPColorScale::rangeDrag() const
- {
- if (!mAxisRect)
- {
- qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
- return false;
- }
- return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
- mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
- mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
- }
- bool QCPColorScale::rangeZoom() const
- {
- if (!mAxisRect)
- {
- qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
- return false;
- }
- return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
- mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
- mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
- }
- void QCPColorScale::setType(QCPAxis::AxisType type)
- {
- if (!mAxisRect)
- {
- qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
- return;
- }
- if (mType != type)
- {
- mType = type;
- QCPRange rangeTransfer(0, 6);
- QString labelTransfer;
- QSharedPointer<QCPAxisTicker> tickerTransfer;
-
- bool doTransfer = !mColorAxis.isNull();
- if (doTransfer)
- {
- rangeTransfer = mColorAxis.data()->range();
- labelTransfer = mColorAxis.data()->label();
- tickerTransfer = mColorAxis.data()->ticker();
- mColorAxis.data()->setLabel(QString());
- disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
- disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
- }
- const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
- foreach (QCPAxis::AxisType atype, allAxisTypes)
- {
- mAxisRect.data()->axis(atype)->setTicks(atype == mType);
- mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
- }
-
- mColorAxis = mAxisRect.data()->axis(mType);
-
- if (doTransfer)
- {
- mColorAxis.data()->setRange(rangeTransfer);
- mColorAxis.data()->setLabel(labelTransfer);
- mColorAxis.data()->setTicker(tickerTransfer);
- }
- connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
- connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
- mAxisRect.data()->setRangeDragAxes(QList<QCPAxis*>() << mColorAxis.data());
- }
- }
- void QCPColorScale::setDataRange(const QCPRange &dataRange)
- {
- if (!qFuzzyCompare(mDataRange.lower, dataRange.lower) || !qFuzzyCompare(mDataRange.upper, dataRange.upper))
- {
- mDataRange = dataRange;
- if (mColorAxis)
- mColorAxis.data()->setRange(mDataRange);
- emit dataRangeChanged(mDataRange);
- }
- }
- void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType)
- {
- if (mDataScaleType != scaleType)
- {
- mDataScaleType = scaleType;
- if (mColorAxis)
- mColorAxis.data()->setScaleType(mDataScaleType);
- if (mDataScaleType == QCPAxis::stLogarithmic)
- setDataRange(mDataRange.sanitizedForLogScale());
- emit dataScaleTypeChanged(mDataScaleType);
- }
- }
- void QCPColorScale::setGradient(const QCPColorGradient &gradient)
- {
- if (mGradient != gradient)
- {
- mGradient = gradient;
- if (mAxisRect)
- mAxisRect.data()->mGradientImageInvalidated = true;
- emit gradientChanged(mGradient);
- }
- }
- void QCPColorScale::setLabel(const QString &str)
- {
- if (!mColorAxis)
- {
- qDebug() << Q_FUNC_INFO << "internal color axis undefined";
- return;
- }
- mColorAxis.data()->setLabel(str);
- }
- void QCPColorScale::setBarWidth(int width)
- {
- mBarWidth = width;
- }
- void QCPColorScale::setRangeDrag(bool enabled)
- {
- if (!mAxisRect)
- {
- qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
- return;
- }
- if (enabled)
- {
- mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
- } else
- {
- #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
- mAxisRect.data()->setRangeDrag(nullptr);
- #else
- mAxisRect.data()->setRangeDrag({});
- #endif
- }
- }
- void QCPColorScale::setRangeZoom(bool enabled)
- {
- if (!mAxisRect)
- {
- qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
- return;
- }
- if (enabled)
- {
- mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
- } else
- {
- #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
- mAxisRect.data()->setRangeDrag(nullptr);
- #else
- mAxisRect.data()->setRangeZoom({});
- #endif
- }
- }
- QList<QCPColorMap*> QCPColorScale::colorMaps() const
- {
- QList<QCPColorMap*> result;
- for (int i=0; i<mParentPlot->plottableCount(); ++i)
- {
- if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
- if (cm->colorScale() == this)
- result.append(cm);
- }
- return result;
- }
- void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
- {
- QList<QCPColorMap*> maps = colorMaps();
- QCPRange newRange;
- bool haveRange = false;
- QCP::SignDomain sign = QCP::sdBoth;
- if (mDataScaleType == QCPAxis::stLogarithmic)
- sign = (mDataRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);
- foreach (QCPColorMap *map, maps)
- {
- if (!map->realVisibility() && onlyVisibleMaps)
- continue;
- QCPRange mapRange;
- if (map->colorScale() == this)
- {
- bool currentFoundRange = true;
- mapRange = map->data()->dataBounds();
- if (sign == QCP::sdPositive)
- {
- if (mapRange.lower <= 0 && mapRange.upper > 0)
- mapRange.lower = mapRange.upper*1e-3;
- else if (mapRange.lower <= 0 && mapRange.upper <= 0)
- currentFoundRange = false;
- } else if (sign == QCP::sdNegative)
- {
- if (mapRange.upper >= 0 && mapRange.lower < 0)
- mapRange.upper = mapRange.lower*1e-3;
- else if (mapRange.upper >= 0 && mapRange.lower >= 0)
- currentFoundRange = false;
- }
- if (currentFoundRange)
- {
- if (!haveRange)
- newRange = mapRange;
- else
- newRange.expand(mapRange);
- haveRange = true;
- }
- }
- }
- if (haveRange)
- {
- if (!QCPRange::validRange(newRange))
- {
- double center = (newRange.lower+newRange.upper)*0.5;
- if (mDataScaleType == QCPAxis::stLinear)
- {
- newRange.lower = center-mDataRange.size()/2.0;
- newRange.upper = center+mDataRange.size()/2.0;
- } else
- {
- newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
- newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
- }
- }
- setDataRange(newRange);
- }
- }
- void QCPColorScale::update(UpdatePhase phase)
- {
- QCPLayoutElement::update(phase);
- if (!mAxisRect)
- {
- qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
- return;
- }
- mAxisRect.data()->update(phase);
- switch (phase)
- {
- case upMargins:
- {
- if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop)
- {
- setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom());
- setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom());
- } else
- {
- setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), QWIDGETSIZE_MAX);
- setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), 0);
- }
- break;
- }
- case upLayout:
- {
- mAxisRect.data()->setOuterRect(rect());
- break;
- }
- default: break;
- }
- }
- void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- painter->setAntialiasing(false);
- }
- void QCPColorScale::mousePressEvent(QMouseEvent *event, const QVariant &details)
- {
- if (!mAxisRect)
- {
- qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
- return;
- }
- mAxisRect.data()->mousePressEvent(event, details);
- }
- void QCPColorScale::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
- {
- if (!mAxisRect)
- {
- qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
- return;
- }
- mAxisRect.data()->mouseMoveEvent(event, startPos);
- }
- void QCPColorScale::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
- {
- if (!mAxisRect)
- {
- qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
- return;
- }
- mAxisRect.data()->mouseReleaseEvent(event, startPos);
- }
- void QCPColorScale::wheelEvent(QWheelEvent *event)
- {
- if (!mAxisRect)
- {
- qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
- return;
- }
- mAxisRect.data()->wheelEvent(event);
- }
- QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) :
- QCPAxisRect(parentColorScale->parentPlot(), true),
- mParentColorScale(parentColorScale),
- mGradientImageInvalidated(true)
- {
- setParentLayerable(parentColorScale);
- setMinimumMargins(QMargins(0, 0, 0, 0));
- const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
- foreach (QCPAxis::AxisType type, allAxisTypes)
- {
- axis(type)->setVisible(true);
- axis(type)->grid()->setVisible(false);
- axis(type)->setPadding(0);
- connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
- connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
- }
- connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
- connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
- connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
- connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
- connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
- connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
- connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
- connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
-
-
- connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
- foreach (QCPAxis::AxisType type, allAxisTypes)
- connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
- }
- void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter)
- {
- if (mGradientImageInvalidated)
- updateGradientImage();
- bool mirrorHorz = false;
- bool mirrorVert = false;
- if (mParentColorScale->mColorAxis)
- {
- mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
- mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
- }
- painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));
- QCPAxisRect::draw(painter);
- }
- void QCPColorScaleAxisRectPrivate::updateGradientImage()
- {
- if (rect().isEmpty())
- return;
- const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
- int n = mParentColorScale->mGradient.levelCount();
- int w, h;
- QVector<double> data(n);
- for (int i=0; i<n; ++i)
- data[i] = i;
- if (mParentColorScale->mType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop)
- {
- w = n;
- h = rect().height();
- mGradientImage = QImage(w, h, format);
- QVector<QRgb*> pixels;
- for (int y=0; y<h; ++y)
- pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
- mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
- for (int y=1; y<h; ++y)
- memcpy(pixels.at(y), pixels.first(), size_t(n)*sizeof(QRgb));
- } else
- {
- w = rect().width();
- h = n;
- mGradientImage = QImage(w, h, format);
- for (int y=0; y<h; ++y)
- {
- QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
- const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
- for (int x=0; x<w; ++x)
- pixels[x] = lineColor;
- }
- }
- mGradientImageInvalidated = false;
- }
- void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
- {
-
- const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
- foreach (QCPAxis::AxisType type, allAxisTypes)
- {
- if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
- if (senderAxis->axisType() == type)
- continue;
- if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
- {
- if (selectedParts.testFlag(QCPAxis::spAxis))
- axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
- else
- axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
- }
- }
- }
- void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
- {
-
- const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
- foreach (QCPAxis::AxisType type, allAxisTypes)
- {
- if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
- if (senderAxis->axisType() == type)
- continue;
- if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
- {
- if (selectableParts.testFlag(QCPAxis::spAxis))
- axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
- else
- axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
- }
- }
- }
- QCPGraphData::QCPGraphData() :
- key(0),
- value(0)
- {
- }
- QCPGraphData::QCPGraphData(double key, double value) :
- key(key),
- value(value)
- {
- }
- QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
- QCPAbstractPlottable1D<QCPGraphData>(keyAxis, valueAxis),
- mLineStyle{},
- mScatterSkip{},
- mAdaptiveSampling{}
- {
-
- mParentPlot->registerGraph(this);
- setPen(QPen(Qt::blue, 0));
- setBrush(Qt::NoBrush);
- setLineStyle(lsLine);
- setScatterSkip(0);
- setChannelFillGraph(nullptr);
- setAdaptiveSampling(true);
- }
- QCPGraph::~QCPGraph()
- {
- }
- void QCPGraph::setData(QSharedPointer<QCPGraphDataContainer> data)
- {
- mDataContainer = data;
- }
- void QCPGraph::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
- {
- mDataContainer->clear();
- addData(keys, values, alreadySorted);
- }
- void QCPGraph::setLineStyle(LineStyle ls)
- {
- mLineStyle = ls;
- }
- void QCPGraph::setScatterStyle(const QCPScatterStyle &style)
- {
- mScatterStyle = style;
- }
- void QCPGraph::setScatterSkip(int skip)
- {
- mScatterSkip = qMax(0, skip);
- }
- void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph)
- {
-
- if (targetGraph == this)
- {
- qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
- mChannelFillGraph = nullptr;
- return;
- }
-
- if (targetGraph && targetGraph->mParentPlot != mParentPlot)
- {
- qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
- mChannelFillGraph = nullptr;
- return;
- }
- mChannelFillGraph = targetGraph;
- }
- void QCPGraph::setAdaptiveSampling(bool enabled)
- {
- mAdaptiveSampling = enabled;
- }
- void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
- {
- if (keys.size() != values.size())
- qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
- const int n = qMin(keys.size(), values.size());
- QVector<QCPGraphData> tempData(n);
- QVector<QCPGraphData>::iterator it = tempData.begin();
- const QVector<QCPGraphData>::iterator itEnd = tempData.end();
- int i = 0;
- while (it != itEnd)
- {
- it->key = keys[i];
- it->value = values[i];
- ++it;
- ++i;
- }
- mDataContainer->add(tempData, alreadySorted);
- }
- void QCPGraph::addData(double key, double value)
- {
- mDataContainer->add(QCPGraphData(key, value));
- }
- double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return -1;
- if (!mKeyAxis || !mValueAxis)
- return -1;
- if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
- {
- QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
- double result = pointDistance(pos, closestDataPoint);
- if (details)
- {
- int pointIndex = int(closestDataPoint-mDataContainer->constBegin());
- details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
- }
- return result;
- } else
- return -1;
- }
- QCPRange QCPGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
- {
- return mDataContainer->keyRange(foundRange, inSignDomain);
- }
- QCPRange QCPGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
- {
- return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
- }
- void QCPGraph::draw(QCPPainter *painter)
- {
- if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
- if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
- QVector<QPointF> lines, scatters;
-
- QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
- getDataSegments(selectedSegments, unselectedSegments);
- allSegments << unselectedSegments << selectedSegments;
- for (int i=0; i<allSegments.size(); ++i)
- {
- bool isSelectedSegment = i >= unselectedSegments.size();
-
- QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1);
- getLines(&lines, lineDataRange);
-
- #ifdef QCUSTOMPLOT_CHECK_DATA
- QCPGraphDataContainer::const_iterator it;
- for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
- {
- if (QCP::isInvalidData(it->key, it->value))
- qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
- }
- #endif
-
- if (isSelectedSegment && mSelectionDecorator)
- mSelectionDecorator->applyBrush(painter);
- else
- painter->setBrush(mBrush);
- painter->setPen(Qt::NoPen);
- drawFill(painter, &lines);
-
- if (mLineStyle != lsNone)
- {
- if (isSelectedSegment && mSelectionDecorator)
- mSelectionDecorator->applyPen(painter);
- else
- painter->setPen(mPen);
- painter->setBrush(Qt::NoBrush);
- if (mLineStyle == lsImpulse)
- drawImpulsePlot(painter, lines);
- else
- drawLinePlot(painter, lines);
- }
-
- QCPScatterStyle finalScatterStyle = mScatterStyle;
- if (isSelectedSegment && mSelectionDecorator)
- finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle);
- if (!finalScatterStyle.isNone())
- {
- getScatters(&scatters, allSegments.at(i));
- drawScatterPlot(painter, scatters, finalScatterStyle);
- }
- }
-
- if (mSelectionDecorator)
- mSelectionDecorator->drawDecoration(painter, selection());
- }
- void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
- {
-
- if (mBrush.style() != Qt::NoBrush)
- {
- applyFillAntialiasingHint(painter);
- painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
- }
-
- if (mLineStyle != lsNone)
- {
- applyDefaultAntialiasingHint(painter);
- painter->setPen(mPen);
- painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0));
- }
-
- if (!mScatterStyle.isNone())
- {
- applyScattersAntialiasingHint(painter);
-
- if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
- {
- QCPScatterStyle scaledStyle(mScatterStyle);
- scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
- scaledStyle.applyTo(painter, mPen);
- scaledStyle.drawShape(painter, QRectF(rect).center());
- } else
- {
- mScatterStyle.applyTo(painter, mPen);
- mScatterStyle.drawShape(painter, QRectF(rect).center());
- }
- }
- }
- void QCPGraph::getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const
- {
- if (!lines) return;
- QCPGraphDataContainer::const_iterator begin, end;
- getVisibleDataBounds(begin, end, dataRange);
- if (begin == end)
- {
- lines->clear();
- return;
- }
- QVector<QCPGraphData> lineData;
- if (mLineStyle != lsNone)
- getOptimizedLineData(&lineData, begin, end);
- if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical))
- std::reverse(lineData.begin(), lineData.end());
- switch (mLineStyle)
- {
- case lsNone: lines->clear(); break;
- case lsLine: *lines = dataToLines(lineData); break;
- case lsStepLeft: *lines = dataToStepLeftLines(lineData); break;
- case lsStepRight: *lines = dataToStepRightLines(lineData); break;
- case lsStepCenter: *lines = dataToStepCenterLines(lineData); break;
- case lsImpulse: *lines = dataToImpulseLines(lineData); break;
- }
- }
- void QCPGraph::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const
- {
- if (!scatters) return;
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; scatters->clear(); return; }
- QCPGraphDataContainer::const_iterator begin, end;
- getVisibleDataBounds(begin, end, dataRange);
- if (begin == end)
- {
- scatters->clear();
- return;
- }
- QVector<QCPGraphData> data;
- getOptimizedScatterData(&data, begin, end);
- if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical))
- std::reverse(data.begin(), data.end());
- scatters->resize(data.size());
- if (keyAxis->orientation() == Qt::Vertical)
- {
- for (int i=0; i<data.size(); ++i)
- {
- if (!qIsNaN(data.at(i).value))
- {
- (*scatters)[i].setX(valueAxis->coordToPixel(data.at(i).value));
- (*scatters)[i].setY(keyAxis->coordToPixel(data.at(i).key));
- }
- }
- } else
- {
- for (int i=0; i<data.size(); ++i)
- {
- if (!qIsNaN(data.at(i).value))
- {
- (*scatters)[i].setX(keyAxis->coordToPixel(data.at(i).key));
- (*scatters)[i].setY(valueAxis->coordToPixel(data.at(i).value));
- }
- }
- }
- }
- QVector<QPointF> QCPGraph::dataToLines(const QVector<QCPGraphData> &data) const
- {
- QVector<QPointF> result;
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
- result.resize(data.size());
-
- if (keyAxis->orientation() == Qt::Vertical)
- {
- for (int i=0; i<data.size(); ++i)
- {
- result[i].setX(valueAxis->coordToPixel(data.at(i).value));
- result[i].setY(keyAxis->coordToPixel(data.at(i).key));
- }
- } else
- {
- for (int i=0; i<data.size(); ++i)
- {
- result[i].setX(keyAxis->coordToPixel(data.at(i).key));
- result[i].setY(valueAxis->coordToPixel(data.at(i).value));
- }
- }
- return result;
- }
- QVector<QPointF> QCPGraph::dataToStepLeftLines(const QVector<QCPGraphData> &data) const
- {
- QVector<QPointF> result;
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
- result.resize(data.size()*2);
-
- if (keyAxis->orientation() == Qt::Vertical)
- {
- double lastValue = valueAxis->coordToPixel(data.first().value);
- for (int i=0; i<data.size(); ++i)
- {
- const double key = keyAxis->coordToPixel(data.at(i).key);
- result[i*2+0].setX(lastValue);
- result[i*2+0].setY(key);
- lastValue = valueAxis->coordToPixel(data.at(i).value);
- result[i*2+1].setX(lastValue);
- result[i*2+1].setY(key);
- }
- } else
- {
- double lastValue = valueAxis->coordToPixel(data.first().value);
- for (int i=0; i<data.size(); ++i)
- {
- const double key = keyAxis->coordToPixel(data.at(i).key);
- result[i*2+0].setX(key);
- result[i*2+0].setY(lastValue);
- lastValue = valueAxis->coordToPixel(data.at(i).value);
- result[i*2+1].setX(key);
- result[i*2+1].setY(lastValue);
- }
- }
- return result;
- }
- QVector<QPointF> QCPGraph::dataToStepRightLines(const QVector<QCPGraphData> &data) const
- {
- QVector<QPointF> result;
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
- result.resize(data.size()*2);
-
- if (keyAxis->orientation() == Qt::Vertical)
- {
- double lastKey = keyAxis->coordToPixel(data.first().key);
- for (int i=0; i<data.size(); ++i)
- {
- const double value = valueAxis->coordToPixel(data.at(i).value);
- result[i*2+0].setX(value);
- result[i*2+0].setY(lastKey);
- lastKey = keyAxis->coordToPixel(data.at(i).key);
- result[i*2+1].setX(value);
- result[i*2+1].setY(lastKey);
- }
- } else
- {
- double lastKey = keyAxis->coordToPixel(data.first().key);
- for (int i=0; i<data.size(); ++i)
- {
- const double value = valueAxis->coordToPixel(data.at(i).value);
- result[i*2+0].setX(lastKey);
- result[i*2+0].setY(value);
- lastKey = keyAxis->coordToPixel(data.at(i).key);
- result[i*2+1].setX(lastKey);
- result[i*2+1].setY(value);
- }
- }
- return result;
- }
- QVector<QPointF> QCPGraph::dataToStepCenterLines(const QVector<QCPGraphData> &data) const
- {
- QVector<QPointF> result;
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
- result.resize(data.size()*2);
-
- if (keyAxis->orientation() == Qt::Vertical)
- {
- double lastKey = keyAxis->coordToPixel(data.first().key);
- double lastValue = valueAxis->coordToPixel(data.first().value);
- result[0].setX(lastValue);
- result[0].setY(lastKey);
- for (int i=1; i<data.size(); ++i)
- {
- const double key = (keyAxis->coordToPixel(data.at(i).key)+lastKey)*0.5;
- result[i*2-1].setX(lastValue);
- result[i*2-1].setY(key);
- lastValue = valueAxis->coordToPixel(data.at(i).value);
- lastKey = keyAxis->coordToPixel(data.at(i).key);
- result[i*2+0].setX(lastValue);
- result[i*2+0].setY(key);
- }
- result[data.size()*2-1].setX(lastValue);
- result[data.size()*2-1].setY(lastKey);
- } else
- {
- double lastKey = keyAxis->coordToPixel(data.first().key);
- double lastValue = valueAxis->coordToPixel(data.first().value);
- result[0].setX(lastKey);
- result[0].setY(lastValue);
- for (int i=1; i<data.size(); ++i)
- {
- const double key = (keyAxis->coordToPixel(data.at(i).key)+lastKey)*0.5;
- result[i*2-1].setX(key);
- result[i*2-1].setY(lastValue);
- lastValue = valueAxis->coordToPixel(data.at(i).value);
- lastKey = keyAxis->coordToPixel(data.at(i).key);
- result[i*2+0].setX(key);
- result[i*2+0].setY(lastValue);
- }
- result[data.size()*2-1].setX(lastKey);
- result[data.size()*2-1].setY(lastValue);
- }
- return result;
- }
- QVector<QPointF> QCPGraph::dataToImpulseLines(const QVector<QCPGraphData> &data) const
- {
- QVector<QPointF> result;
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
- result.resize(data.size()*2);
-
- if (keyAxis->orientation() == Qt::Vertical)
- {
- for (int i=0; i<data.size(); ++i)
- {
- const double key = keyAxis->coordToPixel(data.at(i).key);
- result[i*2+0].setX(valueAxis->coordToPixel(0));
- result[i*2+0].setY(key);
- result[i*2+1].setX(valueAxis->coordToPixel(data.at(i).value));
- result[i*2+1].setY(key);
- }
- } else
- {
- for (int i=0; i<data.size(); ++i)
- {
- const double key = keyAxis->coordToPixel(data.at(i).key);
- result[i*2+0].setX(key);
- result[i*2+0].setY(valueAxis->coordToPixel(0));
- result[i*2+1].setX(key);
- result[i*2+1].setY(valueAxis->coordToPixel(data.at(i).value));
- }
- }
- return result;
- }
- void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const
- {
- if (mLineStyle == lsImpulse) return;
- if (painter->brush().style() == Qt::NoBrush || painter->brush().color().alpha() == 0) return;
- applyFillAntialiasingHint(painter);
- const QVector<QCPDataRange> segments = getNonNanSegments(lines, keyAxis()->orientation());
- if (!mChannelFillGraph)
- {
-
- foreach (QCPDataRange segment, segments)
- painter->drawPolygon(getFillPolygon(lines, segment));
- } else
- {
-
- QVector<QPointF> otherLines;
- mChannelFillGraph->getLines(&otherLines, QCPDataRange(0, mChannelFillGraph->dataCount()));
- if (!otherLines.isEmpty())
- {
- QVector<QCPDataRange> otherSegments = getNonNanSegments(&otherLines, mChannelFillGraph->keyAxis()->orientation());
- QVector<QPair<QCPDataRange, QCPDataRange> > segmentPairs = getOverlappingSegments(segments, lines, otherSegments, &otherLines);
- for (int i=0; i<segmentPairs.size(); ++i)
- painter->drawPolygon(getChannelFillPolygon(lines, segmentPairs.at(i).first, &otherLines, segmentPairs.at(i).second));
- }
- }
- }
- void QCPGraph::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const
- {
- applyScattersAntialiasingHint(painter);
- style.applyTo(painter, mPen);
- foreach (const QPointF &scatter, scatters)
- style.drawShape(painter, scatter.x(), scatter.y());
- }
- void QCPGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
- {
- if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
- {
- applyDefaultAntialiasingHint(painter);
- drawPolyline(painter, lines);
- }
- }
- void QCPGraph::drawImpulsePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
- {
- if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
- {
- applyDefaultAntialiasingHint(painter);
- QPen oldPen = painter->pen();
- QPen newPen = painter->pen();
- newPen.setCapStyle(Qt::FlatCap);
- painter->setPen(newPen);
- painter->drawLines(lines);
- painter->setPen(oldPen);
- }
- }
- void QCPGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const
- {
- if (!lineData) return;
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- if (begin == end) return;
- int dataCount = int(end-begin);
- int maxCount = (std::numeric_limits<int>::max)();
- if (mAdaptiveSampling)
- {
- double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key));
- if (2*keyPixelSpan+2 < static_cast<double>((std::numeric_limits<int>::max)()))
- maxCount = int(2*keyPixelSpan+2);
- }
- if (mAdaptiveSampling && dataCount >= maxCount)
- {
- QCPGraphDataContainer::const_iterator it = begin;
- double minValue = it->value;
- double maxValue = it->value;
- QCPGraphDataContainer::const_iterator currentIntervalFirstPoint = it;
- int reversedFactor = keyAxis->pixelOrientation();
- int reversedRound = reversedFactor==-1 ? 1 : 0;
- double currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(begin->key)+reversedRound));
- double lastIntervalEndKey = currentIntervalStartKey;
- double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
- bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic;
- int intervalDataCount = 1;
- ++it;
- while (it != end)
- {
- if (it->key < currentIntervalStartKey+keyEpsilon)
- {
- if (it->value < minValue)
- minValue = it->value;
- else if (it->value > maxValue)
- maxValue = it->value;
- ++intervalDataCount;
- } else
- {
- if (intervalDataCount >= 2)
- {
- if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon)
- lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value));
- lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
- lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
- if (it->key > currentIntervalStartKey+keyEpsilon*2)
- lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.8, (it-1)->value));
- } else
- lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));
- lastIntervalEndKey = (it-1)->key;
- minValue = it->value;
- maxValue = it->value;
- currentIntervalFirstPoint = it;
- currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(it->key)+reversedRound));
- if (keyEpsilonVariable)
- keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
- intervalDataCount = 1;
- }
- ++it;
- }
-
- if (intervalDataCount >= 2)
- {
- if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon)
- lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value));
- lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
- lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
- } else
- lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));
- } else
- {
- lineData->resize(dataCount);
- std::copy(begin, end, lineData->begin());
- }
- }
- void QCPGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const
- {
- if (!scatterData) return;
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- const int scatterModulo = mScatterSkip+1;
- const bool doScatterSkip = mScatterSkip > 0;
- int beginIndex = int(begin-mDataContainer->constBegin());
- int endIndex = int(end-mDataContainer->constBegin());
- while (doScatterSkip && begin != end && beginIndex % scatterModulo != 0)
- {
- ++beginIndex;
- ++begin;
- }
- if (begin == end) return;
- int dataCount = int(end-begin);
- int maxCount = (std::numeric_limits<int>::max)();
- if (mAdaptiveSampling)
- {
- int keyPixelSpan = int(qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)));
- maxCount = 2*keyPixelSpan+2;
- }
- if (mAdaptiveSampling && dataCount >= maxCount)
- {
- double valueMaxRange = valueAxis->range().upper;
- double valueMinRange = valueAxis->range().lower;
- QCPGraphDataContainer::const_iterator it = begin;
- int itIndex = int(beginIndex);
- double minValue = it->value;
- double maxValue = it->value;
- QCPGraphDataContainer::const_iterator minValueIt = it;
- QCPGraphDataContainer::const_iterator maxValueIt = it;
- QCPGraphDataContainer::const_iterator currentIntervalStart = it;
- int reversedFactor = keyAxis->pixelOrientation();
- int reversedRound = reversedFactor==-1 ? 1 : 0;
- double currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(begin->key)+reversedRound));
- double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
- bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic;
- int intervalDataCount = 1;
-
- if (!doScatterSkip)
- ++it;
- else
- {
- itIndex += scatterModulo;
- if (itIndex < endIndex)
- it += scatterModulo;
- else
- {
- it = end;
- itIndex = endIndex;
- }
- }
-
- while (it != end)
- {
- if (it->key < currentIntervalStartKey+keyEpsilon)
- {
- if (it->value < minValue && it->value > valueMinRange && it->value < valueMaxRange)
- {
- minValue = it->value;
- minValueIt = it;
- } else if (it->value > maxValue && it->value > valueMinRange && it->value < valueMaxRange)
- {
- maxValue = it->value;
- maxValueIt = it;
- }
- ++intervalDataCount;
- } else
- {
- if (intervalDataCount >= 2)
- {
-
- double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
- int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0)));
- QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;
- int c = 0;
- while (intervalIt != it)
- {
- if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange)
- scatterData->append(*intervalIt);
- ++c;
- if (!doScatterSkip)
- ++intervalIt;
- else
- intervalIt += scatterModulo;
- }
- } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange)
- scatterData->append(*currentIntervalStart);
- minValue = it->value;
- maxValue = it->value;
- currentIntervalStart = it;
- currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(it->key)+reversedRound));
- if (keyEpsilonVariable)
- keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
- intervalDataCount = 1;
- }
-
- if (!doScatterSkip)
- ++it;
- else
- {
- itIndex += scatterModulo;
- if (itIndex < endIndex)
- it += scatterModulo;
- else
- {
- it = end;
- itIndex = endIndex;
- }
- }
- }
-
- if (intervalDataCount >= 2)
- {
-
- double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
- int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0)));
- QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;
- int intervalItIndex = int(intervalIt-mDataContainer->constBegin());
- int c = 0;
- while (intervalIt != it)
- {
- if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange)
- scatterData->append(*intervalIt);
- ++c;
- if (!doScatterSkip)
- ++intervalIt;
- else
- {
- intervalItIndex += scatterModulo;
- if (intervalItIndex < itIndex)
- intervalIt += scatterModulo;
- else
- {
- intervalIt = it;
- intervalItIndex = itIndex;
- }
- }
- }
- } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange)
- scatterData->append(*currentIntervalStart);
- } else
- {
- QCPGraphDataContainer::const_iterator it = begin;
- int itIndex = beginIndex;
- scatterData->reserve(dataCount);
- while (it != end)
- {
- scatterData->append(*it);
-
- if (!doScatterSkip)
- ++it;
- else
- {
- itIndex += scatterModulo;
- if (itIndex < endIndex)
- it += scatterModulo;
- else
- {
- it = end;
- itIndex = endIndex;
- }
- }
- }
- }
- }
- void QCPGraph::getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
- {
- if (rangeRestriction.isEmpty())
- {
- end = mDataContainer->constEnd();
- begin = end;
- } else
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
-
- begin = mDataContainer->findBegin(keyAxis->range().lower);
- end = mDataContainer->findEnd(keyAxis->range().upper);
-
- mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction);
- }
- }
- QVector<QCPDataRange> QCPGraph::getNonNanSegments(const QVector<QPointF> *lineData, Qt::Orientation keyOrientation) const
- {
- QVector<QCPDataRange> result;
- const int n = lineData->size();
- QCPDataRange currentSegment(-1, -1);
- int i = 0;
- if (keyOrientation == Qt::Horizontal)
- {
- while (i < n)
- {
- while (i < n && qIsNaN(lineData->at(i).y()))
- ++i;
- if (i == n)
- break;
- currentSegment.setBegin(i++);
- while (i < n && !qIsNaN(lineData->at(i).y()))
- ++i;
- currentSegment.setEnd(i++);
- result.append(currentSegment);
- }
- } else
- {
- while (i < n)
- {
- while (i < n && qIsNaN(lineData->at(i).x()))
- ++i;
- if (i == n)
- break;
- currentSegment.setBegin(i++);
- while (i < n && !qIsNaN(lineData->at(i).x()))
- ++i;
- currentSegment.setEnd(i++);
- result.append(currentSegment);
- }
- }
- return result;
- }
- QVector<QPair<QCPDataRange, QCPDataRange> > QCPGraph::getOverlappingSegments(QVector<QCPDataRange> thisSegments, const QVector<QPointF> *thisData, QVector<QCPDataRange> otherSegments, const QVector<QPointF> *otherData) const
- {
- QVector<QPair<QCPDataRange, QCPDataRange> > result;
- if (thisData->isEmpty() || otherData->isEmpty() || thisSegments.isEmpty() || otherSegments.isEmpty())
- return result;
- int thisIndex = 0;
- int otherIndex = 0;
- const bool verticalKey = mKeyAxis->orientation() == Qt::Vertical;
- while (thisIndex < thisSegments.size() && otherIndex < otherSegments.size())
- {
- if (thisSegments.at(thisIndex).size() < 2)
- {
- ++thisIndex;
- continue;
- }
- if (otherSegments.at(otherIndex).size() < 2)
- {
- ++otherIndex;
- continue;
- }
- double thisLower, thisUpper, otherLower, otherUpper;
- if (!verticalKey)
- {
- thisLower = thisData->at(thisSegments.at(thisIndex).begin()).x();
- thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).x();
- otherLower = otherData->at(otherSegments.at(otherIndex).begin()).x();
- otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).x();
- } else
- {
- thisLower = thisData->at(thisSegments.at(thisIndex).begin()).y();
- thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).y();
- otherLower = otherData->at(otherSegments.at(otherIndex).begin()).y();
- otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).y();
- }
- int bPrecedence;
- if (segmentsIntersect(thisLower, thisUpper, otherLower, otherUpper, bPrecedence))
- result.append(QPair<QCPDataRange, QCPDataRange>(thisSegments.at(thisIndex), otherSegments.at(otherIndex)));
- if (bPrecedence <= 0)
- ++otherIndex;
- else
- ++thisIndex;
- }
- return result;
- }
- bool QCPGraph::segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const
- {
- bPrecedence = 0;
- if (aLower > bUpper)
- {
- bPrecedence = -1;
- return false;
- } else if (bLower > aUpper)
- {
- bPrecedence = 1;
- return false;
- } else
- {
- if (aUpper > bUpper)
- bPrecedence = -1;
- else if (aUpper < bUpper)
- bPrecedence = 1;
- return true;
- }
- }
- QPointF QCPGraph::getFillBasePoint(QPointF matchingDataPoint) const
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; }
- QPointF result;
- if (valueAxis->scaleType() == QCPAxis::stLinear)
- {
- if (keyAxis->orientation() == Qt::Horizontal)
- {
- result.setX(matchingDataPoint.x());
- result.setY(valueAxis->coordToPixel(0));
- } else
- {
- result.setX(valueAxis->coordToPixel(0));
- result.setY(matchingDataPoint.y());
- }
- } else
- {
-
-
- if (keyAxis->orientation() == Qt::Vertical)
- {
- if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
- (valueAxis->range().upper > 0 && valueAxis->rangeReversed()))
- result.setX(keyAxis->axisRect()->right());
- else
- result.setX(keyAxis->axisRect()->left());
- result.setY(matchingDataPoint.y());
- } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
- {
- result.setX(matchingDataPoint.x());
- if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
- (valueAxis->range().upper > 0 && valueAxis->rangeReversed()))
- result.setY(keyAxis->axisRect()->top());
- else
- result.setY(keyAxis->axisRect()->bottom());
- }
- }
- return result;
- }
- const QPolygonF QCPGraph::getFillPolygon(const QVector<QPointF> *lineData, QCPDataRange segment) const
- {
- if (segment.size() < 2)
- return QPolygonF();
- QPolygonF result(segment.size()+2);
- result[0] = getFillBasePoint(lineData->at(segment.begin()));
- std::copy(lineData->constBegin()+segment.begin(), lineData->constBegin()+segment.end(), result.begin()+1);
- result[result.size()-1] = getFillBasePoint(lineData->at(segment.end()-1));
- return result;
- }
- const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *thisData, QCPDataRange thisSegment, const QVector<QPointF> *otherData, QCPDataRange otherSegment) const
- {
- if (!mChannelFillGraph)
- return QPolygonF();
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
- if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
- if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
- return QPolygonF();
- if (thisData->isEmpty()) return QPolygonF();
- QVector<QPointF> thisSegmentData(thisSegment.size());
- QVector<QPointF> otherSegmentData(otherSegment.size());
- std::copy(thisData->constBegin()+thisSegment.begin(), thisData->constBegin()+thisSegment.end(), thisSegmentData.begin());
- std::copy(otherData->constBegin()+otherSegment.begin(), otherData->constBegin()+otherSegment.end(), otherSegmentData.begin());
-
- QVector<QPointF> *staticData = &thisSegmentData;
- QVector<QPointF> *croppedData = &otherSegmentData;
-
- if (keyAxis->orientation() == Qt::Horizontal)
- {
-
-
- if (staticData->first().x() < croppedData->first().x())
- qSwap(staticData, croppedData);
- const int lowBound = findIndexBelowX(croppedData, staticData->first().x());
- if (lowBound == -1) return QPolygonF();
- croppedData->remove(0, lowBound);
-
- if (croppedData->size() < 2) return QPolygonF();
- double slope;
- if (!qFuzzyCompare(croppedData->at(1).x(), croppedData->at(0).x()))
- slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
- else
- slope = 0;
- (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
- (*croppedData)[0].setX(staticData->first().x());
-
- if (staticData->last().x() > croppedData->last().x())
- qSwap(staticData, croppedData);
- int highBound = findIndexAboveX(croppedData, staticData->last().x());
- if (highBound == -1) return QPolygonF();
- croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
-
- if (croppedData->size() < 2) return QPolygonF();
- const int li = croppedData->size()-1;
- if (!qFuzzyCompare(croppedData->at(li).x(), croppedData->at(li-1).x()))
- slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
- else
- slope = 0;
- (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
- (*croppedData)[li].setX(staticData->last().x());
- } else
- {
-
-
- if (staticData->first().y() < croppedData->first().y())
- qSwap(staticData, croppedData);
- int lowBound = findIndexBelowY(croppedData, staticData->first().y());
- if (lowBound == -1) return QPolygonF();
- croppedData->remove(0, lowBound);
-
- if (croppedData->size() < 2) return QPolygonF();
- double slope;
- if (!qFuzzyCompare(croppedData->at(1).y(), croppedData->at(0).y()))
- slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
- else
- slope = 0;
- (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
- (*croppedData)[0].setY(staticData->first().y());
-
- if (staticData->last().y() > croppedData->last().y())
- qSwap(staticData, croppedData);
- int highBound = findIndexAboveY(croppedData, staticData->last().y());
- if (highBound == -1) return QPolygonF();
- croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
-
- if (croppedData->size() < 2) return QPolygonF();
- int li = croppedData->size()-1;
- if (!qFuzzyCompare(croppedData->at(li).y(), croppedData->at(li-1).y()))
- slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
- else
- slope = 0;
- (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
- (*croppedData)[li].setY(staticData->last().y());
- }
-
- for (int i=otherSegmentData.size()-1; i>=0; --i)
- thisSegmentData << otherSegmentData.at(i);
- return QPolygonF(thisSegmentData);
- }
- int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
- {
- for (int i=data->size()-1; i>=0; --i)
- {
- if (data->at(i).x() < x)
- {
- if (i<data->size()-1)
- return i+1;
- else
- return data->size()-1;
- }
- }
- return -1;
- }
- int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
- {
- for (int i=0; i<data->size(); ++i)
- {
- if (data->at(i).x() > x)
- {
- if (i>0)
- return i-1;
- else
- return 0;
- }
- }
- return -1;
- }
- int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
- {
- for (int i=data->size()-1; i>=0; --i)
- {
- if (data->at(i).y() < y)
- {
- if (i<data->size()-1)
- return i+1;
- else
- return data->size()-1;
- }
- }
- return -1;
- }
- double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const
- {
- closestData = mDataContainer->constEnd();
- if (mDataContainer->isEmpty())
- return -1.0;
- if (mLineStyle == lsNone && mScatterStyle.isNone())
- return -1.0;
-
- double minDistSqr = (std::numeric_limits<double>::max)();
-
- double posKeyMin, posKeyMax, dummy;
- pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy);
- pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy);
- if (posKeyMin > posKeyMax)
- qSwap(posKeyMin, posKeyMax);
-
- QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true);
- QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true);
- for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it)
- {
- const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();
- if (currentDistSqr < minDistSqr)
- {
- minDistSqr = currentDistSqr;
- closestData = it;
- }
- }
-
- if (mLineStyle != lsNone)
- {
-
- QVector<QPointF> lineData;
- getLines(&lineData, QCPDataRange(0, dataCount()));
- QCPVector2D p(pixelPoint);
- const int step = mLineStyle==lsImpulse ? 2 : 1;
- for (int i=0; i<lineData.size()-1; i+=step)
- {
- const double currentDistSqr = p.distanceSquaredToLine(lineData.at(i), lineData.at(i+1));
- if (currentDistSqr < minDistSqr)
- minDistSqr = currentDistSqr;
- }
- }
- return qSqrt(minDistSqr);
- }
- int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
- {
- for (int i=0; i<data->size(); ++i)
- {
- if (data->at(i).y() > y)
- {
- if (i>0)
- return i-1;
- else
- return 0;
- }
- }
- return -1;
- }
- QCPCurveData::QCPCurveData() :
- t(0),
- key(0),
- value(0)
- {
- }
- QCPCurveData::QCPCurveData(double t, double key, double value) :
- t(t),
- key(key),
- value(value)
- {
- }
- QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
- QCPAbstractPlottable1D<QCPCurveData>(keyAxis, valueAxis),
- mScatterSkip{},
- mLineStyle{}
- {
-
- setPen(QPen(Qt::blue, 0));
- setBrush(Qt::NoBrush);
- setScatterStyle(QCPScatterStyle());
- setLineStyle(lsLine);
- setScatterSkip(0);
- }
- QCPCurve::~QCPCurve()
- {
- }
- void QCPCurve::setData(QSharedPointer<QCPCurveDataContainer> data)
- {
- mDataContainer = data;
- }
- void QCPCurve::setData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
- {
- mDataContainer->clear();
- addData(t, keys, values, alreadySorted);
- }
- void QCPCurve::setData(const QVector<double> &keys, const QVector<double> &values)
- {
- mDataContainer->clear();
- addData(keys, values);
- }
- void QCPCurve::setScatterStyle(const QCPScatterStyle &style)
- {
- mScatterStyle = style;
- }
- void QCPCurve::setScatterSkip(int skip)
- {
- mScatterSkip = qMax(0, skip);
- }
- void QCPCurve::setLineStyle(QCPCurve::LineStyle style)
- {
- mLineStyle = style;
- }
- void QCPCurve::addData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
- {
- if (t.size() != keys.size() || t.size() != values.size())
- qDebug() << Q_FUNC_INFO << "ts, keys and values have different sizes:" << t.size() << keys.size() << values.size();
- const int n = qMin(qMin(t.size(), keys.size()), values.size());
- QVector<QCPCurveData> tempData(n);
- QVector<QCPCurveData>::iterator it = tempData.begin();
- const QVector<QCPCurveData>::iterator itEnd = tempData.end();
- int i = 0;
- while (it != itEnd)
- {
- it->t = t[i];
- it->key = keys[i];
- it->value = values[i];
- ++it;
- ++i;
- }
- mDataContainer->add(tempData, alreadySorted);
- }
- void QCPCurve::addData(const QVector<double> &keys, const QVector<double> &values)
- {
- if (keys.size() != values.size())
- qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
- const int n = qMin(keys.size(), values.size());
- double tStart;
- if (!mDataContainer->isEmpty())
- tStart = (mDataContainer->constEnd()-1)->t + 1.0;
- else
- tStart = 0;
- QVector<QCPCurveData> tempData(n);
- QVector<QCPCurveData>::iterator it = tempData.begin();
- const QVector<QCPCurveData>::iterator itEnd = tempData.end();
- int i = 0;
- while (it != itEnd)
- {
- it->t = tStart + i;
- it->key = keys[i];
- it->value = values[i];
- ++it;
- ++i;
- }
- mDataContainer->add(tempData, true);
- }
- void QCPCurve::addData(double t, double key, double value)
- {
- mDataContainer->add(QCPCurveData(t, key, value));
- }
- void QCPCurve::addData(double key, double value)
- {
- if (!mDataContainer->isEmpty())
- mDataContainer->add(QCPCurveData((mDataContainer->constEnd()-1)->t + 1.0, key, value));
- else
- mDataContainer->add(QCPCurveData(0.0, key, value));
- }
- double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return -1;
- if (!mKeyAxis || !mValueAxis)
- return -1;
- if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
- {
- QCPCurveDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
- double result = pointDistance(pos, closestDataPoint);
- if (details)
- {
- int pointIndex = int( closestDataPoint-mDataContainer->constBegin() );
- details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
- }
- return result;
- } else
- return -1;
- }
- QCPRange QCPCurve::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
- {
- return mDataContainer->keyRange(foundRange, inSignDomain);
- }
- QCPRange QCPCurve::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
- {
- return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
- }
- void QCPCurve::draw(QCPPainter *painter)
- {
- if (mDataContainer->isEmpty()) return;
-
- QVector<QPointF> lines, scatters;
-
- QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
- getDataSegments(selectedSegments, unselectedSegments);
- allSegments << unselectedSegments << selectedSegments;
- for (int i=0; i<allSegments.size(); ++i)
- {
- bool isSelectedSegment = i >= unselectedSegments.size();
-
- QPen finalCurvePen = mPen;
- if (isSelectedSegment && mSelectionDecorator)
- finalCurvePen = mSelectionDecorator->pen();
- QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1);
- getCurveLines(&lines, lineDataRange, finalCurvePen.widthF());
-
- #ifdef QCUSTOMPLOT_CHECK_DATA
- for (QCPCurveDataContainer::const_iterator it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
- {
- if (QCP::isInvalidData(it->t) ||
- QCP::isInvalidData(it->key, it->value))
- qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
- }
- #endif
-
- applyFillAntialiasingHint(painter);
- if (isSelectedSegment && mSelectionDecorator)
- mSelectionDecorator->applyBrush(painter);
- else
- painter->setBrush(mBrush);
- painter->setPen(Qt::NoPen);
- if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0)
- painter->drawPolygon(QPolygonF(lines));
-
- if (mLineStyle != lsNone)
- {
- painter->setPen(finalCurvePen);
- painter->setBrush(Qt::NoBrush);
- drawCurveLine(painter, lines);
- }
-
- QCPScatterStyle finalScatterStyle = mScatterStyle;
- if (isSelectedSegment && mSelectionDecorator)
- finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle);
- if (!finalScatterStyle.isNone())
- {
- getScatters(&scatters, allSegments.at(i), finalScatterStyle.size());
- drawScatterPlot(painter, scatters, finalScatterStyle);
- }
- }
-
- if (mSelectionDecorator)
- mSelectionDecorator->drawDecoration(painter, selection());
- }
- void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
- {
-
- if (mBrush.style() != Qt::NoBrush)
- {
- applyFillAntialiasingHint(painter);
- painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
- }
-
- if (mLineStyle != lsNone)
- {
- applyDefaultAntialiasingHint(painter);
- painter->setPen(mPen);
- painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0));
- }
-
- if (!mScatterStyle.isNone())
- {
- applyScattersAntialiasingHint(painter);
-
- if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
- {
- QCPScatterStyle scaledStyle(mScatterStyle);
- scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
- scaledStyle.applyTo(painter, mPen);
- scaledStyle.drawShape(painter, QRectF(rect).center());
- } else
- {
- mScatterStyle.applyTo(painter, mPen);
- mScatterStyle.drawShape(painter, QRectF(rect).center());
- }
- }
- }
- void QCPCurve::drawCurveLine(QCPPainter *painter, const QVector<QPointF> &lines) const
- {
- if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
- {
- applyDefaultAntialiasingHint(painter);
- drawPolyline(painter, lines);
- }
- }
- void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &points, const QCPScatterStyle &style) const
- {
-
- applyScattersAntialiasingHint(painter);
- style.applyTo(painter, mPen);
- foreach (const QPointF &point, points)
- if (!qIsNaN(point.x()) && !qIsNaN(point.y()))
- style.drawShape(painter, point);
- }
- void QCPCurve::getCurveLines(QVector<QPointF> *lines, const QCPDataRange &dataRange, double penWidth) const
- {
- if (!lines) return;
- lines->clear();
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
-
- const double strokeMargin = qMax(qreal(1.0), qreal(penWidth*0.75));
- const double keyMin = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*keyAxis->pixelOrientation());
- const double keyMax = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*keyAxis->pixelOrientation());
- const double valueMin = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)-strokeMargin*valueAxis->pixelOrientation());
- const double valueMax = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)+strokeMargin*valueAxis->pixelOrientation());
- QCPCurveDataContainer::const_iterator itBegin = mDataContainer->constBegin();
- QCPCurveDataContainer::const_iterator itEnd = mDataContainer->constEnd();
- mDataContainer->limitIteratorsToDataRange(itBegin, itEnd, dataRange);
- if (itBegin == itEnd)
- return;
- QCPCurveDataContainer::const_iterator it = itBegin;
- QCPCurveDataContainer::const_iterator prevIt = itEnd-1;
- int prevRegion = getRegion(prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin);
- QVector<QPointF> trailingPoints;
- while (it != itEnd)
- {
- const int currentRegion = getRegion(it->key, it->value, keyMin, valueMax, keyMax, valueMin);
- if (currentRegion != prevRegion)
- {
- if (currentRegion != 5)
- {
- QPointF crossA, crossB;
- if (prevRegion == 5)
- {
- lines->append(getOptimizedPoint(currentRegion, it->key, it->value, prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin));
-
- *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
- } else if (mayTraverse(prevRegion, currentRegion) &&
- getTraverse(prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin, crossA, crossB))
- {
-
- QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
- getTraverseCornerPoints(prevRegion, currentRegion, keyMin, valueMax, keyMax, valueMin, beforeTraverseCornerPoints, afterTraverseCornerPoints);
- if (it != itBegin)
- {
- *lines << beforeTraverseCornerPoints;
- lines->append(crossA);
- lines->append(crossB);
- *lines << afterTraverseCornerPoints;
- } else
- {
- lines->append(crossB);
- *lines << afterTraverseCornerPoints;
- trailingPoints << beforeTraverseCornerPoints << crossA ;
- }
- } else
- {
- *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
- }
- } else
- {
- if (it == itBegin)
- trailingPoints << getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
- else
- lines->append(getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin));
- lines->append(coordsToPixels(it->key, it->value));
- }
- } else
- {
- if (currentRegion == 5)
- {
- lines->append(coordsToPixels(it->key, it->value));
- } else
- {
-
- }
- }
- prevIt = it;
- prevRegion = currentRegion;
- ++it;
- }
- *lines << trailingPoints;
- }
- void QCPCurve::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange, double scatterWidth) const
- {
- if (!scatters) return;
- scatters->clear();
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- QCPCurveDataContainer::const_iterator begin = mDataContainer->constBegin();
- QCPCurveDataContainer::const_iterator end = mDataContainer->constEnd();
- mDataContainer->limitIteratorsToDataRange(begin, end, dataRange);
- if (begin == end)
- return;
- const int scatterModulo = mScatterSkip+1;
- const bool doScatterSkip = mScatterSkip > 0;
- int endIndex = int( end-mDataContainer->constBegin() );
- QCPRange keyRange = keyAxis->range();
- QCPRange valueRange = valueAxis->range();
-
- keyRange.lower = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.lower)-scatterWidth*keyAxis->pixelOrientation());
- keyRange.upper = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.upper)+scatterWidth*keyAxis->pixelOrientation());
- valueRange.lower = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.lower)-scatterWidth*valueAxis->pixelOrientation());
- valueRange.upper = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.upper)+scatterWidth*valueAxis->pixelOrientation());
- QCPCurveDataContainer::const_iterator it = begin;
- int itIndex = int( begin-mDataContainer->constBegin() );
- while (doScatterSkip && it != end && itIndex % scatterModulo != 0)
- {
- ++itIndex;
- ++it;
- }
- if (keyAxis->orientation() == Qt::Vertical)
- {
- while (it != end)
- {
- if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value))
- scatters->append(QPointF(valueAxis->coordToPixel(it->value), keyAxis->coordToPixel(it->key)));
-
- if (!doScatterSkip)
- ++it;
- else
- {
- itIndex += scatterModulo;
- if (itIndex < endIndex)
- it += scatterModulo;
- else
- {
- it = end;
- itIndex = endIndex;
- }
- }
- }
- } else
- {
- while (it != end)
- {
- if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value))
- scatters->append(QPointF(keyAxis->coordToPixel(it->key), valueAxis->coordToPixel(it->value)));
-
- if (!doScatterSkip)
- ++it;
- else
- {
- itIndex += scatterModulo;
- if (itIndex < endIndex)
- it += scatterModulo;
- else
- {
- it = end;
- itIndex = endIndex;
- }
- }
- }
- }
- }
- int QCPCurve::getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
- {
- if (key < keyMin)
- {
- if (value > valueMax)
- return 1;
- else if (value < valueMin)
- return 3;
- else
- return 2;
- } else if (key > keyMax)
- {
- if (value > valueMax)
- return 7;
- else if (value < valueMin)
- return 9;
- else
- return 8;
- } else
- {
- if (value > valueMax)
- return 4;
- else if (value < valueMin)
- return 6;
- else
- return 5;
- }
- }
- QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
- {
-
-
-
-
- const double keyMinPx = mKeyAxis->coordToPixel(keyMin);
- const double keyMaxPx = mKeyAxis->coordToPixel(keyMax);
- const double valueMinPx = mValueAxis->coordToPixel(valueMin);
- const double valueMaxPx = mValueAxis->coordToPixel(valueMax);
- const double otherValuePx = mValueAxis->coordToPixel(otherValue);
- const double valuePx = mValueAxis->coordToPixel(value);
- const double otherKeyPx = mKeyAxis->coordToPixel(otherKey);
- const double keyPx = mKeyAxis->coordToPixel(key);
- double intersectKeyPx = keyMinPx;
- double intersectValuePx = valueMinPx;
- switch (otherRegion)
- {
- case 1:
- {
- intersectValuePx = valueMaxPx;
- intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
- if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx))
- {
- intersectKeyPx = keyMinPx;
- intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
- }
- break;
- }
- case 2:
- {
- intersectKeyPx = keyMinPx;
- intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
- break;
- }
- case 3:
- {
- intersectValuePx = valueMinPx;
- intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
- if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx))
- {
- intersectKeyPx = keyMinPx;
- intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
- }
- break;
- }
- case 4:
- {
- intersectValuePx = valueMaxPx;
- intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
- break;
- }
- case 5:
- {
- break;
- }
- case 6:
- {
- intersectValuePx = valueMinPx;
- intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
- break;
- }
- case 7:
- {
- intersectValuePx = valueMaxPx;
- intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
- if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx))
- {
- intersectKeyPx = keyMaxPx;
- intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
- }
- break;
- }
- case 8:
- {
- intersectKeyPx = keyMaxPx;
- intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
- break;
- }
- case 9:
- {
- intersectValuePx = valueMinPx;
- intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
- if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx))
- {
- intersectKeyPx = keyMaxPx;
- intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
- }
- break;
- }
- }
- if (mKeyAxis->orientation() == Qt::Horizontal)
- return {intersectKeyPx, intersectValuePx};
- else
- return {intersectValuePx, intersectKeyPx};
- }
- QVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
- {
- QVector<QPointF> result;
- switch (prevRegion)
- {
- case 1:
- {
- switch (currentRegion)
- {
- case 2: { result << coordsToPixels(keyMin, valueMax); break; }
- case 4: { result << coordsToPixels(keyMin, valueMax); break; }
- case 3: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); break; }
- case 7: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); break; }
- case 6: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
- case 8: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
- case 9: {
- if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin)
- { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); }
- else
- { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); }
- break;
- }
- }
- break;
- }
- case 2:
- {
- switch (currentRegion)
- {
- case 1: { result << coordsToPixels(keyMin, valueMax); break; }
- case 3: { result << coordsToPixels(keyMin, valueMin); break; }
- case 4: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
- case 6: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
- case 7: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; }
- case 9: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; }
- }
- break;
- }
- case 3:
- {
- switch (currentRegion)
- {
- case 2: { result << coordsToPixels(keyMin, valueMin); break; }
- case 6: { result << coordsToPixels(keyMin, valueMin); break; }
- case 1: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); break; }
- case 9: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); break; }
- case 4: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
- case 8: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
- case 7: {
- if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin)
- { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); }
- else
- { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); }
- break;
- }
- }
- break;
- }
- case 4:
- {
- switch (currentRegion)
- {
- case 1: { result << coordsToPixels(keyMin, valueMax); break; }
- case 7: { result << coordsToPixels(keyMax, valueMax); break; }
- case 2: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
- case 8: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
- case 3: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; }
- case 9: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; }
- }
- break;
- }
- case 5:
- {
- switch (currentRegion)
- {
- case 1: { result << coordsToPixels(keyMin, valueMax); break; }
- case 7: { result << coordsToPixels(keyMax, valueMax); break; }
- case 9: { result << coordsToPixels(keyMax, valueMin); break; }
- case 3: { result << coordsToPixels(keyMin, valueMin); break; }
- }
- break;
- }
- case 6:
- {
- switch (currentRegion)
- {
- case 3: { result << coordsToPixels(keyMin, valueMin); break; }
- case 9: { result << coordsToPixels(keyMax, valueMin); break; }
- case 2: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
- case 8: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
- case 1: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; }
- case 7: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; }
- }
- break;
- }
- case 7:
- {
- switch (currentRegion)
- {
- case 4: { result << coordsToPixels(keyMax, valueMax); break; }
- case 8: { result << coordsToPixels(keyMax, valueMax); break; }
- case 1: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); break; }
- case 9: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); break; }
- case 2: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
- case 6: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
- case 3: {
- if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin)
- { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); }
- else
- { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); }
- break;
- }
- }
- break;
- }
- case 8:
- {
- switch (currentRegion)
- {
- case 7: { result << coordsToPixels(keyMax, valueMax); break; }
- case 9: { result << coordsToPixels(keyMax, valueMin); break; }
- case 4: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
- case 6: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
- case 1: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; }
- case 3: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; }
- }
- break;
- }
- case 9:
- {
- switch (currentRegion)
- {
- case 6: { result << coordsToPixels(keyMax, valueMin); break; }
- case 8: { result << coordsToPixels(keyMax, valueMin); break; }
- case 3: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); break; }
- case 7: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); break; }
- case 2: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
- case 4: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
- case 1: {
- if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin)
- { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); }
- else
- { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); }
- break;
- }
- }
- break;
- }
- }
- return result;
- }
- bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
- {
- switch (prevRegion)
- {
- case 1:
- {
- switch (currentRegion)
- {
- case 4:
- case 7:
- case 2:
- case 3: return false;
- default: return true;
- }
- }
- case 2:
- {
- switch (currentRegion)
- {
- case 1:
- case 3: return false;
- default: return true;
- }
- }
- case 3:
- {
- switch (currentRegion)
- {
- case 1:
- case 2:
- case 6:
- case 9: return false;
- default: return true;
- }
- }
- case 4:
- {
- switch (currentRegion)
- {
- case 1:
- case 7: return false;
- default: return true;
- }
- }
- case 5: return false;
- case 6:
- {
- switch (currentRegion)
- {
- case 3:
- case 9: return false;
- default: return true;
- }
- }
- case 7:
- {
- switch (currentRegion)
- {
- case 1:
- case 4:
- case 8:
- case 9: return false;
- default: return true;
- }
- }
- case 8:
- {
- switch (currentRegion)
- {
- case 7:
- case 9: return false;
- default: return true;
- }
- }
- case 9:
- {
- switch (currentRegion)
- {
- case 3:
- case 6:
- case 8:
- case 7: return false;
- default: return true;
- }
- }
- default: return true;
- }
- }
- bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const
- {
-
-
-
-
- QList<QPointF> intersections;
- const double valueMinPx = mValueAxis->coordToPixel(valueMin);
- const double valueMaxPx = mValueAxis->coordToPixel(valueMax);
- const double keyMinPx = mKeyAxis->coordToPixel(keyMin);
- const double keyMaxPx = mKeyAxis->coordToPixel(keyMax);
- const double keyPx = mKeyAxis->coordToPixel(key);
- const double valuePx = mValueAxis->coordToPixel(value);
- const double prevKeyPx = mKeyAxis->coordToPixel(prevKey);
- const double prevValuePx = mValueAxis->coordToPixel(prevValue);
- if (qFuzzyIsNull(keyPx-prevKeyPx))
- {
-
- intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMinPx) : QPointF(valueMinPx, keyPx));
- intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMaxPx) : QPointF(valueMaxPx, keyPx));
- } else if (qFuzzyIsNull(valuePx-prevValuePx))
- {
-
- intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, valuePx) : QPointF(valuePx, keyMinPx));
- intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, valuePx) : QPointF(valuePx, keyMaxPx));
- } else
- {
- double gamma;
- double keyPerValuePx = (keyPx-prevKeyPx)/(valuePx-prevValuePx);
-
- gamma = prevKeyPx + (valueMaxPx-prevValuePx)*keyPerValuePx;
- if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx))
- intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMaxPx) : QPointF(valueMaxPx, gamma));
-
- gamma = prevKeyPx + (valueMinPx-prevValuePx)*keyPerValuePx;
- if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx))
- intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMinPx) : QPointF(valueMinPx, gamma));
- const double valuePerKeyPx = 1.0/keyPerValuePx;
-
- gamma = prevValuePx + (keyMinPx-prevKeyPx)*valuePerKeyPx;
- if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx))
- intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, gamma) : QPointF(gamma, keyMinPx));
-
- gamma = prevValuePx + (keyMaxPx-prevKeyPx)*valuePerKeyPx;
- if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx))
- intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, gamma) : QPointF(gamma, keyMaxPx));
- }
-
- if (intersections.size() > 2)
- {
-
- double distSqrMax = 0;
- QPointF pv1, pv2;
- for (int i=0; i<intersections.size()-1; ++i)
- {
- for (int k=i+1; k<intersections.size(); ++k)
- {
- QPointF distPoint = intersections.at(i)-intersections.at(k);
- double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
- if (distSqr > distSqrMax)
- {
- pv1 = intersections.at(i);
- pv2 = intersections.at(k);
- distSqrMax = distSqr;
- }
- }
- }
- intersections = QList<QPointF>() << pv1 << pv2;
- } else if (intersections.size() != 2)
- {
-
- return false;
- }
-
- double xDelta = keyPx-prevKeyPx;
- double yDelta = valuePx-prevValuePx;
- if (mKeyAxis->orientation() != Qt::Horizontal)
- qSwap(xDelta, yDelta);
- if (xDelta*(intersections.at(1).x()-intersections.at(0).x()) + yDelta*(intersections.at(1).y()-intersections.at(0).y()) < 0)
- intersections.move(0, 1);
- crossA = intersections.at(0);
- crossB = intersections.at(1);
- return true;
- }
- void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
- {
- switch (prevRegion)
- {
- case 1:
- {
- switch (currentRegion)
- {
- case 6: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; }
- case 9: { beforeTraverse << coordsToPixels(keyMin, valueMax); afterTraverse << coordsToPixels(keyMax, valueMin); break; }
- case 8: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; }
- }
- break;
- }
- case 2:
- {
- switch (currentRegion)
- {
- case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; }
- case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; }
- }
- break;
- }
- case 3:
- {
- switch (currentRegion)
- {
- case 4: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; }
- case 7: { beforeTraverse << coordsToPixels(keyMin, valueMin); afterTraverse << coordsToPixels(keyMax, valueMax); break; }
- case 8: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; }
- }
- break;
- }
- case 4:
- {
- switch (currentRegion)
- {
- case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; }
- case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; }
- }
- break;
- }
- case 5: { break; }
- case 6:
- {
- switch (currentRegion)
- {
- case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; }
- case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; }
- }
- break;
- }
- case 7:
- {
- switch (currentRegion)
- {
- case 2: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; }
- case 3: { beforeTraverse << coordsToPixels(keyMax, valueMax); afterTraverse << coordsToPixels(keyMin, valueMin); break; }
- case 6: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; }
- }
- break;
- }
- case 8:
- {
- switch (currentRegion)
- {
- case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; }
- case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; }
- }
- break;
- }
- case 9:
- {
- switch (currentRegion)
- {
- case 2: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; }
- case 1: { beforeTraverse << coordsToPixels(keyMax, valueMin); afterTraverse << coordsToPixels(keyMin, valueMax); break; }
- case 4: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; }
- }
- break;
- }
- }
- }
- double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const
- {
- closestData = mDataContainer->constEnd();
- if (mDataContainer->isEmpty())
- return -1.0;
- if (mLineStyle == lsNone && mScatterStyle.isNone())
- return -1.0;
- if (mDataContainer->size() == 1)
- {
- QPointF dataPoint = coordsToPixels(mDataContainer->constBegin()->key, mDataContainer->constBegin()->value);
- closestData = mDataContainer->constBegin();
- return QCPVector2D(dataPoint-pixelPoint).length();
- }
-
- double minDistSqr = (std::numeric_limits<double>::max)();
-
- QCPCurveDataContainer::const_iterator begin = mDataContainer->constBegin();
- QCPCurveDataContainer::const_iterator end = mDataContainer->constEnd();
- for (QCPCurveDataContainer::const_iterator it=begin; it!=end; ++it)
- {
- const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();
- if (currentDistSqr < minDistSqr)
- {
- minDistSqr = currentDistSqr;
- closestData = it;
- }
- }
-
- if (mLineStyle != lsNone)
- {
- QVector<QPointF> lines;
- getCurveLines(&lines, QCPDataRange(0, dataCount()), mParentPlot->selectionTolerance()*1.2);
- for (int i=0; i<lines.size()-1; ++i)
- {
- double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(lines.at(i), lines.at(i+1));
- if (currentDistSqr < minDistSqr)
- minDistSqr = currentDistSqr;
- }
- }
- return qSqrt(minDistSqr);
- }
- QCPBarsGroup::QCPBarsGroup(QCustomPlot *parentPlot) :
- QObject(parentPlot),
- mParentPlot(parentPlot),
- mSpacingType(stAbsolute),
- mSpacing(4)
- {
- }
- QCPBarsGroup::~QCPBarsGroup()
- {
- clear();
- }
- void QCPBarsGroup::setSpacingType(SpacingType spacingType)
- {
- mSpacingType = spacingType;
- }
- void QCPBarsGroup::setSpacing(double spacing)
- {
- mSpacing = spacing;
- }
- QCPBars *QCPBarsGroup::bars(int index) const
- {
- if (index >= 0 && index < mBars.size())
- {
- return mBars.at(index);
- } else
- {
- qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
- return nullptr;
- }
- }
- void QCPBarsGroup::clear()
- {
- const QList<QCPBars*> oldBars = mBars;
- foreach (QCPBars *bars, oldBars)
- bars->setBarsGroup(nullptr);
- }
- void QCPBarsGroup::append(QCPBars *bars)
- {
- if (!bars)
- {
- qDebug() << Q_FUNC_INFO << "bars is 0";
- return;
- }
- if (!mBars.contains(bars))
- bars->setBarsGroup(this);
- else
- qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
- }
- void QCPBarsGroup::insert(int i, QCPBars *bars)
- {
- if (!bars)
- {
- qDebug() << Q_FUNC_INFO << "bars is 0";
- return;
- }
-
- if (!mBars.contains(bars))
- bars->setBarsGroup(this);
-
- mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
- }
- void QCPBarsGroup::remove(QCPBars *bars)
- {
- if (!bars)
- {
- qDebug() << Q_FUNC_INFO << "bars is 0";
- return;
- }
- if (mBars.contains(bars))
- bars->setBarsGroup(nullptr);
- else
- qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
- }
- void QCPBarsGroup::registerBars(QCPBars *bars)
- {
- if (!mBars.contains(bars))
- mBars.append(bars);
- }
- void QCPBarsGroup::unregisterBars(QCPBars *bars)
- {
- mBars.removeOne(bars);
- }
- double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
- {
-
- QList<const QCPBars*> baseBars;
- foreach (const QCPBars *b, mBars)
- {
- while (b->barBelow())
- b = b->barBelow();
- if (!baseBars.contains(b))
- baseBars.append(b);
- }
-
- const QCPBars *thisBase = bars;
- while (thisBase->barBelow())
- thisBase = thisBase->barBelow();
-
- double result = 0;
- int index = baseBars.indexOf(thisBase);
- if (index >= 0)
- {
- if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2)
- {
- return result;
- } else
- {
- double lowerPixelWidth, upperPixelWidth;
- int startIndex;
- int dir = (index <= (baseBars.size()-1)/2) ? -1 : 1;
- if (baseBars.size() % 2 == 0)
- {
- startIndex = baseBars.size()/2 + (dir < 0 ? -1 : 0);
- result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5;
- } else
- {
- startIndex = (baseBars.size()-1)/2+dir;
- baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
- result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
- result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord);
- }
- for (int i = startIndex; i != index; i += dir)
- {
- baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
- result += qAbs(upperPixelWidth-lowerPixelWidth);
- result += getPixelSpacing(baseBars.at(i), keyCoord);
- }
-
- baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
- result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
-
- result *= dir*thisBase->keyAxis()->pixelOrientation();
- }
- }
- return result;
- }
- double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
- {
- switch (mSpacingType)
- {
- case stAbsolute:
- {
- return mSpacing;
- }
- case stAxisRectRatio:
- {
- if (bars->keyAxis()->orientation() == Qt::Horizontal)
- return bars->keyAxis()->axisRect()->width()*mSpacing;
- else
- return bars->keyAxis()->axisRect()->height()*mSpacing;
- }
- case stPlotCoords:
- {
- double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
- return qAbs(bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel);
- }
- }
- return 0;
- }
- QCPBarsData::QCPBarsData() :
- key(0),
- value(0)
- {
- }
- QCPBarsData::QCPBarsData(double key, double value) :
- key(key),
- value(value)
- {
- }
- QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
- QCPAbstractPlottable1D<QCPBarsData>(keyAxis, valueAxis),
- mWidth(0.75),
- mWidthType(wtPlotCoords),
- mBarsGroup(nullptr),
- mBaseValue(0),
- mStackingGap(1)
- {
-
- mPen.setColor(Qt::blue);
- mPen.setStyle(Qt::SolidLine);
- mBrush.setColor(QColor(40, 50, 255, 30));
- mBrush.setStyle(Qt::SolidPattern);
- mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
- }
- QCPBars::~QCPBars()
- {
- setBarsGroup(nullptr);
- if (mBarBelow || mBarAbove)
- connectBars(mBarBelow.data(), mBarAbove.data());
- }
- void QCPBars::setData(QSharedPointer<QCPBarsDataContainer> data)
- {
- mDataContainer = data;
- }
- void QCPBars::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
- {
- mDataContainer->clear();
- addData(keys, values, alreadySorted);
- }
- void QCPBars::setWidth(double width)
- {
- mWidth = width;
- }
- void QCPBars::setWidthType(QCPBars::WidthType widthType)
- {
- mWidthType = widthType;
- }
- void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup)
- {
-
- if (mBarsGroup)
- mBarsGroup->unregisterBars(this);
- mBarsGroup = barsGroup;
-
- if (mBarsGroup)
- mBarsGroup->registerBars(this);
- }
- void QCPBars::setBaseValue(double baseValue)
- {
- mBaseValue = baseValue;
- }
- void QCPBars::setStackingGap(double pixels)
- {
- mStackingGap = pixels;
- }
- void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
- {
- if (keys.size() != values.size())
- qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
- const int n = qMin(keys.size(), values.size());
- QVector<QCPBarsData> tempData(n);
- QVector<QCPBarsData>::iterator it = tempData.begin();
- const QVector<QCPBarsData>::iterator itEnd = tempData.end();
- int i = 0;
- while (it != itEnd)
- {
- it->key = keys[i];
- it->value = values[i];
- ++it;
- ++i;
- }
- mDataContainer->add(tempData, alreadySorted);
- }
- void QCPBars::addData(double key, double value)
- {
- mDataContainer->add(QCPBarsData(key, value));
- }
- void QCPBars::moveBelow(QCPBars *bars)
- {
- if (bars == this) return;
- if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
- {
- qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
- return;
- }
-
- connectBars(mBarBelow.data(), mBarAbove.data());
-
- if (bars)
- {
- if (bars->mBarBelow)
- connectBars(bars->mBarBelow.data(), this);
- connectBars(this, bars);
- }
- }
- void QCPBars::moveAbove(QCPBars *bars)
- {
- if (bars == this) return;
- if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
- {
- qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
- return;
- }
-
- connectBars(mBarBelow.data(), mBarAbove.data());
-
- if (bars)
- {
- if (bars->mBarAbove)
- connectBars(this, bars->mBarAbove.data());
- connectBars(bars, this);
- }
- }
- QCPDataSelection QCPBars::selectTestRect(const QRectF &rect, bool onlySelectable) const
- {
- QCPDataSelection result;
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return result;
- if (!mKeyAxis || !mValueAxis)
- return result;
- QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
- getVisibleDataBounds(visibleBegin, visibleEnd);
- for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
- {
- if (rect.intersects(getBarRect(it->key, it->value)))
- result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);
- }
- result.simplify();
- return result;
- }
- double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return -1;
- if (!mKeyAxis || !mValueAxis)
- return -1;
- if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
- {
-
- QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
- getVisibleDataBounds(visibleBegin, visibleEnd);
- for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
- {
- if (getBarRect(it->key, it->value).contains(pos))
- {
- if (details)
- {
- int pointIndex = int(it-mDataContainer->constBegin());
- details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
- }
- return mParentPlot->selectionTolerance()*0.99;
- }
- }
- }
- return -1;
- }
- QCPRange QCPBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
- {
-
- QCPRange range;
- range = mDataContainer->keyRange(foundRange, inSignDomain);
-
- if (foundRange && mKeyAxis)
- {
- double lowerPixelWidth, upperPixelWidth, keyPixel;
-
- getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
- keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
- if (mBarsGroup)
- keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
- const double lowerCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);
- if (!qIsNaN(lowerCorrected) && qIsFinite(lowerCorrected) && range.lower > lowerCorrected)
- range.lower = lowerCorrected;
-
- getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
- keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
- if (mBarsGroup)
- keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
- const double upperCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);
- if (!qIsNaN(upperCorrected) && qIsFinite(upperCorrected) && range.upper < upperCorrected)
- range.upper = upperCorrected;
- }
- return range;
- }
- QCPRange QCPBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
- {
-
-
- QCPRange range;
- range.lower = mBaseValue;
- range.upper = mBaseValue;
- bool haveLower = true;
- bool haveUpper = true;
- QCPBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();
- QCPBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd();
- if (inKeyRange != QCPRange())
- {
- itBegin = mDataContainer->findBegin(inKeyRange.lower, false);
- itEnd = mDataContainer->findEnd(inKeyRange.upper, false);
- }
- for (QCPBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)
- {
- const double current = it->value + getStackedBaseValue(it->key, it->value >= 0);
- if (qIsNaN(current)) continue;
- if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
- {
- if (current < range.lower || !haveLower)
- {
- range.lower = current;
- haveLower = true;
- }
- if (current > range.upper || !haveUpper)
- {
- range.upper = current;
- haveUpper = true;
- }
- }
- }
- foundRange = true;
- return range;
- }
- QPointF QCPBars::dataPixelPosition(int index) const
- {
- if (index >= 0 && index < mDataContainer->size())
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; }
- const QCPDataContainer<QCPBarsData>::const_iterator it = mDataContainer->constBegin()+index;
- const double valuePixel = valueAxis->coordToPixel(getStackedBaseValue(it->key, it->value >= 0) + it->value);
- const double keyPixel = keyAxis->coordToPixel(it->key) + (mBarsGroup ? mBarsGroup->keyPixelOffset(this, it->key) : 0);
- if (keyAxis->orientation() == Qt::Horizontal)
- return {keyPixel, valuePixel};
- else
- return {valuePixel, keyPixel};
- } else
- {
- qDebug() << Q_FUNC_INFO << "Index out of bounds" << index;
- return {};
- }
- }
- void QCPBars::draw(QCPPainter *painter)
- {
- if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- if (mDataContainer->isEmpty()) return;
- QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
- getVisibleDataBounds(visibleBegin, visibleEnd);
-
- QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
- getDataSegments(selectedSegments, unselectedSegments);
- allSegments << unselectedSegments << selectedSegments;
- for (int i=0; i<allSegments.size(); ++i)
- {
- bool isSelectedSegment = i >= unselectedSegments.size();
- QCPBarsDataContainer::const_iterator begin = visibleBegin;
- QCPBarsDataContainer::const_iterator end = visibleEnd;
- mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
- if (begin == end)
- continue;
- for (QCPBarsDataContainer::const_iterator it=begin; it!=end; ++it)
- {
-
- #ifdef QCUSTOMPLOT_CHECK_DATA
- if (QCP::isInvalidData(it->key, it->value))
- qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range invalid." << "Plottable name:" << name();
- #endif
-
- if (isSelectedSegment && mSelectionDecorator)
- {
- mSelectionDecorator->applyBrush(painter);
- mSelectionDecorator->applyPen(painter);
- } else
- {
- painter->setBrush(mBrush);
- painter->setPen(mPen);
- }
- applyDefaultAntialiasingHint(painter);
- painter->drawPolygon(getBarRect(it->key, it->value));
- }
- }
-
- if (mSelectionDecorator)
- mSelectionDecorator->drawDecoration(painter, selection());
- }
- void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
- {
-
- applyDefaultAntialiasingHint(painter);
- painter->setBrush(mBrush);
- painter->setPen(mPen);
- QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
- r.moveCenter(rect.center());
- painter->drawRect(r);
- }
- void QCPBars::getVisibleDataBounds(QCPBarsDataContainer::const_iterator &begin, QCPBarsDataContainer::const_iterator &end) const
- {
- if (!mKeyAxis)
- {
- qDebug() << Q_FUNC_INFO << "invalid key axis";
- begin = mDataContainer->constEnd();
- end = mDataContainer->constEnd();
- return;
- }
- if (mDataContainer->isEmpty())
- {
- begin = mDataContainer->constEnd();
- end = mDataContainer->constEnd();
- return;
- }
-
- begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower);
- end = mDataContainer->findEnd(mKeyAxis.data()->range().upper);
- double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
- double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
- bool isVisible = false;
-
- QCPBarsDataContainer::const_iterator it = begin;
- while (it != mDataContainer->constBegin())
- {
- --it;
- const QRectF barRect = getBarRect(it->key, it->value);
- if (mKeyAxis.data()->orientation() == Qt::Horizontal)
- isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.left() <= lowerPixelBound));
- else
- isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.bottom() >= lowerPixelBound));
- if (isVisible)
- begin = it;
- else
- break;
- }
-
- it = end;
- while (it != mDataContainer->constEnd())
- {
- const QRectF barRect = getBarRect(it->key, it->value);
- if (mKeyAxis.data()->orientation() == Qt::Horizontal)
- isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.right() >= upperPixelBound));
- else
- isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.top() <= upperPixelBound));
- if (isVisible)
- end = it+1;
- else
- break;
- ++it;
- }
- }
- QRectF QCPBars::getBarRect(double key, double value) const
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; }
- double lowerPixelWidth, upperPixelWidth;
- getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
- double base = getStackedBaseValue(key, value >= 0);
- double basePixel = valueAxis->coordToPixel(base);
- double valuePixel = valueAxis->coordToPixel(base+value);
- double keyPixel = keyAxis->coordToPixel(key);
- if (mBarsGroup)
- keyPixel += mBarsGroup->keyPixelOffset(this, key);
- double bottomOffset = (mBarBelow && mPen != Qt::NoPen ? 1 : 0)*(mPen.isCosmetic() ? 1 : mPen.widthF());
- bottomOffset += mBarBelow ? mStackingGap : 0;
- bottomOffset *= (value<0 ? -1 : 1)*valueAxis->pixelOrientation();
- if (qAbs(valuePixel-basePixel) <= qAbs(bottomOffset))
- bottomOffset = valuePixel-basePixel;
- if (keyAxis->orientation() == Qt::Horizontal)
- {
- return QRectF(QPointF(keyPixel+lowerPixelWidth, valuePixel), QPointF(keyPixel+upperPixelWidth, basePixel+bottomOffset)).normalized();
- } else
- {
- return QRectF(QPointF(basePixel+bottomOffset, keyPixel+lowerPixelWidth), QPointF(valuePixel, keyPixel+upperPixelWidth)).normalized();
- }
- }
- void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
- {
- lower = 0;
- upper = 0;
- switch (mWidthType)
- {
- case wtAbsolute:
- {
- upper = mWidth*0.5*mKeyAxis.data()->pixelOrientation();
- lower = -upper;
- break;
- }
- case wtAxisRectRatio:
- {
- if (mKeyAxis && mKeyAxis.data()->axisRect())
- {
- if (mKeyAxis.data()->orientation() == Qt::Horizontal)
- upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
- else
- upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
- lower = -upper;
- } else
- qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
- break;
- }
- case wtPlotCoords:
- {
- if (mKeyAxis)
- {
- double keyPixel = mKeyAxis.data()->coordToPixel(key);
- upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
- lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
-
-
- } else
- qDebug() << Q_FUNC_INFO << "No key axis defined";
- break;
- }
- }
- }
- double QCPBars::getStackedBaseValue(double key, bool positive) const
- {
- if (mBarBelow)
- {
- double max = 0;
-
- double epsilon = qAbs(key)*(sizeof(key)==4 ? 1e-6 : 1e-14);
- if (qFuzzyCompare(key, 0))
- epsilon = (sizeof(key)==4 ? 1e-6 : 1e-14);
- QCPBarsDataContainer::const_iterator it = mBarBelow.data()->mDataContainer->findBegin(key-epsilon);
- QCPBarsDataContainer::const_iterator itEnd = mBarBelow.data()->mDataContainer->findEnd(key+epsilon);
- while (it != itEnd)
- {
- if (it->key > key-epsilon && it->key < key+epsilon)
- {
- if ((positive && it->value > max) ||
- (!positive && it->value < max))
- max = it->value;
- }
- ++it;
- }
-
- return max + mBarBelow.data()->getStackedBaseValue(key, positive);
- } else
- return mBaseValue;
- }
- void QCPBars::connectBars(QCPBars *lower, QCPBars *upper)
- {
- if (!lower && !upper) return;
- if (!lower)
- {
-
- if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
- upper->mBarBelow.data()->mBarAbove = nullptr;
- upper->mBarBelow = nullptr;
- } else if (!upper)
- {
-
- if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
- lower->mBarAbove.data()->mBarBelow = nullptr;
- lower->mBarAbove = nullptr;
- } else
- {
-
- if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
- lower->mBarAbove.data()->mBarBelow = nullptr;
-
- if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
- upper->mBarBelow.data()->mBarAbove = nullptr;
- lower->mBarAbove = upper;
- upper->mBarBelow = lower;
- }
- }
- QCPStatisticalBoxData::QCPStatisticalBoxData() :
- key(0),
- minimum(0),
- lowerQuartile(0),
- median(0),
- upperQuartile(0),
- maximum(0)
- {
- }
- QCPStatisticalBoxData::QCPStatisticalBoxData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers) :
- key(key),
- minimum(minimum),
- lowerQuartile(lowerQuartile),
- median(median),
- upperQuartile(upperQuartile),
- maximum(maximum),
- outliers(outliers)
- {
- }
- QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) :
- QCPAbstractPlottable1D<QCPStatisticalBoxData>(keyAxis, valueAxis),
- mWidth(0.5),
- mWhiskerWidth(0.2),
- mWhiskerPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap),
- mWhiskerBarPen(Qt::black),
- mWhiskerAntialiased(false),
- mMedianPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap),
- mOutlierStyle(QCPScatterStyle::ssCircle, Qt::blue, 6)
- {
- setPen(QPen(Qt::black));
- setBrush(Qt::NoBrush);
- }
- void QCPStatisticalBox::setData(QSharedPointer<QCPStatisticalBoxDataContainer> data)
- {
- mDataContainer = data;
- }
- void QCPStatisticalBox::setData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted)
- {
- mDataContainer->clear();
- addData(keys, minimum, lowerQuartile, median, upperQuartile, maximum, alreadySorted);
- }
- void QCPStatisticalBox::setWidth(double width)
- {
- mWidth = width;
- }
- void QCPStatisticalBox::setWhiskerWidth(double width)
- {
- mWhiskerWidth = width;
- }
- void QCPStatisticalBox::setWhiskerPen(const QPen &pen)
- {
- mWhiskerPen = pen;
- }
- void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen)
- {
- mWhiskerBarPen = pen;
- }
- void QCPStatisticalBox::setWhiskerAntialiased(bool enabled)
- {
- mWhiskerAntialiased = enabled;
- }
- void QCPStatisticalBox::setMedianPen(const QPen &pen)
- {
- mMedianPen = pen;
- }
- void QCPStatisticalBox::setOutlierStyle(const QCPScatterStyle &style)
- {
- mOutlierStyle = style;
- }
- void QCPStatisticalBox::addData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted)
- {
- if (keys.size() != minimum.size() || minimum.size() != lowerQuartile.size() || lowerQuartile.size() != median.size() ||
- median.size() != upperQuartile.size() || upperQuartile.size() != maximum.size() || maximum.size() != keys.size())
- qDebug() << Q_FUNC_INFO << "keys, minimum, lowerQuartile, median, upperQuartile, maximum have different sizes:"
- << keys.size() << minimum.size() << lowerQuartile.size() << median.size() << upperQuartile.size() << maximum.size();
- const int n = qMin(keys.size(), qMin(minimum.size(), qMin(lowerQuartile.size(), qMin(median.size(), qMin(upperQuartile.size(), maximum.size())))));
- QVector<QCPStatisticalBoxData> tempData(n);
- QVector<QCPStatisticalBoxData>::iterator it = tempData.begin();
- const QVector<QCPStatisticalBoxData>::iterator itEnd = tempData.end();
- int i = 0;
- while (it != itEnd)
- {
- it->key = keys[i];
- it->minimum = minimum[i];
- it->lowerQuartile = lowerQuartile[i];
- it->median = median[i];
- it->upperQuartile = upperQuartile[i];
- it->maximum = maximum[i];
- ++it;
- ++i;
- }
- mDataContainer->add(tempData, alreadySorted);
- }
- void QCPStatisticalBox::addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers)
- {
- mDataContainer->add(QCPStatisticalBoxData(key, minimum, lowerQuartile, median, upperQuartile, maximum, outliers));
- }
- QCPDataSelection QCPStatisticalBox::selectTestRect(const QRectF &rect, bool onlySelectable) const
- {
- QCPDataSelection result;
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return result;
- if (!mKeyAxis || !mValueAxis)
- return result;
- QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
- getVisibleDataBounds(visibleBegin, visibleEnd);
- for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
- {
- if (rect.intersects(getQuartileBox(it)))
- result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);
- }
- result.simplify();
- return result;
- }
- double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return -1;
- if (!mKeyAxis || !mValueAxis)
- return -1;
- if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
- {
-
- QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
- QCPStatisticalBoxDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
- getVisibleDataBounds(visibleBegin, visibleEnd);
- double minDistSqr = (std::numeric_limits<double>::max)();
- for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
- {
- if (getQuartileBox(it).contains(pos))
- {
- double currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
- if (currentDistSqr < minDistSqr)
- {
- minDistSqr = currentDistSqr;
- closestDataPoint = it;
- }
- } else
- {
- const QVector<QLineF> whiskerBackbones = getWhiskerBackboneLines(it);
- const QCPVector2D posVec(pos);
- foreach (const QLineF &backbone, whiskerBackbones)
- {
- double currentDistSqr = posVec.distanceSquaredToLine(backbone);
- if (currentDistSqr < minDistSqr)
- {
- minDistSqr = currentDistSqr;
- closestDataPoint = it;
- }
- }
- }
- }
- if (details)
- {
- int pointIndex = int(closestDataPoint-mDataContainer->constBegin());
- details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
- }
- return qSqrt(minDistSqr);
- }
- return -1;
- }
- QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
- {
- QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);
-
- if (foundRange)
- {
- if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0)
- range.lower -= mWidth*0.5;
- if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0)
- range.upper += mWidth*0.5;
- }
- return range;
- }
- QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
- {
- return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
- }
- void QCPStatisticalBox::draw(QCPPainter *painter)
- {
- if (mDataContainer->isEmpty()) return;
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
- getVisibleDataBounds(visibleBegin, visibleEnd);
-
- QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
- getDataSegments(selectedSegments, unselectedSegments);
- allSegments << unselectedSegments << selectedSegments;
- for (int i=0; i<allSegments.size(); ++i)
- {
- bool isSelectedSegment = i >= unselectedSegments.size();
- QCPStatisticalBoxDataContainer::const_iterator begin = visibleBegin;
- QCPStatisticalBoxDataContainer::const_iterator end = visibleEnd;
- mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
- if (begin == end)
- continue;
- for (QCPStatisticalBoxDataContainer::const_iterator it=begin; it!=end; ++it)
- {
-
- # ifdef QCUSTOMPLOT_CHECK_DATA
- if (QCP::isInvalidData(it->key, it->minimum) ||
- QCP::isInvalidData(it->lowerQuartile, it->median) ||
- QCP::isInvalidData(it->upperQuartile, it->maximum))
- qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range has invalid data." << "Plottable name:" << name();
- for (int i=0; i<it->outliers.size(); ++i)
- if (QCP::isInvalidData(it->outliers.at(i)))
- qDebug() << Q_FUNC_INFO << "Data point outlier at" << it->key << "of drawn range invalid." << "Plottable name:" << name();
- # endif
- if (isSelectedSegment && mSelectionDecorator)
- {
- mSelectionDecorator->applyPen(painter);
- mSelectionDecorator->applyBrush(painter);
- } else
- {
- painter->setPen(mPen);
- painter->setBrush(mBrush);
- }
- QCPScatterStyle finalOutlierStyle = mOutlierStyle;
- if (isSelectedSegment && mSelectionDecorator)
- finalOutlierStyle = mSelectionDecorator->getFinalScatterStyle(mOutlierStyle);
- drawStatisticalBox(painter, it, finalOutlierStyle);
- }
- }
-
- if (mSelectionDecorator)
- mSelectionDecorator->drawDecoration(painter, selection());
- }
- void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
- {
-
- applyDefaultAntialiasingHint(painter);
- painter->setPen(mPen);
- painter->setBrush(mBrush);
- QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
- r.moveCenter(rect.center());
- painter->drawRect(r);
- }
- void QCPStatisticalBox::drawStatisticalBox(QCPPainter *painter, QCPStatisticalBoxDataContainer::const_iterator it, const QCPScatterStyle &outlierStyle) const
- {
-
- applyDefaultAntialiasingHint(painter);
- const QRectF quartileBox = getQuartileBox(it);
- painter->drawRect(quartileBox);
-
- painter->save();
- painter->setClipRect(quartileBox, Qt::IntersectClip);
- painter->setPen(mMedianPen);
- painter->drawLine(QLineF(coordsToPixels(it->key-mWidth*0.5, it->median), coordsToPixels(it->key+mWidth*0.5, it->median)));
- painter->restore();
-
- applyAntialiasingHint(painter, mWhiskerAntialiased, QCP::aePlottables);
- painter->setPen(mWhiskerPen);
- painter->drawLines(getWhiskerBackboneLines(it));
- painter->setPen(mWhiskerBarPen);
- painter->drawLines(getWhiskerBarLines(it));
-
- applyScattersAntialiasingHint(painter);
- outlierStyle.applyTo(painter, mPen);
- for (int i=0; i<it->outliers.size(); ++i)
- outlierStyle.drawShape(painter, coordsToPixels(it->key, it->outliers.at(i)));
- }
- void QCPStatisticalBox::getVisibleDataBounds(QCPStatisticalBoxDataContainer::const_iterator &begin, QCPStatisticalBoxDataContainer::const_iterator &end) const
- {
- if (!mKeyAxis)
- {
- qDebug() << Q_FUNC_INFO << "invalid key axis";
- begin = mDataContainer->constEnd();
- end = mDataContainer->constEnd();
- return;
- }
- begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5);
- end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5);
- }
- QRectF QCPStatisticalBox::getQuartileBox(QCPStatisticalBoxDataContainer::const_iterator it) const
- {
- QRectF result;
- result.setTopLeft(coordsToPixels(it->key-mWidth*0.5, it->upperQuartile));
- result.setBottomRight(coordsToPixels(it->key+mWidth*0.5, it->lowerQuartile));
- return result;
- }
- QVector<QLineF> QCPStatisticalBox::getWhiskerBackboneLines(QCPStatisticalBoxDataContainer::const_iterator it) const
- {
- QVector<QLineF> result(2);
- result[0].setPoints(coordsToPixels(it->key, it->lowerQuartile), coordsToPixels(it->key, it->minimum));
- result[1].setPoints(coordsToPixels(it->key, it->upperQuartile), coordsToPixels(it->key, it->maximum));
- return result;
- }
- QVector<QLineF> QCPStatisticalBox::getWhiskerBarLines(QCPStatisticalBoxDataContainer::const_iterator it) const
- {
- QVector<QLineF> result(2);
- result[0].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->minimum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->minimum));
- result[1].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->maximum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->maximum));
- return result;
- }
- QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
- mKeySize(0),
- mValueSize(0),
- mKeyRange(keyRange),
- mValueRange(valueRange),
- mIsEmpty(true),
- mData(nullptr),
- mAlpha(nullptr),
- mDataModified(true)
- {
- setSize(keySize, valueSize);
- fill(0);
- }
- QCPColorMapData::~QCPColorMapData()
- {
- delete[] mData;
- delete[] mAlpha;
- }
- QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) :
- mKeySize(0),
- mValueSize(0),
- mIsEmpty(true),
- mData(nullptr),
- mAlpha(nullptr),
- mDataModified(true)
- {
- *this = other;
- }
- QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other)
- {
- if (&other != this)
- {
- const int keySize = other.keySize();
- const int valueSize = other.valueSize();
- if (!other.mAlpha && mAlpha)
- clearAlpha();
- setSize(keySize, valueSize);
- if (other.mAlpha && !mAlpha)
- createAlpha(false);
- setRange(other.keyRange(), other.valueRange());
- if (!isEmpty())
- {
- memcpy(mData, other.mData, sizeof(mData[0])*size_t(keySize*valueSize));
- if (mAlpha)
- memcpy(mAlpha, other.mAlpha, sizeof(mAlpha[0])*size_t(keySize*valueSize));
- }
- mDataBounds = other.mDataBounds;
- mDataModified = true;
- }
- return *this;
- }
- double QCPColorMapData::data(double key, double value)
- {
- int keyCell = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 );
- int valueCell = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 );
- if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
- return mData[valueCell*mKeySize + keyCell];
- else
- return 0;
- }
- double QCPColorMapData::cell(int keyIndex, int valueIndex)
- {
- if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
- return mData[valueIndex*mKeySize + keyIndex];
- else
- return 0;
- }
- unsigned char QCPColorMapData::alpha(int keyIndex, int valueIndex)
- {
- if (mAlpha && keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
- return mAlpha[valueIndex*mKeySize + keyIndex];
- else
- return 255;
- }
- void QCPColorMapData::setSize(int keySize, int valueSize)
- {
- if (keySize != mKeySize || valueSize != mValueSize)
- {
- mKeySize = keySize;
- mValueSize = valueSize;
- delete[] mData;
- mIsEmpty = mKeySize == 0 || mValueSize == 0;
- if (!mIsEmpty)
- {
- #ifdef __EXCEPTIONS
- try {
- #endif
- mData = new double[size_t(mKeySize*mValueSize)];
- #ifdef __EXCEPTIONS
- } catch (...) { mData = nullptr; }
- #endif
- if (mData)
- fill(0);
- else
- qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
- } else
- mData = nullptr;
- if (mAlpha)
- createAlpha();
- mDataModified = true;
- }
- }
- void QCPColorMapData::setKeySize(int keySize)
- {
- setSize(keySize, mValueSize);
- }
- void QCPColorMapData::setValueSize(int valueSize)
- {
- setSize(mKeySize, valueSize);
- }
- void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)
- {
- setKeyRange(keyRange);
- setValueRange(valueRange);
- }
- void QCPColorMapData::setKeyRange(const QCPRange &keyRange)
- {
- mKeyRange = keyRange;
- }
- void QCPColorMapData::setValueRange(const QCPRange &valueRange)
- {
- mValueRange = valueRange;
- }
- void QCPColorMapData::setData(double key, double value, double z)
- {
- int keyCell = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 );
- int valueCell = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 );
- if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
- {
- mData[valueCell*mKeySize + keyCell] = z;
- if (z < mDataBounds.lower)
- mDataBounds.lower = z;
- if (z > mDataBounds.upper)
- mDataBounds.upper = z;
- mDataModified = true;
- }
- }
- void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
- {
- if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
- {
- mData[valueIndex*mKeySize + keyIndex] = z;
- if (z < mDataBounds.lower)
- mDataBounds.lower = z;
- if (z > mDataBounds.upper)
- mDataBounds.upper = z;
- mDataModified = true;
- } else
- qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex << valueIndex;
- }
- void QCPColorMapData::setAlpha(int keyIndex, int valueIndex, unsigned char alpha)
- {
- if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
- {
- if (mAlpha || createAlpha())
- {
- mAlpha[valueIndex*mKeySize + keyIndex] = alpha;
- mDataModified = true;
- }
- } else
- qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex << valueIndex;
- }
- void QCPColorMapData::recalculateDataBounds()
- {
- if (mKeySize > 0 && mValueSize > 0)
- {
- double minHeight = mData[0];
- double maxHeight = mData[0];
- const int dataCount = mValueSize*mKeySize;
- for (int i=0; i<dataCount; ++i)
- {
- if (mData[i] > maxHeight)
- maxHeight = mData[i];
- if (mData[i] < minHeight)
- minHeight = mData[i];
- }
- mDataBounds.lower = minHeight;
- mDataBounds.upper = maxHeight;
- }
- }
- void QCPColorMapData::clear()
- {
- setSize(0, 0);
- }
- void QCPColorMapData::clearAlpha()
- {
- if (mAlpha)
- {
- delete[] mAlpha;
- mAlpha = nullptr;
- mDataModified = true;
- }
- }
- void QCPColorMapData::fill(double z)
- {
- const int dataCount = mValueSize*mKeySize;
- for (int i=0; i<dataCount; ++i)
- mData[i] = z;
- mDataBounds = QCPRange(z, z);
- mDataModified = true;
- }
- void QCPColorMapData::fillAlpha(unsigned char alpha)
- {
- if (mAlpha || createAlpha(false))
- {
- const int dataCount = mValueSize*mKeySize;
- for (int i=0; i<dataCount; ++i)
- mAlpha[i] = alpha;
- mDataModified = true;
- }
- }
- void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
- {
- if (keyIndex)
- *keyIndex = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 );
- if (valueIndex)
- *valueIndex = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 );
- }
- void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
- {
- if (key)
- *key = keyIndex/double(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
- if (value)
- *value = valueIndex/double(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
- }
- bool QCPColorMapData::createAlpha(bool initializeOpaque)
- {
- clearAlpha();
- if (isEmpty())
- return false;
- #ifdef __EXCEPTIONS
- try {
- #endif
- mAlpha = new unsigned char[size_t(mKeySize*mValueSize)];
- #ifdef __EXCEPTIONS
- } catch (...) { mAlpha = nullptr; }
- #endif
- if (mAlpha)
- {
- if (initializeOpaque)
- fillAlpha(255);
- return true;
- } else
- {
- qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
- return false;
- }
- }
- QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) :
- QCPAbstractPlottable(keyAxis, valueAxis),
- mDataScaleType(QCPAxis::stLinear),
- mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
- mGradient(QCPColorGradient::gpCold),
- mInterpolate(true),
- mTightBoundary(false),
- mMapImageInvalidated(true)
- {
- }
- QCPColorMap::~QCPColorMap()
- {
- delete mMapData;
- }
- void QCPColorMap::setData(QCPColorMapData *data, bool copy)
- {
- if (mMapData == data)
- {
- qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
- return;
- }
- if (copy)
- {
- *mMapData = *data;
- } else
- {
- delete mMapData;
- mMapData = data;
- }
- mMapImageInvalidated = true;
- }
- void QCPColorMap::setDataRange(const QCPRange &dataRange)
- {
- if (!QCPRange::validRange(dataRange)) return;
- if (!qFuzzyCompare(mDataRange.lower, dataRange.lower) || !qFuzzyCompare(mDataRange.upper, dataRange.upper))
- {
- if (mDataScaleType == QCPAxis::stLogarithmic)
- mDataRange = dataRange.sanitizedForLogScale();
- else
- mDataRange = dataRange.sanitizedForLinScale();
- mMapImageInvalidated = true;
- emit dataRangeChanged(mDataRange);
- }
- }
- void QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType)
- {
- if (mDataScaleType != scaleType)
- {
- mDataScaleType = scaleType;
- mMapImageInvalidated = true;
- emit dataScaleTypeChanged(mDataScaleType);
- if (mDataScaleType == QCPAxis::stLogarithmic)
- setDataRange(mDataRange.sanitizedForLogScale());
- }
- }
- void QCPColorMap::setGradient(const QCPColorGradient &gradient)
- {
- if (mGradient != gradient)
- {
- mGradient = gradient;
- mMapImageInvalidated = true;
- emit gradientChanged(mGradient);
- }
- }
- void QCPColorMap::setInterpolate(bool enabled)
- {
- mInterpolate = enabled;
- mMapImageInvalidated = true;
- }
- void QCPColorMap::setTightBoundary(bool enabled)
- {
- mTightBoundary = enabled;
- }
- void QCPColorMap::setColorScale(QCPColorScale *colorScale)
- {
- if (mColorScale)
- {
- disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
- disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
- disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
- disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
- disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
- disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
- }
- mColorScale = colorScale;
- if (mColorScale)
- {
- setGradient(mColorScale.data()->gradient());
- setDataRange(mColorScale.data()->dataRange());
- setDataScaleType(mColorScale.data()->dataScaleType());
- connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
- connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
- connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
- connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
- connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
- connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
- }
- }
- void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
- {
- if (recalculateDataBounds)
- mMapData->recalculateDataBounds();
- setDataRange(mMapData->dataBounds());
- }
- void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
- {
- if (mMapImage.isNull() && !data()->isEmpty())
- updateMapImage();
- if (!mMapImage.isNull())
- {
- bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
- bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
- mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
- }
- }
- double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if ((onlySelectable && mSelectable == QCP::stNone) || mMapData->isEmpty())
- return -1;
- if (!mKeyAxis || !mValueAxis)
- return -1;
- if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
- {
- double posKey, posValue;
- pixelsToCoords(pos, posKey, posValue);
- if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
- {
- if (details)
- details->setValue(QCPDataSelection(QCPDataRange(0, 1)));
- return mParentPlot->selectionTolerance()*0.99;
- }
- }
- return -1;
- }
- QCPRange QCPColorMap::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
- {
- foundRange = true;
- QCPRange result = mMapData->keyRange();
- result.normalize();
- if (inSignDomain == QCP::sdPositive)
- {
- if (result.lower <= 0 && result.upper > 0)
- result.lower = result.upper*1e-3;
- else if (result.lower <= 0 && result.upper <= 0)
- foundRange = false;
- } else if (inSignDomain == QCP::sdNegative)
- {
- if (result.upper >= 0 && result.lower < 0)
- result.upper = result.lower*1e-3;
- else if (result.upper >= 0 && result.lower >= 0)
- foundRange = false;
- }
- return result;
- }
- QCPRange QCPColorMap::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
- {
- if (inKeyRange != QCPRange())
- {
- if (mMapData->keyRange().upper < inKeyRange.lower || mMapData->keyRange().lower > inKeyRange.upper)
- {
- foundRange = false;
- return {};
- }
- }
- foundRange = true;
- QCPRange result = mMapData->valueRange();
- result.normalize();
- if (inSignDomain == QCP::sdPositive)
- {
- if (result.lower <= 0 && result.upper > 0)
- result.lower = result.upper*1e-3;
- else if (result.lower <= 0 && result.upper <= 0)
- foundRange = false;
- } else if (inSignDomain == QCP::sdNegative)
- {
- if (result.upper >= 0 && result.lower < 0)
- result.upper = result.lower*1e-3;
- else if (result.upper >= 0 && result.lower >= 0)
- foundRange = false;
- }
- return result;
- }
- void QCPColorMap::updateMapImage()
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- if (!keyAxis) return;
- if (mMapData->isEmpty()) return;
- const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
- const int keySize = mMapData->keySize();
- const int valueSize = mMapData->valueSize();
- int keyOversamplingFactor = mInterpolate ? 1 : int(1.0+100.0/double(keySize));
- int valueOversamplingFactor = mInterpolate ? 1 : int(1.0+100.0/double(valueSize));
-
- if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
- mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), format);
- else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))
- mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), format);
- if (mMapImage.isNull())
- {
- qDebug() << Q_FUNC_INFO << "Couldn't create map image (possibly too large for memory)";
- mMapImage = QImage(QSize(10, 10), format);
- mMapImage.fill(Qt::black);
- } else
- {
- QImage *localMapImage = &mMapImage;
- if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
- {
-
- if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))
- mUndersampledMapImage = QImage(QSize(keySize, valueSize), format);
- else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))
- mUndersampledMapImage = QImage(QSize(valueSize, keySize), format);
- localMapImage = &mUndersampledMapImage;
- } else if (!mUndersampledMapImage.isNull())
- mUndersampledMapImage = QImage();
- const double *rawData = mMapData->mData;
- const unsigned char *rawAlpha = mMapData->mAlpha;
- if (keyAxis->orientation() == Qt::Horizontal)
- {
- const int lineCount = valueSize;
- const int rowCount = keySize;
- for (int line=0; line<lineCount; ++line)
- {
- QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line));
- if (rawAlpha)
- mGradient.colorize(rawData+line*rowCount, rawAlpha+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
- else
- mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
- }
- } else
- {
- const int lineCount = keySize;
- const int rowCount = valueSize;
- for (int line=0; line<lineCount; ++line)
- {
- QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line));
- if (rawAlpha)
- mGradient.colorize(rawData+line, rawAlpha+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
- else
- mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
- }
- }
- if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
- {
- if (keyAxis->orientation() == Qt::Horizontal)
- mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
- else
- mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
- }
- }
- mMapData->mDataModified = false;
- mMapImageInvalidated = false;
- }
- void QCPColorMap::draw(QCPPainter *painter)
- {
- if (mMapData->isEmpty()) return;
- if (!mKeyAxis || !mValueAxis) return;
- applyDefaultAntialiasingHint(painter);
- if (mMapData->mDataModified || mMapImageInvalidated)
- updateMapImage();
-
- const bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
- QCPPainter *localPainter = painter;
- QRectF mapBufferTarget;
- QPixmap mapBuffer;
- if (useBuffer)
- {
- const double mapBufferPixelRatio = 3;
- mapBufferTarget = painter->clipRegion().boundingRect();
- mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());
- mapBuffer.fill(Qt::transparent);
- localPainter = new QCPPainter(&mapBuffer);
- localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
- localPainter->translate(-mapBufferTarget.topLeft());
- }
- QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
- coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();
-
- double halfCellWidth = 0;
- double halfCellHeight = 0;
- if (keyAxis()->orientation() == Qt::Horizontal)
- {
- if (mMapData->keySize() > 1)
- halfCellWidth = 0.5*imageRect.width()/double(mMapData->keySize()-1);
- if (mMapData->valueSize() > 1)
- halfCellHeight = 0.5*imageRect.height()/double(mMapData->valueSize()-1);
- } else
- {
- if (mMapData->keySize() > 1)
- halfCellHeight = 0.5*imageRect.height()/double(mMapData->keySize()-1);
- if (mMapData->valueSize() > 1)
- halfCellWidth = 0.5*imageRect.width()/double(mMapData->valueSize()-1);
- }
- imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
- const bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
- const bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
- const bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
- localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
- QRegion clipBackup;
- if (mTightBoundary)
- {
- clipBackup = localPainter->clipRegion();
- QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
- coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();
- localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
- }
- localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
- if (mTightBoundary)
- localPainter->setClipRegion(clipBackup);
- localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
- if (useBuffer)
- {
- delete localPainter;
- painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
- }
- }
- void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
- {
- applyDefaultAntialiasingHint(painter);
-
- if (!mLegendIcon.isNull())
- {
- QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
- QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
- iconRect.moveCenter(rect.center());
- painter->drawPixmap(iconRect.topLeft(), scaledIcon);
- }
-
- }
- QCPFinancialData::QCPFinancialData() :
- key(0),
- open(0),
- high(0),
- low(0),
- close(0)
- {
- }
- QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
- key(key),
- open(open),
- high(high),
- low(low),
- close(close)
- {
- }
- QCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis) :
- QCPAbstractPlottable1D<QCPFinancialData>(keyAxis, valueAxis),
- mChartStyle(csCandlestick),
- mWidth(0.5),
- mWidthType(wtPlotCoords),
- mTwoColored(true),
- mBrushPositive(QBrush(QColor(50, 160, 0))),
- mBrushNegative(QBrush(QColor(180, 0, 15))),
- mPenPositive(QPen(QColor(40, 150, 0))),
- mPenNegative(QPen(QColor(170, 5, 5)))
- {
- mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
- }
- QCPFinancial::~QCPFinancial()
- {
- }
- void QCPFinancial::setData(QSharedPointer<QCPFinancialDataContainer> data)
- {
- mDataContainer = data;
- }
- void QCPFinancial::setData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted)
- {
- mDataContainer->clear();
- addData(keys, open, high, low, close, alreadySorted);
- }
- void QCPFinancial::setChartStyle(QCPFinancial::ChartStyle style)
- {
- mChartStyle = style;
- }
- void QCPFinancial::setWidth(double width)
- {
- mWidth = width;
- }
- void QCPFinancial::setWidthType(QCPFinancial::WidthType widthType)
- {
- mWidthType = widthType;
- }
- void QCPFinancial::setTwoColored(bool twoColored)
- {
- mTwoColored = twoColored;
- }
- void QCPFinancial::setBrushPositive(const QBrush &brush)
- {
- mBrushPositive = brush;
- }
- void QCPFinancial::setBrushNegative(const QBrush &brush)
- {
- mBrushNegative = brush;
- }
- void QCPFinancial::setPenPositive(const QPen &pen)
- {
- mPenPositive = pen;
- }
- void QCPFinancial::setPenNegative(const QPen &pen)
- {
- mPenNegative = pen;
- }
- void QCPFinancial::addData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted)
- {
- if (keys.size() != open.size() || open.size() != high.size() || high.size() != low.size() || low.size() != close.size() || close.size() != keys.size())
- qDebug() << Q_FUNC_INFO << "keys, open, high, low, close have different sizes:" << keys.size() << open.size() << high.size() << low.size() << close.size();
- const int n = qMin(keys.size(), qMin(open.size(), qMin(high.size(), qMin(low.size(), close.size()))));
- QVector<QCPFinancialData> tempData(n);
- QVector<QCPFinancialData>::iterator it = tempData.begin();
- const QVector<QCPFinancialData>::iterator itEnd = tempData.end();
- int i = 0;
- while (it != itEnd)
- {
- it->key = keys[i];
- it->open = open[i];
- it->high = high[i];
- it->low = low[i];
- it->close = close[i];
- ++it;
- ++i;
- }
- mDataContainer->add(tempData, alreadySorted);
- }
- void QCPFinancial::addData(double key, double open, double high, double low, double close)
- {
- mDataContainer->add(QCPFinancialData(key, open, high, low, close));
- }
- QCPDataSelection QCPFinancial::selectTestRect(const QRectF &rect, bool onlySelectable) const
- {
- QCPDataSelection result;
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return result;
- if (!mKeyAxis || !mValueAxis)
- return result;
- QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
- getVisibleDataBounds(visibleBegin, visibleEnd);
- for (QCPFinancialDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
- {
- if (rect.intersects(selectionHitBox(it)))
- result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);
- }
- result.simplify();
- return result;
- }
- double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return -1;
- if (!mKeyAxis || !mValueAxis)
- return -1;
- if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
- {
-
- QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
- QCPFinancialDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
- getVisibleDataBounds(visibleBegin, visibleEnd);
-
- double result = -1;
- switch (mChartStyle)
- {
- case QCPFinancial::csOhlc:
- result = ohlcSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break;
- case QCPFinancial::csCandlestick:
- result = candlestickSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break;
- }
- if (details)
- {
- int pointIndex = int(closestDataPoint-mDataContainer->constBegin());
- details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
- }
- return result;
- }
- return -1;
- }
- QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
- {
- QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);
-
- if (foundRange)
- {
- if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0)
- range.lower -= mWidth*0.5;
- if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0)
- range.upper += mWidth*0.5;
- }
- return range;
- }
- QCPRange QCPFinancial::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
- {
- return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
- }
- QCPFinancialDataContainer QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
- {
- QCPFinancialDataContainer data;
- int count = qMin(time.size(), value.size());
- if (count == 0)
- return QCPFinancialDataContainer();
- QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
- int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
- for (int i=0; i<count; ++i)
- {
- int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
- if (currentBinIndex == index)
- {
- if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
- if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
- if (i == count-1)
- {
- currentBinData.close = value.at(i);
- currentBinData.key = timeBinOffset+(index)*timeBinSize;
- data.add(currentBinData);
- }
- } else
- {
-
- currentBinData.close = value.at(i-1);
- currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
- data.add(currentBinData);
-
- currentBinIndex = index;
- currentBinData.open = value.at(i);
- currentBinData.high = value.at(i);
- currentBinData.low = value.at(i);
- }
- }
- return data;
- }
- void QCPFinancial::draw(QCPPainter *painter)
- {
-
- QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
- getVisibleDataBounds(visibleBegin, visibleEnd);
-
- QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
- getDataSegments(selectedSegments, unselectedSegments);
- allSegments << unselectedSegments << selectedSegments;
- for (int i=0; i<allSegments.size(); ++i)
- {
- bool isSelectedSegment = i >= unselectedSegments.size();
- QCPFinancialDataContainer::const_iterator begin = visibleBegin;
- QCPFinancialDataContainer::const_iterator end = visibleEnd;
- mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
- if (begin == end)
- continue;
-
- switch (mChartStyle)
- {
- case QCPFinancial::csOhlc:
- drawOhlcPlot(painter, begin, end, isSelectedSegment); break;
- case QCPFinancial::csCandlestick:
- drawCandlestickPlot(painter, begin, end, isSelectedSegment); break;
- }
- }
-
- if (mSelectionDecorator)
- mSelectionDecorator->drawDecoration(painter, selection());
- }
- void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
- {
- painter->setAntialiasing(false);
- if (mChartStyle == csOhlc)
- {
- if (mTwoColored)
- {
-
- painter->setBrush(mBrushPositive);
- painter->setPen(mPenPositive);
- painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
- painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
- painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
- painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
-
- painter->setBrush(mBrushNegative);
- painter->setPen(mPenNegative);
- painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
- painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
- painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
- painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
- } else
- {
- painter->setBrush(mBrush);
- painter->setPen(mPen);
- painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
- painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
- painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
- }
- } else if (mChartStyle == csCandlestick)
- {
- if (mTwoColored)
- {
-
- painter->setBrush(mBrushPositive);
- painter->setPen(mPenPositive);
- painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
- painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
- painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
- painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
-
- painter->setBrush(mBrushNegative);
- painter->setPen(mPenNegative);
- painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
- painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
- painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
- painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
- } else
- {
- painter->setBrush(mBrush);
- painter->setPen(mPen);
- painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
- painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
- painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
- }
- }
- }
- void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- if (keyAxis->orientation() == Qt::Horizontal)
- {
- for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
- {
- if (isSelected && mSelectionDecorator)
- mSelectionDecorator->applyPen(painter);
- else if (mTwoColored)
- painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
- else
- painter->setPen(mPen);
- double keyPixel = keyAxis->coordToPixel(it->key);
- double openPixel = valueAxis->coordToPixel(it->open);
- double closePixel = valueAxis->coordToPixel(it->close);
-
- painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(it->low)));
-
- double pixelWidth = getPixelWidth(it->key, keyPixel);
- painter->drawLine(QPointF(keyPixel-pixelWidth, openPixel), QPointF(keyPixel, openPixel));
-
- painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+pixelWidth, closePixel));
- }
- } else
- {
- for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
- {
- if (isSelected && mSelectionDecorator)
- mSelectionDecorator->applyPen(painter);
- else if (mTwoColored)
- painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
- else
- painter->setPen(mPen);
- double keyPixel = keyAxis->coordToPixel(it->key);
- double openPixel = valueAxis->coordToPixel(it->open);
- double closePixel = valueAxis->coordToPixel(it->close);
-
- painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(it->low), keyPixel));
-
- double pixelWidth = getPixelWidth(it->key, keyPixel);
- painter->drawLine(QPointF(openPixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel));
-
- painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+pixelWidth));
- }
- }
- }
- void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- if (keyAxis->orientation() == Qt::Horizontal)
- {
- for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
- {
- if (isSelected && mSelectionDecorator)
- {
- mSelectionDecorator->applyPen(painter);
- mSelectionDecorator->applyBrush(painter);
- } else if (mTwoColored)
- {
- painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
- painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative);
- } else
- {
- painter->setPen(mPen);
- painter->setBrush(mBrush);
- }
- double keyPixel = keyAxis->coordToPixel(it->key);
- double openPixel = valueAxis->coordToPixel(it->open);
- double closePixel = valueAxis->coordToPixel(it->close);
-
- painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close))));
-
- painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close))));
-
- double pixelWidth = getPixelWidth(it->key, keyPixel);
- painter->drawRect(QRectF(QPointF(keyPixel-pixelWidth, closePixel), QPointF(keyPixel+pixelWidth, openPixel)));
- }
- } else
- {
- for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
- {
- if (isSelected && mSelectionDecorator)
- {
- mSelectionDecorator->applyPen(painter);
- mSelectionDecorator->applyBrush(painter);
- } else if (mTwoColored)
- {
- painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
- painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative);
- } else
- {
- painter->setPen(mPen);
- painter->setBrush(mBrush);
- }
- double keyPixel = keyAxis->coordToPixel(it->key);
- double openPixel = valueAxis->coordToPixel(it->open);
- double closePixel = valueAxis->coordToPixel(it->close);
-
- painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel));
-
- painter->drawLine(QPointF(valueAxis->coordToPixel(it->low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel));
-
- double pixelWidth = getPixelWidth(it->key, keyPixel);
- painter->drawRect(QRectF(QPointF(closePixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel+pixelWidth)));
- }
- }
- }
- double QCPFinancial::getPixelWidth(double key, double keyPixel) const
- {
- double result = 0;
- switch (mWidthType)
- {
- case wtAbsolute:
- {
- if (mKeyAxis)
- result = mWidth*0.5*mKeyAxis.data()->pixelOrientation();
- break;
- }
- case wtAxisRectRatio:
- {
- if (mKeyAxis && mKeyAxis.data()->axisRect())
- {
- if (mKeyAxis.data()->orientation() == Qt::Horizontal)
- result = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
- else
- result = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
- } else
- qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
- break;
- }
- case wtPlotCoords:
- {
- if (mKeyAxis)
- result = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
- else
- qDebug() << Q_FUNC_INFO << "No key axis defined";
- break;
- }
- }
- return result;
- }
- double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const
- {
- closestDataPoint = mDataContainer->constEnd();
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
- double minDistSqr = (std::numeric_limits<double>::max)();
- if (keyAxis->orientation() == Qt::Horizontal)
- {
- for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
- {
- double keyPixel = keyAxis->coordToPixel(it->key);
-
- double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)));
- if (currentDistSqr < minDistSqr)
- {
- minDistSqr = currentDistSqr;
- closestDataPoint = it;
- }
- }
- } else
- {
- for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
- {
- double keyPixel = keyAxis->coordToPixel(it->key);
-
- double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel));
- if (currentDistSqr < minDistSqr)
- {
- minDistSqr = currentDistSqr;
- closestDataPoint = it;
- }
- }
- }
- return qSqrt(minDistSqr);
- }
- double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const
- {
- closestDataPoint = mDataContainer->constEnd();
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
- double minDistSqr = (std::numeric_limits<double>::max)();
- if (keyAxis->orientation() == Qt::Horizontal)
- {
- for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
- {
- double currentDistSqr;
-
- QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5);
- QCPRange boxValueRange(it->close, it->open);
- double posKey, posValue;
- pixelsToCoords(pos, posKey, posValue);
- if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue))
- {
- currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
- } else
- {
-
- double keyPixel = keyAxis->coordToPixel(it->key);
- double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close))));
- double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close))));
- currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
- }
- if (currentDistSqr < minDistSqr)
- {
- minDistSqr = currentDistSqr;
- closestDataPoint = it;
- }
- }
- } else
- {
- for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
- {
- double currentDistSqr;
-
- QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5);
- QCPRange boxValueRange(it->close, it->open);
- double posKey, posValue;
- pixelsToCoords(pos, posKey, posValue);
- if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue))
- {
- currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
- } else
- {
-
- double keyPixel = keyAxis->coordToPixel(it->key);
- double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel));
- double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel));
- currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
- }
- if (currentDistSqr < minDistSqr)
- {
- minDistSqr = currentDistSqr;
- closestDataPoint = it;
- }
- }
- }
- return qSqrt(minDistSqr);
- }
- void QCPFinancial::getVisibleDataBounds(QCPFinancialDataContainer::const_iterator &begin, QCPFinancialDataContainer::const_iterator &end) const
- {
- if (!mKeyAxis)
- {
- qDebug() << Q_FUNC_INFO << "invalid key axis";
- begin = mDataContainer->constEnd();
- end = mDataContainer->constEnd();
- return;
- }
- begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5);
- end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5);
- }
- QRectF QCPFinancial::selectionHitBox(QCPFinancialDataContainer::const_iterator it) const
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; }
- double keyPixel = keyAxis->coordToPixel(it->key);
- double highPixel = valueAxis->coordToPixel(it->high);
- double lowPixel = valueAxis->coordToPixel(it->low);
- double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it->key-mWidth*0.5);
- if (keyAxis->orientation() == Qt::Horizontal)
- return QRectF(keyPixel-keyWidthPixels, highPixel, keyWidthPixels*2, lowPixel-highPixel).normalized();
- else
- return QRectF(highPixel, keyPixel-keyWidthPixels, lowPixel-highPixel, keyWidthPixels*2).normalized();
- }
- QCPErrorBarsData::QCPErrorBarsData() :
- errorMinus(0),
- errorPlus(0)
- {
- }
- QCPErrorBarsData::QCPErrorBarsData(double error) :
- errorMinus(error),
- errorPlus(error)
- {
- }
- QCPErrorBarsData::QCPErrorBarsData(double errorMinus, double errorPlus) :
- errorMinus(errorMinus),
- errorPlus(errorPlus)
- {
- }
- QCPErrorBars::QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
- QCPAbstractPlottable(keyAxis, valueAxis),
- mDataContainer(new QVector<QCPErrorBarsData>),
- mErrorType(etValueError),
- mWhiskerWidth(9),
- mSymbolGap(10)
- {
- setPen(QPen(Qt::black, 0));
- setBrush(Qt::NoBrush);
- }
- QCPErrorBars::~QCPErrorBars()
- {
- }
- void QCPErrorBars::setData(QSharedPointer<QCPErrorBarsDataContainer> data)
- {
- mDataContainer = data;
- }
- void QCPErrorBars::setData(const QVector<double> &error)
- {
- mDataContainer->clear();
- addData(error);
- }
- void QCPErrorBars::setData(const QVector<double> &errorMinus, const QVector<double> &errorPlus)
- {
- mDataContainer->clear();
- addData(errorMinus, errorPlus);
- }
- void QCPErrorBars::setDataPlottable(QCPAbstractPlottable *plottable)
- {
- if (plottable && qobject_cast<QCPErrorBars*>(plottable))
- {
- mDataPlottable = nullptr;
- qDebug() << Q_FUNC_INFO << "can't set another QCPErrorBars instance as data plottable";
- return;
- }
- if (plottable && !plottable->interface1D())
- {
- mDataPlottable = nullptr;
- qDebug() << Q_FUNC_INFO << "passed plottable doesn't implement 1d interface, can't associate with QCPErrorBars";
- return;
- }
- mDataPlottable = plottable;
- }
- void QCPErrorBars::setErrorType(ErrorType type)
- {
- mErrorType = type;
- }
- void QCPErrorBars::setWhiskerWidth(double pixels)
- {
- mWhiskerWidth = pixels;
- }
- void QCPErrorBars::setSymbolGap(double pixels)
- {
- mSymbolGap = pixels;
- }
- void QCPErrorBars::addData(const QVector<double> &error)
- {
- addData(error, error);
- }
- void QCPErrorBars::addData(const QVector<double> &errorMinus, const QVector<double> &errorPlus)
- {
- if (errorMinus.size() != errorPlus.size())
- qDebug() << Q_FUNC_INFO << "minus and plus error vectors have different sizes:" << errorMinus.size() << errorPlus.size();
- const int n = qMin(errorMinus.size(), errorPlus.size());
- mDataContainer->reserve(n);
- for (int i=0; i<n; ++i)
- mDataContainer->append(QCPErrorBarsData(errorMinus.at(i), errorPlus.at(i)));
- }
- void QCPErrorBars::addData(double error)
- {
- mDataContainer->append(QCPErrorBarsData(error));
- }
- void QCPErrorBars::addData(double errorMinus, double errorPlus)
- {
- mDataContainer->append(QCPErrorBarsData(errorMinus, errorPlus));
- }
- int QCPErrorBars::dataCount() const
- {
- return mDataContainer->size();
- }
- double QCPErrorBars::dataMainKey(int index) const
- {
- if (mDataPlottable)
- return mDataPlottable->interface1D()->dataMainKey(index);
- else
- qDebug() << Q_FUNC_INFO << "no data plottable set";
- return 0;
- }
- double QCPErrorBars::dataSortKey(int index) const
- {
- if (mDataPlottable)
- return mDataPlottable->interface1D()->dataSortKey(index);
- else
- qDebug() << Q_FUNC_INFO << "no data plottable set";
- return 0;
- }
- double QCPErrorBars::dataMainValue(int index) const
- {
- if (mDataPlottable)
- return mDataPlottable->interface1D()->dataMainValue(index);
- else
- qDebug() << Q_FUNC_INFO << "no data plottable set";
- return 0;
- }
- QCPRange QCPErrorBars::dataValueRange(int index) const
- {
- if (mDataPlottable)
- {
- const double value = mDataPlottable->interface1D()->dataMainValue(index);
- if (index >= 0 && index < mDataContainer->size() && mErrorType == etValueError)
- return {value-mDataContainer->at(index).errorMinus, value+mDataContainer->at(index).errorPlus};
- else
- return {value, value};
- } else
- {
- qDebug() << Q_FUNC_INFO << "no data plottable set";
- return {};
- }
- }
- QPointF QCPErrorBars::dataPixelPosition(int index) const
- {
- if (mDataPlottable)
- return mDataPlottable->interface1D()->dataPixelPosition(index);
- else
- qDebug() << Q_FUNC_INFO << "no data plottable set";
- return {};
- }
- bool QCPErrorBars::sortKeyIsMainKey() const
- {
- if (mDataPlottable)
- {
- return mDataPlottable->interface1D()->sortKeyIsMainKey();
- } else
- {
- qDebug() << Q_FUNC_INFO << "no data plottable set";
- return true;
- }
- }
- QCPDataSelection QCPErrorBars::selectTestRect(const QRectF &rect, bool onlySelectable) const
- {
- QCPDataSelection result;
- if (!mDataPlottable)
- return result;
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return result;
- if (!mKeyAxis || !mValueAxis)
- return result;
- QCPErrorBarsDataContainer::const_iterator visibleBegin, visibleEnd;
- getVisibleDataBounds(visibleBegin, visibleEnd, QCPDataRange(0, dataCount()));
- QVector<QLineF> backbones, whiskers;
- for (QCPErrorBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
- {
- backbones.clear();
- whiskers.clear();
- getErrorBarLines(it, backbones, whiskers);
- foreach (const QLineF &backbone, backbones)
- {
- if (rectIntersectsLine(rect, backbone))
- {
- result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);
- break;
- }
- }
- }
- result.simplify();
- return result;
- }
- int QCPErrorBars::findBegin(double sortKey, bool expandedRange) const
- {
- if (mDataPlottable)
- {
- if (mDataContainer->isEmpty())
- return 0;
- int beginIndex = mDataPlottable->interface1D()->findBegin(sortKey, expandedRange);
- if (beginIndex >= mDataContainer->size())
- beginIndex = mDataContainer->size()-1;
- return beginIndex;
- } else
- qDebug() << Q_FUNC_INFO << "no data plottable set";
- return 0;
- }
- int QCPErrorBars::findEnd(double sortKey, bool expandedRange) const
- {
- if (mDataPlottable)
- {
- if (mDataContainer->isEmpty())
- return 0;
- int endIndex = mDataPlottable->interface1D()->findEnd(sortKey, expandedRange);
- if (endIndex > mDataContainer->size())
- endIndex = mDataContainer->size();
- return endIndex;
- } else
- qDebug() << Q_FUNC_INFO << "no data plottable set";
- return 0;
- }
- double QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- if (!mDataPlottable) return -1;
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return -1;
- if (!mKeyAxis || !mValueAxis)
- return -1;
- if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
- {
- QCPErrorBarsDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
- double result = pointDistance(pos, closestDataPoint);
- if (details)
- {
- int pointIndex = int(closestDataPoint-mDataContainer->constBegin());
- details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
- }
- return result;
- } else
- return -1;
- }
- void QCPErrorBars::draw(QCPPainter *painter)
- {
- if (!mDataPlottable) return;
- if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
-
-
- bool checkPointVisibility = !mDataPlottable->interface1D()->sortKeyIsMainKey();
-
- #ifdef QCUSTOMPLOT_CHECK_DATA
- QCPErrorBarsDataContainer::const_iterator it;
- for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
- {
- if (QCP::isInvalidData(it->errorMinus, it->errorPlus))
- qDebug() << Q_FUNC_INFO << "Data point at index" << it-mDataContainer->constBegin() << "invalid." << "Plottable name:" << name();
- }
- #endif
- applyDefaultAntialiasingHint(painter);
- painter->setBrush(Qt::NoBrush);
-
- QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
- getDataSegments(selectedSegments, unselectedSegments);
- allSegments << unselectedSegments << selectedSegments;
- QVector<QLineF> backbones, whiskers;
- for (int i=0; i<allSegments.size(); ++i)
- {
- QCPErrorBarsDataContainer::const_iterator begin, end;
- getVisibleDataBounds(begin, end, allSegments.at(i));
- if (begin == end)
- continue;
- bool isSelectedSegment = i >= unselectedSegments.size();
- if (isSelectedSegment && mSelectionDecorator)
- mSelectionDecorator->applyPen(painter);
- else
- painter->setPen(mPen);
- if (painter->pen().capStyle() == Qt::SquareCap)
- {
- QPen capFixPen(painter->pen());
- capFixPen.setCapStyle(Qt::FlatCap);
- painter->setPen(capFixPen);
- }
- backbones.clear();
- whiskers.clear();
- for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)
- {
- if (!checkPointVisibility || errorBarVisible(int(it-mDataContainer->constBegin())))
- getErrorBarLines(it, backbones, whiskers);
- }
- painter->drawLines(backbones);
- painter->drawLines(whiskers);
- }
-
- if (mSelectionDecorator)
- mSelectionDecorator->drawDecoration(painter, selection());
- }
- void QCPErrorBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
- {
- applyDefaultAntialiasingHint(painter);
- painter->setPen(mPen);
- if (mErrorType == etValueError && mValueAxis && mValueAxis->orientation() == Qt::Vertical)
- {
- painter->drawLine(QLineF(rect.center().x(), rect.top()+2, rect.center().x(), rect.bottom()-1));
- painter->drawLine(QLineF(rect.center().x()-4, rect.top()+2, rect.center().x()+4, rect.top()+2));
- painter->drawLine(QLineF(rect.center().x()-4, rect.bottom()-1, rect.center().x()+4, rect.bottom()-1));
- } else
- {
- painter->drawLine(QLineF(rect.left()+2, rect.center().y(), rect.right()-2, rect.center().y()));
- painter->drawLine(QLineF(rect.left()+2, rect.center().y()-4, rect.left()+2, rect.center().y()+4));
- painter->drawLine(QLineF(rect.right()-2, rect.center().y()-4, rect.right()-2, rect.center().y()+4));
- }
- }
- QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
- {
- if (!mDataPlottable)
- {
- foundRange = false;
- return {};
- }
- QCPRange range;
- bool haveLower = false;
- bool haveUpper = false;
- QCPErrorBarsDataContainer::const_iterator it;
- for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
- {
- if (mErrorType == etValueError)
- {
-
- const double current = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin()));
- if (qIsNaN(current)) continue;
- if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
- {
- if (current < range.lower || !haveLower)
- {
- range.lower = current;
- haveLower = true;
- }
- if (current > range.upper || !haveUpper)
- {
- range.upper = current;
- haveUpper = true;
- }
- }
- } else
- {
- const double dataKey = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin()));
- if (qIsNaN(dataKey)) continue;
-
- double current = dataKey + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
- if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
- {
- if (current > range.upper || !haveUpper)
- {
- range.upper = current;
- haveUpper = true;
- }
- }
-
- current = dataKey - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);
- if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
- {
- if (current < range.lower || !haveLower)
- {
- range.lower = current;
- haveLower = true;
- }
- }
- }
- }
- if (haveUpper && !haveLower)
- {
- range.lower = range.upper;
- haveLower = true;
- } else if (haveLower && !haveUpper)
- {
- range.upper = range.lower;
- haveUpper = true;
- }
- foundRange = haveLower && haveUpper;
- return range;
- }
- QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
- {
- if (!mDataPlottable)
- {
- foundRange = false;
- return {};
- }
- QCPRange range;
- const bool restrictKeyRange = inKeyRange != QCPRange();
- bool haveLower = false;
- bool haveUpper = false;
- QCPErrorBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();
- QCPErrorBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd();
- if (mDataPlottable->interface1D()->sortKeyIsMainKey() && restrictKeyRange)
- {
- itBegin = mDataContainer->constBegin()+findBegin(inKeyRange.lower, false);
- itEnd = mDataContainer->constBegin()+findEnd(inKeyRange.upper, false);
- }
- for (QCPErrorBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)
- {
- if (restrictKeyRange)
- {
- const double dataKey = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin()));
- if (dataKey < inKeyRange.lower || dataKey > inKeyRange.upper)
- continue;
- }
- if (mErrorType == etValueError)
- {
- const double dataValue = mDataPlottable->interface1D()->dataMainValue(int(it-mDataContainer->constBegin()));
- if (qIsNaN(dataValue)) continue;
-
- double current = dataValue + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
- if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
- {
- if (current > range.upper || !haveUpper)
- {
- range.upper = current;
- haveUpper = true;
- }
- }
-
- current = dataValue - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);
- if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
- {
- if (current < range.lower || !haveLower)
- {
- range.lower = current;
- haveLower = true;
- }
- }
- } else
- {
-
- const double current = mDataPlottable->interface1D()->dataMainValue(int(it-mDataContainer->constBegin()));
- if (qIsNaN(current)) continue;
- if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
- {
- if (current < range.lower || !haveLower)
- {
- range.lower = current;
- haveLower = true;
- }
- if (current > range.upper || !haveUpper)
- {
- range.upper = current;
- haveUpper = true;
- }
- }
- }
- }
- if (haveUpper && !haveLower)
- {
- range.lower = range.upper;
- haveLower = true;
- } else if (haveLower && !haveUpper)
- {
- range.upper = range.lower;
- haveUpper = true;
- }
- foundRange = haveLower && haveUpper;
- return range;
- }
- void QCPErrorBars::getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector<QLineF> &backbones, QVector<QLineF> &whiskers) const
- {
- if (!mDataPlottable) return;
- int index = int(it-mDataContainer->constBegin());
- QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);
- if (qIsNaN(centerPixel.x()) || qIsNaN(centerPixel.y()))
- return;
- QCPAxis *errorAxis = mErrorType == etValueError ? mValueAxis.data() : mKeyAxis.data();
- QCPAxis *orthoAxis = mErrorType == etValueError ? mKeyAxis.data() : mValueAxis.data();
- const double centerErrorAxisPixel = errorAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
- const double centerOrthoAxisPixel = orthoAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
- const double centerErrorAxisCoord = errorAxis->pixelToCoord(centerErrorAxisPixel);
- const double symbolGap = mSymbolGap*0.5*errorAxis->pixelOrientation();
-
- double errorStart, errorEnd;
- if (!qIsNaN(it->errorPlus))
- {
- errorStart = centerErrorAxisPixel+symbolGap;
- errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord+it->errorPlus);
- if (errorAxis->orientation() == Qt::Vertical)
- {
- if ((errorStart > errorEnd) != errorAxis->rangeReversed())
- backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd));
- whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd));
- } else
- {
- if ((errorStart < errorEnd) != errorAxis->rangeReversed())
- backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel));
- whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5));
- }
- }
-
- if (!qIsNaN(it->errorMinus))
- {
- errorStart = centerErrorAxisPixel-symbolGap;
- errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord-it->errorMinus);
- if (errorAxis->orientation() == Qt::Vertical)
- {
- if ((errorStart < errorEnd) != errorAxis->rangeReversed())
- backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd));
- whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd));
- } else
- {
- if ((errorStart > errorEnd) != errorAxis->rangeReversed())
- backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel));
- whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5));
- }
- }
- }
- void QCPErrorBars::getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
- {
- QCPAxis *keyAxis = mKeyAxis.data();
- QCPAxis *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis)
- {
- qDebug() << Q_FUNC_INFO << "invalid key or value axis";
- end = mDataContainer->constEnd();
- begin = end;
- return;
- }
- if (!mDataPlottable || rangeRestriction.isEmpty())
- {
- end = mDataContainer->constEnd();
- begin = end;
- return;
- }
- if (!mDataPlottable->interface1D()->sortKeyIsMainKey())
- {
-
-
-
- QCPDataRange dataRange(0, mDataContainer->size());
- dataRange = dataRange.bounded(rangeRestriction);
- begin = mDataContainer->constBegin()+dataRange.begin();
- end = mDataContainer->constBegin()+dataRange.end();
- return;
- }
-
- const int n = qMin(mDataContainer->size(), mDataPlottable->interface1D()->dataCount());
- int beginIndex = mDataPlottable->interface1D()->findBegin(keyAxis->range().lower);
- int endIndex = mDataPlottable->interface1D()->findEnd(keyAxis->range().upper);
- int i = beginIndex;
- while (i > 0 && i < n && i > rangeRestriction.begin())
- {
- if (errorBarVisible(i))
- beginIndex = i;
- --i;
- }
- i = endIndex;
- while (i >= 0 && i < n && i < rangeRestriction.end())
- {
- if (errorBarVisible(i))
- endIndex = i+1;
- ++i;
- }
- QCPDataRange dataRange(beginIndex, endIndex);
- dataRange = dataRange.bounded(rangeRestriction.bounded(QCPDataRange(0, mDataContainer->size())));
- begin = mDataContainer->constBegin()+dataRange.begin();
- end = mDataContainer->constBegin()+dataRange.end();
- }
- double QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const
- {
- closestData = mDataContainer->constEnd();
- if (!mDataPlottable || mDataContainer->isEmpty())
- return -1.0;
- if (!mKeyAxis || !mValueAxis)
- {
- qDebug() << Q_FUNC_INFO << "invalid key or value axis";
- return -1.0;
- }
- QCPErrorBarsDataContainer::const_iterator begin, end;
- getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount()));
-
- double minDistSqr = (std::numeric_limits<double>::max)();
- QVector<QLineF> backbones, whiskers;
- for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)
- {
- getErrorBarLines(it, backbones, whiskers);
- foreach (const QLineF &backbone, backbones)
- {
- const double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(backbone);
- if (currentDistSqr < minDistSqr)
- {
- minDistSqr = currentDistSqr;
- closestData = it;
- }
- }
- }
- return qSqrt(minDistSqr);
- }
- void QCPErrorBars::getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const
- {
- selectedSegments.clear();
- unselectedSegments.clear();
- if (mSelectable == QCP::stWhole)
- {
- if (selected())
- selectedSegments << QCPDataRange(0, dataCount());
- else
- unselectedSegments << QCPDataRange(0, dataCount());
- } else
- {
- QCPDataSelection sel(selection());
- sel.simplify();
- selectedSegments = sel.dataRanges();
- unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges();
- }
- }
- bool QCPErrorBars::errorBarVisible(int index) const
- {
- QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);
- const double centerKeyPixel = mKeyAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
- if (qIsNaN(centerKeyPixel))
- return false;
- double keyMin, keyMax;
- if (mErrorType == etKeyError)
- {
- const double centerKey = mKeyAxis->pixelToCoord(centerKeyPixel);
- const double errorPlus = mDataContainer->at(index).errorPlus;
- const double errorMinus = mDataContainer->at(index).errorMinus;
- keyMax = centerKey+(qIsNaN(errorPlus) ? 0 : errorPlus);
- keyMin = centerKey-(qIsNaN(errorMinus) ? 0 : errorMinus);
- } else
- {
- keyMax = mKeyAxis->pixelToCoord(centerKeyPixel+mWhiskerWidth*0.5*mKeyAxis->pixelOrientation());
- keyMin = mKeyAxis->pixelToCoord(centerKeyPixel-mWhiskerWidth*0.5*mKeyAxis->pixelOrientation());
- }
- return ((keyMax > mKeyAxis->range().lower) && (keyMin < mKeyAxis->range().upper));
- }
- bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const
- {
- if (pixelRect.left() > line.x1() && pixelRect.left() > line.x2())
- return false;
- else if (pixelRect.right() < line.x1() && pixelRect.right() < line.x2())
- return false;
- else if (pixelRect.top() > line.y1() && pixelRect.top() > line.y2())
- return false;
- else if (pixelRect.bottom() < line.y1() && pixelRect.bottom() < line.y2())
- return false;
- else
- return true;
- }
- QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) :
- QCPAbstractItem(parentPlot),
- point1(createPosition(QLatin1String("point1"))),
- point2(createPosition(QLatin1String("point2")))
- {
- point1->setCoords(0, 0);
- point2->setCoords(1, 1);
- setPen(QPen(Qt::black));
- setSelectedPen(QPen(Qt::blue,2));
- }
- QCPItemStraightLine::~QCPItemStraightLine()
- {
- }
- void QCPItemStraightLine::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPItemStraightLine::setSelectedPen(const QPen &pen)
- {
- mSelectedPen = pen;
- }
- double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable && !mSelectable)
- return -1;
- return QCPVector2D(pos).distanceToStraightLine(point1->pixelPosition(), point2->pixelPosition()-point1->pixelPosition());
- }
- void QCPItemStraightLine::draw(QCPPainter *painter)
- {
- QCPVector2D start(point1->pixelPosition());
- QCPVector2D end(point2->pixelPosition());
-
- double clipPad = mainPen().widthF();
- QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
-
- if (!line.isNull())
- {
- painter->setPen(mainPen());
- painter->drawLine(line);
- }
- }
- QLineF QCPItemStraightLine::getRectClippedStraightLine(const QCPVector2D &base, const QCPVector2D &vec, const QRectF &rect) const
- {
- double bx, by;
- double gamma;
- QLineF result;
- if (qFuzzyCompare(vec.x(), 0) && qFuzzyCompare(vec.y(), 0))
- return result;
- if (qFuzzyIsNull(vec.x()))
- {
-
- bx = rect.left();
- by = rect.top();
- gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
- if (gamma >= 0 && gamma <= rect.width())
- result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom());
- } else if (qFuzzyIsNull(vec.y()))
- {
-
- bx = rect.left();
- by = rect.top();
- gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
- if (gamma >= 0 && gamma <= rect.height())
- result.setLine(rect.left(), by+gamma, rect.right(), by+gamma);
- } else
- {
- QList<QCPVector2D> pointVectors;
-
- bx = rect.left();
- by = rect.top();
- gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
- if (gamma >= 0 && gamma <= rect.width())
- pointVectors.append(QCPVector2D(bx+gamma, by));
-
- bx = rect.left();
- by = rect.bottom();
- gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
- if (gamma >= 0 && gamma <= rect.width())
- pointVectors.append(QCPVector2D(bx+gamma, by));
-
- bx = rect.left();
- by = rect.top();
- gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
- if (gamma >= 0 && gamma <= rect.height())
- pointVectors.append(QCPVector2D(bx, by+gamma));
-
- bx = rect.right();
- by = rect.top();
- gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
- if (gamma >= 0 && gamma <= rect.height())
- pointVectors.append(QCPVector2D(bx, by+gamma));
-
- if (pointVectors.size() == 2)
- {
- result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
- } else if (pointVectors.size() > 2)
- {
-
- double distSqrMax = 0;
- QCPVector2D pv1, pv2;
- for (int i=0; i<pointVectors.size()-1; ++i)
- {
- for (int k=i+1; k<pointVectors.size(); ++k)
- {
- double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
- if (distSqr > distSqrMax)
- {
- pv1 = pointVectors.at(i);
- pv2 = pointVectors.at(k);
- distSqrMax = distSqr;
- }
- }
- }
- result.setPoints(pv1.toPointF(), pv2.toPointF());
- }
- }
- return result;
- }
- QPen QCPItemStraightLine::mainPen() const
- {
- return mSelected ? mSelectedPen : mPen;
- }
- QCPItemLine::QCPItemLine(QCustomPlot *parentPlot) :
- QCPAbstractItem(parentPlot),
- start(createPosition(QLatin1String("start"))),
- end(createPosition(QLatin1String("end")))
- {
- start->setCoords(0, 0);
- end->setCoords(1, 1);
- setPen(QPen(Qt::black));
- setSelectedPen(QPen(Qt::blue,2));
- }
- QCPItemLine::~QCPItemLine()
- {
- }
- void QCPItemLine::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPItemLine::setSelectedPen(const QPen &pen)
- {
- mSelectedPen = pen;
- }
- void QCPItemLine::setHead(const QCPLineEnding &head)
- {
- mHead = head;
- }
- void QCPItemLine::setTail(const QCPLineEnding &tail)
- {
- mTail = tail;
- }
- double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable && !mSelectable)
- return -1;
- return qSqrt(QCPVector2D(pos).distanceSquaredToLine(start->pixelPosition(), end->pixelPosition()));
- }
- void QCPItemLine::draw(QCPPainter *painter)
- {
- QCPVector2D startVec(start->pixelPosition());
- QCPVector2D endVec(end->pixelPosition());
- if (qFuzzyIsNull((startVec-endVec).lengthSquared()))
- return;
-
- double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
- clipPad = qMax(clipPad, double(mainPen().widthF()));
- QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
-
- if (!line.isNull())
- {
- painter->setPen(mainPen());
- painter->drawLine(line);
- painter->setBrush(Qt::SolidPattern);
- if (mTail.style() != QCPLineEnding::esNone)
- mTail.draw(painter, startVec, startVec-endVec);
- if (mHead.style() != QCPLineEnding::esNone)
- mHead.draw(painter, endVec, endVec-startVec);
- }
- }
- QLineF QCPItemLine::getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRectF &rect) const
- {
- bool containsStart = rect.contains(start.x(), start.y());
- bool containsEnd = rect.contains(end.x(), end.y());
- if (containsStart && containsEnd)
- return {start.toPointF(), end.toPointF()};
- QCPVector2D base = start;
- QCPVector2D vec = end-start;
- double bx, by;
- double gamma, mu;
- QLineF result;
- QList<QCPVector2D> pointVectors;
- if (!qFuzzyIsNull(vec.y()))
- {
-
- bx = rect.left();
- by = rect.top();
- mu = (by-base.y())/vec.y();
- if (mu >= 0 && mu <= 1)
- {
- gamma = base.x()-bx + mu*vec.x();
- if (gamma >= 0 && gamma <= rect.width())
- pointVectors.append(QCPVector2D(bx+gamma, by));
- }
-
- bx = rect.left();
- by = rect.bottom();
- mu = (by-base.y())/vec.y();
- if (mu >= 0 && mu <= 1)
- {
- gamma = base.x()-bx + mu*vec.x();
- if (gamma >= 0 && gamma <= rect.width())
- pointVectors.append(QCPVector2D(bx+gamma, by));
- }
- }
- if (!qFuzzyIsNull(vec.x()))
- {
-
- bx = rect.left();
- by = rect.top();
- mu = (bx-base.x())/vec.x();
- if (mu >= 0 && mu <= 1)
- {
- gamma = base.y()-by + mu*vec.y();
- if (gamma >= 0 && gamma <= rect.height())
- pointVectors.append(QCPVector2D(bx, by+gamma));
- }
-
- bx = rect.right();
- by = rect.top();
- mu = (bx-base.x())/vec.x();
- if (mu >= 0 && mu <= 1)
- {
- gamma = base.y()-by + mu*vec.y();
- if (gamma >= 0 && gamma <= rect.height())
- pointVectors.append(QCPVector2D(bx, by+gamma));
- }
- }
- if (containsStart)
- pointVectors.append(start);
- if (containsEnd)
- pointVectors.append(end);
-
- if (pointVectors.size() == 2)
- {
- result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
- } else if (pointVectors.size() > 2)
- {
-
- double distSqrMax = 0;
- QCPVector2D pv1, pv2;
- for (int i=0; i<pointVectors.size()-1; ++i)
- {
- for (int k=i+1; k<pointVectors.size(); ++k)
- {
- double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
- if (distSqr > distSqrMax)
- {
- pv1 = pointVectors.at(i);
- pv2 = pointVectors.at(k);
- distSqrMax = distSqr;
- }
- }
- }
- result.setPoints(pv1.toPointF(), pv2.toPointF());
- }
- return result;
- }
- QPen QCPItemLine::mainPen() const
- {
- return mSelected ? mSelectedPen : mPen;
- }
- QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) :
- QCPAbstractItem(parentPlot),
- start(createPosition(QLatin1String("start"))),
- startDir(createPosition(QLatin1String("startDir"))),
- endDir(createPosition(QLatin1String("endDir"))),
- end(createPosition(QLatin1String("end")))
- {
- start->setCoords(0, 0);
- startDir->setCoords(0.5, 0);
- endDir->setCoords(0, 0.5);
- end->setCoords(1, 1);
- setPen(QPen(Qt::black));
- setSelectedPen(QPen(Qt::blue,2));
- }
- QCPItemCurve::~QCPItemCurve()
- {
- }
- void QCPItemCurve::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPItemCurve::setSelectedPen(const QPen &pen)
- {
- mSelectedPen = pen;
- }
- void QCPItemCurve::setHead(const QCPLineEnding &head)
- {
- mHead = head;
- }
- void QCPItemCurve::setTail(const QCPLineEnding &tail)
- {
- mTail = tail;
- }
- double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable && !mSelectable)
- return -1;
- QPointF startVec(start->pixelPosition());
- QPointF startDirVec(startDir->pixelPosition());
- QPointF endDirVec(endDir->pixelPosition());
- QPointF endVec(end->pixelPosition());
- QPainterPath cubicPath(startVec);
- cubicPath.cubicTo(startDirVec, endDirVec, endVec);
- QList<QPolygonF> polygons = cubicPath.toSubpathPolygons();
- if (polygons.isEmpty())
- return -1;
- const QPolygonF polygon = polygons.first();
- QCPVector2D p(pos);
- double minDistSqr = (std::numeric_limits<double>::max)();
- for (int i=1; i<polygon.size(); ++i)
- {
- double distSqr = p.distanceSquaredToLine(polygon.at(i-1), polygon.at(i));
- if (distSqr < minDistSqr)
- minDistSqr = distSqr;
- }
- return qSqrt(minDistSqr);
- }
- void QCPItemCurve::draw(QCPPainter *painter)
- {
- QCPVector2D startVec(start->pixelPosition());
- QCPVector2D startDirVec(startDir->pixelPosition());
- QCPVector2D endDirVec(endDir->pixelPosition());
- QCPVector2D endVec(end->pixelPosition());
- if ((endVec-startVec).length() > 1e10)
- return;
- QPainterPath cubicPath(startVec.toPointF());
- cubicPath.cubicTo(startDirVec.toPointF(), endDirVec.toPointF(), endVec.toPointF());
-
- QRectF clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
- QRectF cubicRect = cubicPath.controlPointRect();
- if (cubicRect.isEmpty())
- cubicRect.adjust(0, 0, 1, 1);
- if (clip.intersects(cubicRect))
- {
- painter->setPen(mainPen());
- painter->drawPath(cubicPath);
- painter->setBrush(Qt::SolidPattern);
- if (mTail.style() != QCPLineEnding::esNone)
- mTail.draw(painter, startVec, M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
- if (mHead.style() != QCPLineEnding::esNone)
- mHead.draw(painter, endVec, -cubicPath.angleAtPercent(1)/180.0*M_PI);
- }
- }
- QPen QCPItemCurve::mainPen() const
- {
- return mSelected ? mSelectedPen : mPen;
- }
- QCPItemRect::QCPItemRect(QCustomPlot *parentPlot) :
- QCPAbstractItem(parentPlot),
- topLeft(createPosition(QLatin1String("topLeft"))),
- bottomRight(createPosition(QLatin1String("bottomRight"))),
- top(createAnchor(QLatin1String("top"), aiTop)),
- topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
- right(createAnchor(QLatin1String("right"), aiRight)),
- bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
- bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
- left(createAnchor(QLatin1String("left"), aiLeft))
- {
- topLeft->setCoords(0, 1);
- bottomRight->setCoords(1, 0);
- setPen(QPen(Qt::black));
- setSelectedPen(QPen(Qt::blue,2));
- setBrush(Qt::NoBrush);
- setSelectedBrush(Qt::NoBrush);
- }
- QCPItemRect::~QCPItemRect()
- {
- }
- void QCPItemRect::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPItemRect::setSelectedPen(const QPen &pen)
- {
- mSelectedPen = pen;
- }
- void QCPItemRect::setBrush(const QBrush &brush)
- {
- mBrush = brush;
- }
- void QCPItemRect::setSelectedBrush(const QBrush &brush)
- {
- mSelectedBrush = brush;
- }
- double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable && !mSelectable)
- return -1;
- QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition()).normalized();
- bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
- return rectDistance(rect, pos, filledRect);
- }
- void QCPItemRect::draw(QCPPainter *painter)
- {
- QPointF p1 = topLeft->pixelPosition();
- QPointF p2 = bottomRight->pixelPosition();
- if (p1.toPoint() == p2.toPoint())
- return;
- QRectF rect = QRectF(p1, p2).normalized();
- double clipPad = mainPen().widthF();
- QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
- if (boundingRect.intersects(clipRect()))
- {
- painter->setPen(mainPen());
- painter->setBrush(mainBrush());
- painter->drawRect(rect);
- }
- }
- QPointF QCPItemRect::anchorPixelPosition(int anchorId) const
- {
- QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition());
- switch (anchorId)
- {
- case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
- case aiTopRight: return rect.topRight();
- case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
- case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
- case aiBottomLeft: return rect.bottomLeft();
- case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
- }
- qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
- return {};
- }
- QPen QCPItemRect::mainPen() const
- {
- return mSelected ? mSelectedPen : mPen;
- }
- QBrush QCPItemRect::mainBrush() const
- {
- return mSelected ? mSelectedBrush : mBrush;
- }
- QCPItemText::QCPItemText(QCustomPlot *parentPlot) :
- QCPAbstractItem(parentPlot),
- position(createPosition(QLatin1String("position"))),
- topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
- top(createAnchor(QLatin1String("top"), aiTop)),
- topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
- right(createAnchor(QLatin1String("right"), aiRight)),
- bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
- bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
- bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
- left(createAnchor(QLatin1String("left"), aiLeft)),
- mText(QLatin1String("text")),
- mPositionAlignment(Qt::AlignCenter),
- mTextAlignment(Qt::AlignTop|Qt::AlignHCenter),
- mRotation(0)
- {
- position->setCoords(0, 0);
- setPen(Qt::NoPen);
- setSelectedPen(Qt::NoPen);
- setBrush(Qt::NoBrush);
- setSelectedBrush(Qt::NoBrush);
- setColor(Qt::black);
- setSelectedColor(Qt::blue);
- }
- QCPItemText::~QCPItemText()
- {
- }
- void QCPItemText::setColor(const QColor &color)
- {
- mColor = color;
- }
- void QCPItemText::setSelectedColor(const QColor &color)
- {
- mSelectedColor = color;
- }
- void QCPItemText::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPItemText::setSelectedPen(const QPen &pen)
- {
- mSelectedPen = pen;
- }
- void QCPItemText::setBrush(const QBrush &brush)
- {
- mBrush = brush;
- }
- void QCPItemText::setSelectedBrush(const QBrush &brush)
- {
- mSelectedBrush = brush;
- }
- void QCPItemText::setFont(const QFont &font)
- {
- mFont = font;
- }
- void QCPItemText::setSelectedFont(const QFont &font)
- {
- mSelectedFont = font;
- }
- void QCPItemText::setText(const QString &text)
- {
- mText = text;
- }
- void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
- {
- mPositionAlignment = alignment;
- }
- void QCPItemText::setTextAlignment(Qt::Alignment alignment)
- {
- mTextAlignment = alignment;
- }
- void QCPItemText::setRotation(double degrees)
- {
- mRotation = degrees;
- }
- void QCPItemText::setPadding(const QMargins &padding)
- {
- mPadding = padding;
- }
- double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable && !mSelectable)
- return -1;
-
-
- QPointF positionPixels(position->pixelPosition());
- QTransform inputTransform;
- inputTransform.translate(positionPixels.x(), positionPixels.y());
- inputTransform.rotate(-mRotation);
- inputTransform.translate(-positionPixels.x(), -positionPixels.y());
- QPointF rotatedPos = inputTransform.map(pos);
- QFontMetrics fontMetrics(mFont);
- QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
- QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
- QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
- textBoxRect.moveTopLeft(textPos.toPoint());
- return rectDistance(textBoxRect, rotatedPos, true);
- }
- void QCPItemText::draw(QCPPainter *painter)
- {
- QPointF pos(position->pixelPosition());
- QTransform transform = painter->transform();
- transform.translate(pos.x(), pos.y());
- if (!qFuzzyIsNull(mRotation))
- transform.rotate(mRotation);
- painter->setFont(mainFont());
- QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
- QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
- QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment);
- textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
- textBoxRect.moveTopLeft(textPos.toPoint());
- double clipPad = mainPen().widthF();
- QRectF boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
- if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
- {
- painter->setTransform(transform);
- if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
- (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
- {
- painter->setPen(mainPen());
- painter->setBrush(mainBrush());
- painter->drawRect(textBoxRect);
- }
- painter->setBrush(Qt::NoBrush);
- painter->setPen(QPen(mainColor()));
- painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
- }
- }
- QPointF QCPItemText::anchorPixelPosition(int anchorId) const
- {
-
- QPointF pos(position->pixelPosition());
- QTransform transform;
- transform.translate(pos.x(), pos.y());
- if (!qFuzzyIsNull(mRotation))
- transform.rotate(mRotation);
- QFontMetrics fontMetrics(mainFont());
- QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
- QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
- QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment);
- textBoxRect.moveTopLeft(textPos.toPoint());
- QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
- switch (anchorId)
- {
- case aiTopLeft: return rectPoly.at(0);
- case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
- case aiTopRight: return rectPoly.at(1);
- case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
- case aiBottomRight: return rectPoly.at(2);
- case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
- case aiBottomLeft: return rectPoly.at(3);
- case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
- }
- qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
- return {};
- }
- QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
- {
- if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
- return pos;
- QPointF result = pos;
- if (positionAlignment.testFlag(Qt::AlignHCenter))
- result.rx() -= rect.width()/2.0;
- else if (positionAlignment.testFlag(Qt::AlignRight))
- result.rx() -= rect.width();
- if (positionAlignment.testFlag(Qt::AlignVCenter))
- result.ry() -= rect.height()/2.0;
- else if (positionAlignment.testFlag(Qt::AlignBottom))
- result.ry() -= rect.height();
- return result;
- }
- QFont QCPItemText::mainFont() const
- {
- return mSelected ? mSelectedFont : mFont;
- }
- QColor QCPItemText::mainColor() const
- {
- return mSelected ? mSelectedColor : mColor;
- }
- QPen QCPItemText::mainPen() const
- {
- return mSelected ? mSelectedPen : mPen;
- }
- QBrush QCPItemText::mainBrush() const
- {
- return mSelected ? mSelectedBrush : mBrush;
- }
- QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) :
- QCPAbstractItem(parentPlot),
- topLeft(createPosition(QLatin1String("topLeft"))),
- bottomRight(createPosition(QLatin1String("bottomRight"))),
- topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
- top(createAnchor(QLatin1String("top"), aiTop)),
- topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
- right(createAnchor(QLatin1String("right"), aiRight)),
- bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
- bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
- bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
- left(createAnchor(QLatin1String("left"), aiLeft)),
- center(createAnchor(QLatin1String("center"), aiCenter))
- {
- topLeft->setCoords(0, 1);
- bottomRight->setCoords(1, 0);
- setPen(QPen(Qt::black));
- setSelectedPen(QPen(Qt::blue, 2));
- setBrush(Qt::NoBrush);
- setSelectedBrush(Qt::NoBrush);
- }
- QCPItemEllipse::~QCPItemEllipse()
- {
- }
- void QCPItemEllipse::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPItemEllipse::setSelectedPen(const QPen &pen)
- {
- mSelectedPen = pen;
- }
- void QCPItemEllipse::setBrush(const QBrush &brush)
- {
- mBrush = brush;
- }
- void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
- {
- mSelectedBrush = brush;
- }
- double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable && !mSelectable)
- return -1;
- QPointF p1 = topLeft->pixelPosition();
- QPointF p2 = bottomRight->pixelPosition();
- QPointF center((p1+p2)/2.0);
- double a = qAbs(p1.x()-p2.x())/2.0;
- double b = qAbs(p1.y()-p2.y())/2.0;
- double x = pos.x()-center.x();
- double y = pos.y()-center.y();
-
- double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
- double result = qAbs(c-1)*qSqrt(x*x+y*y);
-
- if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
- {
- if (x*x/(a*a) + y*y/(b*b) <= 1)
- result = mParentPlot->selectionTolerance()*0.99;
- }
- return result;
- }
- void QCPItemEllipse::draw(QCPPainter *painter)
- {
- QPointF p1 = topLeft->pixelPosition();
- QPointF p2 = bottomRight->pixelPosition();
- if (p1.toPoint() == p2.toPoint())
- return;
- QRectF ellipseRect = QRectF(p1, p2).normalized();
- QRectF clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
- if (ellipseRect.intersects(clip))
- {
- painter->setPen(mainPen());
- painter->setBrush(mainBrush());
- #ifdef __EXCEPTIONS
- try
- {
- #endif
- painter->drawEllipse(ellipseRect);
- #ifdef __EXCEPTIONS
- } catch (...)
- {
- qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
- setVisible(false);
- }
- #endif
- }
- }
- QPointF QCPItemEllipse::anchorPixelPosition(int anchorId) const
- {
- QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition());
- switch (anchorId)
- {
- case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
- case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
- case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
- case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
- case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
- case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
- case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
- case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
- case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
- }
- qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
- return {};
- }
- QPen QCPItemEllipse::mainPen() const
- {
- return mSelected ? mSelectedPen : mPen;
- }
- QBrush QCPItemEllipse::mainBrush() const
- {
- return mSelected ? mSelectedBrush : mBrush;
- }
- QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) :
- QCPAbstractItem(parentPlot),
- topLeft(createPosition(QLatin1String("topLeft"))),
- bottomRight(createPosition(QLatin1String("bottomRight"))),
- top(createAnchor(QLatin1String("top"), aiTop)),
- topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
- right(createAnchor(QLatin1String("right"), aiRight)),
- bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
- bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
- left(createAnchor(QLatin1String("left"), aiLeft)),
- mScaled(false),
- mScaledPixmapInvalidated(true),
- mAspectRatioMode(Qt::KeepAspectRatio),
- mTransformationMode(Qt::SmoothTransformation)
- {
- topLeft->setCoords(0, 1);
- bottomRight->setCoords(1, 0);
- setPen(Qt::NoPen);
- setSelectedPen(QPen(Qt::blue));
- }
- QCPItemPixmap::~QCPItemPixmap()
- {
- }
- void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
- {
- mPixmap = pixmap;
- mScaledPixmapInvalidated = true;
- if (mPixmap.isNull())
- qDebug() << Q_FUNC_INFO << "pixmap is null";
- }
- void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
- {
- mScaled = scaled;
- mAspectRatioMode = aspectRatioMode;
- mTransformationMode = transformationMode;
- mScaledPixmapInvalidated = true;
- }
- void QCPItemPixmap::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPItemPixmap::setSelectedPen(const QPen &pen)
- {
- mSelectedPen = pen;
- }
- double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable && !mSelectable)
- return -1;
- return rectDistance(getFinalRect(), pos, true);
- }
- void QCPItemPixmap::draw(QCPPainter *painter)
- {
- bool flipHorz = false;
- bool flipVert = false;
- QRectF rect = getFinalRect(&flipHorz, &flipVert);
- double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
- QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
- if (boundingRect.intersects(clipRect()))
- {
- updateScaledPixmap(rect, flipHorz, flipVert);
- painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
- QPen pen = mainPen();
- if (pen.style() != Qt::NoPen)
- {
- painter->setPen(pen);
- painter->setBrush(Qt::NoBrush);
- painter->drawRect(rect);
- }
- }
- }
- QPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const
- {
- bool flipHorz = false;
- bool flipVert = false;
- QRectF rect = getFinalRect(&flipHorz, &flipVert);
-
-
- if (flipHorz)
- rect.adjust(rect.width(), 0, -rect.width(), 0);
- if (flipVert)
- rect.adjust(0, rect.height(), 0, -rect.height());
- switch (anchorId)
- {
- case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
- case aiTopRight: return rect.topRight();
- case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
- case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
- case aiBottomLeft: return rect.bottomLeft();
- case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
- }
- qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
- return {};
- }
- void QCPItemPixmap::updateScaledPixmap(QRectF finalRect, bool flipHorz, bool flipVert)
- {
- if (mPixmap.isNull())
- return;
- if (mScaled)
- {
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- double devicePixelRatio = mPixmap.devicePixelRatio();
- #else
- double devicePixelRatio = 1.0;
- #endif
- if (finalRect.isNull())
- finalRect = getFinalRect(&flipHorz, &flipVert);
- if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size()/devicePixelRatio)
- {
- mScaledPixmap = mPixmap.scaled(finalRect.size().toSize()*devicePixelRatio, mAspectRatioMode, mTransformationMode);
- if (flipHorz || flipVert)
- mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- mScaledPixmap.setDevicePixelRatio(devicePixelRatio);
- #endif
- }
- } else if (!mScaledPixmap.isNull())
- mScaledPixmap = QPixmap();
- mScaledPixmapInvalidated = false;
- }
- QRectF QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
- {
- QRectF result;
- bool flipHorz = false;
- bool flipVert = false;
- QPointF p1 = topLeft->pixelPosition();
- QPointF p2 = bottomRight->pixelPosition();
- if (p1 == p2)
- return {p1, QSizeF(0, 0)};
- if (mScaled)
- {
- QSizeF newSize = QSizeF(p2.x()-p1.x(), p2.y()-p1.y());
- QPointF topLeft = p1;
- if (newSize.width() < 0)
- {
- flipHorz = true;
- newSize.rwidth() *= -1;
- topLeft.setX(p2.x());
- }
- if (newSize.height() < 0)
- {
- flipVert = true;
- newSize.rheight() *= -1;
- topLeft.setY(p2.y());
- }
- QSize scaledSize = mPixmap.size();
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- scaledSize /= mPixmap.devicePixelRatio();
- scaledSize.scale(newSize.toSize()*mPixmap.devicePixelRatio(), mAspectRatioMode);
- #else
- scaledSize.scale(newSize, mAspectRatioMode);
- #endif
- result = QRectF(topLeft, scaledSize);
- } else
- {
- #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
- result = QRectF(p1, mPixmap.size()/mPixmap.devicePixelRatio());
- #else
- result = QRect(p1, mPixmap.size());
- #endif
- }
- if (flippedHorz)
- *flippedHorz = flipHorz;
- if (flippedVert)
- *flippedVert = flipVert;
- return result;
- }
- QPen QCPItemPixmap::mainPen() const
- {
- return mSelected ? mSelectedPen : mPen;
- }
- QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) :
- QCPAbstractItem(parentPlot),
- position(createPosition(QLatin1String("position"))),
- mSize(6),
- mStyle(tsCrosshair),
- mGraph(nullptr),
- mGraphKey(0),
- mInterpolating(false)
- {
- position->setCoords(0, 0);
- setBrush(Qt::NoBrush);
- setSelectedBrush(Qt::NoBrush);
- setPen(QPen(Qt::black));
- setSelectedPen(QPen(Qt::blue, 2));
- }
- QCPItemTracer::~QCPItemTracer()
- {
- }
- void QCPItemTracer::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPItemTracer::setSelectedPen(const QPen &pen)
- {
- mSelectedPen = pen;
- }
- void QCPItemTracer::setBrush(const QBrush &brush)
- {
- mBrush = brush;
- }
- void QCPItemTracer::setSelectedBrush(const QBrush &brush)
- {
- mSelectedBrush = brush;
- }
- void QCPItemTracer::setSize(double size)
- {
- mSize = size;
- }
- void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style)
- {
- mStyle = style;
- }
- void QCPItemTracer::setGraph(QCPGraph *graph)
- {
- if (graph)
- {
- if (graph->parentPlot() == mParentPlot)
- {
- position->setType(QCPItemPosition::ptPlotCoords);
- position->setAxes(graph->keyAxis(), graph->valueAxis());
- mGraph = graph;
- updatePosition();
- } else
- qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
- } else
- {
- mGraph = nullptr;
- }
- }
- void QCPItemTracer::setGraphKey(double key)
- {
- mGraphKey = key;
- }
- void QCPItemTracer::setInterpolating(bool enabled)
- {
- mInterpolating = enabled;
- }
- double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable && !mSelectable)
- return -1;
- QPointF center(position->pixelPosition());
- double w = mSize/2.0;
- QRectF clip = clipRect();
- switch (mStyle)
- {
- case tsNone: return -1;
- case tsPlus:
- {
- if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
- return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(center+QPointF(-w, 0), center+QPointF(w, 0)),
- QCPVector2D(pos).distanceSquaredToLine(center+QPointF(0, -w), center+QPointF(0, w))));
- break;
- }
- case tsCrosshair:
- {
- return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(clip.left(), center.y()), QCPVector2D(clip.right(), center.y())),
- QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(center.x(), clip.top()), QCPVector2D(center.x(), clip.bottom()))));
- }
- case tsCircle:
- {
- if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
- {
-
- double centerDist = QCPVector2D(center-pos).length();
- double circleLine = w;
- double result = qAbs(centerDist-circleLine);
-
- if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
- {
- if (centerDist <= circleLine)
- result = mParentPlot->selectionTolerance()*0.99;
- }
- return result;
- }
- break;
- }
- case tsSquare:
- {
- if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
- {
- QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
- bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
- return rectDistance(rect, pos, filledRect);
- }
- break;
- }
- }
- return -1;
- }
- void QCPItemTracer::draw(QCPPainter *painter)
- {
- updatePosition();
- if (mStyle == tsNone)
- return;
- painter->setPen(mainPen());
- painter->setBrush(mainBrush());
- QPointF center(position->pixelPosition());
- double w = mSize/2.0;
- QRectF clip = clipRect();
- switch (mStyle)
- {
- case tsNone: return;
- case tsPlus:
- {
- if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
- {
- painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
- painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
- }
- break;
- }
- case tsCrosshair:
- {
- if (center.y() > clip.top() && center.y() < clip.bottom())
- painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
- if (center.x() > clip.left() && center.x() < clip.right())
- painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
- break;
- }
- case tsCircle:
- {
- if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
- painter->drawEllipse(center, w, w);
- break;
- }
- case tsSquare:
- {
- if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
- painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
- break;
- }
- }
- }
- void QCPItemTracer::updatePosition()
- {
- if (mGraph)
- {
- if (mParentPlot->hasPlottable(mGraph))
- {
- if (mGraph->data()->size() > 1)
- {
- QCPGraphDataContainer::const_iterator first = mGraph->data()->constBegin();
- QCPGraphDataContainer::const_iterator last = mGraph->data()->constEnd()-1;
- if (mGraphKey <= first->key)
- position->setCoords(first->key, first->value);
- else if (mGraphKey >= last->key)
- position->setCoords(last->key, last->value);
- else
- {
- QCPGraphDataContainer::const_iterator it = mGraph->data()->findBegin(mGraphKey);
- if (it != mGraph->data()->constEnd())
- {
- QCPGraphDataContainer::const_iterator prevIt = it;
- ++it;
- if (mInterpolating)
- {
-
- double slope = 0;
- if (!qFuzzyCompare(double(it->key), double(prevIt->key)))
- slope = (it->value-prevIt->value)/(it->key-prevIt->key);
- position->setCoords(mGraphKey, (mGraphKey-prevIt->key)*slope+prevIt->value);
- } else
- {
-
- if (mGraphKey < (prevIt->key+it->key)*0.5)
- position->setCoords(prevIt->key, prevIt->value);
- else
- position->setCoords(it->key, it->value);
- }
- } else
- position->setCoords(it->key, it->value);
- }
- } else if (mGraph->data()->size() == 1)
- {
- QCPGraphDataContainer::const_iterator it = mGraph->data()->constBegin();
- position->setCoords(it->key, it->value);
- } else {
- }
- } else
- qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
- }
- }
- QPen QCPItemTracer::mainPen() const
- {
- return mSelected ? mSelectedPen : mPen;
- }
- QBrush QCPItemTracer::mainBrush() const
- {
- return mSelected ? mSelectedBrush : mBrush;
- }
- QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) :
- QCPAbstractItem(parentPlot),
- left(createPosition(QLatin1String("left"))),
- right(createPosition(QLatin1String("right"))),
- center(createAnchor(QLatin1String("center"), aiCenter)),
- mLength(8),
- mStyle(bsCalligraphic)
- {
- left->setCoords(0, 0);
- right->setCoords(1, 1);
- setPen(QPen(Qt::black));
- setSelectedPen(QPen(Qt::blue, 2));
- }
- QCPItemBracket::~QCPItemBracket()
- {
- }
- void QCPItemBracket::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPItemBracket::setSelectedPen(const QPen &pen)
- {
- mSelectedPen = pen;
- }
- void QCPItemBracket::setLength(double length)
- {
- mLength = length;
- }
- void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)
- {
- mStyle = style;
- }
- double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- Q_UNUSED(details)
- if (onlySelectable && !mSelectable)
- return -1;
- QCPVector2D p(pos);
- QCPVector2D leftVec(left->pixelPosition());
- QCPVector2D rightVec(right->pixelPosition());
- if (leftVec.toPoint() == rightVec.toPoint())
- return -1;
- QCPVector2D widthVec = (rightVec-leftVec)*0.5;
- QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
- QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
- switch (mStyle)
- {
- case QCPItemBracket::bsSquare:
- case QCPItemBracket::bsRound:
- {
- double a = p.distanceSquaredToLine(centerVec-widthVec, centerVec+widthVec);
- double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec, centerVec-widthVec);
- double c = p.distanceSquaredToLine(centerVec+widthVec+lengthVec, centerVec+widthVec);
- return qSqrt(qMin(qMin(a, b), c));
- }
- case QCPItemBracket::bsCurly:
- case QCPItemBracket::bsCalligraphic:
- {
- double a = p.distanceSquaredToLine(centerVec-widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
- double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec*0.7, centerVec-widthVec*0.75+lengthVec*0.15);
- double c = p.distanceSquaredToLine(centerVec+widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
- double d = p.distanceSquaredToLine(centerVec+widthVec+lengthVec*0.7, centerVec+widthVec*0.75+lengthVec*0.15);
- return qSqrt(qMin(qMin(a, b), qMin(c, d)));
- }
- }
- return -1;
- }
- void QCPItemBracket::draw(QCPPainter *painter)
- {
- QCPVector2D leftVec(left->pixelPosition());
- QCPVector2D rightVec(right->pixelPosition());
- if (leftVec.toPoint() == rightVec.toPoint())
- return;
- QCPVector2D widthVec = (rightVec-leftVec)*0.5;
- QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
- QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
- QPolygon boundingPoly;
- boundingPoly << leftVec.toPoint() << rightVec.toPoint()
- << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
- QRectF clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
- if (clip.intersects(boundingPoly.boundingRect()))
- {
- painter->setPen(mainPen());
- switch (mStyle)
- {
- case bsSquare:
- {
- painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
- painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
- painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
- break;
- }
- case bsRound:
- {
- painter->setBrush(Qt::NoBrush);
- QPainterPath path;
- path.moveTo((centerVec+widthVec+lengthVec).toPointF());
- path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
- path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
- painter->drawPath(path);
- break;
- }
- case bsCurly:
- {
- painter->setBrush(Qt::NoBrush);
- QPainterPath path;
- path.moveTo((centerVec+widthVec+lengthVec).toPointF());
- path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+lengthVec).toPointF(), centerVec.toPointF());
- path.cubicTo((centerVec-0.4*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
- painter->drawPath(path);
- break;
- }
- case bsCalligraphic:
- {
- painter->setPen(Qt::NoPen);
- painter->setBrush(QBrush(mainPen().color()));
- QPainterPath path;
- path.moveTo((centerVec+widthVec+lengthVec).toPointF());
- path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());
- path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
- path.cubicTo((centerVec-widthVec-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());
- path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
- painter->drawPath(path);
- break;
- }
- }
- }
- }
- QPointF QCPItemBracket::anchorPixelPosition(int anchorId) const
- {
- QCPVector2D leftVec(left->pixelPosition());
- QCPVector2D rightVec(right->pixelPosition());
- if (leftVec.toPoint() == rightVec.toPoint())
- return leftVec.toPointF();
- QCPVector2D widthVec = (rightVec-leftVec)*0.5;
- QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
- QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
- switch (anchorId)
- {
- case aiCenter:
- return centerVec.toPointF();
- }
- qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
- return {};
- }
- QPen QCPItemBracket::mainPen() const
- {
- return mSelected ? mSelectedPen : mPen;
- }
- QCPPolarAxisRadial::QCPPolarAxisRadial(QCPPolarAxisAngular *parent) :
- QCPLayerable(parent->parentPlot(), QString(), parent),
- mRangeDrag(true),
- mRangeZoom(true),
- mRangeZoomFactor(0.85),
-
- mAngularAxis(parent),
- mAngle(45),
- mAngleReference(arAngularAxis),
- mSelectableParts(spAxis | spTickLabels | spAxisLabel),
- mSelectedParts(spNone),
- mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- mSelectedBasePen(QPen(Qt::blue, 2)),
-
- mLabelPadding(0),
- mLabel(),
- mLabelFont(mParentPlot->font()),
- mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
- mLabelColor(Qt::black),
- mSelectedLabelColor(Qt::blue),
-
-
- mTickLabels(true),
-
- mTickLabelFont(mParentPlot->font()),
- mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
- mTickLabelColor(Qt::black),
- mSelectedTickLabelColor(Qt::blue),
- mNumberPrecision(6),
- mNumberFormatChar('g'),
- mNumberBeautifulPowers(true),
- mNumberMultiplyCross(false),
-
- mTicks(true),
- mSubTicks(true),
- mTickLengthIn(5),
- mTickLengthOut(0),
- mSubTickLengthIn(2),
- mSubTickLengthOut(0),
- mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- mSelectedTickPen(QPen(Qt::blue, 2)),
- mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- mSelectedSubTickPen(QPen(Qt::blue, 2)),
-
- mRange(0, 5),
- mRangeReversed(false),
- mScaleType(stLinear),
-
- mRadius(1),
- mTicker(new QCPAxisTicker),
- mLabelPainter(mParentPlot)
- {
- setParent(parent);
- setAntialiased(true);
- setTickLabelPadding(5);
- setTickLabelRotation(0);
- setTickLabelMode(lmUpright);
- mLabelPainter.setAnchorReferenceType(QCPLabelPainterPrivate::artTangent);
- mLabelPainter.setAbbreviateDecimalPowers(false);
- }
- QCPPolarAxisRadial::~QCPPolarAxisRadial()
- {
- }
- QCPPolarAxisRadial::LabelMode QCPPolarAxisRadial::tickLabelMode() const
- {
- switch (mLabelPainter.anchorMode())
- {
- case QCPLabelPainterPrivate::amSkewedUpright: return lmUpright;
- case QCPLabelPainterPrivate::amSkewedRotated: return lmRotated;
- default: qDebug() << Q_FUNC_INFO << "invalid mode for polar axis"; break;
- }
- return lmUpright;
- }
- QString QCPPolarAxisRadial::numberFormat() const
- {
- QString result;
- result.append(mNumberFormatChar);
- if (mNumberBeautifulPowers)
- {
- result.append(QLatin1Char('b'));
- if (mNumberMultiplyCross)
- result.append(QLatin1Char('c'));
- }
- return result;
- }
- int QCPPolarAxisRadial::tickLengthIn() const
- {
- return mTickLengthIn;
- }
- int QCPPolarAxisRadial::tickLengthOut() const
- {
- return mTickLengthOut;
- }
- int QCPPolarAxisRadial::subTickLengthIn() const
- {
- return mSubTickLengthIn;
- }
- int QCPPolarAxisRadial::subTickLengthOut() const
- {
- return mSubTickLengthOut;
- }
- int QCPPolarAxisRadial::labelPadding() const
- {
- return mLabelPadding;
- }
- void QCPPolarAxisRadial::setRangeDrag(bool enabled)
- {
- mRangeDrag = enabled;
- }
- void QCPPolarAxisRadial::setRangeZoom(bool enabled)
- {
- mRangeZoom = enabled;
- }
- void QCPPolarAxisRadial::setRangeZoomFactor(double factor)
- {
- mRangeZoomFactor = factor;
- }
- void QCPPolarAxisRadial::setScaleType(QCPPolarAxisRadial::ScaleType type)
- {
- if (mScaleType != type)
- {
- mScaleType = type;
- if (mScaleType == stLogarithmic)
- setRange(mRange.sanitizedForLogScale());
-
- emit scaleTypeChanged(mScaleType);
- }
- }
- void QCPPolarAxisRadial::setRange(const QCPRange &range)
- {
- if (qFuzzyCompare(range.lower, mRange.lower) && qFuzzyCompare(range.upper, mRange.upper))
- return;
- if (!QCPRange::validRange(range)) return;
- QCPRange oldRange = mRange;
- if (mScaleType == stLogarithmic)
- {
- mRange = range.sanitizedForLogScale();
- } else
- {
- mRange = range.sanitizedForLinScale();
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisRadial::setSelectableParts(const SelectableParts &selectable)
- {
- if (mSelectableParts != selectable)
- {
- mSelectableParts = selectable;
- emit selectableChanged(mSelectableParts);
- }
- }
- void QCPPolarAxisRadial::setSelectedParts(const SelectableParts &selected)
- {
- if (mSelectedParts != selected)
- {
- mSelectedParts = selected;
- emit selectionChanged(mSelectedParts);
- }
- }
- void QCPPolarAxisRadial::setRange(double lower, double upper)
- {
- if (qFuzzyCompare(lower, mRange.lower) && qFuzzyCompare(upper, mRange.upper))
- return;
- if (!QCPRange::validRange(lower, upper)) return;
- QCPRange oldRange = mRange;
- mRange.lower = lower;
- mRange.upper = upper;
- if (mScaleType == stLogarithmic)
- {
- mRange = mRange.sanitizedForLogScale();
- } else
- {
- mRange = mRange.sanitizedForLinScale();
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisRadial::setRange(double position, double size, Qt::AlignmentFlag alignment)
- {
- if (alignment == Qt::AlignLeft)
- setRange(position, position+size);
- else if (alignment == Qt::AlignRight)
- setRange(position-size, position);
- else
- setRange(position-size/2.0, position+size/2.0);
- }
- void QCPPolarAxisRadial::setRangeLower(double lower)
- {
- if (qFuzzyCompare(mRange.lower, lower))
- return;
- QCPRange oldRange = mRange;
- mRange.lower = lower;
- if (mScaleType == stLogarithmic)
- {
- mRange = mRange.sanitizedForLogScale();
- } else
- {
- mRange = mRange.sanitizedForLinScale();
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisRadial::setRangeUpper(double upper)
- {
- if (qFuzzyCompare(mRange.upper, upper))
- return;
- QCPRange oldRange = mRange;
- mRange.upper = upper;
- if (mScaleType == stLogarithmic)
- {
- mRange = mRange.sanitizedForLogScale();
- } else
- {
- mRange = mRange.sanitizedForLinScale();
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisRadial::setRangeReversed(bool reversed)
- {
- mRangeReversed = reversed;
- }
- void QCPPolarAxisRadial::setAngle(double degrees)
- {
- mAngle = degrees;
- }
- void QCPPolarAxisRadial::setAngleReference(AngleReference reference)
- {
- mAngleReference = reference;
- }
- void QCPPolarAxisRadial::setTicker(QSharedPointer<QCPAxisTicker> ticker)
- {
- if (ticker)
- mTicker = ticker;
- else
- qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker";
-
- }
- void QCPPolarAxisRadial::setTicks(bool show)
- {
- if (mTicks != show)
- {
- mTicks = show;
-
- }
- }
- void QCPPolarAxisRadial::setTickLabels(bool show)
- {
- if (mTickLabels != show)
- {
- mTickLabels = show;
-
- if (!mTickLabels)
- mTickVectorLabels.clear();
- }
- }
- void QCPPolarAxisRadial::setTickLabelPadding(int padding)
- {
- mLabelPainter.setPadding(padding);
- }
- void QCPPolarAxisRadial::setTickLabelFont(const QFont &font)
- {
- if (font != mTickLabelFont)
- {
- mTickLabelFont = font;
-
- }
- }
- void QCPPolarAxisRadial::setTickLabelColor(const QColor &color)
- {
- mTickLabelColor = color;
- }
- void QCPPolarAxisRadial::setTickLabelRotation(double degrees)
- {
- mLabelPainter.setRotation(degrees);
- }
- void QCPPolarAxisRadial::setTickLabelMode(LabelMode mode)
- {
- switch (mode)
- {
- case lmUpright: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedUpright); break;
- case lmRotated: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedRotated); break;
- }
- }
- void QCPPolarAxisRadial::setNumberFormat(const QString &formatCode)
- {
- if (formatCode.isEmpty())
- {
- qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
- return;
- }
-
-
- QString allowedFormatChars(QLatin1String("eEfgG"));
- if (allowedFormatChars.contains(formatCode.at(0)))
- {
- mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
- } else
- {
- qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
- return;
- }
- if (formatCode.length() < 2)
- {
- mNumberBeautifulPowers = false;
- mNumberMultiplyCross = false;
- } else
- {
-
- if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
- mNumberBeautifulPowers = true;
- else
- qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
- if (formatCode.length() < 3)
- {
- mNumberMultiplyCross = false;
- } else
- {
-
- if (formatCode.at(2) == QLatin1Char('c'))
- mNumberMultiplyCross = true;
- else if (formatCode.at(2) == QLatin1Char('d'))
- mNumberMultiplyCross = false;
- else
- qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
- }
- }
- mLabelPainter.setSubstituteExponent(mNumberBeautifulPowers);
- mLabelPainter.setMultiplicationSymbol(mNumberMultiplyCross ? QCPLabelPainterPrivate::SymbolCross : QCPLabelPainterPrivate::SymbolDot);
- }
- void QCPPolarAxisRadial::setNumberPrecision(int precision)
- {
- if (mNumberPrecision != precision)
- {
- mNumberPrecision = precision;
-
- }
- }
- void QCPPolarAxisRadial::setTickLength(int inside, int outside)
- {
- setTickLengthIn(inside);
- setTickLengthOut(outside);
- }
- void QCPPolarAxisRadial::setTickLengthIn(int inside)
- {
- if (mTickLengthIn != inside)
- {
- mTickLengthIn = inside;
- }
- }
- void QCPPolarAxisRadial::setTickLengthOut(int outside)
- {
- if (mTickLengthOut != outside)
- {
- mTickLengthOut = outside;
-
- }
- }
- void QCPPolarAxisRadial::setSubTicks(bool show)
- {
- if (mSubTicks != show)
- {
- mSubTicks = show;
-
- }
- }
- void QCPPolarAxisRadial::setSubTickLength(int inside, int outside)
- {
- setSubTickLengthIn(inside);
- setSubTickLengthOut(outside);
- }
- void QCPPolarAxisRadial::setSubTickLengthIn(int inside)
- {
- if (mSubTickLengthIn != inside)
- {
- mSubTickLengthIn = inside;
- }
- }
- void QCPPolarAxisRadial::setSubTickLengthOut(int outside)
- {
- if (mSubTickLengthOut != outside)
- {
- mSubTickLengthOut = outside;
-
- }
- }
- void QCPPolarAxisRadial::setBasePen(const QPen &pen)
- {
- mBasePen = pen;
- }
- void QCPPolarAxisRadial::setTickPen(const QPen &pen)
- {
- mTickPen = pen;
- }
- void QCPPolarAxisRadial::setSubTickPen(const QPen &pen)
- {
- mSubTickPen = pen;
- }
- void QCPPolarAxisRadial::setLabelFont(const QFont &font)
- {
- if (mLabelFont != font)
- {
- mLabelFont = font;
-
- }
- }
- void QCPPolarAxisRadial::setLabelColor(const QColor &color)
- {
- mLabelColor = color;
- }
- void QCPPolarAxisRadial::setLabel(const QString &str)
- {
- if (mLabel != str)
- {
- mLabel = str;
-
- }
- }
- void QCPPolarAxisRadial::setLabelPadding(int padding)
- {
- if (mLabelPadding != padding)
- {
- mLabelPadding = padding;
-
- }
- }
- void QCPPolarAxisRadial::setSelectedTickLabelFont(const QFont &font)
- {
- if (font != mSelectedTickLabelFont)
- {
- mSelectedTickLabelFont = font;
-
- }
- }
- void QCPPolarAxisRadial::setSelectedLabelFont(const QFont &font)
- {
- mSelectedLabelFont = font;
-
- }
- void QCPPolarAxisRadial::setSelectedTickLabelColor(const QColor &color)
- {
- if (color != mSelectedTickLabelColor)
- {
- mSelectedTickLabelColor = color;
- }
- }
- void QCPPolarAxisRadial::setSelectedLabelColor(const QColor &color)
- {
- mSelectedLabelColor = color;
- }
- void QCPPolarAxisRadial::setSelectedBasePen(const QPen &pen)
- {
- mSelectedBasePen = pen;
- }
- void QCPPolarAxisRadial::setSelectedTickPen(const QPen &pen)
- {
- mSelectedTickPen = pen;
- }
- void QCPPolarAxisRadial::setSelectedSubTickPen(const QPen &pen)
- {
- mSelectedSubTickPen = pen;
- }
- void QCPPolarAxisRadial::moveRange(double diff)
- {
- QCPRange oldRange = mRange;
- if (mScaleType == stLinear)
- {
- mRange.lower += diff;
- mRange.upper += diff;
- } else
- {
- mRange.lower *= diff;
- mRange.upper *= diff;
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisRadial::scaleRange(double factor)
- {
- scaleRange(factor, range().center());
- }
- void QCPPolarAxisRadial::scaleRange(double factor, double center)
- {
- QCPRange oldRange = mRange;
- if (mScaleType == stLinear)
- {
- QCPRange newRange;
- newRange.lower = (mRange.lower-center)*factor + center;
- newRange.upper = (mRange.upper-center)*factor + center;
- if (QCPRange::validRange(newRange))
- mRange = newRange.sanitizedForLinScale();
- } else
- {
- if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0))
- {
- QCPRange newRange;
- newRange.lower = qPow(mRange.lower/center, factor)*center;
- newRange.upper = qPow(mRange.upper/center, factor)*center;
- if (QCPRange::validRange(newRange))
- mRange = newRange.sanitizedForLogScale();
- } else
- qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
- }
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisRadial::rescale(bool onlyVisiblePlottables)
- {
- Q_UNUSED(onlyVisiblePlottables)
-
- }
- void QCPPolarAxisRadial::pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const
- {
- QCPVector2D posVector(pixelPos-mCenter);
- radiusCoord = radiusToCoord(posVector.length());
- angleCoord = mAngularAxis->angleRadToCoord(posVector.angle());
- }
- QPointF QCPPolarAxisRadial::coordToPixel(double angleCoord, double radiusCoord) const
- {
- const double radiusPixel = coordToRadius(radiusCoord);
- const double angleRad = mAngularAxis->coordToAngleRad(angleCoord);
- return QPointF(mCenter.x()+qCos(angleRad)*radiusPixel, mCenter.y()+qSin(angleRad)*radiusPixel);
- }
- double QCPPolarAxisRadial::coordToRadius(double coord) const
- {
- if (mScaleType == stLinear)
- {
- if (!mRangeReversed)
- return (coord-mRange.lower)/mRange.size()*mRadius;
- else
- return (mRange.upper-coord)/mRange.size()*mRadius;
- } else
- {
- if (coord >= 0.0 && mRange.upper < 0.0)
- return !mRangeReversed ? mRadius+200 : mRadius-200;
- else if (coord <= 0.0 && mRange.upper >= 0.0)
- return !mRangeReversed ? mRadius-200 :mRadius+200;
- else
- {
- if (!mRangeReversed)
- return qLn(coord/mRange.lower)/qLn(mRange.upper/mRange.lower)*mRadius;
- else
- return qLn(mRange.upper/coord)/qLn(mRange.upper/mRange.lower)*mRadius;
- }
- }
- }
- double QCPPolarAxisRadial::radiusToCoord(double radius) const
- {
- if (mScaleType == stLinear)
- {
- if (!mRangeReversed)
- return (radius)/mRadius*mRange.size()+mRange.lower;
- else
- return -(radius)/mRadius*mRange.size()+mRange.upper;
- } else
- {
- if (!mRangeReversed)
- return qPow(mRange.upper/mRange.lower, (radius)/mRadius)*mRange.lower;
- else
- return qPow(mRange.upper/mRange.lower, (-radius)/mRadius)*mRange.upper;
- }
- }
- QCPPolarAxisRadial::SelectablePart QCPPolarAxisRadial::getPartAt(const QPointF &pos) const
- {
- Q_UNUSED(pos)
- if (!mVisible)
- return spNone;
-
- return spNone;
- }
- double QCPPolarAxisRadial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- if (!mParentPlot) return -1;
- SelectablePart part = getPartAt(pos);
- if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
- return -1;
- if (details)
- details->setValue(part);
- return mParentPlot->selectionTolerance()*0.99;
- }
- void QCPPolarAxisRadial::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
- {
- Q_UNUSED(event)
- SelectablePart part = details.value<SelectablePart>();
- if (mSelectableParts.testFlag(part))
- {
- SelectableParts selBefore = mSelectedParts;
- setSelectedParts(additive ? mSelectedParts^part : part);
- if (selectionStateChanged)
- *selectionStateChanged = mSelectedParts != selBefore;
- }
- }
- void QCPPolarAxisRadial::deselectEvent(bool *selectionStateChanged)
- {
- SelectableParts selBefore = mSelectedParts;
- setSelectedParts(mSelectedParts & ~mSelectableParts);
- if (selectionStateChanged)
- *selectionStateChanged = mSelectedParts != selBefore;
- }
- void QCPPolarAxisRadial::mousePressEvent(QMouseEvent *event, const QVariant &details)
- {
- Q_UNUSED(details)
- if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag))
- {
- event->ignore();
- return;
- }
- if (event->buttons() & Qt::LeftButton)
- {
- mDragging = true;
-
- if (mParentPlot->noAntialiasingOnDrag())
- {
- mAADragBackup = mParentPlot->antialiasedElements();
- mNotAADragBackup = mParentPlot->notAntialiasedElements();
- }
-
- if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
- mDragStartRange = mRange;
- }
- }
- void QCPPolarAxisRadial::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
- {
- Q_UNUSED(event)
- Q_UNUSED(startPos)
- if (mDragging)
- {
-
- if (mParentPlot->noAntialiasingOnDrag())
- mParentPlot->setNotAntialiasedElements(QCP::aeAll);
- mParentPlot->replot(QCustomPlot::rpQueuedReplot);
- }
- }
- void QCPPolarAxisRadial::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
- {
- Q_UNUSED(event)
- Q_UNUSED(startPos)
- mDragging = false;
- if (mParentPlot->noAntialiasingOnDrag())
- {
- mParentPlot->setAntialiasedElements(mAADragBackup);
- mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
- }
- }
- void QCPPolarAxisRadial::wheelEvent(QWheelEvent *event)
- {
-
- if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom))
- {
- event->ignore();
- return;
- }
-
-
-
-
- mParentPlot->replot();
- }
- void QCPPolarAxisRadial::updateGeometry(const QPointF ¢er, double radius)
- {
- mCenter = center;
- mRadius = radius;
- if (mRadius < 1) mRadius = 1;
- }
- void QCPPolarAxisRadial::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
- }
- void QCPPolarAxisRadial::draw(QCPPainter *painter)
- {
- const double axisAngleRad = (mAngle+(mAngleReference==arAngularAxis ? mAngularAxis->angle() : 0))/180.0*M_PI;
- const QPointF axisVector(qCos(axisAngleRad), qSin(axisAngleRad));
- const QPointF tickNormal = QCPVector2D(axisVector).perpendicular().toPointF();
-
- painter->setPen(getBasePen());
- painter->drawLine(QLineF(mCenter, mCenter+axisVector*(mRadius-0.5)));
-
- if (!mSubTickVector.isEmpty())
- {
- painter->setPen(getSubTickPen());
- for (int i=0; i<mSubTickVector.size(); ++i)
- {
- const QPointF tickPosition = mCenter+axisVector*coordToRadius(mSubTickVector.at(i));
- painter->drawLine(QLineF(tickPosition-tickNormal*mSubTickLengthIn, tickPosition+tickNormal*mSubTickLengthOut));
- }
- }
-
- if (!mTickVector.isEmpty())
- {
- mLabelPainter.setAnchorReference(mCenter-axisVector);
- mLabelPainter.setFont(getTickLabelFont());
- mLabelPainter.setColor(getTickLabelColor());
- const QPen ticksPen = getTickPen();
- painter->setPen(ticksPen);
- for (int i=0; i<mTickVector.size(); ++i)
- {
- const double r = coordToRadius(mTickVector.at(i));
- const QPointF tickPosition = mCenter+axisVector*r;
- painter->drawLine(QLineF(tickPosition-tickNormal*mTickLengthIn, tickPosition+tickNormal*mTickLengthOut));
-
- if (!mTickVectorLabels.isEmpty())
- {
- if ((!mRangeReversed && (i < mTickVectorLabels.count()-1 || mRadius-r > 10)) ||
- (mRangeReversed && (i > 0 || mRadius-r > 10)))
- mLabelPainter.drawTickLabel(painter, tickPosition+tickNormal*mSubTickLengthOut, mTickVectorLabels.at(i));
- }
- }
- }
- }
- void QCPPolarAxisRadial::setupTickVectors()
- {
- if (!mParentPlot) return;
- if ((!mTicks && !mTickLabels) || mRange.size() <= 0) return;
- mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : nullptr, mTickLabels ? &mTickVectorLabels : nullptr);
- }
- QPen QCPPolarAxisRadial::getBasePen() const
- {
- return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
- }
- QPen QCPPolarAxisRadial::getTickPen() const
- {
- return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
- }
- QPen QCPPolarAxisRadial::getSubTickPen() const
- {
- return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
- }
- QFont QCPPolarAxisRadial::getTickLabelFont() const
- {
- return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
- }
- QFont QCPPolarAxisRadial::getLabelFont() const
- {
- return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
- }
- QColor QCPPolarAxisRadial::getTickLabelColor() const
- {
- return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
- }
- QColor QCPPolarAxisRadial::getLabelColor() const
- {
- return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
- }
- QCP::Interaction QCPPolarAxisRadial::selectionCategory() const
- {
- return QCP::iSelectAxes;
- }
- QCPPolarAxisAngular::QCPPolarAxisAngular(QCustomPlot *parentPlot) :
- QCPLayoutElement(parentPlot),
- mBackgroundBrush(Qt::NoBrush),
- mBackgroundScaled(true),
- mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
- mInsetLayout(new QCPLayoutInset),
- mRangeDrag(false),
- mRangeZoom(false),
- mRangeZoomFactor(0.85),
-
- mAngle(-90),
- mAngleRad(mAngle/180.0*M_PI),
- mSelectableParts(spAxis | spTickLabels | spAxisLabel),
- mSelectedParts(spNone),
- mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- mSelectedBasePen(QPen(Qt::blue, 2)),
-
- mLabelPadding(0),
- mLabel(),
- mLabelFont(mParentPlot->font()),
- mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
- mLabelColor(Qt::black),
- mSelectedLabelColor(Qt::blue),
-
-
- mTickLabels(true),
-
- mTickLabelFont(mParentPlot->font()),
- mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
- mTickLabelColor(Qt::black),
- mSelectedTickLabelColor(Qt::blue),
- mNumberPrecision(6),
- mNumberFormatChar('g'),
- mNumberBeautifulPowers(true),
- mNumberMultiplyCross(false),
-
- mTicks(true),
- mSubTicks(true),
- mTickLengthIn(5),
- mTickLengthOut(0),
- mSubTickLengthIn(2),
- mSubTickLengthOut(0),
- mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- mSelectedTickPen(QPen(Qt::blue, 2)),
- mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
- mSelectedSubTickPen(QPen(Qt::blue, 2)),
-
- mRange(0, 360),
- mRangeReversed(false),
-
- mRadius(1),
- mGrid(new QCPPolarGrid(this)),
- mTicker(new QCPAxisTickerFixed),
- mDragging(false),
- mLabelPainter(parentPlot)
- {
-
-
-
-
- if (QCPAxisTickerFixed *fixedTicker = mTicker.dynamicCast<QCPAxisTickerFixed>().data())
- {
- fixedTicker->setTickStep(30);
- }
- setAntialiased(true);
- setLayer(mParentPlot->currentLayer());
- setTickLabelPadding(5);
- setTickLabelRotation(0);
- setTickLabelMode(lmUpright);
- mLabelPainter.setAnchorReferenceType(QCPLabelPainterPrivate::artNormal);
- mLabelPainter.setAbbreviateDecimalPowers(false);
- mLabelPainter.setCacheSize(24);
- setMinimumSize(50, 50);
- setMinimumMargins(QMargins(30, 30, 30, 30));
- addRadialAxis();
- mGrid->setRadialAxis(radialAxis());
- }
- QCPPolarAxisAngular::~QCPPolarAxisAngular()
- {
- delete mGrid;
- mGrid = nullptr;
- delete mInsetLayout;
- mInsetLayout = nullptr;
- QList<QCPPolarAxisRadial*> radialAxesList = radialAxes();
- for (int i=0; i<radialAxesList.size(); ++i)
- removeRadialAxis(radialAxesList.at(i));
- }
- QCPPolarAxisAngular::LabelMode QCPPolarAxisAngular::tickLabelMode() const
- {
- switch (mLabelPainter.anchorMode())
- {
- case QCPLabelPainterPrivate::amSkewedUpright: return lmUpright;
- case QCPLabelPainterPrivate::amSkewedRotated: return lmRotated;
- default: qDebug() << Q_FUNC_INFO << "invalid mode for polar axis"; break;
- }
- return lmUpright;
- }
- QString QCPPolarAxisAngular::numberFormat() const
- {
- QString result;
- result.append(mNumberFormatChar);
- if (mNumberBeautifulPowers)
- {
- result.append(QLatin1Char('b'));
- if (mLabelPainter.multiplicationSymbol() == QCPLabelPainterPrivate::SymbolCross)
- result.append(QLatin1Char('c'));
- }
- return result;
- }
- int QCPPolarAxisAngular::radialAxisCount() const
- {
- return mRadialAxes.size();
- }
- QCPPolarAxisRadial *QCPPolarAxisAngular::radialAxis(int index) const
- {
- if (index >= 0 && index < mRadialAxes.size())
- {
- return mRadialAxes.at(index);
- } else
- {
- qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
- return nullptr;
- }
- }
- QList<QCPPolarAxisRadial*> QCPPolarAxisAngular::radialAxes() const
- {
- return mRadialAxes;
- }
- QCPPolarAxisRadial *QCPPolarAxisAngular::addRadialAxis(QCPPolarAxisRadial *axis)
- {
- QCPPolarAxisRadial *newAxis = axis;
- if (!newAxis)
- {
- newAxis = new QCPPolarAxisRadial(this);
- } else
- {
- if (newAxis->angularAxis() != this)
- {
- qDebug() << Q_FUNC_INFO << "passed radial axis doesn't have this angular axis as parent angular axis";
- return nullptr;
- }
- if (radialAxes().contains(newAxis))
- {
- qDebug() << Q_FUNC_INFO << "passed axis is already owned by this angular axis";
- return nullptr;
- }
- }
- mRadialAxes.append(newAxis);
- return newAxis;
- }
- bool QCPPolarAxisAngular::removeRadialAxis(QCPPolarAxisRadial *radialAxis)
- {
- if (mRadialAxes.contains(radialAxis))
- {
- mRadialAxes.removeOne(radialAxis);
- delete radialAxis;
- return true;
- } else
- {
- qDebug() << Q_FUNC_INFO << "Radial axis isn't associated with this angular axis:" << reinterpret_cast<quintptr>(radialAxis);
- return false;
- }
- }
- QRegion QCPPolarAxisAngular::exactClipRegion() const
- {
- return QRegion(mCenter.x()-mRadius, mCenter.y()-mRadius, qRound(2*mRadius), qRound(2*mRadius), QRegion::Ellipse);
- }
- void QCPPolarAxisAngular::moveRange(double diff)
- {
- QCPRange oldRange = mRange;
- mRange.lower += diff;
- mRange.upper += diff;
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisAngular::scaleRange(double factor)
- {
- scaleRange(factor, range().center());
- }
- void QCPPolarAxisAngular::scaleRange(double factor, double center)
- {
- QCPRange oldRange = mRange;
- QCPRange newRange;
- newRange.lower = (mRange.lower-center)*factor + center;
- newRange.upper = (mRange.upper-center)*factor + center;
- if (QCPRange::validRange(newRange))
- mRange = newRange.sanitizedForLinScale();
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisAngular::rescale(bool onlyVisiblePlottables)
- {
- QCPRange newRange;
- bool haveRange = false;
- for (int i=0; i<mGraphs.size(); ++i)
- {
- if (!mGraphs.at(i)->realVisibility() && onlyVisiblePlottables)
- continue;
- QCPRange range;
- bool currentFoundRange;
- if (mGraphs.at(i)->keyAxis() == this)
- range = mGraphs.at(i)->getKeyRange(currentFoundRange, QCP::sdBoth);
- else
- range = mGraphs.at(i)->getValueRange(currentFoundRange, QCP::sdBoth);
- if (currentFoundRange)
- {
- if (!haveRange)
- newRange = range;
- else
- newRange.expand(range);
- haveRange = true;
- }
- }
- if (haveRange)
- {
- if (!QCPRange::validRange(newRange))
- {
- double center = (newRange.lower+newRange.upper)*0.5;
- newRange.lower = center-mRange.size()/2.0;
- newRange.upper = center+mRange.size()/2.0;
- }
- setRange(newRange);
- }
- }
- void QCPPolarAxisAngular::pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const
- {
- if (!mRadialAxes.isEmpty())
- mRadialAxes.first()->pixelToCoord(pixelPos, angleCoord, radiusCoord);
- else
- qDebug() << Q_FUNC_INFO << "no radial axis configured";
- }
- QPointF QCPPolarAxisAngular::coordToPixel(double angleCoord, double radiusCoord) const
- {
- if (!mRadialAxes.isEmpty())
- {
- return mRadialAxes.first()->coordToPixel(angleCoord, radiusCoord);
- } else
- {
- qDebug() << Q_FUNC_INFO << "no radial axis configured";
- return QPointF();
- }
- }
- QCPPolarAxisAngular::SelectablePart QCPPolarAxisAngular::getPartAt(const QPointF &pos) const
- {
- Q_UNUSED(pos)
- if (!mVisible)
- return spNone;
-
- return spNone;
- }
- double QCPPolarAxisAngular::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
-
- Q_UNUSED(details)
- if (onlySelectable)
- return -1;
- if (QRectF(mOuterRect).contains(pos))
- {
- if (mParentPlot)
- return mParentPlot->selectionTolerance()*0.99;
- else
- {
- qDebug() << Q_FUNC_INFO << "parent plot not defined";
- return -1;
- }
- } else
- return -1;
- }
- void QCPPolarAxisAngular::update(UpdatePhase phase)
- {
- QCPLayoutElement::update(phase);
- switch (phase)
- {
- case upPreparation:
- {
- setupTickVectors();
- for (int i=0; i<mRadialAxes.size(); ++i)
- mRadialAxes.at(i)->setupTickVectors();
- break;
- }
- case upLayout:
- {
- mCenter = mRect.center();
- mRadius = 0.5*qMin(qAbs(mRect.width()), qAbs(mRect.height()));
- if (mRadius < 1) mRadius = 1;
- for (int i=0; i<mRadialAxes.size(); ++i)
- mRadialAxes.at(i)->updateGeometry(mCenter, mRadius);
- mInsetLayout->setOuterRect(rect());
- break;
- }
- default: break;
- }
-
- mInsetLayout->update(phase);
- }
- QList<QCPLayoutElement*> QCPPolarAxisAngular::elements(bool recursive) const
- {
- QList<QCPLayoutElement*> result;
- if (mInsetLayout)
- {
- result << mInsetLayout;
- if (recursive)
- result << mInsetLayout->elements(recursive);
- }
- return result;
- }
- bool QCPPolarAxisAngular::removeGraph(QCPPolarGraph *graph)
- {
- if (!mGraphs.contains(graph))
- {
- qDebug() << Q_FUNC_INFO << "graph not in list:" << reinterpret_cast<quintptr>(graph);
- return false;
- }
-
- graph->removeFromLegend();
-
- delete graph;
- mGraphs.removeOne(graph);
- return true;
- }
- void QCPPolarAxisAngular::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
- }
- void QCPPolarAxisAngular::draw(QCPPainter *painter)
- {
- drawBackground(painter, mCenter, mRadius);
-
- painter->setPen(getBasePen());
- painter->drawEllipse(mCenter, mRadius, mRadius);
-
- if (!mSubTickVector.isEmpty())
- {
- painter->setPen(getSubTickPen());
- for (int i=0; i<mSubTickVector.size(); ++i)
- {
- painter->drawLine(mCenter+mSubTickVectorCosSin.at(i)*(mRadius-mSubTickLengthIn),
- mCenter+mSubTickVectorCosSin.at(i)*(mRadius+mSubTickLengthOut));
- }
- }
-
- if (!mTickVector.isEmpty())
- {
- mLabelPainter.setAnchorReference(mCenter);
- mLabelPainter.setFont(getTickLabelFont());
- mLabelPainter.setColor(getTickLabelColor());
- const QPen ticksPen = getTickPen();
- painter->setPen(ticksPen);
- for (int i=0; i<mTickVector.size(); ++i)
- {
- const QPointF outerTick = mCenter+mTickVectorCosSin.at(i)*(mRadius+mTickLengthOut);
- painter->drawLine(mCenter+mTickVectorCosSin.at(i)*(mRadius-mTickLengthIn), outerTick);
-
- if (!mTickVectorLabels.isEmpty())
- {
- if (i < mTickVectorLabels.count()-1 || (mTickVectorCosSin.at(i)-mTickVectorCosSin.first()).manhattanLength() > 5/180.0*M_PI)
- mLabelPainter.drawTickLabel(painter, outerTick, mTickVectorLabels.at(i));
- }
- }
- }
- }
- QCP::Interaction QCPPolarAxisAngular::selectionCategory() const
- {
- return QCP::iSelectAxes;
- }
- void QCPPolarAxisAngular::setBackground(const QPixmap &pm)
- {
- mBackgroundPixmap = pm;
- mScaledBackgroundPixmap = QPixmap();
- }
- void QCPPolarAxisAngular::setBackground(const QBrush &brush)
- {
- mBackgroundBrush = brush;
- }
- void QCPPolarAxisAngular::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
- {
- mBackgroundPixmap = pm;
- mScaledBackgroundPixmap = QPixmap();
- mBackgroundScaled = scaled;
- mBackgroundScaledMode = mode;
- }
- void QCPPolarAxisAngular::setBackgroundScaled(bool scaled)
- {
- mBackgroundScaled = scaled;
- }
- void QCPPolarAxisAngular::setBackgroundScaledMode(Qt::AspectRatioMode mode)
- {
- mBackgroundScaledMode = mode;
- }
- void QCPPolarAxisAngular::setRangeDrag(bool enabled)
- {
- mRangeDrag = enabled;
- }
- void QCPPolarAxisAngular::setRangeZoom(bool enabled)
- {
- mRangeZoom = enabled;
- }
- void QCPPolarAxisAngular::setRangeZoomFactor(double factor)
- {
- mRangeZoomFactor = factor;
- }
- void QCPPolarAxisAngular::setRange(const QCPRange &range)
- {
- if (qFuzzyCompare(range.lower, mRange.lower) && qFuzzyCompare(range.upper, mRange.upper))
- return;
- if (!QCPRange::validRange(range)) return;
- QCPRange oldRange = mRange;
- mRange = range.sanitizedForLinScale();
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisAngular::setSelectableParts(const SelectableParts &selectable)
- {
- if (mSelectableParts != selectable)
- {
- mSelectableParts = selectable;
- emit selectableChanged(mSelectableParts);
- }
- }
- void QCPPolarAxisAngular::setSelectedParts(const SelectableParts &selected)
- {
- if (mSelectedParts != selected)
- {
- mSelectedParts = selected;
- emit selectionChanged(mSelectedParts);
- }
- }
- void QCPPolarAxisAngular::setRange(double lower, double upper)
- {
- if (qFuzzyCompare(lower, mRange.lower) && qFuzzyCompare(upper, mRange.upper))
- return;
- if (!QCPRange::validRange(lower, upper)) return;
- QCPRange oldRange = mRange;
- mRange.lower = lower;
- mRange.upper = upper;
- mRange = mRange.sanitizedForLinScale();
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisAngular::setRange(double position, double size, Qt::AlignmentFlag alignment)
- {
- if (alignment == Qt::AlignLeft)
- setRange(position, position+size);
- else if (alignment == Qt::AlignRight)
- setRange(position-size, position);
- else
- setRange(position-size/2.0, position+size/2.0);
- }
- void QCPPolarAxisAngular::setRangeLower(double lower)
- {
- if (qFuzzyCompare(mRange.lower, lower))
- return;
- QCPRange oldRange = mRange;
- mRange.lower = lower;
- mRange = mRange.sanitizedForLinScale();
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisAngular::setRangeUpper(double upper)
- {
- if (qFuzzyCompare(mRange.upper, upper))
- return;
- QCPRange oldRange = mRange;
- mRange.upper = upper;
- mRange = mRange.sanitizedForLinScale();
- emit rangeChanged(mRange);
- emit rangeChanged(mRange, oldRange);
- }
- void QCPPolarAxisAngular::setRangeReversed(bool reversed)
- {
- mRangeReversed = reversed;
- }
- void QCPPolarAxisAngular::setAngle(double degrees)
- {
- mAngle = degrees;
- mAngleRad = mAngle/180.0*M_PI;
- }
- void QCPPolarAxisAngular::setTicker(QSharedPointer<QCPAxisTicker> ticker)
- {
- if (ticker)
- mTicker = ticker;
- else
- qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker";
-
- }
- void QCPPolarAxisAngular::setTicks(bool show)
- {
- if (mTicks != show)
- {
- mTicks = show;
-
- }
- }
- void QCPPolarAxisAngular::setTickLabels(bool show)
- {
- if (mTickLabels != show)
- {
- mTickLabels = show;
-
- if (!mTickLabels)
- mTickVectorLabels.clear();
- }
- }
- void QCPPolarAxisAngular::setTickLabelPadding(int padding)
- {
- mLabelPainter.setPadding(padding);
- }
- void QCPPolarAxisAngular::setTickLabelFont(const QFont &font)
- {
- mTickLabelFont = font;
- }
- void QCPPolarAxisAngular::setTickLabelColor(const QColor &color)
- {
- mTickLabelColor = color;
- }
- void QCPPolarAxisAngular::setTickLabelRotation(double degrees)
- {
- mLabelPainter.setRotation(degrees);
- }
- void QCPPolarAxisAngular::setTickLabelMode(LabelMode mode)
- {
- switch (mode)
- {
- case lmUpright: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedUpright); break;
- case lmRotated: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedRotated); break;
- }
- }
- void QCPPolarAxisAngular::setNumberFormat(const QString &formatCode)
- {
- if (formatCode.isEmpty())
- {
- qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
- return;
- }
-
-
- QString allowedFormatChars(QLatin1String("eEfgG"));
- if (allowedFormatChars.contains(formatCode.at(0)))
- {
- mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
- } else
- {
- qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
- return;
- }
- if (formatCode.length() < 2)
- {
- mNumberBeautifulPowers = false;
- mNumberMultiplyCross = false;
- } else
- {
-
- if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
- mNumberBeautifulPowers = true;
- else
- qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
- if (formatCode.length() < 3)
- {
- mNumberMultiplyCross = false;
- } else
- {
-
- if (formatCode.at(2) == QLatin1Char('c'))
- mNumberMultiplyCross = true;
- else if (formatCode.at(2) == QLatin1Char('d'))
- mNumberMultiplyCross = false;
- else
- qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
- }
- }
- mLabelPainter.setSubstituteExponent(mNumberBeautifulPowers);
- mLabelPainter.setMultiplicationSymbol(mNumberMultiplyCross ? QCPLabelPainterPrivate::SymbolCross : QCPLabelPainterPrivate::SymbolDot);
- }
- void QCPPolarAxisAngular::setNumberPrecision(int precision)
- {
- if (mNumberPrecision != precision)
- {
- mNumberPrecision = precision;
-
- }
- }
- void QCPPolarAxisAngular::setTickLength(int inside, int outside)
- {
- setTickLengthIn(inside);
- setTickLengthOut(outside);
- }
- void QCPPolarAxisAngular::setTickLengthIn(int inside)
- {
- if (mTickLengthIn != inside)
- {
- mTickLengthIn = inside;
- }
- }
- void QCPPolarAxisAngular::setTickLengthOut(int outside)
- {
- if (mTickLengthOut != outside)
- {
- mTickLengthOut = outside;
-
- }
- }
- void QCPPolarAxisAngular::setSubTicks(bool show)
- {
- if (mSubTicks != show)
- {
- mSubTicks = show;
-
- }
- }
- void QCPPolarAxisAngular::setSubTickLength(int inside, int outside)
- {
- setSubTickLengthIn(inside);
- setSubTickLengthOut(outside);
- }
- void QCPPolarAxisAngular::setSubTickLengthIn(int inside)
- {
- if (mSubTickLengthIn != inside)
- {
- mSubTickLengthIn = inside;
- }
- }
- void QCPPolarAxisAngular::setSubTickLengthOut(int outside)
- {
- if (mSubTickLengthOut != outside)
- {
- mSubTickLengthOut = outside;
-
- }
- }
- void QCPPolarAxisAngular::setBasePen(const QPen &pen)
- {
- mBasePen = pen;
- }
- void QCPPolarAxisAngular::setTickPen(const QPen &pen)
- {
- mTickPen = pen;
- }
- void QCPPolarAxisAngular::setSubTickPen(const QPen &pen)
- {
- mSubTickPen = pen;
- }
- void QCPPolarAxisAngular::setLabelFont(const QFont &font)
- {
- if (mLabelFont != font)
- {
- mLabelFont = font;
-
- }
- }
- void QCPPolarAxisAngular::setLabelColor(const QColor &color)
- {
- mLabelColor = color;
- }
- void QCPPolarAxisAngular::setLabel(const QString &str)
- {
- if (mLabel != str)
- {
- mLabel = str;
-
- }
- }
- void QCPPolarAxisAngular::setLabelPadding(int padding)
- {
- if (mLabelPadding != padding)
- {
- mLabelPadding = padding;
-
- }
- }
- void QCPPolarAxisAngular::setSelectedTickLabelFont(const QFont &font)
- {
- if (font != mSelectedTickLabelFont)
- {
- mSelectedTickLabelFont = font;
-
- }
- }
- void QCPPolarAxisAngular::setSelectedLabelFont(const QFont &font)
- {
- mSelectedLabelFont = font;
-
- }
- void QCPPolarAxisAngular::setSelectedTickLabelColor(const QColor &color)
- {
- if (color != mSelectedTickLabelColor)
- {
- mSelectedTickLabelColor = color;
- }
- }
- void QCPPolarAxisAngular::setSelectedLabelColor(const QColor &color)
- {
- mSelectedLabelColor = color;
- }
- void QCPPolarAxisAngular::setSelectedBasePen(const QPen &pen)
- {
- mSelectedBasePen = pen;
- }
- void QCPPolarAxisAngular::setSelectedTickPen(const QPen &pen)
- {
- mSelectedTickPen = pen;
- }
- void QCPPolarAxisAngular::setSelectedSubTickPen(const QPen &pen)
- {
- mSelectedSubTickPen = pen;
- }
- void QCPPolarAxisAngular::drawBackground(QCPPainter *painter, const QPointF ¢er, double radius)
- {
-
- if (mBackgroundBrush != Qt::NoBrush)
- {
- QPainterPath ellipsePath;
- ellipsePath.addEllipse(center, radius, radius);
- painter->fillPath(ellipsePath, mBackgroundBrush);
- }
-
- if (!mBackgroundPixmap.isNull())
- {
- QRegion clipCircle(int(center.x()-radius), int(center.y()-radius), qRound(2*radius), qRound(2*radius), QRegion::Ellipse);
- QRegion originalClip = painter->clipRegion();
- painter->setClipRegion(clipCircle);
- if (mBackgroundScaled)
- {
-
- QSizeF scaledSize(mBackgroundPixmap.size());
- scaledSize.scale(mRect.size(), mBackgroundScaledMode);
- if (mScaledBackgroundPixmap.size() != scaledSize)
- mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size().toSize(), mBackgroundScaledMode, Qt::SmoothTransformation);
- painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRectF(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
- } else
- {
- painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRectF(0, 0, mRect.width(), mRect.height()));
- }
- painter->setClipRegion(originalClip);
- }
- }
- void QCPPolarAxisAngular::setupTickVectors()
- {
- if (!mParentPlot) return;
- if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
- mSubTickVector.clear();
- mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : nullptr, mTickLabels ? &mTickVectorLabels : nullptr);
-
- mTickVectorCosSin.resize(mTickVector.size());
- for (int i=0; i<mTickVector.size(); ++i)
- {
- const double theta = coordToAngleRad(mTickVector.at(i));
- mTickVectorCosSin[i] = QPointF(qCos(theta), qSin(theta));
- }
- mSubTickVectorCosSin.resize(mSubTickVector.size());
- for (int i=0; i<mSubTickVector.size(); ++i)
- {
- const double theta = coordToAngleRad(mSubTickVector.at(i));
- mSubTickVectorCosSin[i] = QPointF(qCos(theta), qSin(theta));
- }
- }
- QPen QCPPolarAxisAngular::getBasePen() const
- {
- return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
- }
- QPen QCPPolarAxisAngular::getTickPen() const
- {
- return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
- }
- QPen QCPPolarAxisAngular::getSubTickPen() const
- {
- return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
- }
- QFont QCPPolarAxisAngular::getTickLabelFont() const
- {
- return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
- }
- QFont QCPPolarAxisAngular::getLabelFont() const
- {
- return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
- }
- QColor QCPPolarAxisAngular::getTickLabelColor() const
- {
- return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
- }
- QColor QCPPolarAxisAngular::getLabelColor() const
- {
- return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
- }
- void QCPPolarAxisAngular::mousePressEvent(QMouseEvent *event, const QVariant &details)
- {
- Q_UNUSED(details)
- if (event->buttons() & Qt::LeftButton)
- {
- mDragging = true;
-
- if (mParentPlot->noAntialiasingOnDrag())
- {
- mAADragBackup = mParentPlot->antialiasedElements();
- mNotAADragBackup = mParentPlot->notAntialiasedElements();
- }
-
- if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
- {
- mDragAngularStart = range();
- mDragRadialStart.clear();
- for (int i=0; i<mRadialAxes.size(); ++i)
- mDragRadialStart.append(mRadialAxes.at(i)->range());
- }
- }
- }
- void QCPPolarAxisAngular::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
- {
- Q_UNUSED(startPos)
- bool doReplot = false;
-
- if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
- {
- if (mRangeDrag)
- {
- doReplot = true;
- double angleCoordStart, radiusCoordStart;
- double angleCoord, radiusCoord;
- pixelToCoord(startPos, angleCoordStart, radiusCoordStart);
- pixelToCoord(event->pos(), angleCoord, radiusCoord);
- double diff = angleCoordStart - angleCoord;
- setRange(mDragAngularStart.lower+diff, mDragAngularStart.upper+diff);
- }
- for (int i=0; i<mRadialAxes.size(); ++i)
- {
- QCPPolarAxisRadial *ax = mRadialAxes.at(i);
- if (!ax->rangeDrag())
- continue;
- doReplot = true;
- double angleCoordStart, radiusCoordStart;
- double angleCoord, radiusCoord;
- ax->pixelToCoord(startPos, angleCoordStart, radiusCoordStart);
- ax->pixelToCoord(event->pos(), angleCoord, radiusCoord);
- if (ax->scaleType() == QCPPolarAxisRadial::stLinear)
- {
- double diff = radiusCoordStart - radiusCoord;
- ax->setRange(mDragRadialStart.at(i).lower+diff, mDragRadialStart.at(i).upper+diff);
- } else if (ax->scaleType() == QCPPolarAxisRadial::stLogarithmic)
- {
- if (!qFuzzyCompare(radiusCoord, 0))
- {
- double diff = radiusCoordStart/radiusCoord;
- ax->setRange(mDragRadialStart.at(i).lower*diff, mDragRadialStart.at(i).upper*diff);
- }
- }
- }
- if (doReplot)
- {
- if (mParentPlot->noAntialiasingOnDrag())
- mParentPlot->setNotAntialiasedElements(QCP::aeAll);
- mParentPlot->replot(QCustomPlot::rpQueuedReplot);
- }
- }
- }
- void QCPPolarAxisAngular::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
- {
- Q_UNUSED(event)
- Q_UNUSED(startPos)
- mDragging = false;
- if (mParentPlot->noAntialiasingOnDrag())
- {
- mParentPlot->setAntialiasedElements(mAADragBackup);
- mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
- }
- }
- void QCPPolarAxisAngular::wheelEvent(QWheelEvent *event)
- {
- bool doReplot = false;
-
- if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
- {
- #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
- const double delta = event->delta();
- #else
- const double delta = event->angleDelta().y();
- #endif
- #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
- const QPointF pos = event->pos();
- #else
- const QPointF pos = event->position();
- #endif
- const double wheelSteps = delta/120.0;
- if (mRangeZoom)
- {
- double angleCoord, radiusCoord;
- pixelToCoord(pos, angleCoord, radiusCoord);
- scaleRange(qPow(mRangeZoomFactor, wheelSteps), angleCoord);
- }
- for (int i=0; i<mRadialAxes.size(); ++i)
- {
- QCPPolarAxisRadial *ax = mRadialAxes.at(i);
- if (!ax->rangeZoom())
- continue;
- doReplot = true;
- double angleCoord, radiusCoord;
- ax->pixelToCoord(pos, angleCoord, radiusCoord);
- ax->scaleRange(qPow(ax->rangeZoomFactor(), wheelSteps), radiusCoord);
- }
- }
- if (doReplot)
- mParentPlot->replot();
- }
- bool QCPPolarAxisAngular::registerPolarGraph(QCPPolarGraph *graph)
- {
- if (mGraphs.contains(graph))
- {
- qDebug() << Q_FUNC_INFO << "plottable already added:" << reinterpret_cast<quintptr>(graph);
- return false;
- }
- if (graph->keyAxis() != this)
- {
- qDebug() << Q_FUNC_INFO << "plottable not created with this as axis:" << reinterpret_cast<quintptr>(graph);
- return false;
- }
- mGraphs.append(graph);
-
- if (mParentPlot->autoAddPlottableToLegend())
- graph->addToLegend();
- if (!graph->layer())
- graph->setLayer(mParentPlot->currentLayer());
- return true;
- }
- QCPPolarGrid::QCPPolarGrid(QCPPolarAxisAngular *parentAxis) :
- QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
- mType(gtNone),
- mSubGridType(gtNone),
- mAntialiasedSubGrid(true),
- mAntialiasedZeroLine(true),
- mParentAxis(parentAxis)
- {
-
- setParent(parentAxis);
- setType(gtAll);
- setSubGridType(gtNone);
- setAngularPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
- setAngularSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
- setRadialPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
- setRadialSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
- setRadialZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
- setAntialiased(true);
- }
- void QCPPolarGrid::setRadialAxis(QCPPolarAxisRadial *axis)
- {
- mRadialAxis = axis;
- }
- void QCPPolarGrid::setType(GridTypes type)
- {
- mType = type;
- }
- void QCPPolarGrid::setSubGridType(GridTypes type)
- {
- mSubGridType = type;
- }
- void QCPPolarGrid::setAntialiasedSubGrid(bool enabled)
- {
- mAntialiasedSubGrid = enabled;
- }
- void QCPPolarGrid::setAntialiasedZeroLine(bool enabled)
- {
- mAntialiasedZeroLine = enabled;
- }
- void QCPPolarGrid::setAngularPen(const QPen &pen)
- {
- mAngularPen = pen;
- }
- void QCPPolarGrid::setAngularSubGridPen(const QPen &pen)
- {
- mAngularSubGridPen = pen;
- }
- void QCPPolarGrid::setRadialPen(const QPen &pen)
- {
- mRadialPen = pen;
- }
- void QCPPolarGrid::setRadialSubGridPen(const QPen &pen)
- {
- mRadialSubGridPen = pen;
- }
- void QCPPolarGrid::setRadialZeroLinePen(const QPen &pen)
- {
- mRadialZeroLinePen = pen;
- }
- void QCPPolarGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
- }
- void QCPPolarGrid::draw(QCPPainter *painter)
- {
- if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
- const QPointF center = mParentAxis->mCenter;
- const double radius = mParentAxis->mRadius;
- painter->setBrush(Qt::NoBrush);
-
- if (mType.testFlag(gtAngular))
- drawAngularGrid(painter, center, radius, mParentAxis->mTickVectorCosSin, mAngularPen);
-
- if (mType.testFlag(gtRadial) && mRadialAxis)
- drawRadialGrid(painter, center, mRadialAxis->tickVector(), mRadialPen, mRadialZeroLinePen);
- applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeGrid);
-
- if (mSubGridType.testFlag(gtAngular))
- drawAngularGrid(painter, center, radius, mParentAxis->mSubTickVectorCosSin, mAngularSubGridPen);
-
- if (mSubGridType.testFlag(gtRadial) && mRadialAxis)
- drawRadialGrid(painter, center, mRadialAxis->subTickVector(), mRadialSubGridPen);
- }
- void QCPPolarGrid::drawRadialGrid(QCPPainter *painter, const QPointF ¢er, const QVector<double> &coords, const QPen &pen, const QPen &zeroPen)
- {
- if (!mRadialAxis) return;
- if (coords.isEmpty()) return;
- const bool drawZeroLine = zeroPen != Qt::NoPen;
- const double zeroLineEpsilon = qAbs(coords.last()-coords.first())*1e-6;
- painter->setPen(pen);
- for (int i=0; i<coords.size(); ++i)
- {
- const double r = mRadialAxis->coordToRadius(coords.at(i));
- if (drawZeroLine && qAbs(coords.at(i)) < zeroLineEpsilon)
- {
- applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
- painter->setPen(zeroPen);
- painter->drawEllipse(center, r, r);
- painter->setPen(pen);
- applyDefaultAntialiasingHint(painter);
- } else
- {
- painter->drawEllipse(center, r, r);
- }
- }
- }
- void QCPPolarGrid::drawAngularGrid(QCPPainter *painter, const QPointF ¢er, double radius, const QVector<QPointF> &ticksCosSin, const QPen &pen)
- {
- if (ticksCosSin.isEmpty()) return;
- painter->setPen(pen);
- for (int i=0; i<ticksCosSin.size(); ++i)
- painter->drawLine(center, center+ticksCosSin.at(i)*radius);
- }
- QCPPolarLegendItem::QCPPolarLegendItem(QCPLegend *parent, QCPPolarGraph *graph) :
- QCPAbstractLegendItem(parent),
- mPolarGraph(graph)
- {
- setAntialiased(false);
- }
- void QCPPolarLegendItem::draw(QCPPainter *painter)
- {
- if (!mPolarGraph) return;
- painter->setFont(getFont());
- painter->setPen(QPen(getTextColor()));
- QSizeF iconSize = mParentLegend->iconSize();
- QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPolarGraph->name());
- QRectF iconRect(mRect.topLeft(), iconSize);
- int textHeight = qMax(textRect.height(), iconSize.height());
- painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPolarGraph->name());
-
- painter->save();
- painter->setClipRect(iconRect, Qt::IntersectClip);
- mPolarGraph->drawLegendIcon(painter, iconRect);
- painter->restore();
-
- if (getIconBorderPen().style() != Qt::NoPen)
- {
- painter->setPen(getIconBorderPen());
- painter->setBrush(Qt::NoBrush);
- int halfPen = qCeil(painter->pen().widthF()*0.5)+1;
- painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen));
- painter->drawRect(iconRect);
- }
- }
- QSizeF QCPPolarLegendItem::minimumOuterSizeHint() const
- {
- if (!mPolarGraph) return QSize();
- QSizeF result(0, 0);
- QRectF textRect;
- QFontMetrics fontMetrics(getFont());
- QSizeF iconSize = mParentLegend->iconSize();
- textRect = fontMetrics.boundingRect(0, 0, 0, int(iconSize.height()), Qt::TextDontClip, mPolarGraph->name());
- result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width());
- result.setHeight(qMax(textRect.height(), iconSize.height()));
- result.rwidth() += mMargins.left()+mMargins.right();
- result.rheight() += mMargins.top()+mMargins.bottom();
- return result;
- }
- QPen QCPPolarLegendItem::getIconBorderPen() const
- {
- return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
- }
- QColor QCPPolarLegendItem::getTextColor() const
- {
- return mSelected ? mSelectedTextColor : mTextColor;
- }
- QFont QCPPolarLegendItem::getFont() const
- {
- return mSelected ? mSelectedFont : mFont;
- }
- QCPPolarGraph::QCPPolarGraph(QCPPolarAxisAngular *keyAxis, QCPPolarAxisRadial *valueAxis) :
- QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis),
- mDataContainer(new QCPGraphDataContainer),
- mName(),
- mAntialiasedFill(true),
- mAntialiasedScatters(true),
- mPen(Qt::black),
- mBrush(Qt::NoBrush),
- mPeriodic(true),
- mKeyAxis(keyAxis),
- mValueAxis(valueAxis),
- mSelectable(QCP::stWhole)
-
- {
- if (keyAxis->parentPlot() != valueAxis->parentPlot())
- qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
- mKeyAxis->registerPolarGraph(this);
-
- setPen(QPen(Qt::blue, 0));
- setBrush(Qt::NoBrush);
- setLineStyle(lsLine);
- }
- QCPPolarGraph::~QCPPolarGraph()
- {
-
- }
- void QCPPolarGraph::setName(const QString &name)
- {
- mName = name;
- }
- void QCPPolarGraph::setAntialiasedFill(bool enabled)
- {
- mAntialiasedFill = enabled;
- }
- void QCPPolarGraph::setAntialiasedScatters(bool enabled)
- {
- mAntialiasedScatters = enabled;
- }
- void QCPPolarGraph::setPen(const QPen &pen)
- {
- mPen = pen;
- }
- void QCPPolarGraph::setBrush(const QBrush &brush)
- {
- mBrush = brush;
- }
- void QCPPolarGraph::setPeriodic(bool enabled)
- {
- mPeriodic = enabled;
- }
- void QCPPolarGraph::setKeyAxis(QCPPolarAxisAngular *axis)
- {
- mKeyAxis = axis;
- }
- void QCPPolarGraph::setValueAxis(QCPPolarAxisRadial *axis)
- {
- mValueAxis = axis;
- }
- void QCPPolarGraph::setSelectable(QCP::SelectionType selectable)
- {
- if (mSelectable != selectable)
- {
- mSelectable = selectable;
- QCPDataSelection oldSelection = mSelection;
- mSelection.enforceType(mSelectable);
- emit selectableChanged(mSelectable);
- if (mSelection != oldSelection)
- {
- emit selectionChanged(selected());
- emit selectionChanged(mSelection);
- }
- }
- }
- void QCPPolarGraph::setSelection(QCPDataSelection selection)
- {
- selection.enforceType(mSelectable);
- if (mSelection != selection)
- {
- mSelection = selection;
- emit selectionChanged(selected());
- emit selectionChanged(mSelection);
- }
- }
- void QCPPolarGraph::setData(QSharedPointer<QCPGraphDataContainer> data)
- {
- mDataContainer = data;
- }
- void QCPPolarGraph::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
- {
- mDataContainer->clear();
- addData(keys, values, alreadySorted);
- }
- void QCPPolarGraph::setLineStyle(LineStyle ls)
- {
- mLineStyle = ls;
- }
- void QCPPolarGraph::setScatterStyle(const QCPScatterStyle &style)
- {
- mScatterStyle = style;
- }
- void QCPPolarGraph::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
- {
- if (keys.size() != values.size())
- qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
- const int n = qMin(keys.size(), values.size());
- QVector<QCPGraphData> tempData(n);
- QVector<QCPGraphData>::iterator it = tempData.begin();
- const QVector<QCPGraphData>::iterator itEnd = tempData.end();
- int i = 0;
- while (it != itEnd)
- {
- it->key = keys[i];
- it->value = values[i];
- ++it;
- ++i;
- }
- mDataContainer->add(tempData, alreadySorted);
- }
- void QCPPolarGraph::addData(double key, double value)
- {
- mDataContainer->add(QCPGraphData(key, value));
- }
- void QCPPolarGraph::coordsToPixels(double key, double value, double &x, double &y) const
- {
- if (mValueAxis)
- {
- const QPointF point = mValueAxis->coordToPixel(key, value);
- x = point.x();
- y = point.y();
- } else
- {
- qDebug() << Q_FUNC_INFO << "invalid key or value axis";
- }
- }
- const QPointF QCPPolarGraph::coordsToPixels(double key, double value) const
- {
- if (mValueAxis)
- {
- return mValueAxis->coordToPixel(key, value);
- } else
- {
- qDebug() << Q_FUNC_INFO << "invalid key or value axis";
- return QPointF();
- }
- }
- void QCPPolarGraph::pixelsToCoords(double x, double y, double &key, double &value) const
- {
- if (mValueAxis)
- {
- mValueAxis->pixelToCoord(QPointF(x, y), key, value);
- } else
- {
- qDebug() << Q_FUNC_INFO << "invalid key or value axis";
- }
- }
- void QCPPolarGraph::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
- {
- if (mValueAxis)
- {
- mValueAxis->pixelToCoord(pixelPos, key, value);
- } else
- {
- qDebug() << Q_FUNC_INFO << "invalid key or value axis";
- }
- }
- void QCPPolarGraph::rescaleAxes(bool onlyEnlarge) const
- {
- rescaleKeyAxis(onlyEnlarge);
- rescaleValueAxis(onlyEnlarge);
- }
- void QCPPolarGraph::rescaleKeyAxis(bool onlyEnlarge) const
- {
- QCPPolarAxisAngular *keyAxis = mKeyAxis.data();
- if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
- bool foundRange;
- QCPRange newRange = getKeyRange(foundRange, QCP::sdBoth);
- if (foundRange)
- {
- if (onlyEnlarge)
- newRange.expand(keyAxis->range());
- if (!QCPRange::validRange(newRange))
- {
- double center = (newRange.lower+newRange.upper)*0.5;
- newRange.lower = center-keyAxis->range().size()/2.0;
- newRange.upper = center+keyAxis->range().size()/2.0;
- }
- keyAxis->setRange(newRange);
- }
- }
- void QCPPolarGraph::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const
- {
- QCPPolarAxisAngular *keyAxis = mKeyAxis.data();
- QCPPolarAxisRadial *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- QCP::SignDomain signDomain = QCP::sdBoth;
- if (valueAxis->scaleType() == QCPPolarAxisRadial::stLogarithmic)
- signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);
- bool foundRange;
- QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange());
- if (foundRange)
- {
- if (onlyEnlarge)
- newRange.expand(valueAxis->range());
- if (!QCPRange::validRange(newRange))
- {
- double center = (newRange.lower+newRange.upper)*0.5;
- if (valueAxis->scaleType() == QCPPolarAxisRadial::stLinear)
- {
- newRange.lower = center-valueAxis->range().size()/2.0;
- newRange.upper = center+valueAxis->range().size()/2.0;
- } else
- {
- newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
- newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
- }
- }
- valueAxis->setRange(newRange);
- }
- }
- bool QCPPolarGraph::addToLegend(QCPLegend *legend)
- {
- if (!legend)
- {
- qDebug() << Q_FUNC_INFO << "passed legend is null";
- return false;
- }
- if (legend->parentPlot() != mParentPlot)
- {
- qDebug() << Q_FUNC_INFO << "passed legend isn't in the same QCustomPlot as this plottable";
- return false;
- }
-
-
- legend->addItem(new QCPPolarLegendItem(legend, this));
- return true;
-
-
- }
- bool QCPPolarGraph::addToLegend()
- {
- if (!mParentPlot || !mParentPlot->legend)
- return false;
- else
- return addToLegend(mParentPlot->legend);
- }
- bool QCPPolarGraph::removeFromLegend(QCPLegend *legend) const
- {
- if (!legend)
- {
- qDebug() << Q_FUNC_INFO << "passed legend is null";
- return false;
- }
- QCPPolarLegendItem *removableItem = nullptr;
- for (int i=0; i<legend->itemCount(); ++i)
- {
- if (QCPPolarLegendItem *pli = qobject_cast<QCPPolarLegendItem*>(legend->item(i)))
- {
- if (pli->polarGraph() == this)
- {
- removableItem = pli;
- break;
- }
- }
- }
- if (removableItem)
- return legend->removeItem(removableItem);
- else
- return false;
- }
- bool QCPPolarGraph::removeFromLegend() const
- {
- if (!mParentPlot || !mParentPlot->legend)
- return false;
- else
- return removeFromLegend(mParentPlot->legend);
- }
- double QCPPolarGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
- {
- if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
- return -1;
- if (!mKeyAxis || !mValueAxis)
- return -1;
- if (mKeyAxis->rect().contains(pos.toPoint()))
- {
- QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
- double result = pointDistance(pos, closestDataPoint);
- if (details)
- {
- int pointIndex = int(closestDataPoint-mDataContainer->constBegin());
- details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
- }
- return result;
- } else
- return -1;
- }
- QCPRange QCPPolarGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
- {
- return mDataContainer->keyRange(foundRange, inSignDomain);
- }
- QCPRange QCPPolarGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
- {
- return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
- }
- QRectF QCPPolarGraph::clipRect() const
- {
- if (mKeyAxis)
- return mKeyAxis.data()->rect();
- else
- return {};
- }
- void QCPPolarGraph::draw(QCPPainter *painter)
- {
- if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
- if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
- painter->setClipRegion(mKeyAxis->exactClipRegion());
- QVector<QPointF> lines, scatters;
-
- QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
- getDataSegments(selectedSegments, unselectedSegments);
- allSegments << unselectedSegments << selectedSegments;
- for (int i=0; i<allSegments.size(); ++i)
- {
- bool isSelectedSegment = i >= unselectedSegments.size();
-
- QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1);
- getLines(&lines, lineDataRange);
-
- #ifdef QCUSTOMPLOT_CHECK_DATA
- QCPGraphDataContainer::const_iterator it;
- for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
- {
- if (QCP::isInvalidData(it->key, it->value))
- qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
- }
- #endif
-
-
-
-
- painter->setBrush(mBrush);
- painter->setPen(Qt::NoPen);
- drawFill(painter, &lines);
-
- if (mLineStyle != lsNone)
- {
-
-
-
- painter->setPen(mPen);
- painter->setBrush(Qt::NoBrush);
- drawLinePlot(painter, lines);
- }
-
- QCPScatterStyle finalScatterStyle = mScatterStyle;
-
-
- if (!finalScatterStyle.isNone())
- {
- getScatters(&scatters, allSegments.at(i));
- drawScatterPlot(painter, scatters, finalScatterStyle);
- }
- }
-
-
-
- }
- QCP::Interaction QCPPolarGraph::selectionCategory() const
- {
- return QCP::iSelectPlottables;
- }
- void QCPPolarGraph::applyDefaultAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
- }
- void QCPPolarGraph::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
- {
- Q_UNUSED(event)
- if (mSelectable != QCP::stNone)
- {
- QCPDataSelection newSelection = details.value<QCPDataSelection>();
- QCPDataSelection selectionBefore = mSelection;
- if (additive)
- {
- if (mSelectable == QCP::stWhole)
- {
- if (selected())
- setSelection(QCPDataSelection());
- else
- setSelection(newSelection);
- } else
- {
- if (mSelection.contains(newSelection))
- setSelection(mSelection-newSelection);
- else
- setSelection(mSelection+newSelection);
- }
- } else
- setSelection(newSelection);
- if (selectionStateChanged)
- *selectionStateChanged = mSelection != selectionBefore;
- }
- }
- void QCPPolarGraph::deselectEvent(bool *selectionStateChanged)
- {
- if (mSelectable != QCP::stNone)
- {
- QCPDataSelection selectionBefore = mSelection;
- setSelection(QCPDataSelection());
- if (selectionStateChanged)
- *selectionStateChanged = mSelection != selectionBefore;
- }
- }
- void QCPPolarGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
- {
- if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
- {
- applyDefaultAntialiasingHint(painter);
- drawPolyline(painter, lines);
- }
- }
- void QCPPolarGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const
- {
- applyFillAntialiasingHint(painter);
- if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0)
- painter->drawPolygon(QPolygonF(*lines));
- }
- void QCPPolarGraph::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const
- {
- applyScattersAntialiasingHint(painter);
- style.applyTo(painter, mPen);
- for (int i=0; i<scatters.size(); ++i)
- style.drawShape(painter, scatters.at(i).x(), scatters.at(i).y());
- }
- void QCPPolarGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
- {
-
- if (mBrush.style() != Qt::NoBrush)
- {
- applyFillAntialiasingHint(painter);
- painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
- }
-
- if (mLineStyle != lsNone)
- {
- applyDefaultAntialiasingHint(painter);
- painter->setPen(mPen);
- painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0));
- }
-
- if (!mScatterStyle.isNone())
- {
- applyScattersAntialiasingHint(painter);
-
- if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
- {
- QCPScatterStyle scaledStyle(mScatterStyle);
- scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
- scaledStyle.applyTo(painter, mPen);
- scaledStyle.drawShape(painter, QRectF(rect).center());
- } else
- {
- mScatterStyle.applyTo(painter, mPen);
- mScatterStyle.drawShape(painter, QRectF(rect).center());
- }
- }
- }
- void QCPPolarGraph::applyFillAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
- }
- void QCPPolarGraph::applyScattersAntialiasingHint(QCPPainter *painter) const
- {
- applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
- }
- double QCPPolarGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const
- {
- closestData = mDataContainer->constEnd();
- if (mDataContainer->isEmpty())
- return -1.0;
- if (mLineStyle == lsNone && mScatterStyle.isNone())
- return -1.0;
-
- double minDistSqr = (std::numeric_limits<double>::max)();
-
- double posKeyMin, posKeyMax, dummy;
- pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy);
- pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy);
- if (posKeyMin > posKeyMax)
- qSwap(posKeyMin, posKeyMax);
-
- QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true);
- QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true);
- for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it)
- {
- const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();
- if (currentDistSqr < minDistSqr)
- {
- minDistSqr = currentDistSqr;
- closestData = it;
- }
- }
-
-
- if (mLineStyle != lsNone)
- {
-
- QVector<QPointF> lineData;
- getLines(&lineData, QCPDataRange(0, dataCount()));
- QCPVector2D p(pixelPoint);
- for (int i=0; i<lineData.size()-1; ++i)
- {
- const double currentDistSqr = p.distanceSquaredToLine(lineData.at(i), lineData.at(i+1));
- if (currentDistSqr < minDistSqr)
- minDistSqr = currentDistSqr;
- }
- }
- return qSqrt(minDistSqr);
- }
- int QCPPolarGraph::dataCount() const
- {
- return mDataContainer->size();
- }
- void QCPPolarGraph::getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const
- {
- selectedSegments.clear();
- unselectedSegments.clear();
- if (mSelectable == QCP::stWhole)
- {
- if (selected())
- selectedSegments << QCPDataRange(0, dataCount());
- else
- unselectedSegments << QCPDataRange(0, dataCount());
- } else
- {
- QCPDataSelection sel(selection());
- sel.simplify();
- selectedSegments = sel.dataRanges();
- unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges();
- }
- }
- void QCPPolarGraph::drawPolyline(QCPPainter *painter, const QVector<QPointF> &lineData) const
- {
-
- if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
- painter->pen().style() == Qt::SolidLine &&
- !painter->modes().testFlag(QCPPainter::pmVectorized) &&
- !painter->modes().testFlag(QCPPainter::pmNoCaching))
- {
- int i = 0;
- bool lastIsNan = false;
- const int lineDataSize = lineData.size();
- while (i < lineDataSize && (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x())))
- ++i;
- ++i;
- while (i < lineDataSize)
- {
- if (!qIsNaN(lineData.at(i).y()) && !qIsNaN(lineData.at(i).x()))
- {
- if (!lastIsNan)
- painter->drawLine(lineData.at(i-1), lineData.at(i));
- else
- lastIsNan = false;
- } else
- lastIsNan = true;
- ++i;
- }
- } else
- {
- int segmentStart = 0;
- int i = 0;
- const int lineDataSize = lineData.size();
- while (i < lineDataSize)
- {
- if (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()) || qIsInf(lineData.at(i).y()))
- {
- painter->drawPolyline(lineData.constData()+segmentStart, i-segmentStart);
- segmentStart = i+1;
- }
- ++i;
- }
-
- painter->drawPolyline(lineData.constData()+segmentStart, lineDataSize-segmentStart);
- }
- }
- void QCPPolarGraph::getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
- {
- if (rangeRestriction.isEmpty())
- {
- end = mDataContainer->constEnd();
- begin = end;
- } else
- {
- QCPPolarAxisAngular *keyAxis = mKeyAxis.data();
- QCPPolarAxisRadial *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
-
- if (mPeriodic)
- {
- begin = mDataContainer->constBegin();
- end = mDataContainer->constEnd();
- } else
- {
- begin = mDataContainer->findBegin(keyAxis->range().lower);
- end = mDataContainer->findEnd(keyAxis->range().upper);
- }
-
- mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction);
- }
- }
- void QCPPolarGraph::getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const
- {
- if (!lines) return;
- QCPGraphDataContainer::const_iterator begin, end;
- getVisibleDataBounds(begin, end, dataRange);
- if (begin == end)
- {
- lines->clear();
- return;
- }
- QVector<QCPGraphData> lineData;
- if (mLineStyle != lsNone)
- getOptimizedLineData(&lineData, begin, end);
- switch (mLineStyle)
- {
- case lsNone: lines->clear(); break;
- case lsLine: *lines = dataToLines(lineData); break;
- }
- }
- void QCPPolarGraph::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const
- {
- QCPPolarAxisAngular *keyAxis = mKeyAxis.data();
- QCPPolarAxisRadial *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
- if (!scatters) return;
- QCPGraphDataContainer::const_iterator begin, end;
- getVisibleDataBounds(begin, end, dataRange);
- if (begin == end)
- {
- scatters->clear();
- return;
- }
- QVector<QCPGraphData> data;
- getOptimizedScatterData(&data, begin, end);
- scatters->resize(data.size());
- for (int i=0; i<data.size(); ++i)
- {
- if (!qIsNaN(data.at(i).value))
- (*scatters)[i] = valueAxis->coordToPixel(data.at(i).key, data.at(i).value);
- }
- }
- void QCPPolarGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const
- {
- lineData->clear();
-
- const QCPRange range = mValueAxis->range();
- bool reversed = mValueAxis->rangeReversed();
- const double clipMargin = range.size()*0.05;
- const double upperClipValue = range.upper + (reversed ? 0 : range.size()*0.05+clipMargin);
- const double lowerClipValue = range.lower - (reversed ? range.size()*0.05+clipMargin : 0);
- const double maxKeySkip = qAsin(qSqrt(clipMargin*(clipMargin+2*range.size()))/(range.size()+clipMargin))/M_PI*mKeyAxis->range().size();
- double skipBegin = 0;
- bool belowRange = false;
- bool aboveRange = false;
- QCPGraphDataContainer::const_iterator it = begin;
- while (it != end)
- {
- if (it->value < lowerClipValue)
- {
- if (aboveRange)
- {
- aboveRange = false;
- if (!reversed)
- lineData->append(*(it-1));
- }
- if (!belowRange)
- {
- skipBegin = it->key;
- lineData->append(QCPGraphData(it->key, lowerClipValue));
- belowRange = true;
- }
- if (it->key-skipBegin > maxKeySkip)
- {
- skipBegin += maxKeySkip;
- lineData->append(QCPGraphData(skipBegin, lowerClipValue));
- }
- } else if (it->value > upperClipValue)
- {
- if (belowRange)
- {
- belowRange = false;
- if (reversed)
- lineData->append(*(it-1));
- }
- if (!aboveRange)
- {
- skipBegin = it->key;
- lineData->append(QCPGraphData(it->key, upperClipValue));
- aboveRange = true;
- }
- if (it->key-skipBegin > maxKeySkip)
- {
- skipBegin += maxKeySkip;
- lineData->append(QCPGraphData(skipBegin, upperClipValue));
- }
- } else
- {
- if (aboveRange)
- {
- aboveRange = false;
- if (!reversed)
- lineData->append(*(it-1));
- }
- if (belowRange)
- {
- belowRange = false;
- if (reversed)
- lineData->append(*(it-1));
- }
- lineData->append(*it);
- }
- ++it;
- }
-
- if (aboveRange)
- {
- aboveRange = false;
- if (!reversed)
- lineData->append(*(it-1));
- }
- if (belowRange)
- {
- belowRange = false;
- if (reversed)
- lineData->append(*(it-1));
- }
- }
- void QCPPolarGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const
- {
- scatterData->clear();
- const QCPRange range = mValueAxis->range();
- bool reversed = mValueAxis->rangeReversed();
- const double clipMargin = range.size()*0.05;
- const double upperClipValue = range.upper + (reversed ? 0 : clipMargin);
- const double lowerClipValue = range.lower - (reversed ? clipMargin : 0);
- QCPGraphDataContainer::const_iterator it = begin;
- while (it != end)
- {
- if (it->value > lowerClipValue && it->value < upperClipValue)
- scatterData->append(*it);
- ++it;
- }
- }
- QVector<QPointF> QCPPolarGraph::dataToLines(const QVector<QCPGraphData> &data) const
- {
- QVector<QPointF> result;
- QCPPolarAxisAngular *keyAxis = mKeyAxis.data();
- QCPPolarAxisRadial *valueAxis = mValueAxis.data();
- if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
-
- result.resize(data.size());
- for (int i=0; i<data.size(); ++i)
- result[i] = mValueAxis->coordToPixel(data.at(i).key, data.at(i).value);
- return result;
- }
|