implot.cpp 268 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901
  1. // MIT License
  2. // Copyright (c) 2023 Evan Pezent
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. // The above copyright notice and this permission notice shall be included in all
  10. // copies or substantial portions of the Software.
  11. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  17. // SOFTWARE.
  18. // ImPlot v0.17
  19. /*
  20. API BREAKING CHANGES
  21. ====================
  22. Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
  23. Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code.
  24. When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files.
  25. You can read releases logs https://github.com/epezent/implot/releases for more details.
  26. - 2023/08/20 (0.17) - ImPlotFlags_NoChild was removed as child windows are no longer needed to capture scroll. You can safely remove this flag if you were using it.
  27. - 2023/06/26 (0.15) - Various build fixes related to updates in Dear ImGui internals.
  28. - 2022/11/25 (0.15) - Make PlotText honor ImPlotItemFlags_NoFit.
  29. - 2022/06/19 (0.14) - The signature of ColormapScale has changed to accommodate a new ImPlotColormapScaleFlags parameter
  30. - 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters.
  31. If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see
  32. unexpected results or crashes without a compiler warning since these three are all default args. We apologize for the inconvenience, but this was a necessary evil.
  33. - PlotBarsH has been removed; use PlotBars + ImPlotBarsFlags_Horizontal instead
  34. - PlotErrorBarsH has been removed; use PlotErrorBars + ImPlotErrorBarsFlags_Horizontal
  35. - PlotHistogram/PlotHistogram2D signatures changed; `cumulative`, `density`, and `outliers` options now specified via ImPlotHistogramFlags
  36. - PlotPieChart signature changed; `normalize` option now specified via ImPlotPieChartFlags
  37. - PlotText signature changes; `vertical` option now specified via `ImPlotTextFlags_Vertical`
  38. - `PlotVLines` and `PlotHLines` replaced with `PlotInfLines` (+ ImPlotInfLinesFlags_Horizontal )
  39. - arguments of ImPlotGetter have been reversed to be consistent with other API callbacks
  40. - SetupAxisScale + ImPlotScale have replaced ImPlotAxisFlags_LogScale and ImPlotAxisFlags_Time flags
  41. - ImPlotFormatters should now return an int indicating the size written
  42. - the signature of ImPlotGetter has been reversed so that void* user_data is the last argument and consistent with other callbacks
  43. - 2021/10/19 (0.13) - MAJOR API OVERHAUL! See #168 and #272
  44. - TRIVIAL RENAME:
  45. - ImPlotLimits -> ImPlotRect
  46. - ImPlotYAxis_ -> ImAxis_
  47. - SetPlotYAxis -> SetAxis
  48. - BeginDragDropTarget -> BeginDragDropTargetPlot
  49. - BeginDragDropSource -> BeginDragDropSourcePlot
  50. - ImPlotFlags_NoMousePos -> ImPlotFlags_NoMouseText
  51. - SetNextPlotLimits -> SetNextAxesLimits
  52. - SetMouseTextLocation -> SetupMouseText
  53. - SIGNATURE MODIFIED:
  54. - PixelsToPlot/PlotToPixels -> added optional X-Axis arg
  55. - GetPlotMousePos -> added optional X-Axis arg
  56. - GetPlotLimits -> added optional X-Axis arg
  57. - GetPlotSelection -> added optional X-Axis arg
  58. - DragLineX/Y/DragPoint -> now takes int id; removed labels (render with Annotation/Tag instead)
  59. - REPLACED:
  60. - IsPlotXAxisHovered/IsPlotXYAxisHovered -> IsAxisHovered(ImAxis)
  61. - BeginDragDropTargetX/BeginDragDropTargetY -> BeginDragDropTargetAxis(ImAxis)
  62. - BeginDragDropSourceX/BeginDragDropSourceY -> BeginDragDropSourceAxis(ImAxis)
  63. - ImPlotCol_XAxis, ImPlotCol_YAxis1, etc. -> ImPlotCol_AxisText (push/pop this around SetupAxis to style individual axes)
  64. - ImPlotCol_XAxisGrid, ImPlotCol_Y1AxisGrid -> ImPlotCol_AxisGrid (push/pop this around SetupAxis to style individual axes)
  65. - SetNextPlotLimitsX/Y -> SetNextAxisLimits(ImAxis)
  66. - LinkNextPlotLimits -> SetNextAxisLinks(ImAxis)
  67. - FitNextPlotAxes -> SetNextAxisToFit(ImAxis)/SetNextAxesToFit
  68. - SetLegendLocation -> SetupLegend
  69. - ImPlotFlags_NoHighlight -> ImPlotLegendFlags_NoHighlight
  70. - ImPlotOrientation -> ImPlotLegendFlags_Horizontal
  71. - Annotate -> Annotation
  72. - REMOVED:
  73. - GetPlotQuery, SetPlotQuery, IsPlotQueried -> use DragRect
  74. - SetNextPlotTicksX, SetNextPlotTicksY -> use SetupAxisTicks
  75. - SetNextPlotFormatX, SetNextPlotFormatY -> use SetupAxisFormat
  76. - AnnotateClamped -> use Annotation(bool clamp = true)
  77. - OBSOLETED:
  78. - BeginPlot (original signature) -> use simplified signature + Setup API
  79. - 2021/07/30 (0.12) - The offset argument of `PlotXG` functions was been removed. Implement offsetting in your getter callback instead.
  80. - 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap.
  81. ShowColormapScale was changed to ColormapScale and requires additional arguments.
  82. - 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size.
  83. - 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements.
  84. - 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved
  85. to implot_internal.h due to its immaturity.
  86. - 2020/10/16 (0.8) - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding
  87. - 2020/09/10 (0.8) - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0.
  88. - 2020/09/07 (0.8) - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG)
  89. - 2020/09/06 (0.7) - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset
  90. is simply 0. This more closely matches ImGui's style and makes it easier to enable non-default but commonly used flags (e.g. ImPlotAxisFlags_Time).
  91. - 2020/08/28 (0.5) - ImPlotMarker_ can no longer be combined with bitwise OR, |. This features caused unecessary slow-down, and almost no one used it.
  92. - 2020/08/25 (0.5) - ImPlotAxisFlags_Scientific was removed. Logarithmic axes automatically uses scientific notation.
  93. - 2020/08/17 (0.5) - PlotText was changed so that text is centered horizontally and vertically about the desired point.
  94. - 2020/08/16 (0.5) - An ImPlotContext must be explicitly created and destroyed now with `CreateContext` and `DestroyContext`. Previously, the context was statically initialized in this source file.
  95. - 2020/06/13 (0.4) - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default.
  96. - 2020/06/03 (0.3) - The signature and behavior of PlotPieChart was changed so that data with sum less than 1 can optionally be normalized. The label format can now be specified as well.
  97. - 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`.
  98. - 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead.
  99. - 2020/05/29 (0.3) - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2
  100. - 2020/05/16 (0.2) - All plotting functions were reverted to being prefixed with "Plot" to maintain a consistent VerbNoun style. `Plot` was split into `PlotLine`
  101. and `PlotScatter` (however, `PlotLine` can still be used to plot scatter points as `Plot` did before.). `Bar` is not `PlotBars`, to indicate
  102. that multiple bars will be plotted.
  103. - 2020/05/13 (0.2) - `ImMarker` was change to `ImPlotMarker` and `ImAxisFlags` was changed to `ImPlotAxisFlags`.
  104. - 2020/05/11 (0.2) - `ImPlotFlags_Selection` was changed to `ImPlotFlags_BoxSelect`
  105. - 2020/05/11 (0.2) - The namespace ImGui:: was replaced with ImPlot::. As a result, the following additional changes were made:
  106. - Functions that were prefixed or decorated with the word "Plot" have been truncated. E.g., `ImGui::PlotBars` is now just `ImPlot::Bar`.
  107. It should be fairly obvious what was what.
  108. - Some functions have been given names that would have otherwise collided with the ImGui namespace. This has been done to maintain a consistent
  109. style with ImGui. E.g., 'ImGui::PushPlotStyleVar` is now 'ImPlot::PushStyleVar'.
  110. - 2020/05/10 (0.2) - The following function/struct names were changes:
  111. - ImPlotRange -> ImPlotLimits
  112. - GetPlotRange() -> GetPlotLimits()
  113. - SetNextPlotRange -> SetNextPlotLimits
  114. - SetNextPlotRangeX -> SetNextPlotLimitsX
  115. - SetNextPlotRangeY -> SetNextPlotLimitsY
  116. - 2020/05/10 (0.2) - Plot queries are pixel based by default. Query rects that maintain relative plot position have been removed. This was done to support multi-y-axis.
  117. */
  118. #ifndef IMGUI_DEFINE_MATH_OPERATORS
  119. #define IMGUI_DEFINE_MATH_OPERATORS
  120. #endif
  121. #include "implot.h"
  122. #ifndef IMGUI_DISABLE
  123. #include "implot_internal.h"
  124. #include <stdlib.h>
  125. // Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit.
  126. #if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll)
  127. #define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All
  128. #endif
  129. // Support for pre-1.89.7 versions.
  130. #if (IMGUI_VERSION_NUM < 18966)
  131. #define ImGuiButtonFlags_AllowOverlap ImGuiButtonFlags_AllowItemOverlap
  132. #endif
  133. // Visual Studio warnings
  134. #ifdef _MSC_VER
  135. #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
  136. #endif
  137. // Clang/GCC warnings with -Weverything
  138. #if defined(__clang__)
  139. #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal
  140. #elif defined(__GNUC__)
  141. #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
  142. #endif
  143. // Global plot context
  144. #ifndef GImPlot
  145. ImPlotContext* GImPlot = nullptr;
  146. #endif
  147. //-----------------------------------------------------------------------------
  148. // Struct Implementations
  149. //-----------------------------------------------------------------------------
  150. ImPlotInputMap::ImPlotInputMap() {
  151. ImPlot::MapInputDefault(this);
  152. }
  153. ImPlotStyle::ImPlotStyle() {
  154. LineWeight = 1;
  155. Marker = ImPlotMarker_None;
  156. MarkerSize = 4;
  157. MarkerWeight = 1;
  158. FillAlpha = 1;
  159. ErrorBarSize = 5;
  160. ErrorBarWeight = 1.5f;
  161. DigitalBitHeight = 8;
  162. DigitalBitGap = 4;
  163. PlotBorderSize = 1;
  164. MinorAlpha = 0.25f;
  165. MajorTickLen = ImVec2(10,10);
  166. MinorTickLen = ImVec2(5,5);
  167. MajorTickSize = ImVec2(1,1);
  168. MinorTickSize = ImVec2(1,1);
  169. MajorGridSize = ImVec2(1,1);
  170. MinorGridSize = ImVec2(1,1);
  171. PlotPadding = ImVec2(10,10);
  172. LabelPadding = ImVec2(5,5);
  173. LegendPadding = ImVec2(10,10);
  174. LegendInnerPadding = ImVec2(5,5);
  175. LegendSpacing = ImVec2(5,0);
  176. MousePosPadding = ImVec2(10,10);
  177. AnnotationPadding = ImVec2(2,2);
  178. FitPadding = ImVec2(0,0);
  179. PlotDefaultSize = ImVec2(400,300);
  180. PlotMinSize = ImVec2(200,150);
  181. ImPlot::StyleColorsAuto(this);
  182. Colormap = ImPlotColormap_Deep;
  183. UseLocalTime = false;
  184. Use24HourClock = false;
  185. UseISO8601 = false;
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Style
  189. //-----------------------------------------------------------------------------
  190. namespace ImPlot {
  191. const char* GetStyleColorName(ImPlotCol col) {
  192. static const char* col_names[ImPlotCol_COUNT] = {
  193. "Line",
  194. "Fill",
  195. "MarkerOutline",
  196. "MarkerFill",
  197. "ErrorBar",
  198. "FrameBg",
  199. "PlotBg",
  200. "PlotBorder",
  201. "LegendBg",
  202. "LegendBorder",
  203. "LegendText",
  204. "TitleText",
  205. "InlayText",
  206. "AxisText",
  207. "AxisGrid",
  208. "AxisTick",
  209. "AxisBg",
  210. "AxisBgHovered",
  211. "AxisBgActive",
  212. "Selection",
  213. "Crosshairs"
  214. };
  215. return col_names[col];
  216. }
  217. const char* GetMarkerName(ImPlotMarker marker) {
  218. switch (marker) {
  219. case ImPlotMarker_None: return "None";
  220. case ImPlotMarker_Circle: return "Circle";
  221. case ImPlotMarker_Square: return "Square";
  222. case ImPlotMarker_Diamond: return "Diamond";
  223. case ImPlotMarker_Up: return "Up";
  224. case ImPlotMarker_Down: return "Down";
  225. case ImPlotMarker_Left: return "Left";
  226. case ImPlotMarker_Right: return "Right";
  227. case ImPlotMarker_Cross: return "Cross";
  228. case ImPlotMarker_Plus: return "Plus";
  229. case ImPlotMarker_Asterisk: return "Asterisk";
  230. default: return "";
  231. }
  232. }
  233. ImVec4 GetAutoColor(ImPlotCol idx) {
  234. ImVec4 col(0,0,0,1);
  235. switch(idx) {
  236. case ImPlotCol_Line: return col; // these are plot dependent!
  237. case ImPlotCol_Fill: return col; // these are plot dependent!
  238. case ImPlotCol_MarkerOutline: return col; // these are plot dependent!
  239. case ImPlotCol_MarkerFill: return col; // these are plot dependent!
  240. case ImPlotCol_ErrorBar: return ImGui::GetStyleColorVec4(ImGuiCol_Text);
  241. case ImPlotCol_FrameBg: return ImGui::GetStyleColorVec4(ImGuiCol_FrameBg);
  242. case ImPlotCol_PlotBg: return ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
  243. case ImPlotCol_PlotBorder: return ImGui::GetStyleColorVec4(ImGuiCol_Border);
  244. case ImPlotCol_LegendBg: return ImGui::GetStyleColorVec4(ImGuiCol_PopupBg);
  245. case ImPlotCol_LegendBorder: return GetStyleColorVec4(ImPlotCol_PlotBorder);
  246. case ImPlotCol_LegendText: return GetStyleColorVec4(ImPlotCol_InlayText);
  247. case ImPlotCol_TitleText: return ImGui::GetStyleColorVec4(ImGuiCol_Text);
  248. case ImPlotCol_InlayText: return ImGui::GetStyleColorVec4(ImGuiCol_Text);
  249. case ImPlotCol_AxisText: return ImGui::GetStyleColorVec4(ImGuiCol_Text);
  250. case ImPlotCol_AxisGrid: return GetStyleColorVec4(ImPlotCol_AxisText) * ImVec4(1,1,1,0.25f);
  251. case ImPlotCol_AxisTick: return GetStyleColorVec4(ImPlotCol_AxisGrid);
  252. case ImPlotCol_AxisBg: return ImVec4(0,0,0,0);
  253. case ImPlotCol_AxisBgHovered: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);
  254. case ImPlotCol_AxisBgActive: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
  255. case ImPlotCol_Selection: return ImVec4(1,1,0,1);
  256. case ImPlotCol_Crosshairs: return GetStyleColorVec4(ImPlotCol_PlotBorder);
  257. default: return col;
  258. }
  259. }
  260. struct ImPlotStyleVarInfo {
  261. ImGuiDataType Type;
  262. ImU32 Count;
  263. ImU32 Offset;
  264. void* GetVarPtr(ImPlotStyle* style) const { return (void*)((unsigned char*)style + Offset); }
  265. };
  266. static const ImPlotStyleVarInfo GPlotStyleVarInfo[] =
  267. {
  268. { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight
  269. { ImGuiDataType_S32, 1, (ImU32)offsetof(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker
  270. { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize
  271. { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight
  272. { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha
  273. { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize
  274. { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight
  275. { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight
  276. { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap
  277. { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize
  278. { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha
  279. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen
  280. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen
  281. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize
  282. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize
  283. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize
  284. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize
  285. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding
  286. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine
  287. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding
  288. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding
  289. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing
  290. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding
  291. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding
  292. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding
  293. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize
  294. { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize
  295. };
  296. static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) {
  297. IM_ASSERT(idx >= 0 && idx < ImPlotStyleVar_COUNT);
  298. IM_ASSERT(IM_ARRAYSIZE(GPlotStyleVarInfo) == ImPlotStyleVar_COUNT);
  299. return &GPlotStyleVarInfo[idx];
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Generic Helpers
  303. //-----------------------------------------------------------------------------
  304. void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *text_begin, const char* text_end) {
  305. // the code below is based loosely on ImFont::RenderText
  306. if (!text_end)
  307. text_end = text_begin + strlen(text_begin);
  308. ImGuiContext& g = *GImGui;
  309. #ifdef IMGUI_HAS_TEXTURES
  310. ImFontBaked* font = g.Font->GetFontBaked(g.FontSize);
  311. const float scale = g.FontSize / font->Size;
  312. #else
  313. ImFont* font = g.Font;
  314. const float scale = g.FontSize / font->FontSize;
  315. #endif
  316. // Align to be pixel perfect
  317. pos.x = ImFloor(pos.x);
  318. pos.y = ImFloor(pos.y);
  319. const char* s = text_begin;
  320. int chars_exp = (int)(text_end - s);
  321. int chars_rnd = 0;
  322. const int vtx_count_max = chars_exp * 4;
  323. const int idx_count_max = chars_exp * 6;
  324. DrawList->PrimReserve(idx_count_max, vtx_count_max);
  325. while (s < text_end) {
  326. unsigned int c = (unsigned int)*s;
  327. if (c < 0x80) {
  328. s += 1;
  329. }
  330. else {
  331. s += ImTextCharFromUtf8(&c, s, text_end);
  332. if (c == 0) // Malformed UTF-8?
  333. break;
  334. }
  335. const ImFontGlyph * glyph = font->FindGlyph((ImWchar)c);
  336. if (glyph == nullptr) {
  337. continue;
  338. }
  339. DrawList->PrimQuadUV(pos + ImVec2(glyph->Y0, -glyph->X0) * scale, pos + ImVec2(glyph->Y0, -glyph->X1) * scale,
  340. pos + ImVec2(glyph->Y1, -glyph->X1) * scale, pos + ImVec2(glyph->Y1, -glyph->X0) * scale,
  341. ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V0),
  342. ImVec2(glyph->U1, glyph->V1), ImVec2(glyph->U0, glyph->V1),
  343. col);
  344. pos.y -= glyph->AdvanceX * scale;
  345. chars_rnd++;
  346. }
  347. // Give back unused vertices
  348. int chars_skp = chars_exp-chars_rnd;
  349. DrawList->PrimUnreserve(chars_skp*6, chars_skp*4);
  350. }
  351. void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end) {
  352. float txt_ht = ImGui::GetTextLineHeight();
  353. const char* title_end = ImGui::FindRenderedTextEnd(text_begin, text_end);
  354. ImVec2 text_size;
  355. float y = 0;
  356. while (const char* tmp = (const char*)memchr(text_begin, '\n', title_end-text_begin)) {
  357. text_size = ImGui::CalcTextSize(text_begin,tmp,true);
  358. DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,tmp);
  359. text_begin = tmp + 1;
  360. y += txt_ht;
  361. }
  362. text_size = ImGui::CalcTextSize(text_begin,title_end,true);
  363. DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,title_end);
  364. }
  365. double NiceNum(double x, bool round) {
  366. double f;
  367. double nf;
  368. int expv = (int)floor(ImLog10(x));
  369. f = x / ImPow(10.0, (double)expv);
  370. if (round)
  371. if (f < 1.5)
  372. nf = 1;
  373. else if (f < 3)
  374. nf = 2;
  375. else if (f < 7)
  376. nf = 5;
  377. else
  378. nf = 10;
  379. else if (f <= 1)
  380. nf = 1;
  381. else if (f <= 2)
  382. nf = 2;
  383. else if (f <= 5)
  384. nf = 5;
  385. else
  386. nf = 10;
  387. return nf * ImPow(10.0, expv);
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Context Utils
  391. //-----------------------------------------------------------------------------
  392. void SetImGuiContext(ImGuiContext* ctx) {
  393. ImGui::SetCurrentContext(ctx);
  394. }
  395. ImPlotContext* CreateContext() {
  396. ImPlotContext* ctx = IM_NEW(ImPlotContext)();
  397. Initialize(ctx);
  398. if (GImPlot == nullptr)
  399. SetCurrentContext(ctx);
  400. return ctx;
  401. }
  402. void DestroyContext(ImPlotContext* ctx) {
  403. if (ctx == nullptr)
  404. ctx = GImPlot;
  405. if (GImPlot == ctx)
  406. SetCurrentContext(nullptr);
  407. IM_DELETE(ctx);
  408. }
  409. ImPlotContext* GetCurrentContext() {
  410. return GImPlot;
  411. }
  412. void SetCurrentContext(ImPlotContext* ctx) {
  413. GImPlot = ctx;
  414. }
  415. #define IMPLOT_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32), qual)
  416. #define IM_RGB(r,g,b) IM_COL32(r,g,b,255)
  417. void Initialize(ImPlotContext* ctx) {
  418. ResetCtxForNextPlot(ctx);
  419. ResetCtxForNextAlignedPlots(ctx);
  420. ResetCtxForNextSubplot(ctx);
  421. const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396 };
  422. const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 };
  423. const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986 };
  424. const ImU32 Paired[] = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481};
  425. const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301 };
  426. const ImU32 Plasma[] = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752 };
  427. const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295 };
  428. const ImU32 Cool[] = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015 };
  429. const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295 };
  430. const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 };
  431. const ImU32 Twilight[] = {IM_RGB(226,217,226),IM_RGB(166,191,202),IM_RGB(109,144,192),IM_RGB(95,88,176),IM_RGB(83,30,124),IM_RGB(47,20,54),IM_RGB(100,25,75),IM_RGB(159,60,80),IM_RGB(192,117,94),IM_RGB(208,179,158),IM_RGB(226,217,226)};
  432. const ImU32 RdBu[] = {IM_RGB(103,0,31),IM_RGB(178,24,43),IM_RGB(214,96,77),IM_RGB(244,165,130),IM_RGB(253,219,199),IM_RGB(247,247,247),IM_RGB(209,229,240),IM_RGB(146,197,222),IM_RGB(67,147,195),IM_RGB(33,102,172),IM_RGB(5,48,97)};
  433. const ImU32 BrBG[] = {IM_RGB(84,48,5),IM_RGB(140,81,10),IM_RGB(191,129,45),IM_RGB(223,194,125),IM_RGB(246,232,195),IM_RGB(245,245,245),IM_RGB(199,234,229),IM_RGB(128,205,193),IM_RGB(53,151,143),IM_RGB(1,102,94),IM_RGB(0,60,48)};
  434. const ImU32 PiYG[] = {IM_RGB(142,1,82),IM_RGB(197,27,125),IM_RGB(222,119,174),IM_RGB(241,182,218),IM_RGB(253,224,239),IM_RGB(247,247,247),IM_RGB(230,245,208),IM_RGB(184,225,134),IM_RGB(127,188,65),IM_RGB(77,146,33),IM_RGB(39,100,25)};
  435. const ImU32 Spectral[] = {IM_RGB(158,1,66),IM_RGB(213,62,79),IM_RGB(244,109,67),IM_RGB(253,174,97),IM_RGB(254,224,139),IM_RGB(255,255,191),IM_RGB(230,245,152),IM_RGB(171,221,164),IM_RGB(102,194,165),IM_RGB(50,136,189),IM_RGB(94,79,162)};
  436. const ImU32 Greys[] = {IM_COL32_WHITE, IM_COL32_BLACK };
  437. IMPLOT_APPEND_CMAP(Deep, true);
  438. IMPLOT_APPEND_CMAP(Dark, true);
  439. IMPLOT_APPEND_CMAP(Pastel, true);
  440. IMPLOT_APPEND_CMAP(Paired, true);
  441. IMPLOT_APPEND_CMAP(Viridis, false);
  442. IMPLOT_APPEND_CMAP(Plasma, false);
  443. IMPLOT_APPEND_CMAP(Hot, false);
  444. IMPLOT_APPEND_CMAP(Cool, false);
  445. IMPLOT_APPEND_CMAP(Pink, false);
  446. IMPLOT_APPEND_CMAP(Jet, false);
  447. IMPLOT_APPEND_CMAP(Twilight, false);
  448. IMPLOT_APPEND_CMAP(RdBu, false);
  449. IMPLOT_APPEND_CMAP(BrBG, false);
  450. IMPLOT_APPEND_CMAP(PiYG, false);
  451. IMPLOT_APPEND_CMAP(Spectral, false);
  452. IMPLOT_APPEND_CMAP(Greys, false);
  453. }
  454. void ResetCtxForNextPlot(ImPlotContext* ctx) {
  455. // reset the next plot/item data
  456. ctx->NextPlotData.Reset();
  457. ctx->NextItemData.Reset();
  458. // reset labels
  459. ctx->Annotations.Reset();
  460. ctx->Tags.Reset();
  461. // reset extents/fit
  462. ctx->OpenContextThisFrame = false;
  463. // reset digital plot items count
  464. ctx->DigitalPlotItemCnt = 0;
  465. ctx->DigitalPlotOffset = 0;
  466. // nullify plot
  467. ctx->CurrentPlot = nullptr;
  468. ctx->CurrentItem = nullptr;
  469. ctx->PreviousItem = nullptr;
  470. }
  471. void ResetCtxForNextAlignedPlots(ImPlotContext* ctx) {
  472. ctx->CurrentAlignmentH = nullptr;
  473. ctx->CurrentAlignmentV = nullptr;
  474. }
  475. void ResetCtxForNextSubplot(ImPlotContext* ctx) {
  476. ctx->CurrentSubplot = nullptr;
  477. ctx->CurrentAlignmentH = nullptr;
  478. ctx->CurrentAlignmentV = nullptr;
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Plot Utils
  482. //-----------------------------------------------------------------------------
  483. ImPlotPlot* GetPlot(const char* title) {
  484. ImGuiWindow* Window = GImGui->CurrentWindow;
  485. const ImGuiID ID = Window->GetID(title);
  486. return GImPlot->Plots.GetByKey(ID);
  487. }
  488. ImPlotPlot* GetCurrentPlot() {
  489. return GImPlot->CurrentPlot;
  490. }
  491. void BustPlotCache() {
  492. ImPlotContext& gp = *GImPlot;
  493. gp.Plots.Clear();
  494. gp.Subplots.Clear();
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Legend Utils
  498. //-----------------------------------------------------------------------------
  499. ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation loc, const ImVec2& pad) {
  500. ImVec2 pos;
  501. if (ImHasFlag(loc, ImPlotLocation_West) && !ImHasFlag(loc, ImPlotLocation_East))
  502. pos.x = outer_rect.Min.x + pad.x;
  503. else if (!ImHasFlag(loc, ImPlotLocation_West) && ImHasFlag(loc, ImPlotLocation_East))
  504. pos.x = outer_rect.Max.x - pad.x - inner_size.x;
  505. else
  506. pos.x = outer_rect.GetCenter().x - inner_size.x * 0.5f;
  507. // legend reference point y
  508. if (ImHasFlag(loc, ImPlotLocation_North) && !ImHasFlag(loc, ImPlotLocation_South))
  509. pos.y = outer_rect.Min.y + pad.y;
  510. else if (!ImHasFlag(loc, ImPlotLocation_North) && ImHasFlag(loc, ImPlotLocation_South))
  511. pos.y = outer_rect.Max.y - pad.y - inner_size.y;
  512. else
  513. pos.y = outer_rect.GetCenter().y - inner_size.y * 0.5f;
  514. pos.x = IM_ROUND(pos.x);
  515. pos.y = IM_ROUND(pos.y);
  516. return pos;
  517. }
  518. ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical) {
  519. // vars
  520. const int nItems = items.GetLegendCount();
  521. const float txt_ht = ImGui::GetTextLineHeight();
  522. const float icon_size = txt_ht;
  523. // get label max width
  524. float max_label_width = 0;
  525. float sum_label_width = 0;
  526. for (int i = 0; i < nItems; ++i) {
  527. const char* label = items.GetLegendLabel(i);
  528. const float label_width = ImGui::CalcTextSize(label, nullptr, true).x;
  529. max_label_width = label_width > max_label_width ? label_width : max_label_width;
  530. sum_label_width += label_width;
  531. }
  532. // calc legend size
  533. const ImVec2 legend_size = vertical ?
  534. ImVec2(pad.x * 2 + icon_size + max_label_width, pad.y * 2 + nItems * txt_ht + (nItems - 1) * spacing.y) :
  535. ImVec2(pad.x * 2 + icon_size * nItems + sum_label_width + (nItems - 1) * spacing.x, pad.y * 2 + txt_ht);
  536. return legend_size;
  537. }
  538. bool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad) {
  539. bool clamped = false;
  540. ImRect outer_rect_pad(outer_rect.Min + pad, outer_rect.Max - pad);
  541. if (legend_rect.Min.x < outer_rect_pad.Min.x) {
  542. legend_rect.Min.x = outer_rect_pad.Min.x;
  543. clamped = true;
  544. }
  545. if (legend_rect.Min.y < outer_rect_pad.Min.y) {
  546. legend_rect.Min.y = outer_rect_pad.Min.y;
  547. clamped = true;
  548. }
  549. if (legend_rect.Max.x > outer_rect_pad.Max.x) {
  550. legend_rect.Max.x = outer_rect_pad.Max.x;
  551. clamped = true;
  552. }
  553. if (legend_rect.Max.y > outer_rect_pad.Max.y) {
  554. legend_rect.Max.y = outer_rect_pad.Max.y;
  555. clamped = true;
  556. }
  557. return clamped;
  558. }
  559. int LegendSortingComp(const void* _a, const void* _b) {
  560. ImPlotItemGroup* items = GImPlot->SortItems;
  561. const int a = *(const int*)_a;
  562. const int b = *(const int*)_b;
  563. const char* label_a = items->GetLegendLabel(a);
  564. const char* label_b = items->GetLegendLabel(b);
  565. return strcmp(label_a,label_b);
  566. }
  567. bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool hovered, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList) {
  568. // vars
  569. const float txt_ht = ImGui::GetTextLineHeight();
  570. const float icon_size = txt_ht;
  571. const float icon_shrink = 2;
  572. ImU32 col_txt = GetStyleColorU32(ImPlotCol_LegendText);
  573. ImU32 col_txt_dis = ImAlphaU32(col_txt, 0.25f);
  574. // render each legend item
  575. float sum_label_width = 0;
  576. bool any_item_hovered = false;
  577. const int num_items = items.GetLegendCount();
  578. if (num_items < 1)
  579. return hovered;
  580. // build render order
  581. ImPlotContext& gp = *GImPlot;
  582. ImVector<int>& indices = gp.TempInt1;
  583. indices.resize(num_items);
  584. for (int i = 0; i < num_items; ++i)
  585. indices[i] = i;
  586. if (ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_Sort) && num_items > 1) {
  587. gp.SortItems = &items;
  588. qsort(indices.Data, num_items, sizeof(int), LegendSortingComp);
  589. }
  590. // render
  591. for (int i = 0; i < num_items; ++i) {
  592. const int idx = indices[i];
  593. ImPlotItem* item = items.GetLegendItem(idx);
  594. const char* label = items.GetLegendLabel(idx);
  595. const float label_width = ImGui::CalcTextSize(label, nullptr, true).x;
  596. const ImVec2 top_left = vertical ?
  597. legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y)) :
  598. legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0);
  599. sum_label_width += label_width;
  600. ImRect icon_bb;
  601. icon_bb.Min = top_left + ImVec2(icon_shrink,icon_shrink);
  602. icon_bb.Max = top_left + ImVec2(icon_size - icon_shrink, icon_size - icon_shrink);
  603. ImRect label_bb;
  604. label_bb.Min = top_left;
  605. label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size);
  606. ImU32 col_txt_hl;
  607. ImU32 col_item = ImAlphaU32(item->Color,1);
  608. ImRect button_bb(icon_bb.Min, label_bb.Max);
  609. ImGui::KeepAliveID(item->ID);
  610. bool item_hov = false;
  611. bool item_hld = false;
  612. bool item_clk = ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoButtons)
  613. ? false
  614. : ImGui::ButtonBehavior(button_bb, item->ID, &item_hov, &item_hld);
  615. if (item_clk)
  616. item->Show = !item->Show;
  617. const bool can_hover = (item_hov)
  618. && (!ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightItem)
  619. || !ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightAxis));
  620. if (can_hover) {
  621. item->LegendHoverRect.Min = icon_bb.Min;
  622. item->LegendHoverRect.Max = label_bb.Max;
  623. item->LegendHovered = true;
  624. col_txt_hl = ImMixU32(col_txt, col_item, 64);
  625. any_item_hovered = true;
  626. }
  627. else {
  628. col_txt_hl = ImGui::GetColorU32(col_txt);
  629. }
  630. ImU32 col_icon;
  631. if (item_hld)
  632. col_icon = item->Show ? ImAlphaU32(col_item,0.5f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f);
  633. else if (item_hov)
  634. col_icon = item->Show ? ImAlphaU32(col_item,0.75f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.75f);
  635. else
  636. col_icon = item->Show ? col_item : col_txt_dis;
  637. DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, col_icon);
  638. const char* text_display_end = ImGui::FindRenderedTextEnd(label, nullptr);
  639. if (label != text_display_end)
  640. DrawList.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_txt_hl : col_txt_dis, label, text_display_end);
  641. }
  642. return hovered && !any_item_hovered;
  643. }
  644. //-----------------------------------------------------------------------------
  645. // Locators
  646. //-----------------------------------------------------------------------------
  647. static const float TICK_FILL_X = 0.8f;
  648. static const float TICK_FILL_Y = 1.0f;
  649. void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {
  650. if (range.Min == range.Max)
  651. return;
  652. const int nMinor = 10;
  653. const int nMajor = ImMax(2, (int)IM_ROUND(pixels / (vertical ? 300.0f : 400.0f)));
  654. const double nice_range = NiceNum(range.Size() * 0.99, false);
  655. const double interval = NiceNum(nice_range / (nMajor - 1), true);
  656. const double graphmin = floor(range.Min / interval) * interval;
  657. const double graphmax = ceil(range.Max / interval) * interval;
  658. bool first_major_set = false;
  659. int first_major_idx = 0;
  660. const int idx0 = ticker.TickCount(); // ticker may have user custom ticks
  661. ImVec2 total_size(0,0);
  662. for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) {
  663. // is this zero? combat zero formatting issues
  664. if (major-interval < 0 && major+interval > 0)
  665. major = 0;
  666. if (range.Contains(major)) {
  667. if (!first_major_set) {
  668. first_major_idx = ticker.TickCount();
  669. first_major_set = true;
  670. }
  671. total_size += ticker.AddTick(major, true, 0, true, formatter, formatter_data).LabelSize;
  672. }
  673. for (int i = 1; i < nMinor; ++i) {
  674. double minor = major + i * interval / nMinor;
  675. if (range.Contains(minor)) {
  676. total_size += ticker.AddTick(minor, false, 0, true, formatter, formatter_data).LabelSize;
  677. }
  678. }
  679. }
  680. // prune if necessary
  681. if ((!vertical && total_size.x > pixels*TICK_FILL_X) || (vertical && total_size.y > pixels*TICK_FILL_Y)) {
  682. for (int i = first_major_idx-1; i >= idx0; i -= 2)
  683. ticker.Ticks[i].ShowLabel = false;
  684. for (int i = first_major_idx+1; i < ticker.TickCount(); i += 2)
  685. ticker.Ticks[i].ShowLabel = false;
  686. }
  687. }
  688. bool CalcLogarithmicExponents(const ImPlotRange& range, float pix, bool vertical, int& exp_min, int& exp_max, int& exp_step) {
  689. if (range.Min * range.Max > 0) {
  690. const int nMajor = vertical ? ImMax(2, (int)IM_ROUND(pix * 0.02f)) : ImMax(2, (int)IM_ROUND(pix * 0.01f)); // TODO: magic numbers
  691. double log_min = ImLog10(ImAbs(range.Min));
  692. double log_max = ImLog10(ImAbs(range.Max));
  693. double log_a = ImMin(log_min,log_max);
  694. double log_b = ImMax(log_min,log_max);
  695. exp_step = ImMax(1,(int)(log_b - log_a) / nMajor);
  696. exp_min = (int)log_a;
  697. exp_max = (int)log_b;
  698. if (exp_step != 1) {
  699. while(exp_step % 3 != 0) exp_step++; // make step size multiple of three
  700. while(exp_min % exp_step != 0) exp_min--; // decrease exp_min until exp_min + N * exp_step will be 0
  701. }
  702. return true;
  703. }
  704. return false;
  705. }
  706. void AddTicksLogarithmic(const ImPlotRange& range, int exp_min, int exp_max, int exp_step, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) {
  707. const double sign = ImSign(range.Max);
  708. for (int e = exp_min - exp_step; e < (exp_max + exp_step); e += exp_step) {
  709. double major1 = sign*ImPow(10, (double)(e));
  710. double major2 = sign*ImPow(10, (double)(e + 1));
  711. double interval = (major2 - major1) / 9;
  712. if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON))
  713. ticker.AddTick(major1, true, 0, true, formatter, data);
  714. for (int j = 0; j < exp_step; ++j) {
  715. major1 = sign*ImPow(10, (double)(e+j));
  716. major2 = sign*ImPow(10, (double)(e+j+1));
  717. interval = (major2 - major1) / 9;
  718. for (int i = 1; i < (9 + (int)(j < (exp_step - 1))); ++i) {
  719. double minor = major1 + i * interval;
  720. if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON))
  721. ticker.AddTick(minor, false, 0, false, formatter, data);
  722. }
  723. }
  724. }
  725. }
  726. void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {
  727. int exp_min, exp_max, exp_step;
  728. if (CalcLogarithmicExponents(range, pixels, vertical, exp_min, exp_max, exp_step))
  729. AddTicksLogarithmic(range, exp_min, exp_max, exp_step, ticker, formatter, formatter_data);
  730. }
  731. float CalcSymLogPixel(double plt, const ImPlotRange& range, float pixels) {
  732. double scaleToPixels = pixels / range.Size();
  733. double scaleMin = TransformForward_SymLog(range.Min,nullptr);
  734. double scaleMax = TransformForward_SymLog(range.Max,nullptr);
  735. double s = TransformForward_SymLog(plt, nullptr);
  736. double t = (s - scaleMin) / (scaleMax - scaleMin);
  737. plt = range.Min + range.Size() * t;
  738. return (float)(0 + scaleToPixels * (plt - range.Min));
  739. }
  740. void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {
  741. if (range.Min >= -1 && range.Max <= 1) {
  742. Locator_Default(ticker, range, pixels, vertical, formatter, formatter_data);
  743. }
  744. else if (range.Min * range.Max < 0) { // cross zero
  745. const float pix_min = 0;
  746. const float pix_max = pixels;
  747. const float pix_p1 = CalcSymLogPixel(1, range, pixels);
  748. const float pix_n1 = CalcSymLogPixel(-1, range, pixels);
  749. int exp_min_p, exp_max_p, exp_step_p;
  750. int exp_min_n, exp_max_n, exp_step_n;
  751. CalcLogarithmicExponents(ImPlotRange(1,range.Max), ImAbs(pix_max-pix_p1),vertical,exp_min_p,exp_max_p,exp_step_p);
  752. CalcLogarithmicExponents(ImPlotRange(range.Min,-1),ImAbs(pix_n1-pix_min),vertical,exp_min_n,exp_max_n,exp_step_n);
  753. int exp_step = ImMax(exp_step_n, exp_step_p);
  754. ticker.AddTick(0,true,0,true,formatter,formatter_data);
  755. AddTicksLogarithmic(ImPlotRange(1,range.Max), exp_min_p,exp_max_p,exp_step,ticker,formatter,formatter_data);
  756. AddTicksLogarithmic(ImPlotRange(range.Min,-1),exp_min_n,exp_max_n,exp_step,ticker,formatter,formatter_data);
  757. }
  758. else {
  759. Locator_Log10(ticker, range, pixels, vertical, formatter, formatter_data);
  760. }
  761. }
  762. void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) {
  763. for (int i = 0; i < n; ++i) {
  764. if (labels != nullptr)
  765. ticker.AddTick(values[i], false, 0, true, labels[i]);
  766. else
  767. ticker.AddTick(values[i], false, 0, true, formatter, data);
  768. }
  769. }
  770. //-----------------------------------------------------------------------------
  771. // Time Ticks and Utils
  772. //-----------------------------------------------------------------------------
  773. // this may not be thread safe?
  774. static const double TimeUnitSpans[ImPlotTimeUnit_COUNT] = {
  775. 0.000001,
  776. 0.001,
  777. 1,
  778. 60,
  779. 3600,
  780. 86400,
  781. 2629800,
  782. 31557600
  783. };
  784. inline ImPlotTimeUnit GetUnitForRange(double range) {
  785. static double cutoffs[ImPlotTimeUnit_COUNT] = {0.001, 1, 60, 3600, 86400, 2629800, 31557600, IMPLOT_MAX_TIME};
  786. for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) {
  787. if (range <= cutoffs[i])
  788. return (ImPlotTimeUnit)i;
  789. }
  790. return ImPlotTimeUnit_Yr;
  791. }
  792. inline int LowerBoundStep(int max_divs, const int* divs, const int* step, int size) {
  793. if (max_divs < divs[0])
  794. return 0;
  795. for (int i = 1; i < size; ++i) {
  796. if (max_divs < divs[i])
  797. return step[i-1];
  798. }
  799. return step[size-1];
  800. }
  801. inline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) {
  802. if (unit == ImPlotTimeUnit_Ms || unit == ImPlotTimeUnit_Us) {
  803. static const int step[] = {500,250,200,100,50,25,20,10,5,2,1};
  804. static const int divs[] = {2,4,5,10,20,40,50,100,200,500,1000};
  805. return LowerBoundStep(max_divs, divs, step, 11);
  806. }
  807. if (unit == ImPlotTimeUnit_S || unit == ImPlotTimeUnit_Min) {
  808. static const int step[] = {30,15,10,5,1};
  809. static const int divs[] = {2,4,6,12,60};
  810. return LowerBoundStep(max_divs, divs, step, 5);
  811. }
  812. else if (unit == ImPlotTimeUnit_Hr) {
  813. static const int step[] = {12,6,3,2,1};
  814. static const int divs[] = {2,4,8,12,24};
  815. return LowerBoundStep(max_divs, divs, step, 5);
  816. }
  817. else if (unit == ImPlotTimeUnit_Day) {
  818. static const int step[] = {14,7,2,1};
  819. static const int divs[] = {2,4,14,28};
  820. return LowerBoundStep(max_divs, divs, step, 4);
  821. }
  822. else if (unit == ImPlotTimeUnit_Mo) {
  823. static const int step[] = {6,3,2,1};
  824. static const int divs[] = {2,4,6,12};
  825. return LowerBoundStep(max_divs, divs, step, 4);
  826. }
  827. return 0;
  828. }
  829. ImPlotTime MkGmtTime(struct tm *ptm) {
  830. ImPlotTime t;
  831. #ifdef _WIN32
  832. t.S = _mkgmtime(ptm);
  833. #else
  834. t.S = timegm(ptm);
  835. #endif
  836. if (t.S < 0)
  837. t.S = 0;
  838. return t;
  839. }
  840. tm* GetGmtTime(const ImPlotTime& t, tm* ptm)
  841. {
  842. #ifdef _WIN32
  843. if (gmtime_s(ptm, &t.S) == 0)
  844. return ptm;
  845. else
  846. return nullptr;
  847. #else
  848. return gmtime_r(&t.S, ptm);
  849. #endif
  850. }
  851. ImPlotTime MkLocTime(struct tm *ptm) {
  852. ImPlotTime t;
  853. t.S = mktime(ptm);
  854. if (t.S < 0)
  855. t.S = 0;
  856. return t;
  857. }
  858. tm* GetLocTime(const ImPlotTime& t, tm* ptm) {
  859. #ifdef _WIN32
  860. if (localtime_s(ptm, &t.S) == 0)
  861. return ptm;
  862. else
  863. return nullptr;
  864. #else
  865. return localtime_r(&t.S, ptm);
  866. #endif
  867. }
  868. ImPlotTime MakeTime(int year, int month, int day, int hour, int min, int sec, int us) {
  869. tm& Tm = GImPlot->Tm;
  870. int yr = year - 1900;
  871. if (yr < 0)
  872. yr = 0;
  873. sec = sec + us / 1000000;
  874. us = us % 1000000;
  875. Tm.tm_sec = sec;
  876. Tm.tm_min = min;
  877. Tm.tm_hour = hour;
  878. Tm.tm_mday = day;
  879. Tm.tm_mon = month;
  880. Tm.tm_year = yr;
  881. ImPlotTime t = MkTime(&Tm);
  882. t.Us = us;
  883. return t;
  884. }
  885. int GetYear(const ImPlotTime& t) {
  886. tm& Tm = GImPlot->Tm;
  887. GetTime(t, &Tm);
  888. return Tm.tm_year + 1900;
  889. }
  890. int GetMonth(const ImPlotTime& t) {
  891. tm& Tm = GImPlot->Tm;
  892. ImPlot::GetTime(t, &Tm);
  893. return Tm.tm_mon;
  894. }
  895. ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) {
  896. tm& Tm = GImPlot->Tm;
  897. ImPlotTime t_out = t;
  898. switch(unit) {
  899. case ImPlotTimeUnit_Us: t_out.Us += count; break;
  900. case ImPlotTimeUnit_Ms: t_out.Us += count * 1000; break;
  901. case ImPlotTimeUnit_S: t_out.S += count; break;
  902. case ImPlotTimeUnit_Min: t_out.S += count * 60; break;
  903. case ImPlotTimeUnit_Hr: t_out.S += count * 3600; break;
  904. case ImPlotTimeUnit_Day: t_out.S += count * 86400; break;
  905. case ImPlotTimeUnit_Mo: for (int i = 0; i < abs(count); ++i) {
  906. GetTime(t_out, &Tm);
  907. if (count > 0)
  908. t_out.S += 86400 * GetDaysInMonth(Tm.tm_year + 1900, Tm.tm_mon);
  909. else if (count < 0)
  910. t_out.S -= 86400 * GetDaysInMonth(Tm.tm_year + 1900 - (Tm.tm_mon == 0 ? 1 : 0), Tm.tm_mon == 0 ? 11 : Tm.tm_mon - 1); // NOT WORKING
  911. }
  912. break;
  913. case ImPlotTimeUnit_Yr: for (int i = 0; i < abs(count); ++i) {
  914. if (count > 0)
  915. t_out.S += 86400 * (365 + (int)IsLeapYear(GetYear(t_out)));
  916. else if (count < 0)
  917. t_out.S -= 86400 * (365 + (int)IsLeapYear(GetYear(t_out) - 1));
  918. // this is incorrect if leap year and we are past Feb 28
  919. }
  920. break;
  921. default: break;
  922. }
  923. t_out.RollOver();
  924. return t_out;
  925. }
  926. ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit) {
  927. ImPlotContext& gp = *GImPlot;
  928. GetTime(t, &gp.Tm);
  929. switch (unit) {
  930. case ImPlotTimeUnit_S: return ImPlotTime(t.S, 0);
  931. case ImPlotTimeUnit_Ms: return ImPlotTime(t.S, (t.Us / 1000) * 1000);
  932. case ImPlotTimeUnit_Us: return t;
  933. case ImPlotTimeUnit_Yr: gp.Tm.tm_mon = 0; // fall-through
  934. case ImPlotTimeUnit_Mo: gp.Tm.tm_mday = 1; // fall-through
  935. case ImPlotTimeUnit_Day: gp.Tm.tm_hour = 0; // fall-through
  936. case ImPlotTimeUnit_Hr: gp.Tm.tm_min = 0; // fall-through
  937. case ImPlotTimeUnit_Min: gp.Tm.tm_sec = 0; break;
  938. default: return t;
  939. }
  940. return MkTime(&gp.Tm);
  941. }
  942. ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit) {
  943. return AddTime(FloorTime(t, unit), unit, 1);
  944. }
  945. ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit) {
  946. ImPlotTime t1 = FloorTime(t, unit);
  947. ImPlotTime t2 = AddTime(t1,unit,1);
  948. if (t1.S == t2.S)
  949. return t.Us - t1.Us < t2.Us - t.Us ? t1 : t2;
  950. return t.S - t1.S < t2.S - t.S ? t1 : t2;
  951. }
  952. ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& tod_part) {
  953. ImPlotContext& gp = *GImPlot;
  954. tm& Tm = gp.Tm;
  955. GetTime(date_part, &gp.Tm);
  956. int y = Tm.tm_year;
  957. int m = Tm.tm_mon;
  958. int d = Tm.tm_mday;
  959. GetTime(tod_part, &gp.Tm);
  960. Tm.tm_year = y;
  961. Tm.tm_mon = m;
  962. Tm.tm_mday = d;
  963. ImPlotTime t = MkTime(&Tm);
  964. t.Us = tod_part.Us;
  965. return t;
  966. }
  967. // TODO: allow users to define these
  968. static const char* MONTH_NAMES[] = {"January","February","March","April","May","June","July","August","September","October","November","December"};
  969. static const char* WD_ABRVS[] = {"Su","Mo","Tu","We","Th","Fr","Sa"};
  970. static const char* MONTH_ABRVS[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
  971. int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, bool use_24_hr_clk) {
  972. tm& Tm = GImPlot->Tm;
  973. GetTime(t, &Tm);
  974. const int us = t.Us % 1000;
  975. const int ms = t.Us / 1000;
  976. const int sec = Tm.tm_sec;
  977. const int min = Tm.tm_min;
  978. if (use_24_hr_clk) {
  979. const int hr = Tm.tm_hour;
  980. switch(fmt) {
  981. case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us);
  982. case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us);
  983. case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms);
  984. case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec);
  985. case ImPlotTimeFmt_MinSMs: return ImFormatString(buffer, size, ":%02d:%02d.%03d", min, sec, ms);
  986. case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%02d:%02d:%02d.%03d", hr, min, sec, ms);
  987. case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%02d:%02d:%02d", hr, min, sec);
  988. case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%02d:%02d", hr, min);
  989. case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%02d:00", hr);
  990. default: return 0;
  991. }
  992. }
  993. else {
  994. const char* ap = Tm.tm_hour < 12 ? "am" : "pm";
  995. const int hr = (Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12;
  996. switch(fmt) {
  997. case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us);
  998. case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us);
  999. case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms);
  1000. case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec);
  1001. case ImPlotTimeFmt_MinSMs: return ImFormatString(buffer, size, ":%02d:%02d.%03d", min, sec, ms);
  1002. case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%d:%02d:%02d.%03d%s", hr, min, sec, ms, ap);
  1003. case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%d:%02d:%02d%s", hr, min, sec, ap);
  1004. case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%d:%02d%s", hr, min, ap);
  1005. case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%d%s", hr, ap);
  1006. default: return 0;
  1007. }
  1008. }
  1009. }
  1010. int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601) {
  1011. tm& Tm = GImPlot->Tm;
  1012. GetTime(t, &Tm);
  1013. const int day = Tm.tm_mday;
  1014. const int mon = Tm.tm_mon + 1;
  1015. const int year = Tm.tm_year + 1900;
  1016. const int yr = year % 100;
  1017. if (use_iso_8601) {
  1018. switch (fmt) {
  1019. case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "--%02d-%02d", mon, day);
  1020. case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d-%02d-%02d", year, mon, day);
  1021. case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%d-%02d", year, mon);
  1022. case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "--%02d", mon);
  1023. case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year);
  1024. default: return 0;
  1025. }
  1026. }
  1027. else {
  1028. switch (fmt) {
  1029. case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "%d/%d", mon, day);
  1030. case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d/%d/%02d", mon, day, yr);
  1031. case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%s %d", MONTH_ABRVS[Tm.tm_mon], year);
  1032. case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "%s", MONTH_ABRVS[Tm.tm_mon]);
  1033. case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year);
  1034. default: return 0;
  1035. }
  1036. }
  1037. }
  1038. int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt) {
  1039. int written = 0;
  1040. if (fmt.Date != ImPlotDateFmt_None)
  1041. written += FormatDate(t, buffer, size, fmt.Date, fmt.UseISO8601);
  1042. if (fmt.Time != ImPlotTimeFmt_None) {
  1043. if (fmt.Date != ImPlotDateFmt_None)
  1044. buffer[written++] = ' ';
  1045. written += FormatTime(t, &buffer[written], size - written, fmt.Time, fmt.Use24HourClock);
  1046. }
  1047. return written;
  1048. }
  1049. inline float GetDateTimeWidth(ImPlotDateTimeSpec fmt) {
  1050. static const ImPlotTime t_max_width = MakeTime(2888, 12, 22, 12, 58, 58, 888888); // best guess at time that maximizes pixel width
  1051. char buffer[32];
  1052. FormatDateTime(t_max_width, buffer, 32, fmt);
  1053. return ImGui::CalcTextSize(buffer).x;
  1054. }
  1055. inline bool TimeLabelSame(const char* l1, const char* l2) {
  1056. size_t len1 = strlen(l1);
  1057. size_t len2 = strlen(l2);
  1058. size_t n = len1 < len2 ? len1 : len2;
  1059. return strcmp(l1 + len1 - n, l2 + len2 - n) == 0;
  1060. }
  1061. static const ImPlotDateTimeSpec TimeFormatLevel0[ImPlotTimeUnit_COUNT] = {
  1062. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us),
  1063. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs),
  1064. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_S),
  1065. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin),
  1066. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Hr),
  1067. ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_None),
  1068. ImPlotDateTimeSpec(ImPlotDateFmt_Mo, ImPlotTimeFmt_None),
  1069. ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None)
  1070. };
  1071. static const ImPlotDateTimeSpec TimeFormatLevel1[ImPlotTimeUnit_COUNT] = {
  1072. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin),
  1073. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS),
  1074. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin),
  1075. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin),
  1076. ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None),
  1077. ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None),
  1078. ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None),
  1079. ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None)
  1080. };
  1081. static const ImPlotDateTimeSpec TimeFormatLevel1First[ImPlotTimeUnit_COUNT] = {
  1082. ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS),
  1083. ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS),
  1084. ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin),
  1085. ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin),
  1086. ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None),
  1087. ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None),
  1088. ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None),
  1089. ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None)
  1090. };
  1091. static const ImPlotDateTimeSpec TimeFormatMouseCursor[ImPlotTimeUnit_COUNT] = {
  1092. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us),
  1093. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SUs),
  1094. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs),
  1095. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS),
  1096. ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin),
  1097. ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_Hr),
  1098. ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None),
  1099. ImPlotDateTimeSpec(ImPlotDateFmt_MoYr, ImPlotTimeFmt_None)
  1100. };
  1101. inline ImPlotDateTimeSpec GetDateTimeFmt(const ImPlotDateTimeSpec* ctx, ImPlotTimeUnit idx) {
  1102. ImPlotStyle& style = GetStyle();
  1103. ImPlotDateTimeSpec fmt = ctx[idx];
  1104. fmt.UseISO8601 = style.UseISO8601;
  1105. fmt.Use24HourClock = style.Use24HourClock;
  1106. return fmt;
  1107. }
  1108. void Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {
  1109. IM_ASSERT_USER_ERROR(vertical == false, "Cannot locate Time ticks on vertical axis!");
  1110. (void)vertical;
  1111. // get units for level 0 and level 1 labels
  1112. const ImPlotTimeUnit unit0 = GetUnitForRange(range.Size() / (pixels / 100)); // level = 0 (top)
  1113. const ImPlotTimeUnit unit1 = ImClamp(unit0 + 1, 0, ImPlotTimeUnit_COUNT-1); // level = 1 (bottom)
  1114. // get time format specs
  1115. const ImPlotDateTimeSpec fmt0 = GetDateTimeFmt(TimeFormatLevel0, unit0);
  1116. const ImPlotDateTimeSpec fmt1 = GetDateTimeFmt(TimeFormatLevel1, unit1);
  1117. const ImPlotDateTimeSpec fmtf = GetDateTimeFmt(TimeFormatLevel1First, unit1);
  1118. // min max times
  1119. const ImPlotTime t_min = ImPlotTime::FromDouble(range.Min);
  1120. const ImPlotTime t_max = ImPlotTime::FromDouble(range.Max);
  1121. // maximum allowable density of labels
  1122. const float max_density = 0.5f;
  1123. // book keeping
  1124. int last_major_offset = -1;
  1125. // formatter data
  1126. Formatter_Time_Data ftd;
  1127. ftd.UserFormatter = formatter;
  1128. ftd.UserFormatterData = formatter_data;
  1129. if (unit0 != ImPlotTimeUnit_Yr) {
  1130. // pixels per major (level 1) division
  1131. const float pix_per_major_div = pixels / (float)(range.Size() / TimeUnitSpans[unit1]);
  1132. // nominal pixels taken up by labels
  1133. const float fmt0_width = GetDateTimeWidth(fmt0);
  1134. const float fmt1_width = GetDateTimeWidth(fmt1);
  1135. const float fmtf_width = GetDateTimeWidth(fmtf);
  1136. // the maximum number of minor (level 0) labels that can fit between major (level 1) divisions
  1137. const int minor_per_major = (int)(max_density * pix_per_major_div / fmt0_width);
  1138. // the minor step size (level 0)
  1139. const int step = GetTimeStep(minor_per_major, unit0);
  1140. // generate ticks
  1141. ImPlotTime t1 = FloorTime(ImPlotTime::FromDouble(range.Min), unit1);
  1142. while (t1 < t_max) {
  1143. // get next major
  1144. const ImPlotTime t2 = AddTime(t1, unit1, 1);
  1145. // add major tick
  1146. if (t1 >= t_min && t1 <= t_max) {
  1147. // minor level 0 tick
  1148. ftd.Time = t1; ftd.Spec = fmt0;
  1149. ticker.AddTick(t1.ToDouble(), true, 0, true, Formatter_Time, &ftd);
  1150. // major level 1 tick
  1151. ftd.Time = t1; ftd.Spec = last_major_offset < 0 ? fmtf : fmt1;
  1152. ImPlotTick& tick_maj = ticker.AddTick(t1.ToDouble(), true, 1, true, Formatter_Time, &ftd);
  1153. const char* this_major = ticker.GetText(tick_maj);
  1154. if (last_major_offset >= 0 && TimeLabelSame(ticker.TextBuffer.Buf.Data + last_major_offset, this_major))
  1155. tick_maj.ShowLabel = false;
  1156. last_major_offset = tick_maj.TextOffset;
  1157. }
  1158. // add minor ticks up until next major
  1159. if (minor_per_major > 1 && (t_min <= t2 && t1 <= t_max)) {
  1160. ImPlotTime t12 = AddTime(t1, unit0, step);
  1161. while (t12 < t2) {
  1162. float px_to_t2 = (float)((t2 - t12).ToDouble()/range.Size()) * pixels;
  1163. if (t12 >= t_min && t12 <= t_max) {
  1164. ftd.Time = t12; ftd.Spec = fmt0;
  1165. ticker.AddTick(t12.ToDouble(), false, 0, px_to_t2 >= fmt0_width, Formatter_Time, &ftd);
  1166. if (last_major_offset < 0 && px_to_t2 >= fmt0_width && px_to_t2 >= (fmt1_width + fmtf_width) / 2) {
  1167. ftd.Time = t12; ftd.Spec = fmtf;
  1168. ImPlotTick& tick_maj = ticker.AddTick(t12.ToDouble(), true, 1, true, Formatter_Time, &ftd);
  1169. last_major_offset = tick_maj.TextOffset;
  1170. }
  1171. }
  1172. t12 = AddTime(t12, unit0, step);
  1173. }
  1174. }
  1175. t1 = t2;
  1176. }
  1177. }
  1178. else {
  1179. const ImPlotDateTimeSpec fmty = GetDateTimeFmt(TimeFormatLevel0, ImPlotTimeUnit_Yr);
  1180. const float label_width = GetDateTimeWidth(fmty);
  1181. const int max_labels = (int)(max_density * pixels / label_width);
  1182. const int year_min = GetYear(t_min);
  1183. const int year_max = GetYear(CeilTime(t_max, ImPlotTimeUnit_Yr));
  1184. const double nice_range = NiceNum((year_max - year_min)*0.99,false);
  1185. const double interval = NiceNum(nice_range / (max_labels - 1), true);
  1186. const int graphmin = (int)(floor(year_min / interval) * interval);
  1187. const int graphmax = (int)(ceil(year_max / interval) * interval);
  1188. const int step = (int)interval <= 0 ? 1 : (int)interval;
  1189. for (int y = graphmin; y < graphmax; y += step) {
  1190. ImPlotTime t = MakeTime(y);
  1191. if (t >= t_min && t <= t_max) {
  1192. ftd.Time = t; ftd.Spec = fmty;
  1193. ticker.AddTick(t.ToDouble(), true, 0, true, Formatter_Time, &ftd);
  1194. }
  1195. }
  1196. }
  1197. }
  1198. //-----------------------------------------------------------------------------
  1199. // Context Menu
  1200. //-----------------------------------------------------------------------------
  1201. template <typename F>
  1202. bool DragFloat(const char*, F*, float, F, F) {
  1203. return false;
  1204. }
  1205. template <>
  1206. bool DragFloat<double>(const char* label, double* v, float v_speed, double v_min, double v_max) {
  1207. return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3g", 1);
  1208. }
  1209. template <>
  1210. bool DragFloat<float>(const char* label, float* v, float v_speed, float v_min, float v_max) {
  1211. return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3g", 1);
  1212. }
  1213. inline void BeginDisabledControls(bool cond) {
  1214. if (cond) {
  1215. ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
  1216. ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f);
  1217. }
  1218. }
  1219. inline void EndDisabledControls(bool cond) {
  1220. if (cond) {
  1221. ImGui::PopItemFlag();
  1222. ImGui::PopStyleVar();
  1223. }
  1224. }
  1225. void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool /*time_allowed*/) {
  1226. ImGui::PushItemWidth(75);
  1227. bool always_locked = axis.IsRangeLocked() || axis.IsAutoFitting();
  1228. bool label = axis.HasLabel();
  1229. bool grid = axis.HasGridLines();
  1230. bool ticks = axis.HasTickMarks();
  1231. bool labels = axis.HasTickLabels();
  1232. double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits.
  1233. if (axis.Scale == ImPlotScale_Time) {
  1234. ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min);
  1235. ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max);
  1236. BeginDisabledControls(always_locked);
  1237. ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin);
  1238. EndDisabledControls(always_locked);
  1239. ImGui::SameLine();
  1240. BeginDisabledControls(axis.IsLockedMin() || always_locked);
  1241. if (ImGui::BeginMenu("Min Time")) {
  1242. if (ShowTimePicker("mintime", &tmin)) {
  1243. if (tmin >= tmax)
  1244. tmax = AddTime(tmin, ImPlotTimeUnit_S, 1);
  1245. axis.SetRange(tmin.ToDouble(),tmax.ToDouble());
  1246. }
  1247. ImGui::Separator();
  1248. if (ShowDatePicker("mindate",&axis.PickerLevel,&axis.PickerTimeMin,&tmin,&tmax)) {
  1249. tmin = CombineDateTime(axis.PickerTimeMin, tmin);
  1250. if (tmin >= tmax)
  1251. tmax = AddTime(tmin, ImPlotTimeUnit_S, 1);
  1252. axis.SetRange(tmin.ToDouble(), tmax.ToDouble());
  1253. }
  1254. ImGui::EndMenu();
  1255. }
  1256. EndDisabledControls(axis.IsLockedMin() || always_locked);
  1257. BeginDisabledControls(always_locked);
  1258. ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax);
  1259. EndDisabledControls(always_locked);
  1260. ImGui::SameLine();
  1261. BeginDisabledControls(axis.IsLockedMax() || always_locked);
  1262. if (ImGui::BeginMenu("Max Time")) {
  1263. if (ShowTimePicker("maxtime", &tmax)) {
  1264. if (tmax <= tmin)
  1265. tmin = AddTime(tmax, ImPlotTimeUnit_S, -1);
  1266. axis.SetRange(tmin.ToDouble(),tmax.ToDouble());
  1267. }
  1268. ImGui::Separator();
  1269. if (ShowDatePicker("maxdate",&axis.PickerLevel,&axis.PickerTimeMax,&tmin,&tmax)) {
  1270. tmax = CombineDateTime(axis.PickerTimeMax, tmax);
  1271. if (tmax <= tmin)
  1272. tmin = AddTime(tmax, ImPlotTimeUnit_S, -1);
  1273. axis.SetRange(tmin.ToDouble(), tmax.ToDouble());
  1274. }
  1275. ImGui::EndMenu();
  1276. }
  1277. EndDisabledControls(axis.IsLockedMax() || always_locked);
  1278. }
  1279. else {
  1280. BeginDisabledControls(always_locked);
  1281. ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin);
  1282. EndDisabledControls(always_locked);
  1283. ImGui::SameLine();
  1284. BeginDisabledControls(axis.IsLockedMin() || always_locked);
  1285. double temp_min = axis.Range.Min;
  1286. if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON)) {
  1287. axis.SetMin(temp_min,true);
  1288. if (equal_axis != nullptr)
  1289. equal_axis->SetAspect(axis.GetAspect());
  1290. }
  1291. EndDisabledControls(axis.IsLockedMin() || always_locked);
  1292. BeginDisabledControls(always_locked);
  1293. ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax);
  1294. EndDisabledControls(always_locked);
  1295. ImGui::SameLine();
  1296. BeginDisabledControls(axis.IsLockedMax() || always_locked);
  1297. double temp_max = axis.Range.Max;
  1298. if (DragFloat("Max", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL)) {
  1299. axis.SetMax(temp_max,true);
  1300. if (equal_axis != nullptr)
  1301. equal_axis->SetAspect(axis.GetAspect());
  1302. }
  1303. EndDisabledControls(axis.IsLockedMax() || always_locked);
  1304. }
  1305. ImGui::Separator();
  1306. ImGui::CheckboxFlags("Auto-Fit",(unsigned int*)&axis.Flags, ImPlotAxisFlags_AutoFit);
  1307. // TODO
  1308. // BeginDisabledControls(axis.IsTime() && time_allowed);
  1309. // ImGui::CheckboxFlags("Log Scale",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale);
  1310. // EndDisabledControls(axis.IsTime() && time_allowed);
  1311. // if (time_allowed) {
  1312. // BeginDisabledControls(axis.IsLog() || axis.IsSymLog());
  1313. // ImGui::CheckboxFlags("Time",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time);
  1314. // EndDisabledControls(axis.IsLog() || axis.IsSymLog());
  1315. // }
  1316. ImGui::Separator();
  1317. ImGui::CheckboxFlags("Invert",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Invert);
  1318. ImGui::CheckboxFlags("Opposite",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Opposite);
  1319. ImGui::Separator();
  1320. BeginDisabledControls(axis.LabelOffset == -1);
  1321. if (ImGui::Checkbox("Label", &label))
  1322. ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoLabel);
  1323. EndDisabledControls(axis.LabelOffset == -1);
  1324. if (ImGui::Checkbox("Grid Lines", &grid))
  1325. ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoGridLines);
  1326. if (ImGui::Checkbox("Tick Marks", &ticks))
  1327. ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks);
  1328. if (ImGui::Checkbox("Tick Labels", &labels))
  1329. ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels);
  1330. }
  1331. bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible) {
  1332. const float s = ImGui::GetFrameHeight();
  1333. bool ret = false;
  1334. if (ImGui::Checkbox("Show",&visible))
  1335. ret = true;
  1336. if (legend.CanGoInside)
  1337. ImGui::CheckboxFlags("Outside",(unsigned int*)&legend.Flags, ImPlotLegendFlags_Outside);
  1338. if (ImGui::RadioButton("H", ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal)))
  1339. legend.Flags |= ImPlotLegendFlags_Horizontal;
  1340. ImGui::SameLine();
  1341. if (ImGui::RadioButton("V", !ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal)))
  1342. legend.Flags &= ~ImPlotLegendFlags_Horizontal;
  1343. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2,2));
  1344. if (ImGui::Button("NW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthWest; } ImGui::SameLine();
  1345. if (ImGui::Button("N", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_North; } ImGui::SameLine();
  1346. if (ImGui::Button("NE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthEast; }
  1347. if (ImGui::Button("W", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_West; } ImGui::SameLine();
  1348. if (ImGui::InvisibleButton("C", ImVec2(1.5f*s,s))) { } ImGui::SameLine();
  1349. if (ImGui::Button("E", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_East; }
  1350. if (ImGui::Button("SW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthWest; } ImGui::SameLine();
  1351. if (ImGui::Button("S", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_South; } ImGui::SameLine();
  1352. if (ImGui::Button("SE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthEast; }
  1353. ImGui::PopStyleVar();
  1354. return ret;
  1355. }
  1356. void ShowSubplotsContextMenu(ImPlotSubplot& subplot) {
  1357. if ((ImGui::BeginMenu("Linking"))) {
  1358. if (ImGui::MenuItem("Link Rows",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows)))
  1359. ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows);
  1360. if (ImGui::MenuItem("Link Cols",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols)))
  1361. ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols);
  1362. if (ImGui::MenuItem("Link All X",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX)))
  1363. ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX);
  1364. if (ImGui::MenuItem("Link All Y",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY)))
  1365. ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY);
  1366. ImGui::EndMenu();
  1367. }
  1368. if ((ImGui::BeginMenu("Settings"))) {
  1369. BeginDisabledControls(!subplot.HasTitle);
  1370. if (ImGui::MenuItem("Title",nullptr,subplot.HasTitle && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle)))
  1371. ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle);
  1372. EndDisabledControls(!subplot.HasTitle);
  1373. if (ImGui::MenuItem("Resizable",nullptr,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize)))
  1374. ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoResize);
  1375. if (ImGui::MenuItem("Align",nullptr,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign)))
  1376. ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign);
  1377. if (ImGui::MenuItem("Share Items",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems)))
  1378. ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);
  1379. ImGui::EndMenu();
  1380. }
  1381. }
  1382. void ShowPlotContextMenu(ImPlotPlot& plot) {
  1383. ImPlotContext& gp = *GImPlot;
  1384. const bool owns_legend = gp.CurrentItems == &plot.Items;
  1385. const bool equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal);
  1386. char buf[16] = {};
  1387. for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
  1388. ImPlotAxis& x_axis = plot.XAxis(i);
  1389. if (!x_axis.Enabled || !x_axis.HasMenus())
  1390. continue;
  1391. ImGui::PushID(i);
  1392. ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "X-Axis" : "X-Axis %d", i + 1);
  1393. if (ImGui::BeginMenu(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : buf)) {
  1394. ShowAxisContextMenu(x_axis, equal ? x_axis.OrthoAxis : nullptr, false);
  1395. ImGui::EndMenu();
  1396. }
  1397. ImGui::PopID();
  1398. }
  1399. for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
  1400. ImPlotAxis& y_axis = plot.YAxis(i);
  1401. if (!y_axis.Enabled || !y_axis.HasMenus())
  1402. continue;
  1403. ImGui::PushID(i);
  1404. ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1);
  1405. if (ImGui::BeginMenu(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : buf)) {
  1406. ShowAxisContextMenu(y_axis, equal ? y_axis.OrthoAxis : nullptr, false);
  1407. ImGui::EndMenu();
  1408. }
  1409. ImGui::PopID();
  1410. }
  1411. ImGui::Separator();
  1412. if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoMenus)) {
  1413. if ((ImGui::BeginMenu("Legend"))) {
  1414. if (owns_legend) {
  1415. if (ShowLegendContextMenu(plot.Items.Legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend)))
  1416. ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend);
  1417. }
  1418. else if (gp.CurrentSubplot != nullptr) {
  1419. if (ShowLegendContextMenu(gp.CurrentSubplot->Items.Legend, !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend)))
  1420. ImFlipFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend);
  1421. }
  1422. ImGui::EndMenu();
  1423. }
  1424. }
  1425. if ((ImGui::BeginMenu("Settings"))) {
  1426. if (ImGui::MenuItem("Equal", nullptr, ImHasFlag(plot.Flags, ImPlotFlags_Equal)))
  1427. ImFlipFlag(plot.Flags, ImPlotFlags_Equal);
  1428. if (ImGui::MenuItem("Box Select",nullptr,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect)))
  1429. ImFlipFlag(plot.Flags, ImPlotFlags_NoBoxSelect);
  1430. BeginDisabledControls(plot.TitleOffset == -1);
  1431. if (ImGui::MenuItem("Title",nullptr,plot.HasTitle()))
  1432. ImFlipFlag(plot.Flags, ImPlotFlags_NoTitle);
  1433. EndDisabledControls(plot.TitleOffset == -1);
  1434. if (ImGui::MenuItem("Mouse Position",nullptr,!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText)))
  1435. ImFlipFlag(plot.Flags, ImPlotFlags_NoMouseText);
  1436. if (ImGui::MenuItem("Crosshairs",nullptr,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs)))
  1437. ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs);
  1438. ImGui::EndMenu();
  1439. }
  1440. if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoMenus)) {
  1441. ImGui::Separator();
  1442. if ((ImGui::BeginMenu("Subplots"))) {
  1443. ShowSubplotsContextMenu(*gp.CurrentSubplot);
  1444. ImGui::EndMenu();
  1445. }
  1446. }
  1447. }
  1448. //-----------------------------------------------------------------------------
  1449. // Axis Utils
  1450. //-----------------------------------------------------------------------------
  1451. static inline int AxisPrecision(const ImPlotAxis& axis) {
  1452. const double range = axis.Ticker.TickCount() > 1 ? (axis.Ticker.Ticks[1].PlotPos - axis.Ticker.Ticks[0].PlotPos) : axis.Range.Size();
  1453. return Precision(range);
  1454. }
  1455. static inline double RoundAxisValue(const ImPlotAxis& axis, double value) {
  1456. return RoundTo(value, AxisPrecision(axis));
  1457. }
  1458. void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round) {
  1459. ImPlotContext& gp = *GImPlot;
  1460. // TODO: We shouldn't explicitly check that the axis is Time here. Ideally,
  1461. // Formatter_Time would handle the formatting for us, but the code below
  1462. // needs additional arguments which are not currently available in ImPlotFormatter
  1463. if (axis.Locator == Locator_Time) {
  1464. ImPlotTimeUnit unit = axis.Vertical
  1465. ? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100)) // TODO: magic value!
  1466. : GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100)); // TODO: magic value!
  1467. FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit));
  1468. }
  1469. else {
  1470. if (round)
  1471. value = RoundAxisValue(axis, value);
  1472. axis.Formatter(value, buff, size, axis.FormatterData);
  1473. }
  1474. }
  1475. void UpdateAxisColors(ImPlotAxis& axis) {
  1476. const ImVec4 col_grid = GetStyleColorVec4(ImPlotCol_AxisGrid);
  1477. axis.ColorMaj = ImGui::GetColorU32(col_grid);
  1478. axis.ColorMin = ImGui::GetColorU32(col_grid*ImVec4(1,1,1,GImPlot->Style.MinorAlpha));
  1479. axis.ColorTick = GetStyleColorU32(ImPlotCol_AxisTick);
  1480. axis.ColorTxt = GetStyleColorU32(ImPlotCol_AxisText);
  1481. axis.ColorBg = GetStyleColorU32(ImPlotCol_AxisBg);
  1482. axis.ColorHov = GetStyleColorU32(ImPlotCol_AxisBgHovered);
  1483. axis.ColorAct = GetStyleColorU32(ImPlotCol_AxisBgActive);
  1484. // axis.ColorHiLi = IM_COL32_BLACK_TRANS;
  1485. }
  1486. void PadAndDatumAxesX(ImPlotPlot& plot, float& pad_T, float& pad_B, ImPlotAlignmentData* align) {
  1487. ImPlotContext& gp = *GImPlot;
  1488. const float T = ImGui::GetTextLineHeight();
  1489. const float P = gp.Style.LabelPadding.y;
  1490. const float K = gp.Style.MinorTickLen.x;
  1491. int count_T = 0;
  1492. int count_B = 0;
  1493. float last_T = plot.AxesRect.Min.y;
  1494. float last_B = plot.AxesRect.Max.y;
  1495. for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { // FYI: can iterate forward
  1496. ImPlotAxis& axis = plot.XAxis(i);
  1497. if (!axis.Enabled)
  1498. continue;
  1499. const bool label = axis.HasLabel();
  1500. const bool ticks = axis.HasTickLabels();
  1501. const bool opp = axis.IsOpposite();
  1502. const bool time = axis.Scale == ImPlotScale_Time;
  1503. if (opp) {
  1504. if (count_T++ > 0)
  1505. pad_T += K + P;
  1506. if (label)
  1507. pad_T += T + P;
  1508. if (ticks)
  1509. pad_T += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0);
  1510. axis.Datum1 = plot.CanvasRect.Min.y + pad_T;
  1511. axis.Datum2 = last_T;
  1512. last_T = axis.Datum1;
  1513. }
  1514. else {
  1515. if (count_B++ > 0)
  1516. pad_B += K + P;
  1517. if (label)
  1518. pad_B += T + P;
  1519. if (ticks)
  1520. pad_B += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0);
  1521. axis.Datum1 = plot.CanvasRect.Max.y - pad_B;
  1522. axis.Datum2 = last_B;
  1523. last_B = axis.Datum1;
  1524. }
  1525. }
  1526. if (align) {
  1527. count_T = count_B = 0;
  1528. float delta_T, delta_B;
  1529. align->Update(pad_T,pad_B,delta_T,delta_B);
  1530. for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) {
  1531. ImPlotAxis& axis = plot.XAxis(i);
  1532. if (!axis.Enabled)
  1533. continue;
  1534. if (axis.IsOpposite()) {
  1535. axis.Datum1 += delta_T;
  1536. axis.Datum2 += count_T++ > 1 ? delta_T : 0;
  1537. }
  1538. else {
  1539. axis.Datum1 -= delta_B;
  1540. axis.Datum2 -= count_B++ > 1 ? delta_B : 0;
  1541. }
  1542. }
  1543. }
  1544. }
  1545. void PadAndDatumAxesY(ImPlotPlot& plot, float& pad_L, float& pad_R, ImPlotAlignmentData* align) {
  1546. // [ pad_L ] [ pad_R ]
  1547. // .................CanvasRect................
  1548. // :TPWPK.PTPWP _____PlotRect____ PWPTP.KPWPT:
  1549. // :A # |- A # |- -| # A -| # A:
  1550. // :X | X | | X | x:
  1551. // :I # |- I # |- -| # I -| # I:
  1552. // :S | S | | S | S:
  1553. // :3 # |- 0 # |-_______________-| # 1 -| # 2:
  1554. // :.........................................:
  1555. //
  1556. // T = text height
  1557. // P = label padding
  1558. // K = minor tick length
  1559. // W = label width
  1560. ImPlotContext& gp = *GImPlot;
  1561. const float T = ImGui::GetTextLineHeight();
  1562. const float P = gp.Style.LabelPadding.x;
  1563. const float K = gp.Style.MinorTickLen.y;
  1564. int count_L = 0;
  1565. int count_R = 0;
  1566. float last_L = plot.AxesRect.Min.x;
  1567. float last_R = plot.AxesRect.Max.x;
  1568. for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { // FYI: can iterate forward
  1569. ImPlotAxis& axis = plot.YAxis(i);
  1570. if (!axis.Enabled)
  1571. continue;
  1572. const bool label = axis.HasLabel();
  1573. const bool ticks = axis.HasTickLabels();
  1574. const bool opp = axis.IsOpposite();
  1575. if (opp) {
  1576. if (count_R++ > 0)
  1577. pad_R += K + P;
  1578. if (label)
  1579. pad_R += T + P;
  1580. if (ticks)
  1581. pad_R += axis.Ticker.MaxSize.x + P;
  1582. axis.Datum1 = plot.CanvasRect.Max.x - pad_R;
  1583. axis.Datum2 = last_R;
  1584. last_R = axis.Datum1;
  1585. }
  1586. else {
  1587. if (count_L++ > 0)
  1588. pad_L += K + P;
  1589. if (label)
  1590. pad_L += T + P;
  1591. if (ticks)
  1592. pad_L += axis.Ticker.MaxSize.x + P;
  1593. axis.Datum1 = plot.CanvasRect.Min.x + pad_L;
  1594. axis.Datum2 = last_L;
  1595. last_L = axis.Datum1;
  1596. }
  1597. }
  1598. plot.PlotRect.Min.x = plot.CanvasRect.Min.x + pad_L;
  1599. plot.PlotRect.Max.x = plot.CanvasRect.Max.x - pad_R;
  1600. if (align) {
  1601. count_L = count_R = 0;
  1602. float delta_L, delta_R;
  1603. align->Update(pad_L,pad_R,delta_L,delta_R);
  1604. for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) {
  1605. ImPlotAxis& axis = plot.YAxis(i);
  1606. if (!axis.Enabled)
  1607. continue;
  1608. if (axis.IsOpposite()) {
  1609. axis.Datum1 -= delta_R;
  1610. axis.Datum2 -= count_R++ > 1 ? delta_R : 0;
  1611. }
  1612. else {
  1613. axis.Datum1 += delta_L;
  1614. axis.Datum2 += count_L++ > 1 ? delta_L : 0;
  1615. }
  1616. }
  1617. }
  1618. }
  1619. //-----------------------------------------------------------------------------
  1620. // RENDERING
  1621. //-----------------------------------------------------------------------------
  1622. static inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) {
  1623. const float density = ticker.TickCount() / rect.GetWidth();
  1624. ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min);
  1625. col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f);
  1626. col_min = ImGui::ColorConvertFloat4ToU32(col_min4);
  1627. for (int t = 0; t < ticker.TickCount(); t++) {
  1628. const ImPlotTick& xt = ticker.Ticks[t];
  1629. if (xt.PixelPos < rect.Min.x || xt.PixelPos > rect.Max.x)
  1630. continue;
  1631. if (xt.Level == 0) {
  1632. if (xt.Major)
  1633. DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_maj, size_maj);
  1634. else if (density < 0.2f)
  1635. DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_min, size_min);
  1636. }
  1637. }
  1638. }
  1639. static inline void RenderGridLinesY(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) {
  1640. const float density = ticker.TickCount() / rect.GetHeight();
  1641. ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min);
  1642. col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f);
  1643. col_min = ImGui::ColorConvertFloat4ToU32(col_min4);
  1644. for (int t = 0; t < ticker.TickCount(); t++) {
  1645. const ImPlotTick& yt = ticker.Ticks[t];
  1646. if (yt.PixelPos < rect.Min.y || yt.PixelPos > rect.Max.y)
  1647. continue;
  1648. if (yt.Major)
  1649. DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_maj, size_maj);
  1650. else if (density < 0.2f)
  1651. DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_min, size_min);
  1652. }
  1653. }
  1654. static inline void RenderSelectionRect(ImDrawList& DrawList, const ImVec2& p_min, const ImVec2& p_max, const ImVec4& col) {
  1655. const ImU32 col_bg = ImGui::GetColorU32(col * ImVec4(1,1,1,0.25f));
  1656. const ImU32 col_bd = ImGui::GetColorU32(col);
  1657. DrawList.AddRectFilled(p_min, p_max, col_bg);
  1658. DrawList.AddRect(p_min, p_max, col_bd);
  1659. }
  1660. //-----------------------------------------------------------------------------
  1661. // Input Handling
  1662. //-----------------------------------------------------------------------------
  1663. static const float MOUSE_CURSOR_DRAG_THRESHOLD = 5.0f;
  1664. static const float BOX_SELECT_DRAG_THRESHOLD = 4.0f;
  1665. bool UpdateInput(ImPlotPlot& plot) {
  1666. bool changed = false;
  1667. ImPlotContext& gp = *GImPlot;
  1668. ImGuiIO& IO = ImGui::GetIO();
  1669. // BUTTON STATE -----------------------------------------------------------
  1670. const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowOverlap
  1671. | ImGuiButtonFlags_PressedOnClick
  1672. | ImGuiButtonFlags_PressedOnDoubleClick
  1673. | ImGuiButtonFlags_MouseButtonLeft
  1674. | ImGuiButtonFlags_MouseButtonRight
  1675. | ImGuiButtonFlags_MouseButtonMiddle;
  1676. const ImGuiButtonFlags axis_button_flags = ImGuiButtonFlags_FlattenChildren
  1677. | plot_button_flags;
  1678. const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags);
  1679. #if (IMGUI_VERSION_NUM < 18966)
  1680. ImGui::SetItemAllowOverlap(); // Handled by ButtonBehavior()
  1681. #endif
  1682. if (plot_clicked) {
  1683. if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) {
  1684. plot.Selecting = true;
  1685. plot.SelectStart = IO.MousePos;
  1686. plot.SelectRect = ImRect(0,0,0,0);
  1687. }
  1688. if (IO.MouseDoubleClicked[gp.InputMap.Fit]) {
  1689. plot.FitThisFrame = true;
  1690. for (int i = 0; i < ImAxis_COUNT; ++i)
  1691. plot.Axes[i].FitThisFrame = true;
  1692. }
  1693. }
  1694. const bool can_pan = IO.MouseDown[gp.InputMap.Pan] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod);
  1695. plot.Held = plot.Held && can_pan;
  1696. bool x_click[IMPLOT_NUM_X_AXES] = {false};
  1697. bool x_held[IMPLOT_NUM_X_AXES] = {false};
  1698. bool x_hov[IMPLOT_NUM_X_AXES] = {false};
  1699. bool y_click[IMPLOT_NUM_Y_AXES] = {false};
  1700. bool y_held[IMPLOT_NUM_Y_AXES] = {false};
  1701. bool y_hov[IMPLOT_NUM_Y_AXES] = {false};
  1702. for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {
  1703. ImPlotAxis& xax = plot.XAxis(i);
  1704. if (xax.Enabled) {
  1705. ImGui::KeepAliveID(xax.ID);
  1706. x_click[i] = ImGui::ButtonBehavior(xax.HoverRect,xax.ID,&xax.Hovered,&xax.Held,axis_button_flags);
  1707. if (x_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit])
  1708. plot.FitThisFrame = xax.FitThisFrame = true;
  1709. xax.Held = xax.Held && can_pan;
  1710. x_hov[i] = xax.Hovered || plot.Hovered;
  1711. x_held[i] = xax.Held || plot.Held;
  1712. }
  1713. }
  1714. for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {
  1715. ImPlotAxis& yax = plot.YAxis(i);
  1716. if (yax.Enabled) {
  1717. ImGui::KeepAliveID(yax.ID);
  1718. y_click[i] = ImGui::ButtonBehavior(yax.HoverRect,yax.ID,&yax.Hovered,&yax.Held,axis_button_flags);
  1719. if (y_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit])
  1720. plot.FitThisFrame = yax.FitThisFrame = true;
  1721. yax.Held = yax.Held && can_pan;
  1722. y_hov[i] = yax.Hovered || plot.Hovered;
  1723. y_held[i] = yax.Held || plot.Held;
  1724. }
  1725. }
  1726. // cancel due to DND activity
  1727. if (GImGui->DragDropActive || (IO.KeyMods == gp.InputMap.OverrideMod && gp.InputMap.OverrideMod != 0))
  1728. return false;
  1729. // STATE -------------------------------------------------------------------
  1730. const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal);
  1731. const bool any_x_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);
  1732. const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);
  1733. const bool any_y_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);
  1734. const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);
  1735. const bool any_hov = any_x_hov || any_y_hov;
  1736. const bool any_held = any_x_held || any_y_held;
  1737. const ImVec2 select_drag = ImGui::GetMouseDragDelta(gp.InputMap.Select);
  1738. const ImVec2 pan_drag = ImGui::GetMouseDragDelta(gp.InputMap.Pan);
  1739. const float select_drag_sq = ImLengthSqr(select_drag);
  1740. const float pan_drag_sq = ImLengthSqr(pan_drag);
  1741. const bool selecting = plot.Selecting && select_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD;
  1742. const bool panning = any_held && pan_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD;
  1743. // CONTEXT MENU -----------------------------------------------------------
  1744. if (IO.MouseReleased[gp.InputMap.Menu] && !plot.ContextLocked)
  1745. gp.OpenContextThisFrame = true;
  1746. if (selecting || panning)
  1747. plot.ContextLocked = true;
  1748. else if (!(IO.MouseDown[gp.InputMap.Menu] || IO.MouseReleased[gp.InputMap.Menu]))
  1749. plot.ContextLocked = false;
  1750. // DRAG INPUT -------------------------------------------------------------
  1751. if (any_held && !plot.Selecting) {
  1752. int drag_direction = 0;
  1753. for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
  1754. ImPlotAxis& x_axis = plot.XAxis(i);
  1755. if (x_held[i] && !x_axis.IsInputLocked()) {
  1756. drag_direction |= (1 << 1);
  1757. bool increasing = x_axis.IsInverted() ? IO.MouseDelta.x > 0 : IO.MouseDelta.x < 0;
  1758. if (IO.MouseDelta.x != 0 && !x_axis.IsPanLocked(increasing)) {
  1759. const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x);
  1760. const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x);
  1761. x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);
  1762. x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);
  1763. if (axis_equal && x_axis.OrthoAxis != nullptr)
  1764. x_axis.OrthoAxis->SetAspect(x_axis.GetAspect());
  1765. changed = true;
  1766. }
  1767. }
  1768. }
  1769. for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
  1770. ImPlotAxis& y_axis = plot.YAxis(i);
  1771. if (y_held[i] && !y_axis.IsInputLocked()) {
  1772. drag_direction |= (1 << 2);
  1773. bool increasing = y_axis.IsInverted() ? IO.MouseDelta.y < 0 : IO.MouseDelta.y > 0;
  1774. if (IO.MouseDelta.y != 0 && !y_axis.IsPanLocked(increasing)) {
  1775. const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y);
  1776. const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y);
  1777. y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);
  1778. y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);
  1779. if (axis_equal && y_axis.OrthoAxis != nullptr)
  1780. y_axis.OrthoAxis->SetAspect(y_axis.GetAspect());
  1781. changed = true;
  1782. }
  1783. }
  1784. }
  1785. if (IO.MouseDragMaxDistanceSqr[gp.InputMap.Pan] > MOUSE_CURSOR_DRAG_THRESHOLD) {
  1786. switch (drag_direction) {
  1787. case 0 : ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); break;
  1788. case (1 << 1) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); break;
  1789. case (1 << 2) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); break;
  1790. default : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); break;
  1791. }
  1792. }
  1793. }
  1794. // SCROLL INPUT -----------------------------------------------------------
  1795. if (any_hov && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) {
  1796. float zoom_rate = gp.InputMap.ZoomRate;
  1797. if (IO.MouseWheel == 0.0f)
  1798. zoom_rate = 0;
  1799. else if (IO.MouseWheel > 0)
  1800. zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate));
  1801. ImVec2 rect_size = plot.PlotRect.GetSize();
  1802. float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f);
  1803. float ty = ImRemap(IO.MousePos.y, plot.PlotRect.Min.y, plot.PlotRect.Max.y, 0.0f, 1.0f);
  1804. for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
  1805. ImPlotAxis& x_axis = plot.XAxis(i);
  1806. const bool equal_zoom = axis_equal && x_axis.OrthoAxis != nullptr;
  1807. const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked();
  1808. if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) {
  1809. ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
  1810. if (zoom_rate != 0.0f) {
  1811. float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
  1812. const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction);
  1813. const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction);
  1814. x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);
  1815. x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);
  1816. if (axis_equal && x_axis.OrthoAxis != nullptr)
  1817. x_axis.OrthoAxis->SetAspect(x_axis.GetAspect());
  1818. changed = true;
  1819. }
  1820. }
  1821. }
  1822. for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
  1823. ImPlotAxis& y_axis = plot.YAxis(i);
  1824. const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr;
  1825. const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked();
  1826. if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) {
  1827. ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
  1828. if (zoom_rate != 0.0f) {
  1829. float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
  1830. const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction);
  1831. const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction);
  1832. y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);
  1833. y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);
  1834. if (axis_equal && y_axis.OrthoAxis != nullptr)
  1835. y_axis.OrthoAxis->SetAspect(y_axis.GetAspect());
  1836. changed = true;
  1837. }
  1838. }
  1839. }
  1840. }
  1841. // BOX-SELECTION ----------------------------------------------------------
  1842. if (plot.Selecting) {
  1843. const ImVec2 d = plot.SelectStart - IO.MousePos;
  1844. const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImFabs(d.x) > 2;
  1845. const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod) && ImFabs(d.y) > 2;
  1846. // confirm
  1847. if (IO.MouseReleased[gp.InputMap.Select]) {
  1848. for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
  1849. ImPlotAxis& x_axis = plot.XAxis(i);
  1850. if (!x_axis.IsInputLocked() && x_can_change) {
  1851. const double p1 = x_axis.PixelsToPlot(plot.SelectStart.x);
  1852. const double p2 = x_axis.PixelsToPlot(IO.MousePos.x);
  1853. x_axis.SetMin(ImMin(p1, p2));
  1854. x_axis.SetMax(ImMax(p1, p2));
  1855. changed = true;
  1856. }
  1857. }
  1858. for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
  1859. ImPlotAxis& y_axis = plot.YAxis(i);
  1860. if (!y_axis.IsInputLocked() && y_can_change) {
  1861. const double p1 = y_axis.PixelsToPlot(plot.SelectStart.y);
  1862. const double p2 = y_axis.PixelsToPlot(IO.MousePos.y);
  1863. y_axis.SetMin(ImMin(p1, p2));
  1864. y_axis.SetMax(ImMax(p1, p2));
  1865. changed = true;
  1866. }
  1867. }
  1868. if (x_can_change || y_can_change || (ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod)))
  1869. gp.OpenContextThisFrame = false;
  1870. plot.Selected = plot.Selecting = false;
  1871. }
  1872. // cancel
  1873. else if (IO.MouseReleased[gp.InputMap.SelectCancel]) {
  1874. plot.Selected = plot.Selecting = false;
  1875. gp.OpenContextThisFrame = false;
  1876. }
  1877. else if (ImLengthSqr(d) > BOX_SELECT_DRAG_THRESHOLD) {
  1878. // bad selection
  1879. if (plot.IsInputLocked()) {
  1880. ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
  1881. gp.OpenContextThisFrame = false;
  1882. plot.Selected = false;
  1883. }
  1884. else {
  1885. // TODO: Handle only min or max locked cases
  1886. const bool full_width = ImHasFlag(IO.KeyMods, gp.InputMap.SelectHorzMod) || AllAxesInputLocked(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);
  1887. const bool full_height = ImHasFlag(IO.KeyMods, gp.InputMap.SelectVertMod) || AllAxesInputLocked(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);
  1888. plot.SelectRect.Min.x = full_width ? plot.PlotRect.Min.x : ImMin(plot.SelectStart.x, IO.MousePos.x);
  1889. plot.SelectRect.Max.x = full_width ? plot.PlotRect.Max.x : ImMax(plot.SelectStart.x, IO.MousePos.x);
  1890. plot.SelectRect.Min.y = full_height ? plot.PlotRect.Min.y : ImMin(plot.SelectStart.y, IO.MousePos.y);
  1891. plot.SelectRect.Max.y = full_height ? plot.PlotRect.Max.y : ImMax(plot.SelectStart.y, IO.MousePos.y);
  1892. plot.SelectRect.Min -= plot.PlotRect.Min;
  1893. plot.SelectRect.Max -= plot.PlotRect.Min;
  1894. plot.Selected = true;
  1895. }
  1896. }
  1897. else {
  1898. plot.Selected = false;
  1899. }
  1900. }
  1901. return changed;
  1902. }
  1903. //-----------------------------------------------------------------------------
  1904. // Next Plot Data (Legacy)
  1905. //-----------------------------------------------------------------------------
  1906. void ApplyNextPlotData(ImAxis idx) {
  1907. ImPlotContext& gp = *GImPlot;
  1908. ImPlotPlot& plot = *gp.CurrentPlot;
  1909. ImPlotAxis& axis = plot.Axes[idx];
  1910. if (!axis.Enabled)
  1911. return;
  1912. double* npd_lmin = gp.NextPlotData.LinkedMin[idx];
  1913. double* npd_lmax = gp.NextPlotData.LinkedMax[idx];
  1914. bool npd_rngh = gp.NextPlotData.HasRange[idx];
  1915. ImPlotCond npd_rngc = gp.NextPlotData.RangeCond[idx];
  1916. ImPlotRange npd_rngv = gp.NextPlotData.Range[idx];
  1917. axis.LinkedMin = npd_lmin;
  1918. axis.LinkedMax = npd_lmax;
  1919. axis.PullLinks();
  1920. if (npd_rngh) {
  1921. if (!plot.Initialized || npd_rngc == ImPlotCond_Always)
  1922. axis.SetRange(npd_rngv);
  1923. }
  1924. axis.HasRange = npd_rngh;
  1925. axis.RangeCond = npd_rngc;
  1926. }
  1927. //-----------------------------------------------------------------------------
  1928. // Setup
  1929. //-----------------------------------------------------------------------------
  1930. void SetupAxis(ImAxis idx, const char* label, ImPlotAxisFlags flags) {
  1931. ImPlotContext& gp = *GImPlot;
  1932. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  1933. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  1934. // get plot and axis
  1935. ImPlotPlot& plot = *gp.CurrentPlot;
  1936. ImPlotAxis& axis = plot.Axes[idx];
  1937. // set ID
  1938. axis.ID = plot.ID + idx + 1;
  1939. // check and set flags
  1940. if (plot.JustCreated || flags != axis.PreviousFlags)
  1941. axis.Flags = flags;
  1942. axis.PreviousFlags = flags;
  1943. // enable axis
  1944. axis.Enabled = true;
  1945. // set label
  1946. plot.SetAxisLabel(axis,label);
  1947. // cache colors
  1948. UpdateAxisColors(axis);
  1949. }
  1950. void SetupAxisLimits(ImAxis idx, double min_lim, double max_lim, ImPlotCond cond) {
  1951. ImPlotContext& gp = *GImPlot;
  1952. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  1953. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); // get plot and axis
  1954. ImPlotPlot& plot = *gp.CurrentPlot;
  1955. ImPlotAxis& axis = plot.Axes[idx];
  1956. IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  1957. if (!plot.Initialized || cond == ImPlotCond_Always)
  1958. axis.SetRange(min_lim, max_lim);
  1959. axis.HasRange = true;
  1960. axis.RangeCond = cond;
  1961. }
  1962. void SetupAxisFormat(ImAxis idx, const char* fmt) {
  1963. ImPlotContext& gp = *GImPlot;
  1964. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  1965. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  1966. ImPlotPlot& plot = *gp.CurrentPlot;
  1967. ImPlotAxis& axis = plot.Axes[idx];
  1968. IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  1969. axis.HasFormatSpec = fmt != nullptr;
  1970. if (fmt != nullptr)
  1971. ImStrncpy(axis.FormatSpec,fmt,sizeof(axis.FormatSpec));
  1972. }
  1973. void SetupAxisLinks(ImAxis idx, double* min_lnk, double* max_lnk) {
  1974. ImPlotContext& gp = *GImPlot;
  1975. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  1976. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  1977. ImPlotPlot& plot = *gp.CurrentPlot;
  1978. ImPlotAxis& axis = plot.Axes[idx];
  1979. IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  1980. axis.LinkedMin = min_lnk;
  1981. axis.LinkedMax = max_lnk;
  1982. axis.PullLinks();
  1983. }
  1984. void SetupAxisFormat(ImAxis idx, ImPlotFormatter formatter, void* data) {
  1985. ImPlotContext& gp = *GImPlot;
  1986. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  1987. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  1988. ImPlotPlot& plot = *gp.CurrentPlot;
  1989. ImPlotAxis& axis = plot.Axes[idx];
  1990. IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  1991. axis.Formatter = formatter;
  1992. axis.FormatterData = data;
  1993. }
  1994. void SetupAxisTicks(ImAxis idx, const double* values, int n_ticks, const char* const labels[], bool show_default) {
  1995. ImPlotContext& gp = *GImPlot;
  1996. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  1997. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  1998. ImPlotPlot& plot = *gp.CurrentPlot;
  1999. ImPlotAxis& axis = plot.Axes[idx];
  2000. IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  2001. axis.ShowDefaultTicks = show_default;
  2002. AddTicksCustom(values,
  2003. labels,
  2004. n_ticks,
  2005. axis.Ticker,
  2006. axis.Formatter ? axis.Formatter : Formatter_Default,
  2007. (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT);
  2008. }
  2009. void SetupAxisTicks(ImAxis idx, double v_min, double v_max, int n_ticks, const char* const labels[], bool show_default) {
  2010. ImPlotContext& gp = *GImPlot;
  2011. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  2012. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  2013. n_ticks = n_ticks < 2 ? 2 : n_ticks;
  2014. FillRange(gp.TempDouble1, n_ticks, v_min, v_max);
  2015. SetupAxisTicks(idx, gp.TempDouble1.Data, n_ticks, labels, show_default);
  2016. }
  2017. void SetupAxisScale(ImAxis idx, ImPlotScale scale) {
  2018. ImPlotContext& gp = *GImPlot;
  2019. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  2020. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  2021. ImPlotPlot& plot = *gp.CurrentPlot;
  2022. ImPlotAxis& axis = plot.Axes[idx];
  2023. IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  2024. axis.Scale = scale;
  2025. switch (scale)
  2026. {
  2027. case ImPlotScale_Time:
  2028. axis.TransformForward = nullptr;
  2029. axis.TransformInverse = nullptr;
  2030. axis.TransformData = nullptr;
  2031. axis.Locator = Locator_Time;
  2032. axis.ConstraintRange = ImPlotRange(IMPLOT_MIN_TIME, IMPLOT_MAX_TIME);
  2033. axis.Ticker.Levels = 2;
  2034. break;
  2035. case ImPlotScale_Log10:
  2036. axis.TransformForward = TransformForward_Log10;
  2037. axis.TransformInverse = TransformInverse_Log10;
  2038. axis.TransformData = nullptr;
  2039. axis.Locator = Locator_Log10;
  2040. axis.ConstraintRange = ImPlotRange(DBL_MIN, INFINITY);
  2041. break;
  2042. case ImPlotScale_SymLog:
  2043. axis.TransformForward = TransformForward_SymLog;
  2044. axis.TransformInverse = TransformInverse_SymLog;
  2045. axis.TransformData = nullptr;
  2046. axis.Locator = Locator_SymLog;
  2047. axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY);
  2048. break;
  2049. default:
  2050. axis.TransformForward = nullptr;
  2051. axis.TransformInverse = nullptr;
  2052. axis.TransformData = nullptr;
  2053. axis.Locator = nullptr;
  2054. axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY);
  2055. break;
  2056. }
  2057. }
  2058. void SetupAxisScale(ImAxis idx, ImPlotTransform fwd, ImPlotTransform inv, void* data) {
  2059. ImPlotContext& gp = *GImPlot;
  2060. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  2061. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  2062. ImPlotPlot& plot = *gp.CurrentPlot;
  2063. ImPlotAxis& axis = plot.Axes[idx];
  2064. IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  2065. axis.Scale = IMPLOT_AUTO;
  2066. axis.TransformForward = fwd;
  2067. axis.TransformInverse = inv;
  2068. axis.TransformData = data;
  2069. }
  2070. void SetupAxisLimitsConstraints(ImAxis idx, double v_min, double v_max) {
  2071. ImPlotContext& gp = *GImPlot;
  2072. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  2073. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  2074. ImPlotPlot& plot = *gp.CurrentPlot;
  2075. ImPlotAxis& axis = plot.Axes[idx];
  2076. IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  2077. axis.ConstraintRange.Min = v_min;
  2078. axis.ConstraintRange.Max = v_max;
  2079. }
  2080. void SetupAxisZoomConstraints(ImAxis idx, double z_min, double z_max) {
  2081. ImPlotContext& gp = *GImPlot;
  2082. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  2083. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  2084. ImPlotPlot& plot = *gp.CurrentPlot;
  2085. ImPlotAxis& axis = plot.Axes[idx];
  2086. IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  2087. axis.ConstraintZoom.Min = z_min;
  2088. axis.ConstraintZoom.Max = z_max;
  2089. }
  2090. void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags) {
  2091. SetupAxis(ImAxis_X1, x_label, x_flags);
  2092. SetupAxis(ImAxis_Y1, y_label, y_flags);
  2093. }
  2094. void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) {
  2095. SetupAxisLimits(ImAxis_X1, x_min, x_max, cond);
  2096. SetupAxisLimits(ImAxis_Y1, y_min, y_max, cond);
  2097. }
  2098. void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) {
  2099. ImPlotContext& gp = *GImPlot;
  2100. IM_ASSERT_USER_ERROR((gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked) || (gp.CurrentSubplot != nullptr && gp.CurrentPlot == nullptr),
  2101. "Setup needs to be called after BeginPlot or BeginSubplots and before any setup locking functions (e.g. PlotX)!");
  2102. if (gp.CurrentItems) {
  2103. ImPlotLegend& legend = gp.CurrentItems->Legend;
  2104. // check and set location
  2105. if (location != legend.PreviousLocation)
  2106. legend.Location = location;
  2107. legend.PreviousLocation = location;
  2108. // check and set flags
  2109. if (flags != legend.PreviousFlags)
  2110. legend.Flags = flags;
  2111. legend.PreviousFlags = flags;
  2112. }
  2113. }
  2114. void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) {
  2115. ImPlotContext& gp = *GImPlot;
  2116. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,
  2117. "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!");
  2118. gp.CurrentPlot->MouseTextLocation = location;
  2119. gp.CurrentPlot->MouseTextFlags = flags;
  2120. }
  2121. //-----------------------------------------------------------------------------
  2122. // SetNext
  2123. //-----------------------------------------------------------------------------
  2124. void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond) {
  2125. ImPlotContext& gp = *GImPlot;
  2126. IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisLimits() needs to be called before BeginPlot()!");
  2127. IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
  2128. gp.NextPlotData.HasRange[axis] = true;
  2129. gp.NextPlotData.RangeCond[axis] = cond;
  2130. gp.NextPlotData.Range[axis].Min = v_min;
  2131. gp.NextPlotData.Range[axis].Max = v_max;
  2132. }
  2133. void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max) {
  2134. ImPlotContext& gp = *GImPlot;
  2135. IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisLinks() needs to be called before BeginPlot()!");
  2136. gp.NextPlotData.LinkedMin[axis] = link_min;
  2137. gp.NextPlotData.LinkedMax[axis] = link_max;
  2138. }
  2139. void SetNextAxisToFit(ImAxis axis) {
  2140. ImPlotContext& gp = *GImPlot;
  2141. IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisToFit() needs to be called before BeginPlot()!");
  2142. gp.NextPlotData.Fit[axis] = true;
  2143. }
  2144. void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) {
  2145. SetNextAxisLimits(ImAxis_X1, x_min, x_max, cond);
  2146. SetNextAxisLimits(ImAxis_Y1, y_min, y_max, cond);
  2147. }
  2148. void SetNextAxesToFit() {
  2149. for (int i = 0; i < ImAxis_COUNT; ++i)
  2150. SetNextAxisToFit(i);
  2151. }
  2152. //-----------------------------------------------------------------------------
  2153. // BeginPlot
  2154. //-----------------------------------------------------------------------------
  2155. bool BeginPlot(const char* title_id, const ImVec2& size, ImPlotFlags flags) {
  2156. IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
  2157. ImPlotContext& gp = *GImPlot;
  2158. IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "Mismatched BeginPlot()/EndPlot()!");
  2159. // FRONT MATTER -----------------------------------------------------------
  2160. if (gp.CurrentSubplot != nullptr)
  2161. ImGui::PushID(gp.CurrentSubplot->CurrentIdx);
  2162. // get globals
  2163. ImGuiContext &G = *GImGui;
  2164. ImGuiWindow* Window = G.CurrentWindow;
  2165. // skip if needed
  2166. if (Window->SkipItems && !gp.CurrentSubplot) {
  2167. ResetCtxForNextPlot(GImPlot);
  2168. return false;
  2169. }
  2170. // ID and age (TODO: keep track of plot age in frames)
  2171. const ImGuiID ID = Window->GetID(title_id);
  2172. const bool just_created = gp.Plots.GetByKey(ID) == nullptr;
  2173. gp.CurrentPlot = gp.Plots.GetOrAddByKey(ID);
  2174. ImPlotPlot &plot = *gp.CurrentPlot;
  2175. plot.ID = ID;
  2176. plot.Items.ID = ID - 1;
  2177. plot.JustCreated = just_created;
  2178. plot.SetupLocked = false;
  2179. // check flags
  2180. if (plot.JustCreated)
  2181. plot.Flags = flags;
  2182. else if (flags != plot.PreviousFlags)
  2183. plot.Flags = flags;
  2184. plot.PreviousFlags = flags;
  2185. // setup default axes
  2186. if (plot.JustCreated) {
  2187. SetupAxis(ImAxis_X1);
  2188. SetupAxis(ImAxis_Y1);
  2189. }
  2190. // reset axes
  2191. for (int i = 0; i < ImAxis_COUNT; ++i) {
  2192. plot.Axes[i].Reset();
  2193. UpdateAxisColors(plot.Axes[i]);
  2194. }
  2195. // ensure first axes enabled
  2196. plot.Axes[ImAxis_X1].Enabled = true;
  2197. plot.Axes[ImAxis_Y1].Enabled = true;
  2198. // set initial axes
  2199. plot.CurrentX = ImAxis_X1;
  2200. plot.CurrentY = ImAxis_Y1;
  2201. // process next plot data (legacy)
  2202. for (int i = 0; i < ImAxis_COUNT; ++i)
  2203. ApplyNextPlotData(i);
  2204. // clear text buffers
  2205. plot.ClearTextBuffer();
  2206. plot.SetTitle(title_id);
  2207. // set frame size
  2208. ImVec2 frame_size;
  2209. if (gp.CurrentSubplot != nullptr)
  2210. frame_size = gp.CurrentSubplot->CellSize;
  2211. else
  2212. frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y);
  2213. if (frame_size.x < gp.Style.PlotMinSize.x && (size.x < 0.0f || gp.CurrentSubplot != nullptr))
  2214. frame_size.x = gp.Style.PlotMinSize.x;
  2215. if (frame_size.y < gp.Style.PlotMinSize.y && (size.y < 0.0f || gp.CurrentSubplot != nullptr))
  2216. frame_size.y = gp.Style.PlotMinSize.y;
  2217. plot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
  2218. ImGui::ItemSize(plot.FrameRect);
  2219. if (!ImGui::ItemAdd(plot.FrameRect, plot.ID, &plot.FrameRect) && !gp.CurrentSubplot) {
  2220. ResetCtxForNextPlot(GImPlot);
  2221. return false;
  2222. }
  2223. // setup items (or dont)
  2224. if (gp.CurrentItems == nullptr)
  2225. gp.CurrentItems = &plot.Items;
  2226. return true;
  2227. }
  2228. //-----------------------------------------------------------------------------
  2229. // SetupFinish
  2230. //-----------------------------------------------------------------------------
  2231. void SetupFinish() {
  2232. IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
  2233. ImPlotContext& gp = *GImPlot;
  2234. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetupFinish needs to be called after BeginPlot!");
  2235. ImGuiContext& G = *GImGui;
  2236. ImDrawList& DrawList = *G.CurrentWindow->DrawList;
  2237. const ImGuiStyle& Style = G.Style;
  2238. ImPlotPlot &plot = *gp.CurrentPlot;
  2239. // lock setup
  2240. plot.SetupLocked = true;
  2241. // finalize axes and set default formatter/locator
  2242. for (int i = 0; i < ImAxis_COUNT; ++i) {
  2243. ImPlotAxis& axis = plot.Axes[i];
  2244. if (axis.Enabled) {
  2245. axis.Constrain();
  2246. if (!plot.Initialized && axis.CanInitFit())
  2247. plot.FitThisFrame = axis.FitThisFrame = true;
  2248. }
  2249. if (axis.Formatter == nullptr) {
  2250. axis.Formatter = Formatter_Default;
  2251. if (axis.HasFormatSpec)
  2252. axis.FormatterData = axis.FormatSpec;
  2253. else
  2254. axis.FormatterData = (void*)IMPLOT_LABEL_FORMAT;
  2255. }
  2256. if (axis.Locator == nullptr) {
  2257. axis.Locator = Locator_Default;
  2258. }
  2259. }
  2260. // setup nullptr orthogonal axes
  2261. const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal);
  2262. for (int ix = ImAxis_X1, iy = ImAxis_Y1; ix < ImAxis_Y1 || iy < ImAxis_COUNT; ++ix, ++iy) {
  2263. ImPlotAxis& x_axis = plot.Axes[ix];
  2264. ImPlotAxis& y_axis = plot.Axes[iy];
  2265. if (x_axis.Enabled && y_axis.Enabled) {
  2266. if (x_axis.OrthoAxis == nullptr)
  2267. x_axis.OrthoAxis = &y_axis;
  2268. if (y_axis.OrthoAxis == nullptr)
  2269. y_axis.OrthoAxis = &x_axis;
  2270. }
  2271. else if (x_axis.Enabled)
  2272. {
  2273. if (x_axis.OrthoAxis == nullptr && !axis_equal)
  2274. x_axis.OrthoAxis = &plot.Axes[ImAxis_Y1];
  2275. }
  2276. else if (y_axis.Enabled) {
  2277. if (y_axis.OrthoAxis == nullptr && !axis_equal)
  2278. y_axis.OrthoAxis = &plot.Axes[ImAxis_X1];
  2279. }
  2280. }
  2281. // canvas/axes bb
  2282. plot.CanvasRect = ImRect(plot.FrameRect.Min + gp.Style.PlotPadding, plot.FrameRect.Max - gp.Style.PlotPadding);
  2283. plot.AxesRect = plot.FrameRect;
  2284. // outside legend adjustments
  2285. if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0 && ImHasFlag(plot.Items.Legend.Flags, ImPlotLegendFlags_Outside)) {
  2286. ImPlotLegend& legend = plot.Items.Legend;
  2287. const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal);
  2288. const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz);
  2289. const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East);
  2290. const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West);
  2291. const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South);
  2292. const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North);
  2293. if ((west && !horz) || (west && horz && !north && !south)) {
  2294. plot.CanvasRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x);
  2295. plot.AxesRect.Min.x += (legend_size.x + gp.Style.PlotPadding.x);
  2296. }
  2297. if ((east && !horz) || (east && horz && !north && !south)) {
  2298. plot.CanvasRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x);
  2299. plot.AxesRect.Max.x -= (legend_size.x + gp.Style.PlotPadding.x);
  2300. }
  2301. if ((north && horz) || (north && !horz && !west && !east)) {
  2302. plot.CanvasRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y);
  2303. plot.AxesRect.Min.y += (legend_size.y + gp.Style.PlotPadding.y);
  2304. }
  2305. if ((south && horz) || (south && !horz && !west && !east)) {
  2306. plot.CanvasRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y);
  2307. plot.AxesRect.Max.y -= (legend_size.y + gp.Style.PlotPadding.y);
  2308. }
  2309. }
  2310. // plot bb
  2311. float pad_top = 0, pad_bot = 0, pad_left = 0, pad_right = 0;
  2312. // (0) calc top padding form title
  2313. ImVec2 title_size(0.0f, 0.0f);
  2314. if (plot.HasTitle())
  2315. title_size = ImGui::CalcTextSize(plot.GetTitle(), nullptr, true);
  2316. if (title_size.x > 0) {
  2317. pad_top += title_size.y + gp.Style.LabelPadding.y;
  2318. plot.AxesRect.Min.y += gp.Style.PlotPadding.y + pad_top;
  2319. }
  2320. // (1) calc addition top padding and bot padding
  2321. PadAndDatumAxesX(plot,pad_top,pad_bot,gp.CurrentAlignmentH);
  2322. const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot;
  2323. // (2) get y tick labels (needed for left/right pad)
  2324. for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
  2325. ImPlotAxis& axis = plot.YAxis(i);
  2326. if (axis.WillRender() && axis.ShowDefaultTicks && plot_height > 0) {
  2327. axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData);
  2328. }
  2329. }
  2330. // (3) calc left/right pad
  2331. PadAndDatumAxesY(plot,pad_left,pad_right,gp.CurrentAlignmentV);
  2332. const float plot_width = plot.CanvasRect.GetWidth() - pad_left - pad_right;
  2333. // (4) get x ticks
  2334. for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
  2335. ImPlotAxis& axis = plot.XAxis(i);
  2336. if (axis.WillRender() && axis.ShowDefaultTicks && plot_width > 0) {
  2337. axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData);
  2338. }
  2339. }
  2340. // (5) calc plot bb
  2341. plot.PlotRect = ImRect(plot.CanvasRect.Min + ImVec2(pad_left, pad_top), plot.CanvasRect.Max - ImVec2(pad_right, pad_bot));
  2342. // HOVER------------------------------------------------------------
  2343. // axes hover rect, pixel ranges
  2344. for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {
  2345. ImPlotAxis& xax = plot.XAxis(i);
  2346. xax.HoverRect = ImRect(ImVec2(plot.PlotRect.Min.x, ImMin(xax.Datum1,xax.Datum2)),
  2347. ImVec2(plot.PlotRect.Max.x, ImMax(xax.Datum1,xax.Datum2)));
  2348. xax.PixelMin = xax.IsInverted() ? plot.PlotRect.Max.x : plot.PlotRect.Min.x;
  2349. xax.PixelMax = xax.IsInverted() ? plot.PlotRect.Min.x : plot.PlotRect.Max.x;
  2350. xax.UpdateTransformCache();
  2351. }
  2352. for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {
  2353. ImPlotAxis& yax = plot.YAxis(i);
  2354. yax.HoverRect = ImRect(ImVec2(ImMin(yax.Datum1,yax.Datum2),plot.PlotRect.Min.y),
  2355. ImVec2(ImMax(yax.Datum1,yax.Datum2),plot.PlotRect.Max.y));
  2356. yax.PixelMin = yax.IsInverted() ? plot.PlotRect.Min.y : plot.PlotRect.Max.y;
  2357. yax.PixelMax = yax.IsInverted() ? plot.PlotRect.Max.y : plot.PlotRect.Min.y;
  2358. yax.UpdateTransformCache();
  2359. }
  2360. // Equal axis constraint. Must happen after we set Pixels
  2361. // constrain equal axes for primary x and y if not approximately equal
  2362. // constrains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case
  2363. if (axis_equal) {
  2364. for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {
  2365. ImPlotAxis& x_axis = plot.XAxis(i);
  2366. if (x_axis.OrthoAxis == nullptr)
  2367. continue;
  2368. double xar = x_axis.GetAspect();
  2369. double yar = x_axis.OrthoAxis->GetAspect();
  2370. // edge case: user has set x range this frame, so fit y to x so that we honor their request for x range
  2371. // NB: because of feedback across several frames, the user's x request may not be perfectly honored
  2372. if (x_axis.HasRange)
  2373. x_axis.OrthoAxis->SetAspect(xar);
  2374. else if (!ImAlmostEqual(xar,yar) && !x_axis.OrthoAxis->IsInputLocked())
  2375. x_axis.SetAspect(yar);
  2376. }
  2377. }
  2378. // INPUT ------------------------------------------------------------------
  2379. if (!ImHasFlag(plot.Flags, ImPlotFlags_NoInputs))
  2380. UpdateInput(plot);
  2381. // fit from FitNextPlotAxes or auto fit
  2382. for (int i = 0; i < ImAxis_COUNT; ++i) {
  2383. if (gp.NextPlotData.Fit[i] || plot.Axes[i].IsAutoFitting()) {
  2384. plot.FitThisFrame = true;
  2385. plot.Axes[i].FitThisFrame = true;
  2386. }
  2387. }
  2388. // RENDER -----------------------------------------------------------------
  2389. const float txt_height = ImGui::GetTextLineHeight();
  2390. // render frame
  2391. if (!ImHasFlag(plot.Flags, ImPlotFlags_NoFrame))
  2392. ImGui::RenderFrame(plot.FrameRect.Min, plot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding);
  2393. // grid bg
  2394. DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBg));
  2395. // transform ticks
  2396. for (int i = 0; i < ImAxis_COUNT; i++) {
  2397. ImPlotAxis& axis = plot.Axes[i];
  2398. if (axis.WillRender()) {
  2399. for (int t = 0; t < axis.Ticker.TickCount(); t++) {
  2400. ImPlotTick& tk = axis.Ticker.Ticks[t];
  2401. tk.PixelPos = IM_ROUND(axis.PlotToPixels(tk.PlotPos));
  2402. }
  2403. }
  2404. }
  2405. // render grid (background)
  2406. for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
  2407. ImPlotAxis& x_axis = plot.XAxis(i);
  2408. if (x_axis.Enabled && x_axis.HasGridLines() && !x_axis.IsForeground())
  2409. RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x);
  2410. }
  2411. for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
  2412. ImPlotAxis& y_axis = plot.YAxis(i);
  2413. if (y_axis.Enabled && y_axis.HasGridLines() && !y_axis.IsForeground())
  2414. RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y);
  2415. }
  2416. // render x axis button, label, tick labels
  2417. for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
  2418. ImPlotAxis& ax = plot.XAxis(i);
  2419. if (!ax.Enabled)
  2420. continue;
  2421. if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight))
  2422. DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov);
  2423. else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) {
  2424. DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi);
  2425. ax.ColorHiLi = IM_COL32_BLACK_TRANS;
  2426. }
  2427. else if (ax.ColorBg != IM_COL32_BLACK_TRANS) {
  2428. DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg);
  2429. }
  2430. const ImPlotTicker& tkr = ax.Ticker;
  2431. const bool opp = ax.IsOpposite();
  2432. if (ax.HasLabel()) {
  2433. const char* label = plot.GetAxisLabel(ax);
  2434. const ImVec2 label_size = ImGui::CalcTextSize(label);
  2435. const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.y + gp.Style.LabelPadding.y : 0.0f)
  2436. + (tkr.Levels - 1) * (txt_height + gp.Style.LabelPadding.y)
  2437. + gp.Style.LabelPadding.y;
  2438. const ImVec2 label_pos(plot.PlotRect.GetCenter().x - label_size.x * 0.5f,
  2439. opp ? ax.Datum1 - label_offset - label_size.y : ax.Datum1 + label_offset);
  2440. DrawList.AddText(label_pos, ax.ColorTxt, label);
  2441. }
  2442. if (ax.HasTickLabels()) {
  2443. for (int j = 0; j < tkr.TickCount(); ++j) {
  2444. const ImPlotTick& tk = tkr.Ticks[j];
  2445. const float datum = ax.Datum1 + (opp ? (-gp.Style.LabelPadding.y -txt_height -tk.Level * (txt_height + gp.Style.LabelPadding.y))
  2446. : gp.Style.LabelPadding.y + tk.Level * (txt_height + gp.Style.LabelPadding.y));
  2447. if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.x - 1 && tk.PixelPos <= plot.PlotRect.Max.x + 1) {
  2448. ImVec2 start(tk.PixelPos - 0.5f * tk.LabelSize.x, datum);
  2449. DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j));
  2450. }
  2451. }
  2452. }
  2453. }
  2454. // render y axis button, label, tick labels
  2455. for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
  2456. ImPlotAxis& ax = plot.YAxis(i);
  2457. if (!ax.Enabled)
  2458. continue;
  2459. if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight))
  2460. DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov);
  2461. else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) {
  2462. DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi);
  2463. ax.ColorHiLi = IM_COL32_BLACK_TRANS;
  2464. }
  2465. else if (ax.ColorBg != IM_COL32_BLACK_TRANS) {
  2466. DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg);
  2467. }
  2468. const ImPlotTicker& tkr = ax.Ticker;
  2469. const bool opp = ax.IsOpposite();
  2470. if (ax.HasLabel()) {
  2471. const char* label = plot.GetAxisLabel(ax);
  2472. const ImVec2 label_size = CalcTextSizeVertical(label);
  2473. const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.x + gp.Style.LabelPadding.x : 0.0f)
  2474. + gp.Style.LabelPadding.x;
  2475. const ImVec2 label_pos(opp ? ax.Datum1 + label_offset : ax.Datum1 - label_offset - label_size.x,
  2476. plot.PlotRect.GetCenter().y + label_size.y * 0.5f);
  2477. AddTextVertical(&DrawList, label_pos, ax.ColorTxt, label);
  2478. }
  2479. if (ax.HasTickLabels()) {
  2480. for (int j = 0; j < tkr.TickCount(); ++j) {
  2481. const ImPlotTick& tk = tkr.Ticks[j];
  2482. const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x));
  2483. if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1) {
  2484. ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y);
  2485. DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j));
  2486. }
  2487. }
  2488. }
  2489. }
  2490. // clear legend (TODO: put elsewhere)
  2491. plot.Items.Legend.Reset();
  2492. // push ID to set item hashes (NB: !!!THIS PROBABLY NEEDS TO BE IN BEGIN PLOT!!!!)
  2493. ImGui::PushOverrideID(gp.CurrentItems->ID);
  2494. }
  2495. //-----------------------------------------------------------------------------
  2496. // EndPlot()
  2497. //-----------------------------------------------------------------------------
  2498. void EndPlot() {
  2499. IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
  2500. ImPlotContext& gp = *GImPlot;
  2501. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Mismatched BeginPlot()/EndPlot()!");
  2502. SetupLock();
  2503. ImGuiContext &G = *GImGui;
  2504. ImPlotPlot &plot = *gp.CurrentPlot;
  2505. ImGuiWindow * Window = G.CurrentWindow;
  2506. ImDrawList & DrawList = *Window->DrawList;
  2507. const ImGuiIO & IO = ImGui::GetIO();
  2508. // FINAL RENDER -----------------------------------------------------------
  2509. const bool render_border = gp.Style.PlotBorderSize > 0 && GetStyleColorVec4(ImPlotCol_PlotBorder).w > 0;
  2510. const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);
  2511. const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);
  2512. ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true);
  2513. // render grid (foreground)
  2514. for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
  2515. ImPlotAxis& x_axis = plot.XAxis(i);
  2516. if (x_axis.Enabled && x_axis.HasGridLines() && x_axis.IsForeground())
  2517. RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x);
  2518. }
  2519. for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
  2520. ImPlotAxis& y_axis = plot.YAxis(i);
  2521. if (y_axis.Enabled && y_axis.HasGridLines() && y_axis.IsForeground())
  2522. RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y);
  2523. }
  2524. // render title
  2525. if (plot.HasTitle()) {
  2526. ImU32 col = GetStyleColorU32(ImPlotCol_TitleText);
  2527. AddTextCentered(&DrawList,ImVec2(plot.PlotRect.GetCenter().x, plot.CanvasRect.Min.y),col,plot.GetTitle());
  2528. }
  2529. // render x ticks
  2530. int count_B = 0, count_T = 0;
  2531. for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
  2532. const ImPlotAxis& ax = plot.XAxis(i);
  2533. if (!ax.Enabled)
  2534. continue;
  2535. const ImPlotTicker& tkr = ax.Ticker;
  2536. const bool opp = ax.IsOpposite();
  2537. const bool aux = ((opp && count_T > 0)||(!opp && count_B > 0));
  2538. if (ax.HasTickMarks()) {
  2539. const float direction = opp ? 1.0f : -1.0f;
  2540. for (int j = 0; j < tkr.TickCount(); ++j) {
  2541. const ImPlotTick& tk = tkr.Ticks[j];
  2542. if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.x || tk.PixelPos > plot.PlotRect.Max.x)
  2543. continue;
  2544. const ImVec2 start(tk.PixelPos, ax.Datum1);
  2545. const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x;
  2546. const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x;
  2547. DrawList.AddLine(start, start + ImVec2(0,direction*len), ax.ColorTick, thk);
  2548. }
  2549. if (aux || !render_border)
  2550. DrawList.AddLine(ImVec2(plot.PlotRect.Min.x,ax.Datum1), ImVec2(plot.PlotRect.Max.x,ax.Datum1), ax.ColorTick, gp.Style.MinorTickSize.x);
  2551. }
  2552. count_B += !opp;
  2553. count_T += opp;
  2554. }
  2555. // render y ticks
  2556. int count_L = 0, count_R = 0;
  2557. for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
  2558. const ImPlotAxis& ax = plot.YAxis(i);
  2559. if (!ax.Enabled)
  2560. continue;
  2561. const ImPlotTicker& tkr = ax.Ticker;
  2562. const bool opp = ax.IsOpposite();
  2563. const bool aux = ((opp && count_R > 0)||(!opp && count_L > 0));
  2564. if (ax.HasTickMarks()) {
  2565. const float direction = opp ? -1.0f : 1.0f;
  2566. for (int j = 0; j < tkr.TickCount(); ++j) {
  2567. const ImPlotTick& tk = tkr.Ticks[j];
  2568. if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.y || tk.PixelPos > plot.PlotRect.Max.y)
  2569. continue;
  2570. const ImVec2 start(ax.Datum1, tk.PixelPos);
  2571. const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y;
  2572. const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y;
  2573. DrawList.AddLine(start, start + ImVec2(direction*len,0), ax.ColorTick, thk);
  2574. }
  2575. if (aux || !render_border)
  2576. DrawList.AddLine(ImVec2(ax.Datum1, plot.PlotRect.Min.y), ImVec2(ax.Datum1, plot.PlotRect.Max.y), ax.ColorTick, gp.Style.MinorTickSize.y);
  2577. }
  2578. count_L += !opp;
  2579. count_R += opp;
  2580. }
  2581. ImGui::PopClipRect();
  2582. // render annotations
  2583. PushPlotClipRect();
  2584. for (int i = 0; i < gp.Annotations.Size; ++i) {
  2585. const char* txt = gp.Annotations.GetText(i);
  2586. ImPlotAnnotation& an = gp.Annotations.Annotations[i];
  2587. const ImVec2 txt_size = ImGui::CalcTextSize(txt);
  2588. const ImVec2 size = txt_size + gp.Style.AnnotationPadding * 2;
  2589. ImVec2 pos = an.Pos;
  2590. if (an.Offset.x == 0)
  2591. pos.x -= size.x / 2;
  2592. else if (an.Offset.x > 0)
  2593. pos.x += an.Offset.x;
  2594. else
  2595. pos.x -= size.x - an.Offset.x;
  2596. if (an.Offset.y == 0)
  2597. pos.y -= size.y / 2;
  2598. else if (an.Offset.y > 0)
  2599. pos.y += an.Offset.y;
  2600. else
  2601. pos.y -= size.y - an.Offset.y;
  2602. if (an.Clamp)
  2603. pos = ClampLabelPos(pos, size, plot.PlotRect.Min, plot.PlotRect.Max);
  2604. ImRect rect(pos,pos+size);
  2605. if (an.Offset.x != 0 || an.Offset.y != 0) {
  2606. ImVec2 corners[4] = {rect.GetTL(), rect.GetTR(), rect.GetBR(), rect.GetBL()};
  2607. int min_corner = 0;
  2608. float min_len = FLT_MAX;
  2609. for (int c = 0; c < 4; ++c) {
  2610. float len = ImLengthSqr(an.Pos - corners[c]);
  2611. if (len < min_len) {
  2612. min_corner = c;
  2613. min_len = len;
  2614. }
  2615. }
  2616. DrawList.AddLine(an.Pos, corners[min_corner], an.ColorBg);
  2617. }
  2618. DrawList.AddRectFilled(rect.Min, rect.Max, an.ColorBg);
  2619. DrawList.AddText(pos + gp.Style.AnnotationPadding, an.ColorFg, txt);
  2620. }
  2621. // render selection
  2622. if (plot.Selected)
  2623. RenderSelectionRect(DrawList, plot.SelectRect.Min + plot.PlotRect.Min, plot.SelectRect.Max + plot.PlotRect.Min, GetStyleColorVec4(ImPlotCol_Selection));
  2624. // render crosshairs
  2625. if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && plot.Hovered && !(any_x_held || any_y_held) && !plot.Selecting && !plot.Items.Legend.Hovered) {
  2626. ImGui::SetMouseCursor(ImGuiMouseCursor_None);
  2627. ImVec2 xy = IO.MousePos;
  2628. ImVec2 h1(plot.PlotRect.Min.x, xy.y);
  2629. ImVec2 h2(xy.x - 5, xy.y);
  2630. ImVec2 h3(xy.x + 5, xy.y);
  2631. ImVec2 h4(plot.PlotRect.Max.x, xy.y);
  2632. ImVec2 v1(xy.x, plot.PlotRect.Min.y);
  2633. ImVec2 v2(xy.x, xy.y - 5);
  2634. ImVec2 v3(xy.x, xy.y + 5);
  2635. ImVec2 v4(xy.x, plot.PlotRect.Max.y);
  2636. ImU32 col = GetStyleColorU32(ImPlotCol_Crosshairs);
  2637. DrawList.AddLine(h1, h2, col);
  2638. DrawList.AddLine(h3, h4, col);
  2639. DrawList.AddLine(v1, v2, col);
  2640. DrawList.AddLine(v3, v4, col);
  2641. }
  2642. // render mouse pos
  2643. if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText) && (plot.Hovered || ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_ShowAlways))) {
  2644. const bool no_aux = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoAuxAxes);
  2645. const bool no_fmt = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoFormat);
  2646. ImGuiTextBuffer& builder = gp.MousePosStringBuilder;
  2647. builder.Buf.shrink(0);
  2648. char buff[IMPLOT_LABEL_MAX_SIZE];
  2649. const int num_x = no_aux ? 1 : IMPLOT_NUM_X_AXES;
  2650. for (int i = 0; i < num_x; ++i) {
  2651. ImPlotAxis& x_axis = plot.XAxis(i);
  2652. if (!x_axis.Enabled)
  2653. continue;
  2654. if (i > 0)
  2655. builder.append(", (");
  2656. double v = x_axis.PixelsToPlot(IO.MousePos.x);
  2657. if (no_fmt)
  2658. Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT);
  2659. else
  2660. LabelAxisValue(x_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true);
  2661. builder.append(buff);
  2662. if (i > 0)
  2663. builder.append(")");
  2664. }
  2665. builder.append(", ");
  2666. const int num_y = no_aux ? 1 : IMPLOT_NUM_Y_AXES;
  2667. for (int i = 0; i < num_y; ++i) {
  2668. ImPlotAxis& y_axis = plot.YAxis(i);
  2669. if (!y_axis.Enabled)
  2670. continue;
  2671. if (i > 0)
  2672. builder.append(", (");
  2673. double v = y_axis.PixelsToPlot(IO.MousePos.y);
  2674. if (no_fmt)
  2675. Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT);
  2676. else
  2677. LabelAxisValue(y_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true);
  2678. builder.append(buff);
  2679. if (i > 0)
  2680. builder.append(")");
  2681. }
  2682. if (!builder.empty()) {
  2683. const ImVec2 size = ImGui::CalcTextSize(builder.c_str());
  2684. const ImVec2 pos = GetLocationPos(plot.PlotRect, size, plot.MouseTextLocation, gp.Style.MousePosPadding);
  2685. DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), builder.c_str());
  2686. }
  2687. }
  2688. PopPlotClipRect();
  2689. // axis side switch
  2690. if (!plot.Held) {
  2691. ImVec2 mouse_pos = ImGui::GetIO().MousePos;
  2692. ImRect trigger_rect = plot.PlotRect;
  2693. trigger_rect.Expand(-10);
  2694. for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {
  2695. ImPlotAxis& x_axis = plot.XAxis(i);
  2696. if (ImHasFlag(x_axis.Flags, ImPlotAxisFlags_NoSideSwitch))
  2697. continue;
  2698. if (x_axis.Held && plot.PlotRect.Contains(mouse_pos)) {
  2699. const bool opp = ImHasFlag(x_axis.Flags, ImPlotAxisFlags_Opposite);
  2700. if (!opp) {
  2701. ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5,
  2702. plot.PlotRect.Max.x + 5, plot.PlotRect.Min.y + 5);
  2703. if (mouse_pos.y < plot.PlotRect.Max.y - 10)
  2704. DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov);
  2705. if (rect.Contains(mouse_pos))
  2706. x_axis.Flags |= ImPlotAxisFlags_Opposite;
  2707. }
  2708. else {
  2709. ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Max.y - 5,
  2710. plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5);
  2711. if (mouse_pos.y > plot.PlotRect.Min.y + 10)
  2712. DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov);
  2713. if (rect.Contains(mouse_pos))
  2714. x_axis.Flags &= ~ImPlotAxisFlags_Opposite;
  2715. }
  2716. }
  2717. }
  2718. for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {
  2719. ImPlotAxis& y_axis = plot.YAxis(i);
  2720. if (ImHasFlag(y_axis.Flags, ImPlotAxisFlags_NoSideSwitch))
  2721. continue;
  2722. if (y_axis.Held && plot.PlotRect.Contains(mouse_pos)) {
  2723. const bool opp = ImHasFlag(y_axis.Flags, ImPlotAxisFlags_Opposite);
  2724. if (!opp) {
  2725. ImRect rect(plot.PlotRect.Max.x - 5, plot.PlotRect.Min.y - 5,
  2726. plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5);
  2727. if (mouse_pos.x > plot.PlotRect.Min.x + 10)
  2728. DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov);
  2729. if (rect.Contains(mouse_pos))
  2730. y_axis.Flags |= ImPlotAxisFlags_Opposite;
  2731. }
  2732. else {
  2733. ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5,
  2734. plot.PlotRect.Min.x + 5, plot.PlotRect.Max.y + 5);
  2735. if (mouse_pos.x < plot.PlotRect.Max.x - 10)
  2736. DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov);
  2737. if (rect.Contains(mouse_pos))
  2738. y_axis.Flags &= ~ImPlotAxisFlags_Opposite;
  2739. }
  2740. }
  2741. }
  2742. }
  2743. // reset legend hovers
  2744. plot.Items.Legend.Hovered = false;
  2745. for (int i = 0; i < plot.Items.GetItemCount(); ++i)
  2746. plot.Items.GetItemByIndex(i)->LegendHovered = false;
  2747. // render legend
  2748. if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0) {
  2749. ImPlotLegend& legend = plot.Items.Legend;
  2750. const bool legend_out = ImHasFlag(legend.Flags, ImPlotLegendFlags_Outside);
  2751. const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal);
  2752. const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz);
  2753. const ImVec2 legend_pos = GetLocationPos(legend_out ? plot.FrameRect : plot.PlotRect,
  2754. legend_size,
  2755. legend.Location,
  2756. legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding);
  2757. legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
  2758. legend.RectClamped = legend.Rect;
  2759. const bool legend_scrollable = ClampLegendRect(legend.RectClamped,
  2760. legend_out ? plot.FrameRect : plot.PlotRect,
  2761. legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding
  2762. );
  2763. const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap
  2764. | ImGuiButtonFlags_PressedOnClick
  2765. | ImGuiButtonFlags_PressedOnDoubleClick
  2766. | ImGuiButtonFlags_MouseButtonLeft
  2767. | ImGuiButtonFlags_MouseButtonRight
  2768. | ImGuiButtonFlags_MouseButtonMiddle
  2769. | ImGuiButtonFlags_FlattenChildren;
  2770. ImGui::KeepAliveID(plot.Items.ID);
  2771. ImGui::ButtonBehavior(legend.RectClamped, plot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags);
  2772. legend.Hovered = legend.Hovered || (ImGui::IsWindowHovered() && legend.RectClamped.Contains(IO.MousePos));
  2773. if (legend_scrollable) {
  2774. if (legend.Hovered) {
  2775. ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.Items.ID);
  2776. if (IO.MouseWheel != 0.0f) {
  2777. ImVec2 max_step = legend.Rect.GetSize() * 0.67f;
  2778. #if IMGUI_VERSION_NUM < 19172
  2779. float font_size = ImGui::GetCurrentWindow()->CalcFontSize();
  2780. #else
  2781. float font_size = ImGui::GetCurrentWindow()->FontRefSize;
  2782. #endif
  2783. float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x));
  2784. legend.Scroll.x += scroll_step * IO.MouseWheel;
  2785. legend.Scroll.y += scroll_step * IO.MouseWheel;
  2786. }
  2787. }
  2788. const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize();
  2789. legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f);
  2790. legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f);
  2791. const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y);
  2792. ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset;
  2793. legend.Rect.Min += legend_offset;
  2794. legend.Rect.Max += legend_offset;
  2795. } else {
  2796. legend.Scroll = ImVec2(0,0);
  2797. }
  2798. const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
  2799. const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
  2800. ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true);
  2801. DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg);
  2802. bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)
  2803. && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus);
  2804. DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd);
  2805. ImGui::PopClipRect();
  2806. // main ctx menu
  2807. if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus))
  2808. ImGui::OpenPopup("##LegendContext");
  2809. if (ImGui::BeginPopup("##LegendContext")) {
  2810. ImGui::Text("Legend"); ImGui::Separator();
  2811. if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend)))
  2812. ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend);
  2813. ImGui::EndPopup();
  2814. }
  2815. }
  2816. else {
  2817. plot.Items.Legend.Rect = ImRect();
  2818. }
  2819. // render border
  2820. if (render_border)
  2821. DrawList.AddRect(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawFlags_RoundCornersAll, gp.Style.PlotBorderSize);
  2822. // render tags
  2823. for (int i = 0; i < gp.Tags.Size; ++i) {
  2824. ImPlotTag& tag = gp.Tags.Tags[i];
  2825. ImPlotAxis& axis = plot.Axes[tag.Axis];
  2826. if (!axis.Enabled || !axis.Range.Contains(tag.Value))
  2827. continue;
  2828. const char* txt = gp.Tags.GetText(i);
  2829. ImVec2 text_size = ImGui::CalcTextSize(txt);
  2830. ImVec2 size = text_size + gp.Style.AnnotationPadding * 2;
  2831. ImVec2 pos;
  2832. axis.Ticker.OverrideSizeLate(size);
  2833. float pix = IM_ROUND(axis.PlotToPixels(tag.Value));
  2834. if (axis.Vertical) {
  2835. if (axis.IsOpposite()) {
  2836. pos = ImVec2(axis.Datum1 + gp.Style.LabelPadding.x, pix - size.y * 0.5f);
  2837. DrawList.AddTriangleFilled(ImVec2(axis.Datum1,pix), pos, pos + ImVec2(0,size.y), tag.ColorBg);
  2838. }
  2839. else {
  2840. pos = ImVec2(axis.Datum1 - size.x - gp.Style.LabelPadding.x, pix - size.y * 0.5f);
  2841. DrawList.AddTriangleFilled(pos + ImVec2(size.x,0), ImVec2(axis.Datum1,pix), pos+size, tag.ColorBg);
  2842. }
  2843. }
  2844. else {
  2845. if (axis.IsOpposite()) {
  2846. pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 - size.y - gp.Style.LabelPadding.y );
  2847. DrawList.AddTriangleFilled(pos + ImVec2(0,size.y), pos + size, ImVec2(pix,axis.Datum1), tag.ColorBg);
  2848. }
  2849. else {
  2850. pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 + gp.Style.LabelPadding.y);
  2851. DrawList.AddTriangleFilled(pos, ImVec2(pix,axis.Datum1), pos + ImVec2(size.x, 0), tag.ColorBg);
  2852. }
  2853. }
  2854. DrawList.AddRectFilled(pos,pos+size,tag.ColorBg);
  2855. DrawList.AddText(pos+gp.Style.AnnotationPadding,tag.ColorFg,txt);
  2856. }
  2857. // FIT DATA --------------------------------------------------------------
  2858. const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal);
  2859. if (plot.FitThisFrame) {
  2860. for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {
  2861. ImPlotAxis& x_axis = plot.XAxis(i);
  2862. if (x_axis.FitThisFrame) {
  2863. x_axis.ApplyFit(gp.Style.FitPadding.x);
  2864. if (axis_equal && x_axis.OrthoAxis != nullptr) {
  2865. double aspect = x_axis.GetAspect();
  2866. ImPlotAxis& y_axis = *x_axis.OrthoAxis;
  2867. if (y_axis.FitThisFrame) {
  2868. y_axis.ApplyFit(gp.Style.FitPadding.y);
  2869. y_axis.FitThisFrame = false;
  2870. aspect = ImMax(aspect, y_axis.GetAspect());
  2871. }
  2872. x_axis.SetAspect(aspect);
  2873. y_axis.SetAspect(aspect);
  2874. }
  2875. }
  2876. }
  2877. for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
  2878. ImPlotAxis& y_axis = plot.YAxis(i);
  2879. if (y_axis.FitThisFrame) {
  2880. y_axis.ApplyFit(gp.Style.FitPadding.y);
  2881. if (axis_equal && y_axis.OrthoAxis != nullptr) {
  2882. double aspect = y_axis.GetAspect();
  2883. ImPlotAxis& x_axis = *y_axis.OrthoAxis;
  2884. if (x_axis.FitThisFrame) {
  2885. x_axis.ApplyFit(gp.Style.FitPadding.x);
  2886. x_axis.FitThisFrame = false;
  2887. aspect = ImMax(x_axis.GetAspect(), aspect);
  2888. }
  2889. x_axis.SetAspect(aspect);
  2890. y_axis.SetAspect(aspect);
  2891. }
  2892. }
  2893. }
  2894. plot.FitThisFrame = false;
  2895. }
  2896. // CONTEXT MENUS -----------------------------------------------------------
  2897. ImGui::PushOverrideID(plot.ID);
  2898. const bool can_ctx = gp.OpenContextThisFrame &&
  2899. !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) &&
  2900. !plot.Items.Legend.Hovered;
  2901. // main ctx menu
  2902. if (can_ctx && plot.Hovered)
  2903. ImGui::OpenPopup("##PlotContext");
  2904. if (ImGui::BeginPopup("##PlotContext")) {
  2905. ShowPlotContextMenu(plot);
  2906. ImGui::EndPopup();
  2907. }
  2908. // axes ctx menus
  2909. for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {
  2910. ImGui::PushID(i);
  2911. ImPlotAxis& x_axis = plot.XAxis(i);
  2912. if (can_ctx && x_axis.Hovered && x_axis.HasMenus())
  2913. ImGui::OpenPopup("##XContext");
  2914. if (ImGui::BeginPopup("##XContext")) {
  2915. ImGui::Text(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : i == 0 ? "X-Axis" : "X-Axis %d", i + 1);
  2916. ImGui::Separator();
  2917. ShowAxisContextMenu(x_axis, axis_equal ? x_axis.OrthoAxis : nullptr, true);
  2918. ImGui::EndPopup();
  2919. }
  2920. ImGui::PopID();
  2921. }
  2922. for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {
  2923. ImGui::PushID(i);
  2924. ImPlotAxis& y_axis = plot.YAxis(i);
  2925. if (can_ctx && y_axis.Hovered && y_axis.HasMenus())
  2926. ImGui::OpenPopup("##YContext");
  2927. if (ImGui::BeginPopup("##YContext")) {
  2928. ImGui::Text(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1);
  2929. ImGui::Separator();
  2930. ShowAxisContextMenu(y_axis, axis_equal ? y_axis.OrthoAxis : nullptr, false);
  2931. ImGui::EndPopup();
  2932. }
  2933. ImGui::PopID();
  2934. }
  2935. ImGui::PopID();
  2936. // LINKED AXES ------------------------------------------------------------
  2937. for (int i = 0; i < ImAxis_COUNT; ++i)
  2938. plot.Axes[i].PushLinks();
  2939. // CLEANUP ----------------------------------------------------------------
  2940. // remove items
  2941. if (gp.CurrentItems == &plot.Items)
  2942. gp.CurrentItems = nullptr;
  2943. // reset the plot items for the next frame
  2944. for (int i = 0; i < plot.Items.GetItemCount(); ++i) {
  2945. plot.Items.GetItemByIndex(i)->SeenThisFrame = false;
  2946. }
  2947. // mark the plot as initialized, i.e. having made it through one frame completely
  2948. plot.Initialized = true;
  2949. // Pop ImGui::PushID at the end of BeginPlot
  2950. ImGui::PopID();
  2951. // Reset context for next plot
  2952. ResetCtxForNextPlot(GImPlot);
  2953. // setup next subplot
  2954. if (gp.CurrentSubplot != nullptr) {
  2955. ImGui::PopID();
  2956. SubplotNextCell();
  2957. }
  2958. }
  2959. //-----------------------------------------------------------------------------
  2960. // BEGIN/END SUBPLOT
  2961. //-----------------------------------------------------------------------------
  2962. static const float SUBPLOT_BORDER_SIZE = 1.0f;
  2963. static const float SUBPLOT_SPLITTER_HALF_THICKNESS = 4.0f;
  2964. static const float SUBPLOT_SPLITTER_FEEDBACK_TIMER = 0.06f;
  2965. void SubplotSetCell(int row, int col) {
  2966. ImPlotContext& gp = *GImPlot;
  2967. ImPlotSubplot& subplot = *gp.CurrentSubplot;
  2968. if (row >= subplot.Rows || col >= subplot.Cols)
  2969. return;
  2970. float xoff = 0;
  2971. float yoff = 0;
  2972. for (int c = 0; c < col; ++c)
  2973. xoff += subplot.ColRatios[c];
  2974. for (int r = 0; r < row; ++r)
  2975. yoff += subplot.RowRatios[r];
  2976. const ImVec2 grid_size = subplot.GridRect.GetSize();
  2977. ImVec2 cpos = subplot.GridRect.Min + ImVec2(xoff*grid_size.x,yoff*grid_size.y);
  2978. cpos.x = IM_ROUND(cpos.x);
  2979. cpos.y = IM_ROUND(cpos.y);
  2980. ImGui::GetCurrentWindow()->DC.CursorPos = cpos;
  2981. // set cell size
  2982. subplot.CellSize.x = IM_ROUND(subplot.GridRect.GetWidth() * subplot.ColRatios[col]);
  2983. subplot.CellSize.y = IM_ROUND(subplot.GridRect.GetHeight() * subplot.RowRatios[row]);
  2984. // setup links
  2985. const bool lx = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX);
  2986. const bool ly = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY);
  2987. const bool lr = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows);
  2988. const bool lc = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols);
  2989. SetNextAxisLinks(ImAxis_X1, lx ? &subplot.ColLinkData[0].Min : lc ? &subplot.ColLinkData[col].Min : nullptr,
  2990. lx ? &subplot.ColLinkData[0].Max : lc ? &subplot.ColLinkData[col].Max : nullptr);
  2991. SetNextAxisLinks(ImAxis_Y1, ly ? &subplot.RowLinkData[0].Min : lr ? &subplot.RowLinkData[row].Min : nullptr,
  2992. ly ? &subplot.RowLinkData[0].Max : lr ? &subplot.RowLinkData[row].Max : nullptr);
  2993. // setup alignment
  2994. if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign)) {
  2995. gp.CurrentAlignmentH = &subplot.RowAlignmentData[row];
  2996. gp.CurrentAlignmentV = &subplot.ColAlignmentData[col];
  2997. }
  2998. // set idx
  2999. if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor))
  3000. subplot.CurrentIdx = col * subplot.Rows + row;
  3001. else
  3002. subplot.CurrentIdx = row * subplot.Cols + col;
  3003. }
  3004. void SubplotSetCell(int idx) {
  3005. ImPlotContext& gp = *GImPlot;
  3006. ImPlotSubplot& subplot = *gp.CurrentSubplot;
  3007. if (idx >= subplot.Rows * subplot.Cols)
  3008. return;
  3009. int row = 0, col = 0;
  3010. if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) {
  3011. row = idx % subplot.Rows;
  3012. col = idx / subplot.Rows;
  3013. }
  3014. else {
  3015. row = idx / subplot.Cols;
  3016. col = idx % subplot.Cols;
  3017. }
  3018. return SubplotSetCell(row, col);
  3019. }
  3020. void SubplotNextCell() {
  3021. ImPlotContext& gp = *GImPlot;
  3022. ImPlotSubplot& subplot = *gp.CurrentSubplot;
  3023. SubplotSetCell(++subplot.CurrentIdx);
  3024. }
  3025. bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, ImPlotSubplotFlags flags, float* row_sizes, float* col_sizes) {
  3026. IM_ASSERT_USER_ERROR(rows > 0 && cols > 0, "Invalid sizing arguments!");
  3027. IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
  3028. ImPlotContext& gp = *GImPlot;
  3029. IM_ASSERT_USER_ERROR(gp.CurrentSubplot == nullptr, "Mismatched BeginSubplots()/EndSubplots()!");
  3030. ImGuiContext &G = *GImGui;
  3031. ImGuiWindow * Window = G.CurrentWindow;
  3032. if (Window->SkipItems)
  3033. return false;
  3034. const ImGuiID ID = Window->GetID(title);
  3035. bool just_created = gp.Subplots.GetByKey(ID) == nullptr;
  3036. gp.CurrentSubplot = gp.Subplots.GetOrAddByKey(ID);
  3037. ImPlotSubplot& subplot = *gp.CurrentSubplot;
  3038. subplot.ID = ID;
  3039. subplot.Items.ID = ID - 1;
  3040. subplot.HasTitle = ImGui::FindRenderedTextEnd(title, nullptr) != title;
  3041. // push ID
  3042. ImGui::PushID(ID);
  3043. if (just_created)
  3044. subplot.Flags = flags;
  3045. else if (flags != subplot.PreviousFlags)
  3046. subplot.Flags = flags;
  3047. subplot.PreviousFlags = flags;
  3048. // check for change in rows and cols
  3049. if (subplot.Rows != rows || subplot.Cols != cols) {
  3050. subplot.RowAlignmentData.resize(rows);
  3051. subplot.RowLinkData.resize(rows);
  3052. subplot.RowRatios.resize(rows);
  3053. for (int r = 0; r < rows; ++r) {
  3054. subplot.RowAlignmentData[r].Reset();
  3055. subplot.RowLinkData[r] = ImPlotRange(0,1);
  3056. subplot.RowRatios[r] = 1.0f / rows;
  3057. }
  3058. subplot.ColAlignmentData.resize(cols);
  3059. subplot.ColLinkData.resize(cols);
  3060. subplot.ColRatios.resize(cols);
  3061. for (int c = 0; c < cols; ++c) {
  3062. subplot.ColAlignmentData[c].Reset();
  3063. subplot.ColLinkData[c] = ImPlotRange(0,1);
  3064. subplot.ColRatios[c] = 1.0f / cols;
  3065. }
  3066. }
  3067. // check incoming size requests
  3068. float row_sum = 0, col_sum = 0;
  3069. if (row_sizes != nullptr) {
  3070. row_sum = ImSum(row_sizes, rows);
  3071. for (int r = 0; r < rows; ++r)
  3072. subplot.RowRatios[r] = row_sizes[r] / row_sum;
  3073. }
  3074. if (col_sizes != nullptr) {
  3075. col_sum = ImSum(col_sizes, cols);
  3076. for (int c = 0; c < cols; ++c)
  3077. subplot.ColRatios[c] = col_sizes[c] / col_sum;
  3078. }
  3079. subplot.Rows = rows;
  3080. subplot.Cols = cols;
  3081. // calc plot frame sizes
  3082. ImVec2 title_size(0.0f, 0.0f);
  3083. if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle))
  3084. title_size = ImGui::CalcTextSize(title, nullptr, true);
  3085. const float pad_top = title_size.x > 0.0f ? title_size.y + gp.Style.LabelPadding.y : 0;
  3086. const ImVec2 half_pad = gp.Style.PlotPadding/2;
  3087. const ImVec2 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y);
  3088. subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
  3089. subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top);
  3090. subplot.GridRect.Max = subplot.FrameRect.Max - half_pad;
  3091. subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows|ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
  3092. // outside legend adjustments (TODO: make function)
  3093. const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);
  3094. if (share_items)
  3095. gp.CurrentItems = &subplot.Items;
  3096. if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) {
  3097. ImPlotLegend& legend = subplot.Items.Legend;
  3098. const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal);
  3099. const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz);
  3100. const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East);
  3101. const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West);
  3102. const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South);
  3103. const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North);
  3104. if ((west && !horz) || (west && horz && !north && !south))
  3105. subplot.GridRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x);
  3106. if ((east && !horz) || (east && horz && !north && !south))
  3107. subplot.GridRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x);
  3108. if ((north && horz) || (north && !horz && !west && !east))
  3109. subplot.GridRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y);
  3110. if ((south && horz) || (south && !horz && !west && !east))
  3111. subplot.GridRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y);
  3112. }
  3113. // render single background frame
  3114. ImGui::RenderFrame(subplot.FrameRect.Min, subplot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, ImGui::GetStyle().FrameRounding);
  3115. // render title
  3116. if (title_size.x > 0.0f && !ImHasFlag(subplot.Flags, ImPlotFlags_NoTitle)) {
  3117. const ImU32 col = GetStyleColorU32(ImPlotCol_TitleText);
  3118. AddTextCentered(ImGui::GetWindowDrawList(),ImVec2(subplot.GridRect.GetCenter().x, subplot.GridRect.Min.y - pad_top + half_pad.y),col,title);
  3119. }
  3120. // render splitters
  3121. if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize)) {
  3122. ImDrawList& DrawList = *ImGui::GetWindowDrawList();
  3123. const ImU32 hov_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorHovered]);
  3124. const ImU32 act_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]);
  3125. float xpos = subplot.GridRect.Min.x;
  3126. float ypos = subplot.GridRect.Min.y;
  3127. int separator = 1;
  3128. // bool pass = false;
  3129. for (int r = 0; r < subplot.Rows-1; ++r) {
  3130. ypos += subplot.RowRatios[r] * subplot.GridRect.GetHeight();
  3131. const ImGuiID sep_id = subplot.ID + separator;
  3132. ImGui::KeepAliveID(sep_id);
  3133. const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS);
  3134. bool sep_hov = false, sep_hld = false;
  3135. const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);
  3136. if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) {
  3137. if (sep_clk && ImGui::IsMouseDoubleClicked(0)) {
  3138. float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2;
  3139. subplot.RowRatios[r] = subplot.RowRatios[r+1] = p;
  3140. }
  3141. if (sep_clk) {
  3142. subplot.TempSizes[0] = subplot.RowRatios[r];
  3143. subplot.TempSizes[1] = subplot.RowRatios[r+1];
  3144. }
  3145. if (sep_hld) {
  3146. float dp = ImGui::GetMouseDragDelta(0).y / subplot.GridRect.GetHeight();
  3147. if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) {
  3148. subplot.RowRatios[r] = subplot.TempSizes[0] + dp;
  3149. subplot.RowRatios[r+1] = subplot.TempSizes[1] - dp;
  3150. }
  3151. }
  3152. DrawList.AddLine(ImVec2(IM_ROUND(subplot.GridRect.Min.x),IM_ROUND(ypos)),
  3153. ImVec2(IM_ROUND(subplot.GridRect.Max.x),IM_ROUND(ypos)),
  3154. sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE);
  3155. ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
  3156. }
  3157. separator++;
  3158. }
  3159. for (int c = 0; c < subplot.Cols-1; ++c) {
  3160. xpos += subplot.ColRatios[c] * subplot.GridRect.GetWidth();
  3161. const ImGuiID sep_id = subplot.ID + separator;
  3162. ImGui::KeepAliveID(sep_id);
  3163. const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y);
  3164. bool sep_hov = false, sep_hld = false;
  3165. const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);
  3166. if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) {
  3167. if (sep_clk && ImGui::IsMouseDoubleClicked(0)) {
  3168. float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2;
  3169. subplot.ColRatios[c] = subplot.ColRatios[c+1] = p;
  3170. }
  3171. if (sep_clk) {
  3172. subplot.TempSizes[0] = subplot.ColRatios[c];
  3173. subplot.TempSizes[1] = subplot.ColRatios[c+1];
  3174. }
  3175. if (sep_hld) {
  3176. float dp = ImGui::GetMouseDragDelta(0).x / subplot.GridRect.GetWidth();
  3177. if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) {
  3178. subplot.ColRatios[c] = subplot.TempSizes[0] + dp;
  3179. subplot.ColRatios[c+1] = subplot.TempSizes[1] - dp;
  3180. }
  3181. }
  3182. DrawList.AddLine(ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Min.y)),
  3183. ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Max.y)),
  3184. sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE);
  3185. ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
  3186. }
  3187. separator++;
  3188. }
  3189. }
  3190. // set outgoing sizes
  3191. if (row_sizes != nullptr) {
  3192. for (int r = 0; r < rows; ++r)
  3193. row_sizes[r] = subplot.RowRatios[r] * row_sum;
  3194. }
  3195. if (col_sizes != nullptr) {
  3196. for (int c = 0; c < cols; ++c)
  3197. col_sizes[c] = subplot.ColRatios[c] * col_sum;
  3198. }
  3199. // push styling
  3200. PushStyleColor(ImPlotCol_FrameBg, IM_COL32_BLACK_TRANS);
  3201. PushStyleVar(ImPlotStyleVar_PlotPadding, half_pad);
  3202. PushStyleVar(ImPlotStyleVar_PlotMinSize, ImVec2(0,0));
  3203. ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize,0);
  3204. // set initial cursor pos
  3205. Window->DC.CursorPos = subplot.GridRect.Min;
  3206. // begin alignments
  3207. for (int r = 0; r < subplot.Rows; ++r)
  3208. subplot.RowAlignmentData[r].Begin();
  3209. for (int c = 0; c < subplot.Cols; ++c)
  3210. subplot.ColAlignmentData[c].Begin();
  3211. // clear legend data
  3212. subplot.Items.Legend.Reset();
  3213. // Setup first subplot
  3214. SubplotSetCell(0,0);
  3215. return true;
  3216. }
  3217. void EndSubplots() {
  3218. IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
  3219. ImPlotContext& gp = *GImPlot;
  3220. IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "Mismatched BeginSubplots()/EndSubplots()!");
  3221. ImPlotSubplot& subplot = *gp.CurrentSubplot;
  3222. const ImGuiIO& IO = ImGui::GetIO();
  3223. // set alignments
  3224. for (int r = 0; r < subplot.Rows; ++r)
  3225. subplot.RowAlignmentData[r].End();
  3226. for (int c = 0; c < subplot.Cols; ++c)
  3227. subplot.ColAlignmentData[c].End();
  3228. // pop styling
  3229. PopStyleColor();
  3230. PopStyleVar();
  3231. PopStyleVar();
  3232. ImGui::PopStyleVar();
  3233. // legend
  3234. subplot.Items.Legend.Hovered = false;
  3235. for (int i = 0; i < subplot.Items.GetItemCount(); ++i)
  3236. subplot.Items.GetItemByIndex(i)->LegendHovered = false;
  3237. // render legend
  3238. const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);
  3239. ImDrawList& DrawList = *ImGui::GetWindowDrawList();
  3240. if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) {
  3241. ImPlotLegend& legend = subplot.Items.Legend;
  3242. const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal);
  3243. const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz);
  3244. const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, legend.Location, gp.Style.PlotPadding);
  3245. legend.Rect = ImRect(legend_pos, legend_pos + legend_size);
  3246. legend.RectClamped = legend.Rect;
  3247. const bool legend_scrollable = ClampLegendRect(legend.RectClamped,subplot.FrameRect, gp.Style.PlotPadding);
  3248. const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap
  3249. | ImGuiButtonFlags_PressedOnClick
  3250. | ImGuiButtonFlags_PressedOnDoubleClick
  3251. | ImGuiButtonFlags_MouseButtonLeft
  3252. | ImGuiButtonFlags_MouseButtonRight
  3253. | ImGuiButtonFlags_MouseButtonMiddle
  3254. | ImGuiButtonFlags_FlattenChildren;
  3255. ImGui::KeepAliveID(subplot.Items.ID);
  3256. ImGui::ButtonBehavior(legend.RectClamped, subplot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags);
  3257. legend.Hovered = legend.Hovered || (subplot.FrameHovered && legend.RectClamped.Contains(ImGui::GetIO().MousePos));
  3258. if (legend_scrollable) {
  3259. if (legend.Hovered) {
  3260. ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, subplot.Items.ID);
  3261. if (IO.MouseWheel != 0.0f) {
  3262. ImVec2 max_step = legend.Rect.GetSize() * 0.67f;
  3263. #if IMGUI_VERSION_NUM < 19172
  3264. float font_size = ImGui::GetCurrentWindow()->CalcFontSize();
  3265. #else
  3266. float font_size = ImGui::GetCurrentWindow()->FontRefSize;
  3267. #endif
  3268. float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x));
  3269. legend.Scroll.x += scroll_step * IO.MouseWheel;
  3270. legend.Scroll.y += scroll_step * IO.MouseWheel;
  3271. }
  3272. }
  3273. const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize();
  3274. legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f);
  3275. legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f);
  3276. const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y);
  3277. ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset;
  3278. legend.Rect.Min += legend_offset;
  3279. legend.Rect.Max += legend_offset;
  3280. } else {
  3281. legend.Scroll = ImVec2(0,0);
  3282. }
  3283. const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
  3284. const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
  3285. ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true);
  3286. DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg);
  3287. bool legend_contextable = ShowLegendEntries(subplot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)
  3288. && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus);
  3289. DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd);
  3290. ImGui::PopClipRect();
  3291. if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu])
  3292. ImGui::OpenPopup("##LegendContext");
  3293. if (ImGui::BeginPopup("##LegendContext")) {
  3294. ImGui::Text("Legend"); ImGui::Separator();
  3295. if (ShowLegendContextMenu(legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend)))
  3296. ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend);
  3297. ImGui::EndPopup();
  3298. }
  3299. }
  3300. else {
  3301. subplot.Items.Legend.Rect = ImRect();
  3302. }
  3303. // remove items
  3304. if (gp.CurrentItems == &subplot.Items)
  3305. gp.CurrentItems = nullptr;
  3306. // reset the plot items for the next frame (TODO: put this elswhere)
  3307. for (int i = 0; i < subplot.Items.GetItemCount(); ++i) {
  3308. subplot.Items.GetItemByIndex(i)->SeenThisFrame = false;
  3309. }
  3310. // pop id
  3311. ImGui::PopID();
  3312. // set DC back correctly
  3313. GImGui->CurrentWindow->DC.CursorPos = subplot.FrameRect.Min;
  3314. ImGui::Dummy(subplot.FrameRect.GetSize());
  3315. ResetCtxForNextSubplot(GImPlot);
  3316. }
  3317. //-----------------------------------------------------------------------------
  3318. // [SECTION] Plot Utils
  3319. //-----------------------------------------------------------------------------
  3320. void SetAxis(ImAxis axis) {
  3321. ImPlotContext& gp = *GImPlot;
  3322. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetAxis() needs to be called between BeginPlot() and EndPlot()!");
  3323. IM_ASSERT_USER_ERROR(axis >= ImAxis_X1 && axis < ImAxis_COUNT, "Axis index out of bounds!");
  3324. IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[axis].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  3325. SetupLock();
  3326. if (axis < ImAxis_Y1)
  3327. gp.CurrentPlot->CurrentX = axis;
  3328. else
  3329. gp.CurrentPlot->CurrentY = axis;
  3330. }
  3331. void SetAxes(ImAxis x_idx, ImAxis y_idx) {
  3332. ImPlotContext& gp = *GImPlot;
  3333. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetAxes() needs to be called between BeginPlot() and EndPlot()!");
  3334. IM_ASSERT_USER_ERROR(x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1, "X-Axis index out of bounds!");
  3335. IM_ASSERT_USER_ERROR(y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT, "Y-Axis index out of bounds!");
  3336. IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[x_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  3337. IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[y_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?");
  3338. SetupLock();
  3339. gp.CurrentPlot->CurrentX = x_idx;
  3340. gp.CurrentPlot->CurrentY = y_idx;
  3341. }
  3342. ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_idx, ImAxis y_idx) {
  3343. ImPlotContext& gp = *GImPlot;
  3344. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!");
  3345. IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!");
  3346. IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!");
  3347. SetupLock();
  3348. ImPlotPlot& plot = *gp.CurrentPlot;
  3349. ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx];
  3350. ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx];
  3351. return ImPlotPoint( x_axis.PixelsToPlot(x), y_axis.PixelsToPlot(y) );
  3352. }
  3353. ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_idx, ImAxis y_idx) {
  3354. return PixelsToPlot(pix.x, pix.y, x_idx, y_idx);
  3355. }
  3356. ImVec2 PlotToPixels(double x, double y, ImAxis x_idx, ImAxis y_idx) {
  3357. ImPlotContext& gp = *GImPlot;
  3358. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!");
  3359. IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!");
  3360. IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!");
  3361. SetupLock();
  3362. ImPlotPlot& plot = *gp.CurrentPlot;
  3363. ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx];
  3364. ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx];
  3365. return ImVec2( x_axis.PlotToPixels(x), y_axis.PlotToPixels(y) );
  3366. }
  3367. ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_idx, ImAxis y_idx) {
  3368. return PlotToPixels(plt.x, plt.y, x_idx, y_idx);
  3369. }
  3370. ImVec2 GetPlotPos() {
  3371. ImPlotContext& gp = *GImPlot;
  3372. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotPos() needs to be called between BeginPlot() and EndPlot()!");
  3373. SetupLock();
  3374. return gp.CurrentPlot->PlotRect.Min;
  3375. }
  3376. ImVec2 GetPlotSize() {
  3377. ImPlotContext& gp = *GImPlot;
  3378. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotSize() needs to be called between BeginPlot() and EndPlot()!");
  3379. SetupLock();
  3380. return gp.CurrentPlot->PlotRect.GetSize();
  3381. }
  3382. ImPlotPoint GetPlotMousePos(ImAxis x_idx, ImAxis y_idx) {
  3383. IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!");
  3384. SetupLock();
  3385. return PixelsToPlot(ImGui::GetMousePos(), x_idx, y_idx);
  3386. }
  3387. ImPlotRect GetPlotLimits(ImAxis x_idx, ImAxis y_idx) {
  3388. ImPlotContext& gp = *GImPlot;
  3389. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!");
  3390. IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!");
  3391. IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!");
  3392. SetupLock();
  3393. ImPlotPlot& plot = *gp.CurrentPlot;
  3394. ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx];
  3395. ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx];
  3396. ImPlotRect limits;
  3397. limits.X = x_axis.Range;
  3398. limits.Y = y_axis.Range;
  3399. return limits;
  3400. }
  3401. bool IsPlotHovered() {
  3402. ImPlotContext& gp = *GImPlot;
  3403. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!");
  3404. SetupLock();
  3405. return gp.CurrentPlot->Hovered;
  3406. }
  3407. bool IsAxisHovered(ImAxis axis) {
  3408. ImPlotContext& gp = *GImPlot;
  3409. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotXAxisHovered() needs to be called between BeginPlot() and EndPlot()!");
  3410. SetupLock();
  3411. return gp.CurrentPlot->Axes[axis].Hovered;
  3412. }
  3413. bool IsSubplotsHovered() {
  3414. ImPlotContext& gp = *GImPlot;
  3415. IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "IsSubplotsHovered() needs to be called between BeginSubplots() and EndSubplots()!");
  3416. return gp.CurrentSubplot->FrameHovered;
  3417. }
  3418. bool IsPlotSelected() {
  3419. ImPlotContext& gp = *GImPlot;
  3420. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotSelected() needs to be called between BeginPlot() and EndPlot()!");
  3421. SetupLock();
  3422. return gp.CurrentPlot->Selected;
  3423. }
  3424. ImPlotRect GetPlotSelection(ImAxis x_idx, ImAxis y_idx) {
  3425. ImPlotContext& gp = *GImPlot;
  3426. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotSelection() needs to be called between BeginPlot() and EndPlot()!");
  3427. SetupLock();
  3428. ImPlotPlot& plot = *gp.CurrentPlot;
  3429. if (!plot.Selected)
  3430. return ImPlotRect(0,0,0,0);
  3431. ImPlotPoint p1 = PixelsToPlot(plot.SelectRect.Min + plot.PlotRect.Min, x_idx, y_idx);
  3432. ImPlotPoint p2 = PixelsToPlot(plot.SelectRect.Max + plot.PlotRect.Min, x_idx, y_idx);
  3433. ImPlotRect result;
  3434. result.X.Min = ImMin(p1.x, p2.x);
  3435. result.X.Max = ImMax(p1.x, p2.x);
  3436. result.Y.Min = ImMin(p1.y, p2.y);
  3437. result.Y.Max = ImMax(p1.y, p2.y);
  3438. return result;
  3439. }
  3440. void CancelPlotSelection() {
  3441. ImPlotContext& gp = *GImPlot;
  3442. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "CancelPlotSelection() needs to be called between BeginPlot() and EndPlot()!");
  3443. SetupLock();
  3444. ImPlotPlot& plot = *gp.CurrentPlot;
  3445. if (plot.Selected)
  3446. plot.Selected = plot.Selecting = false;
  3447. }
  3448. void HideNextItem(bool hidden, ImPlotCond cond) {
  3449. ImPlotContext& gp = *GImPlot;
  3450. gp.NextItemData.HasHidden = true;
  3451. gp.NextItemData.Hidden = hidden;
  3452. gp.NextItemData.HiddenCond = cond;
  3453. }
  3454. //-----------------------------------------------------------------------------
  3455. // [SECTION] Plot Tools
  3456. //-----------------------------------------------------------------------------
  3457. void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, bool round) {
  3458. ImPlotContext& gp = *GImPlot;
  3459. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Annotation() needs to be called between BeginPlot() and EndPlot()!");
  3460. SetupLock();
  3461. char x_buff[IMPLOT_LABEL_MAX_SIZE];
  3462. char y_buff[IMPLOT_LABEL_MAX_SIZE];
  3463. ImPlotAxis& x_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX];
  3464. ImPlotAxis& y_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY];
  3465. LabelAxisValue(x_axis, x, x_buff, sizeof(x_buff), round);
  3466. LabelAxisValue(y_axis, y, y_buff, sizeof(y_buff), round);
  3467. Annotation(x,y,col,offset,clamp,"%s, %s",x_buff,y_buff);
  3468. }
  3469. void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, va_list args) {
  3470. ImPlotContext& gp = *GImPlot;
  3471. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Annotation() needs to be called between BeginPlot() and EndPlot()!");
  3472. SetupLock();
  3473. ImVec2 pos = PlotToPixels(x,y,IMPLOT_AUTO,IMPLOT_AUTO);
  3474. ImU32 bg = ImGui::GetColorU32(col);
  3475. ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_InlayText) : CalcTextColor(col);
  3476. gp.Annotations.AppendV(pos, offset, bg, fg, clamp, fmt, args);
  3477. }
  3478. void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, ...) {
  3479. va_list args;
  3480. va_start(args, fmt);
  3481. AnnotationV(x,y,col,offset,clamp,fmt,args);
  3482. va_end(args);
  3483. }
  3484. void TagV(ImAxis axis, double v, const ImVec4& col, const char* fmt, va_list args) {
  3485. ImPlotContext& gp = *GImPlot;
  3486. SetupLock();
  3487. ImU32 bg = ImGui::GetColorU32(col);
  3488. ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_AxisText) : CalcTextColor(col);
  3489. gp.Tags.AppendV(axis,v,bg,fg,fmt,args);
  3490. }
  3491. void Tag(ImAxis axis, double v, const ImVec4& col, const char* fmt, ...) {
  3492. va_list args;
  3493. va_start(args, fmt);
  3494. TagV(axis,v,col,fmt,args);
  3495. va_end(args);
  3496. }
  3497. void Tag(ImAxis axis, double v, const ImVec4& color, bool round) {
  3498. ImPlotContext& gp = *GImPlot;
  3499. SetupLock();
  3500. char buff[IMPLOT_LABEL_MAX_SIZE];
  3501. ImPlotAxis& ax = gp.CurrentPlot->Axes[axis];
  3502. LabelAxisValue(ax, v, buff, sizeof(buff), round);
  3503. Tag(axis,v,color,"%s",buff);
  3504. }
  3505. IMPLOT_API void TagX(double x, const ImVec4& color, bool round) {
  3506. ImPlotContext& gp = *GImPlot;
  3507. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!");
  3508. Tag(gp.CurrentPlot->CurrentX, x, color, round);
  3509. }
  3510. IMPLOT_API void TagX(double x, const ImVec4& color, const char* fmt, ...) {
  3511. ImPlotContext& gp = *GImPlot;
  3512. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!");
  3513. va_list args;
  3514. va_start(args, fmt);
  3515. TagV(gp.CurrentPlot->CurrentX,x,color,fmt,args);
  3516. va_end(args);
  3517. }
  3518. IMPLOT_API void TagXV(double x, const ImVec4& color, const char* fmt, va_list args) {
  3519. ImPlotContext& gp = *GImPlot;
  3520. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!");
  3521. TagV(gp.CurrentPlot->CurrentX, x, color, fmt, args);
  3522. }
  3523. IMPLOT_API void TagY(double y, const ImVec4& color, bool round) {
  3524. ImPlotContext& gp = *GImPlot;
  3525. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!");
  3526. Tag(gp.CurrentPlot->CurrentY, y, color, round);
  3527. }
  3528. IMPLOT_API void TagY(double y, const ImVec4& color, const char* fmt, ...) {
  3529. ImPlotContext& gp = *GImPlot;
  3530. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!");
  3531. va_list args;
  3532. va_start(args, fmt);
  3533. TagV(gp.CurrentPlot->CurrentY,y,color,fmt,args);
  3534. va_end(args);
  3535. }
  3536. IMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list args) {
  3537. ImPlotContext& gp = *GImPlot;
  3538. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!");
  3539. TagV(gp.CurrentPlot->CurrentY, y, color, fmt, args);
  3540. }
  3541. static const float DRAG_GRAB_HALF_SIZE = 4.0f;
  3542. bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
  3543. ImGui::PushID("#IMPLOT_DRAG_POINT");
  3544. IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragPoint() needs to be called between BeginPlot() and EndPlot()!");
  3545. SetupLock();
  3546. if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) {
  3547. FitPoint(ImPlotPoint(*x,*y));
  3548. }
  3549. const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs);
  3550. const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors);
  3551. const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed);
  3552. const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, radius);
  3553. const ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;
  3554. const ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
  3555. ImVec2 pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO);
  3556. const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);
  3557. ImRect rect(pos.x-grab_half_size,pos.y-grab_half_size,pos.x+grab_half_size,pos.y+grab_half_size);
  3558. bool hovered = false, held = false;
  3559. ImGui::KeepAliveID(id);
  3560. if (input) {
  3561. bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);
  3562. if (out_clicked) *out_clicked = clicked;
  3563. if (out_hovered) *out_hovered = hovered;
  3564. if (out_held) *out_held = held;
  3565. }
  3566. bool modified = false;
  3567. if (held && ImGui::IsMouseDragging(0)) {
  3568. *x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
  3569. *y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
  3570. modified = true;
  3571. }
  3572. PushPlotClipRect();
  3573. ImDrawList& DrawList = *GetPlotDrawList();
  3574. if ((hovered || held) && show_curs)
  3575. ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
  3576. if (modified && no_delay)
  3577. pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO);
  3578. DrawList.AddCircleFilled(pos, radius, col32);
  3579. PopPlotClipRect();
  3580. ImGui::PopID();
  3581. return modified;
  3582. }
  3583. bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
  3584. // ImGui::PushID("#IMPLOT_DRAG_LINE_X");
  3585. ImPlotContext& gp = *GImPlot;
  3586. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineX() needs to be called between BeginPlot() and EndPlot()!");
  3587. SetupLock();
  3588. if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) {
  3589. FitPointX(*value);
  3590. }
  3591. const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs);
  3592. const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors);
  3593. const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed);
  3594. const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2);
  3595. float yt = gp.CurrentPlot->PlotRect.Min.y;
  3596. float yb = gp.CurrentPlot->PlotRect.Max.y;
  3597. float x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x);
  3598. const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);
  3599. ImRect rect(x-grab_half_size,yt,x+grab_half_size,yb);
  3600. bool hovered = false, held = false;
  3601. ImGui::KeepAliveID(id);
  3602. if (input) {
  3603. bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);
  3604. if (out_clicked) *out_clicked = clicked;
  3605. if (out_hovered) *out_hovered = hovered;
  3606. if (out_held) *out_held = held;
  3607. }
  3608. if ((hovered || held) && show_curs)
  3609. ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
  3610. float len = gp.Style.MajorTickLen.x;
  3611. ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;
  3612. ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
  3613. bool modified = false;
  3614. if (held && ImGui::IsMouseDragging(0)) {
  3615. *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
  3616. modified = true;
  3617. }
  3618. PushPlotClipRect();
  3619. ImDrawList& DrawList = *GetPlotDrawList();
  3620. if (modified && no_delay)
  3621. x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x);
  3622. DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness);
  3623. DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness);
  3624. DrawList.AddLine(ImVec2(x,yb), ImVec2(x,yb-len), col32, 3*thickness);
  3625. PopPlotClipRect();
  3626. // ImGui::PopID();
  3627. return modified;
  3628. }
  3629. bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
  3630. ImGui::PushID("#IMPLOT_DRAG_LINE_Y");
  3631. ImPlotContext& gp = *GImPlot;
  3632. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineY() needs to be called between BeginPlot() and EndPlot()!");
  3633. SetupLock();
  3634. if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) {
  3635. FitPointY(*value);
  3636. }
  3637. const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs);
  3638. const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors);
  3639. const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed);
  3640. const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2);
  3641. float xl = gp.CurrentPlot->PlotRect.Min.x;
  3642. float xr = gp.CurrentPlot->PlotRect.Max.x;
  3643. float y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y);
  3644. const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);
  3645. ImRect rect(xl,y-grab_half_size,xr,y+grab_half_size);
  3646. bool hovered = false, held = false;
  3647. ImGui::KeepAliveID(id);
  3648. if (input) {
  3649. bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);
  3650. if (out_clicked) *out_clicked = clicked;
  3651. if (out_hovered) *out_hovered = hovered;
  3652. if (out_held) *out_held = held;
  3653. }
  3654. if ((hovered || held) && show_curs)
  3655. ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
  3656. float len = gp.Style.MajorTickLen.y;
  3657. ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;
  3658. ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
  3659. bool modified = false;
  3660. if (held && ImGui::IsMouseDragging(0)) {
  3661. *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
  3662. modified = true;
  3663. }
  3664. PushPlotClipRect();
  3665. ImDrawList& DrawList = *GetPlotDrawList();
  3666. if (modified && no_delay)
  3667. y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y);
  3668. DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness);
  3669. DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness);
  3670. DrawList.AddLine(ImVec2(xr,y), ImVec2(xr-len,y), col32, 3*thickness);
  3671. PopPlotClipRect();
  3672. ImGui::PopID();
  3673. return modified;
  3674. }
  3675. bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
  3676. ImGui::PushID("#IMPLOT_DRAG_RECT");
  3677. IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragRect() needs to be called between BeginPlot() and EndPlot()!");
  3678. SetupLock();
  3679. if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) {
  3680. FitPoint(ImPlotPoint(*x_min,*y_min));
  3681. FitPoint(ImPlotPoint(*x_max,*y_max));
  3682. }
  3683. const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs);
  3684. const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors);
  3685. const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed);
  3686. bool h[] = {true,false,true,false};
  3687. double* x[] = {x_min,x_max,x_max,x_min};
  3688. double* y[] = {y_min,y_min,y_max,y_max};
  3689. ImVec2 p[4];
  3690. for (int i = 0; i < 4; ++i)
  3691. p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO);
  3692. ImVec2 pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO);
  3693. ImRect rect(ImMin(p[0],p[2]),ImMax(p[0],p[2]));
  3694. ImRect rect_grab = rect; rect_grab.Expand(DRAG_GRAB_HALF_SIZE);
  3695. ImGuiMouseCursor cur[4];
  3696. if (show_curs) {
  3697. cur[0] = (rect.Min.x == p[0].x && rect.Min.y == p[0].y) || (rect.Max.x == p[0].x && rect.Max.y == p[0].y) ? ImGuiMouseCursor_ResizeNWSE : ImGuiMouseCursor_ResizeNESW;
  3698. cur[1] = cur[0] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
  3699. cur[2] = cur[1] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
  3700. cur[3] = cur[2] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
  3701. }
  3702. ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;
  3703. ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);
  3704. color.w *= 0.25f;
  3705. ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color);
  3706. const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);
  3707. bool modified = false;
  3708. bool clicked = false, hovered = false, held = false;
  3709. ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE);
  3710. ImGui::KeepAliveID(id);
  3711. if (input) {
  3712. // middle point
  3713. clicked = ImGui::ButtonBehavior(b_rect,id,&hovered,&held);
  3714. if (out_clicked) *out_clicked = clicked;
  3715. if (out_hovered) *out_hovered = hovered;
  3716. if (out_held) *out_held = held;
  3717. }
  3718. if ((hovered || held) && show_curs)
  3719. ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
  3720. if (held && ImGui::IsMouseDragging(0)) {
  3721. for (int i = 0; i < 4; ++i) {
  3722. ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO);
  3723. *y[i] = pp.y;
  3724. *x[i] = pp.x;
  3725. }
  3726. modified = true;
  3727. }
  3728. for (int i = 0; i < 4; ++i) {
  3729. // points
  3730. b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE);
  3731. ImGuiID p_id = id + i + 1;
  3732. ImGui::KeepAliveID(p_id);
  3733. if (input) {
  3734. clicked = ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held);
  3735. if (out_clicked) *out_clicked = *out_clicked || clicked;
  3736. if (out_hovered) *out_hovered = *out_hovered || hovered;
  3737. if (out_held) *out_held = *out_held || held;
  3738. }
  3739. if ((hovered || held) && show_curs)
  3740. ImGui::SetMouseCursor(cur[i]);
  3741. if (held && ImGui::IsMouseDragging(0)) {
  3742. *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
  3743. *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
  3744. modified = true;
  3745. }
  3746. // edges
  3747. ImVec2 e_min = ImMin(p[i],p[(i+1)%4]);
  3748. ImVec2 e_max = ImMax(p[i],p[(i+1)%4]);
  3749. b_rect = h[i] ? ImRect(e_min.x + DRAG_GRAB_HALF_SIZE, e_min.y - DRAG_GRAB_HALF_SIZE, e_max.x - DRAG_GRAB_HALF_SIZE, e_max.y + DRAG_GRAB_HALF_SIZE)
  3750. : ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE);
  3751. ImGuiID e_id = id + i + 5;
  3752. ImGui::KeepAliveID(e_id);
  3753. if (input) {
  3754. clicked = ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held);
  3755. if (out_clicked) *out_clicked = *out_clicked || clicked;
  3756. if (out_hovered) *out_hovered = *out_hovered || hovered;
  3757. if (out_held) *out_held = *out_held || held;
  3758. }
  3759. if ((hovered || held) && show_curs)
  3760. h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
  3761. if (held && ImGui::IsMouseDragging(0)) {
  3762. if (h[i])
  3763. *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;
  3764. else
  3765. *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;
  3766. modified = true;
  3767. }
  3768. if (hovered && ImGui::IsMouseDoubleClicked(0))
  3769. {
  3770. ImPlotRect b = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO);
  3771. if (h[i])
  3772. *y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max;
  3773. else
  3774. *x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max;
  3775. modified = true;
  3776. }
  3777. }
  3778. const bool mouse_inside = rect_grab.Contains(ImGui::GetMousePos());
  3779. const bool mouse_clicked = ImGui::IsMouseClicked(0);
  3780. const bool mouse_down = ImGui::IsMouseDown(0);
  3781. if (input && mouse_inside) {
  3782. if (out_clicked) *out_clicked = *out_clicked || mouse_clicked;
  3783. if (out_hovered) *out_hovered = true;
  3784. if (out_held) *out_held = *out_held || mouse_down;
  3785. }
  3786. PushPlotClipRect();
  3787. ImDrawList& DrawList = *GetPlotDrawList();
  3788. if (modified && no_delay) {
  3789. for (int i = 0; i < 4; ++i)
  3790. p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO);
  3791. pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO);
  3792. rect = ImRect(ImMin(p[0],p[2]),ImMax(p[0],p[2]));
  3793. }
  3794. DrawList.AddRectFilled(rect.Min, rect.Max, col32_a);
  3795. DrawList.AddRect(rect.Min, rect.Max, col32);
  3796. if (input && (modified || mouse_inside)) {
  3797. DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32);
  3798. for (int i = 0; i < 4; ++i)
  3799. DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32);
  3800. }
  3801. PopPlotClipRect();
  3802. ImGui::PopID();
  3803. return modified;
  3804. }
  3805. bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {
  3806. return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags, out_clicked, out_hovered, out_held);
  3807. }
  3808. //-----------------------------------------------------------------------------
  3809. // [SECTION] Legend Utils and Tools
  3810. //-----------------------------------------------------------------------------
  3811. bool IsLegendEntryHovered(const char* label_id) {
  3812. ImPlotContext& gp = *GImPlot;
  3813. IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "IsPlotItemHighlight() needs to be called within an itemized context!");
  3814. SetupLock();
  3815. ImGuiID id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID);
  3816. ImPlotItem* item = gp.CurrentItems->GetItem(id);
  3817. return item && item->LegendHovered;
  3818. }
  3819. bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button) {
  3820. ImPlotContext& gp = *GImPlot;
  3821. IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "BeginLegendPopup() needs to be called within an itemized context!");
  3822. SetupLock();
  3823. ImGuiWindow* window = GImGui->CurrentWindow;
  3824. if (window->SkipItems)
  3825. return false;
  3826. ImGuiID id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID);
  3827. if (ImGui::IsMouseReleased(mouse_button)) {
  3828. ImPlotItem* item = gp.CurrentItems->GetItem(id);
  3829. if (item && item->LegendHovered)
  3830. ImGui::OpenPopupEx(id);
  3831. }
  3832. return ImGui::BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
  3833. }
  3834. void EndLegendPopup() {
  3835. SetupLock();
  3836. ImGui::EndPopup();
  3837. }
  3838. void ShowAltLegend(const char* title_id, bool vertical, const ImVec2 size, bool interactable) {
  3839. ImPlotContext& gp = *GImPlot;
  3840. ImGuiContext &G = *GImGui;
  3841. ImGuiWindow * Window = G.CurrentWindow;
  3842. if (Window->SkipItems)
  3843. return;
  3844. ImDrawList &DrawList = *Window->DrawList;
  3845. ImPlotPlot* plot = GetPlot(title_id);
  3846. ImVec2 legend_size;
  3847. ImVec2 default_size = gp.Style.LegendPadding * 2;
  3848. if (plot != nullptr) {
  3849. legend_size = CalcLegendSize(plot->Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical);
  3850. default_size = legend_size + gp.Style.LegendPadding * 2;
  3851. }
  3852. ImVec2 frame_size = ImGui::CalcItemSize(size, default_size.x, default_size.y);
  3853. ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
  3854. ImGui::ItemSize(bb_frame);
  3855. if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame))
  3856. return;
  3857. ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding);
  3858. DrawList.PushClipRect(bb_frame.Min, bb_frame.Max, true);
  3859. if (plot != nullptr) {
  3860. const ImVec2 legend_pos = GetLocationPos(bb_frame, legend_size, 0, gp.Style.LegendPadding);
  3861. const ImRect legend_bb(legend_pos, legend_pos + legend_size);
  3862. interactable = interactable && bb_frame.Contains(ImGui::GetIO().MousePos);
  3863. // render legend box
  3864. ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
  3865. ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
  3866. DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg);
  3867. DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd);
  3868. // render entries
  3869. ShowLegendEntries(plot->Items, legend_bb, interactable, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical, DrawList);
  3870. }
  3871. DrawList.PopClipRect();
  3872. }
  3873. //-----------------------------------------------------------------------------
  3874. // [SECTION] Drag and Drop Utils
  3875. //-----------------------------------------------------------------------------
  3876. bool BeginDragDropTargetPlot() {
  3877. SetupLock();
  3878. ImPlotContext& gp = *GImPlot;
  3879. ImRect rect = gp.CurrentPlot->PlotRect;
  3880. return ImGui::BeginDragDropTargetCustom(rect, gp.CurrentPlot->ID);
  3881. }
  3882. bool BeginDragDropTargetAxis(ImAxis axis) {
  3883. SetupLock();
  3884. ImPlotPlot& plot = *GImPlot->CurrentPlot;
  3885. ImPlotAxis& ax = plot.Axes[axis];
  3886. ImRect rect = ax.HoverRect;
  3887. rect.Expand(-3.5f);
  3888. return ImGui::BeginDragDropTargetCustom(rect, ax.ID);
  3889. }
  3890. bool BeginDragDropTargetLegend() {
  3891. SetupLock();
  3892. ImPlotItemGroup& items = *GImPlot->CurrentItems;
  3893. ImRect rect = items.Legend.RectClamped;
  3894. return ImGui::BeginDragDropTargetCustom(rect, items.ID);
  3895. }
  3896. void EndDragDropTarget() {
  3897. SetupLock();
  3898. ImGui::EndDragDropTarget();
  3899. }
  3900. bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags) {
  3901. SetupLock();
  3902. ImPlotContext& gp = *GImPlot;
  3903. ImPlotPlot* plot = gp.CurrentPlot;
  3904. if (GImGui->IO.KeyMods == gp.InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == plot->ID)
  3905. return ImGui::ItemAdd(plot->PlotRect, plot->ID) && ImGui::BeginDragDropSource(flags);
  3906. return false;
  3907. }
  3908. bool BeginDragDropSourceAxis(ImAxis idx, ImGuiDragDropFlags flags) {
  3909. SetupLock();
  3910. ImPlotContext& gp = *GImPlot;
  3911. ImPlotAxis& axis = gp.CurrentPlot->Axes[idx];
  3912. if (GImGui->IO.KeyMods == gp.InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == axis.ID)
  3913. return ImGui::ItemAdd(axis.HoverRect, axis.ID) && ImGui::BeginDragDropSource(flags);
  3914. return false;
  3915. }
  3916. bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags) {
  3917. SetupLock();
  3918. ImPlotContext& gp = *GImPlot;
  3919. IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "BeginDragDropSourceItem() needs to be called within an itemized context!");
  3920. ImGuiID item_id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID);
  3921. ImPlotItem* item = gp.CurrentItems->GetItem(item_id);
  3922. if (item != nullptr) {
  3923. return ImGui::ItemAdd(item->LegendHoverRect, item->ID) && ImGui::BeginDragDropSource(flags);
  3924. }
  3925. return false;
  3926. }
  3927. void EndDragDropSource() {
  3928. SetupLock();
  3929. ImGui::EndDragDropSource();
  3930. }
  3931. //-----------------------------------------------------------------------------
  3932. // [SECTION] Aligned Plots
  3933. //-----------------------------------------------------------------------------
  3934. bool BeginAlignedPlots(const char* group_id, bool vertical) {
  3935. IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
  3936. ImPlotContext& gp = *GImPlot;
  3937. IM_ASSERT_USER_ERROR(gp.CurrentAlignmentH == nullptr && gp.CurrentAlignmentV == nullptr, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!");
  3938. ImGuiContext &G = *GImGui;
  3939. ImGuiWindow * Window = G.CurrentWindow;
  3940. if (Window->SkipItems)
  3941. return false;
  3942. const ImGuiID ID = Window->GetID(group_id);
  3943. ImPlotAlignmentData* alignment = gp.AlignmentData.GetOrAddByKey(ID);
  3944. if (vertical)
  3945. gp.CurrentAlignmentV = alignment;
  3946. else
  3947. gp.CurrentAlignmentH = alignment;
  3948. if (alignment->Vertical != vertical)
  3949. alignment->Reset();
  3950. alignment->Vertical = vertical;
  3951. alignment->Begin();
  3952. return true;
  3953. }
  3954. void EndAlignedPlots() {
  3955. IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
  3956. ImPlotContext& gp = *GImPlot;
  3957. IM_ASSERT_USER_ERROR(gp.CurrentAlignmentH != nullptr || gp.CurrentAlignmentV != nullptr, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!");
  3958. ImPlotAlignmentData* alignment = gp.CurrentAlignmentH != nullptr ? gp.CurrentAlignmentH : (gp.CurrentAlignmentV != nullptr ? gp.CurrentAlignmentV : nullptr);
  3959. if (alignment)
  3960. alignment->End();
  3961. ResetCtxForNextAlignedPlots(GImPlot);
  3962. }
  3963. //-----------------------------------------------------------------------------
  3964. // [SECTION] Plot and Item Styling
  3965. //-----------------------------------------------------------------------------
  3966. ImPlotStyle& GetStyle() {
  3967. IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
  3968. ImPlotContext& gp = *GImPlot;
  3969. return gp.Style;
  3970. }
  3971. void PushStyleColor(ImPlotCol idx, ImU32 col) {
  3972. ImPlotContext& gp = *GImPlot;
  3973. ImGuiColorMod backup;
  3974. backup.Col = (ImGuiCol)idx;
  3975. backup.BackupValue = gp.Style.Colors[idx];
  3976. gp.ColorModifiers.push_back(backup);
  3977. gp.Style.Colors[idx] = ImGui::ColorConvertU32ToFloat4(col);
  3978. }
  3979. void PushStyleColor(ImPlotCol idx, const ImVec4& col) {
  3980. ImPlotContext& gp = *GImPlot;
  3981. ImGuiColorMod backup;
  3982. backup.Col = (ImGuiCol)idx;
  3983. backup.BackupValue = gp.Style.Colors[idx];
  3984. gp.ColorModifiers.push_back(backup);
  3985. gp.Style.Colors[idx] = col;
  3986. }
  3987. void PopStyleColor(int count) {
  3988. ImPlotContext& gp = *GImPlot;
  3989. IM_ASSERT_USER_ERROR(count <= gp.ColorModifiers.Size, "You can't pop more modifiers than have been pushed!");
  3990. while (count > 0)
  3991. {
  3992. ImGuiColorMod& backup = gp.ColorModifiers.back();
  3993. gp.Style.Colors[backup.Col] = backup.BackupValue;
  3994. gp.ColorModifiers.pop_back();
  3995. count--;
  3996. }
  3997. }
  3998. void PushStyleVar(ImPlotStyleVar idx, float val) {
  3999. ImPlotContext& gp = *GImPlot;
  4000. const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);
  4001. if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) {
  4002. float* pvar = (float*)var_info->GetVarPtr(&gp.Style);
  4003. gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));
  4004. *pvar = val;
  4005. return;
  4006. }
  4007. IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
  4008. }
  4009. void PushStyleVar(ImPlotStyleVar idx, int val) {
  4010. ImPlotContext& gp = *GImPlot;
  4011. const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);
  4012. if (var_info->Type == ImGuiDataType_S32 && var_info->Count == 1) {
  4013. int* pvar = (int*)var_info->GetVarPtr(&gp.Style);
  4014. gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));
  4015. *pvar = val;
  4016. return;
  4017. }
  4018. else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) {
  4019. float* pvar = (float*)var_info->GetVarPtr(&gp.Style);
  4020. gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));
  4021. *pvar = (float)val;
  4022. return;
  4023. }
  4024. IM_ASSERT(0 && "Called PushStyleVar() int variant but variable is not a int!");
  4025. }
  4026. void PushStyleVar(ImPlotStyleVar idx, const ImVec2& val)
  4027. {
  4028. ImPlotContext& gp = *GImPlot;
  4029. const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);
  4030. if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
  4031. {
  4032. ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&gp.Style);
  4033. gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));
  4034. *pvar = val;
  4035. return;
  4036. }
  4037. IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
  4038. }
  4039. void PopStyleVar(int count) {
  4040. ImPlotContext& gp = *GImPlot;
  4041. IM_ASSERT_USER_ERROR(count <= gp.StyleModifiers.Size, "You can't pop more modifiers than have been pushed!");
  4042. while (count > 0) {
  4043. ImGuiStyleMod& backup = gp.StyleModifiers.back();
  4044. const ImPlotStyleVarInfo* info = GetPlotStyleVarInfo(backup.VarIdx);
  4045. void* data = info->GetVarPtr(&gp.Style);
  4046. if (info->Type == ImGuiDataType_Float && info->Count == 1) {
  4047. ((float*)data)[0] = backup.BackupFloat[0];
  4048. }
  4049. else if (info->Type == ImGuiDataType_Float && info->Count == 2) {
  4050. ((float*)data)[0] = backup.BackupFloat[0];
  4051. ((float*)data)[1] = backup.BackupFloat[1];
  4052. }
  4053. else if (info->Type == ImGuiDataType_S32 && info->Count == 1) {
  4054. ((int*)data)[0] = backup.BackupInt[0];
  4055. }
  4056. gp.StyleModifiers.pop_back();
  4057. count--;
  4058. }
  4059. }
  4060. //------------------------------------------------------------------------------
  4061. // [Section] Colormaps
  4062. //------------------------------------------------------------------------------
  4063. ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size, bool qual) {
  4064. ImPlotContext& gp = *GImPlot;
  4065. IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!");
  4066. IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already been used!");
  4067. ImVector<ImU32> buffer;
  4068. buffer.resize(size);
  4069. for (int i = 0; i < size; ++i)
  4070. buffer[i] = ImGui::ColorConvertFloat4ToU32(colormap[i]);
  4071. return gp.ColormapData.Append(name, buffer.Data, size, qual);
  4072. }
  4073. ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size, bool qual) {
  4074. ImPlotContext& gp = *GImPlot;
  4075. IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!");
  4076. IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already be used!");
  4077. return gp.ColormapData.Append(name, colormap, size, qual);
  4078. }
  4079. int GetColormapCount() {
  4080. ImPlotContext& gp = *GImPlot;
  4081. return gp.ColormapData.Count;
  4082. }
  4083. const char* GetColormapName(ImPlotColormap colormap) {
  4084. ImPlotContext& gp = *GImPlot;
  4085. return gp.ColormapData.GetName(colormap);
  4086. }
  4087. ImPlotColormap GetColormapIndex(const char* name) {
  4088. ImPlotContext& gp = *GImPlot;
  4089. return gp.ColormapData.GetIndex(name);
  4090. }
  4091. void PushColormap(ImPlotColormap colormap) {
  4092. ImPlotContext& gp = *GImPlot;
  4093. IM_ASSERT_USER_ERROR(colormap >= 0 && colormap < gp.ColormapData.Count, "The colormap index is invalid!");
  4094. gp.ColormapModifiers.push_back(gp.Style.Colormap);
  4095. gp.Style.Colormap = colormap;
  4096. }
  4097. void PushColormap(const char* name) {
  4098. ImPlotContext& gp = *GImPlot;
  4099. ImPlotColormap idx = gp.ColormapData.GetIndex(name);
  4100. IM_ASSERT_USER_ERROR(idx != -1, "The colormap name is invalid!");
  4101. PushColormap(idx);
  4102. }
  4103. void PopColormap(int count) {
  4104. ImPlotContext& gp = *GImPlot;
  4105. IM_ASSERT_USER_ERROR(count <= gp.ColormapModifiers.Size, "You can't pop more modifiers than have been pushed!");
  4106. while (count > 0) {
  4107. const ImPlotColormap& backup = gp.ColormapModifiers.back();
  4108. gp.Style.Colormap = backup;
  4109. gp.ColormapModifiers.pop_back();
  4110. count--;
  4111. }
  4112. }
  4113. ImU32 NextColormapColorU32() {
  4114. ImPlotContext& gp = *GImPlot;
  4115. IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!");
  4116. int idx = gp.CurrentItems->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap);
  4117. ImU32 col = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx);
  4118. gp.CurrentItems->ColormapIdx++;
  4119. return col;
  4120. }
  4121. ImVec4 NextColormapColor() {
  4122. return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32());
  4123. }
  4124. int GetColormapSize(ImPlotColormap cmap) {
  4125. ImPlotContext& gp = *GImPlot;
  4126. cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;
  4127. IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!");
  4128. return gp.ColormapData.GetKeyCount(cmap);
  4129. }
  4130. ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap) {
  4131. ImPlotContext& gp = *GImPlot;
  4132. cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;
  4133. IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!");
  4134. idx = idx % gp.ColormapData.GetKeyCount(cmap);
  4135. return gp.ColormapData.GetKeyColor(cmap, idx);
  4136. }
  4137. ImVec4 GetColormapColor(int idx, ImPlotColormap cmap) {
  4138. return ImGui::ColorConvertU32ToFloat4(GetColormapColorU32(idx,cmap));
  4139. }
  4140. ImU32 SampleColormapU32(float t, ImPlotColormap cmap) {
  4141. ImPlotContext& gp = *GImPlot;
  4142. cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;
  4143. IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!");
  4144. return gp.ColormapData.LerpTable(cmap, t);
  4145. }
  4146. ImVec4 SampleColormap(float t, ImPlotColormap cmap) {
  4147. return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t,cmap));
  4148. }
  4149. void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) {
  4150. const int n = continuous ? size - 1 : size;
  4151. ImU32 col1, col2;
  4152. if (vert) {
  4153. const float step = bounds.GetHeight() / n;
  4154. ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Max.x, bounds.Min.y + step);
  4155. for (int i = 0; i < n; ++i) {
  4156. if (reversed) {
  4157. col1 = colors[size-i-1];
  4158. col2 = continuous ? colors[size-i-2] : col1;
  4159. }
  4160. else {
  4161. col1 = colors[i];
  4162. col2 = continuous ? colors[i+1] : col1;
  4163. }
  4164. DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2);
  4165. rect.TranslateY(step);
  4166. }
  4167. }
  4168. else {
  4169. const float step = bounds.GetWidth() / n;
  4170. ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Min.x + step, bounds.Max.y);
  4171. for (int i = 0; i < n; ++i) {
  4172. if (reversed) {
  4173. col1 = colors[size-i-1];
  4174. col2 = continuous ? colors[size-i-2] : col1;
  4175. }
  4176. else {
  4177. col1 = colors[i];
  4178. col2 = continuous ? colors[i+1] : col1;
  4179. }
  4180. DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1);
  4181. rect.TranslateX(step);
  4182. }
  4183. }
  4184. }
  4185. void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size, const char* format, ImPlotColormapScaleFlags flags, ImPlotColormap cmap) {
  4186. ImGuiContext &G = *GImGui;
  4187. ImGuiWindow * Window = G.CurrentWindow;
  4188. if (Window->SkipItems)
  4189. return;
  4190. const ImGuiID ID = Window->GetID(label);
  4191. ImVec2 label_size(0,0);
  4192. if (!ImHasFlag(flags, ImPlotColormapScaleFlags_NoLabel)) {
  4193. label_size = ImGui::CalcTextSize(label,nullptr,true);
  4194. }
  4195. ImPlotContext& gp = *GImPlot;
  4196. cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;
  4197. IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!");
  4198. ImVec2 frame_size = ImGui::CalcItemSize(size, 0, gp.Style.PlotDefaultSize.y);
  4199. if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f)
  4200. frame_size.y = gp.Style.PlotMinSize.y;
  4201. ImPlotRange range(ImMin(scale_min,scale_max), ImMax(scale_min,scale_max));
  4202. gp.CTicker.Reset();
  4203. Locator_Default(gp.CTicker, range, frame_size.y, true, Formatter_Default, (void*)format);
  4204. const bool rend_label = label_size.x > 0;
  4205. const float txt_off = gp.Style.LabelPadding.x;
  4206. const float pad = txt_off + gp.CTicker.MaxSize.x + (rend_label ? txt_off + label_size.y : 0);
  4207. float bar_w = 20;
  4208. if (frame_size.x == 0)
  4209. frame_size.x = bar_w + pad + 2 * gp.Style.PlotPadding.x;
  4210. else {
  4211. bar_w = frame_size.x - (pad + 2 * gp.Style.PlotPadding.x);
  4212. if (bar_w < gp.Style.MajorTickLen.y)
  4213. bar_w = gp.Style.MajorTickLen.y;
  4214. }
  4215. ImDrawList &DrawList = *Window->DrawList;
  4216. ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
  4217. ImGui::ItemSize(bb_frame);
  4218. if (!ImGui::ItemAdd(bb_frame, ID, &bb_frame))
  4219. return;
  4220. ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding);
  4221. const bool opposite = ImHasFlag(flags, ImPlotColormapScaleFlags_Opposite);
  4222. const bool inverted = ImHasFlag(flags, ImPlotColormapScaleFlags_Invert);
  4223. const bool reversed = scale_min > scale_max;
  4224. float bb_grad_shift = opposite ? pad : 0;
  4225. ImRect bb_grad(bb_frame.Min + gp.Style.PlotPadding + ImVec2(bb_grad_shift, 0),
  4226. bb_frame.Min + ImVec2(bar_w + gp.Style.PlotPadding.x + bb_grad_shift,
  4227. frame_size.y - gp.Style.PlotPadding.y));
  4228. ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true);
  4229. const ImU32 col_text = ImGui::GetColorU32(ImGuiCol_Text);
  4230. const bool invert_scale = inverted ? (reversed ? false : true) : (reversed ? true : false);
  4231. const float y_min = invert_scale ? bb_grad.Max.y : bb_grad.Min.y;
  4232. const float y_max = invert_scale ? bb_grad.Min.y : bb_grad.Max.y;
  4233. RenderColorBar(gp.ColormapData.GetKeys(cmap), gp.ColormapData.GetKeyCount(cmap), DrawList, bb_grad, true, !inverted, !gp.ColormapData.IsQual(cmap));
  4234. for (int i = 0; i < gp.CTicker.TickCount(); ++i) {
  4235. const double y_pos_plt = gp.CTicker.Ticks[i].PlotPos;
  4236. const float y_pos = ImRemap((float)y_pos_plt, (float)range.Max, (float)range.Min, y_min, y_max);
  4237. const float tick_width = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y;
  4238. const float tick_thick = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y;
  4239. const float tick_t = (float)((y_pos_plt - scale_min) / (scale_max - scale_min));
  4240. const ImU32 tick_col = CalcTextColor(gp.ColormapData.LerpTable(cmap,tick_t));
  4241. if (y_pos < bb_grad.Max.y - 2 && y_pos > bb_grad.Min.y + 2) {
  4242. DrawList.AddLine(opposite ? ImVec2(bb_grad.Min.x+1, y_pos) : ImVec2(bb_grad.Max.x-1, y_pos),
  4243. opposite ? ImVec2(bb_grad.Min.x + tick_width, y_pos) : ImVec2(bb_grad.Max.x - tick_width, y_pos),
  4244. tick_col,
  4245. tick_thick);
  4246. }
  4247. const float txt_x = opposite ? bb_grad.Min.x - txt_off - gp.CTicker.Ticks[i].LabelSize.x : bb_grad.Max.x + txt_off;
  4248. const float txt_y = y_pos - gp.CTicker.Ticks[i].LabelSize.y * 0.5f;
  4249. DrawList.AddText(ImVec2(txt_x, txt_y), col_text, gp.CTicker.GetText(i));
  4250. }
  4251. if (rend_label) {
  4252. const float pos_x = opposite ? bb_frame.Min.x + gp.Style.PlotPadding.x : bb_grad.Max.x + 2 * txt_off + gp.CTicker.MaxSize.x;
  4253. const float pos_y = bb_grad.GetCenter().y + label_size.x * 0.5f;
  4254. const char* label_end = ImGui::FindRenderedTextEnd(label);
  4255. AddTextVertical(&DrawList,ImVec2(pos_x,pos_y),col_text,label,label_end);
  4256. }
  4257. DrawList.AddRect(bb_grad.Min, bb_grad.Max, GetStyleColorU32(ImPlotCol_PlotBorder));
  4258. ImGui::PopClipRect();
  4259. }
  4260. bool ColormapSlider(const char* label, float* t, ImVec4* out, const char* format, ImPlotColormap cmap) {
  4261. *t = ImClamp(*t,0.0f,1.0f);
  4262. ImGuiContext &G = *GImGui;
  4263. ImGuiWindow * Window = G.CurrentWindow;
  4264. if (Window->SkipItems)
  4265. return false;
  4266. ImPlotContext& gp = *GImPlot;
  4267. cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;
  4268. IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!");
  4269. const ImU32* keys = gp.ColormapData.GetKeys(cmap);
  4270. const int count = gp.ColormapData.GetKeyCount(cmap);
  4271. const bool qual = gp.ColormapData.IsQual(cmap);
  4272. const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;
  4273. const float w = ImGui::CalcItemWidth();
  4274. const float h = ImGui::GetFrameHeight();
  4275. const ImRect rect = ImRect(pos.x,pos.y,pos.x+w,pos.y+h);
  4276. RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual);
  4277. const ImU32 grab = CalcTextColor(gp.ColormapData.LerpTable(cmap,*t));
  4278. // const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,0.5f));
  4279. ImGui::PushStyleColor(ImGuiCol_FrameBg,IM_COL32_BLACK_TRANS);
  4280. ImGui::PushStyleColor(ImGuiCol_FrameBgActive,IM_COL32_BLACK_TRANS);
  4281. ImGui::PushStyleColor(ImGuiCol_FrameBgHovered,ImVec4(1,1,1,0.1f));
  4282. ImGui::PushStyleColor(ImGuiCol_SliderGrab,grab);
  4283. ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, grab);
  4284. ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize,2);
  4285. ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0);
  4286. const bool changed = ImGui::SliderFloat(label,t,0,1,format);
  4287. ImGui::PopStyleColor(5);
  4288. ImGui::PopStyleVar(2);
  4289. if (out != nullptr)
  4290. *out = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.LerpTable(cmap,*t));
  4291. return changed;
  4292. }
  4293. bool ColormapButton(const char* label, const ImVec2& size_arg, ImPlotColormap cmap) {
  4294. ImGuiContext &G = *GImGui;
  4295. const ImGuiStyle& style = G.Style;
  4296. ImGuiWindow * Window = G.CurrentWindow;
  4297. if (Window->SkipItems)
  4298. return false;
  4299. ImPlotContext& gp = *GImPlot;
  4300. cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;
  4301. IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!");
  4302. const ImU32* keys = gp.ColormapData.GetKeys(cmap);
  4303. const int count = gp.ColormapData.GetKeyCount(cmap);
  4304. const bool qual = gp.ColormapData.IsQual(cmap);
  4305. const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;
  4306. const ImVec2 label_size = ImGui::CalcTextSize(label, nullptr, true);
  4307. ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
  4308. const ImRect rect = ImRect(pos.x,pos.y,pos.x+size.x,pos.y+size.y);
  4309. RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual);
  4310. const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,G.Style.ButtonTextAlign.x));
  4311. ImGui::PushStyleColor(ImGuiCol_Button,IM_COL32_BLACK_TRANS);
  4312. ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ImVec4(1,1,1,0.1f));
  4313. ImGui::PushStyleColor(ImGuiCol_ButtonActive,ImVec4(1,1,1,0.2f));
  4314. ImGui::PushStyleColor(ImGuiCol_Text,text);
  4315. ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0);
  4316. const bool pressed = ImGui::Button(label,size);
  4317. ImGui::PopStyleColor(4);
  4318. ImGui::PopStyleVar(1);
  4319. return pressed;
  4320. }
  4321. //-----------------------------------------------------------------------------
  4322. // [Section] Miscellaneous
  4323. //-----------------------------------------------------------------------------
  4324. ImPlotInputMap& GetInputMap() {
  4325. IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
  4326. ImPlotContext& gp = *GImPlot;
  4327. return gp.InputMap;
  4328. }
  4329. void MapInputDefault(ImPlotInputMap* dst) {
  4330. ImPlotInputMap& map = dst ? *dst : GetInputMap();
  4331. map.Pan = ImGuiMouseButton_Left;
  4332. map.PanMod = ImGuiMod_None;
  4333. map.Fit = ImGuiMouseButton_Left;
  4334. map.Menu = ImGuiMouseButton_Right;
  4335. map.Select = ImGuiMouseButton_Right;
  4336. map.SelectMod = ImGuiMod_None;
  4337. map.SelectCancel = ImGuiMouseButton_Left;
  4338. map.SelectHorzMod = ImGuiMod_Alt;
  4339. map.SelectVertMod = ImGuiMod_Shift;
  4340. map.OverrideMod = ImGuiMod_Ctrl;
  4341. map.ZoomMod = ImGuiMod_None;
  4342. map.ZoomRate = 0.1f;
  4343. }
  4344. void MapInputReverse(ImPlotInputMap* dst) {
  4345. ImPlotInputMap& map = dst ? *dst : GetInputMap();
  4346. map.Pan = ImGuiMouseButton_Right;
  4347. map.PanMod = ImGuiMod_None;
  4348. map.Fit = ImGuiMouseButton_Left;
  4349. map.Menu = ImGuiMouseButton_Right;
  4350. map.Select = ImGuiMouseButton_Left;
  4351. map.SelectMod = ImGuiMod_None;
  4352. map.SelectCancel = ImGuiMouseButton_Right;
  4353. map.SelectHorzMod = ImGuiMod_Alt;
  4354. map.SelectVertMod = ImGuiMod_Shift;
  4355. map.OverrideMod = ImGuiMod_Ctrl;
  4356. map.ZoomMod = ImGuiMod_None;
  4357. map.ZoomRate = 0.1f;
  4358. }
  4359. //-----------------------------------------------------------------------------
  4360. // [Section] Miscellaneous
  4361. //-----------------------------------------------------------------------------
  4362. void ItemIcon(const ImVec4& col) {
  4363. ItemIcon(ImGui::ColorConvertFloat4ToU32(col));
  4364. }
  4365. void ItemIcon(ImU32 col) {
  4366. const float txt_size = ImGui::GetTextLineHeight();
  4367. ImVec2 size(txt_size-4,txt_size);
  4368. ImGuiWindow* window = ImGui::GetCurrentWindow();
  4369. ImVec2 pos = window->DC.CursorPos;
  4370. ImGui::GetWindowDrawList()->AddRectFilled(pos + ImVec2(0,2), pos + size - ImVec2(0,2), col);
  4371. ImGui::Dummy(size);
  4372. }
  4373. void ColormapIcon(ImPlotColormap cmap) {
  4374. ImPlotContext& gp = *GImPlot;
  4375. const float txt_size = ImGui::GetTextLineHeight();
  4376. ImVec2 size(txt_size-4,txt_size);
  4377. ImGuiWindow* window = ImGui::GetCurrentWindow();
  4378. ImVec2 pos = window->DC.CursorPos;
  4379. ImRect rect(pos+ImVec2(0,2),pos+size-ImVec2(0,2));
  4380. ImDrawList& DrawList = *ImGui::GetWindowDrawList();
  4381. RenderColorBar(gp.ColormapData.GetKeys(cmap),gp.ColormapData.GetKeyCount(cmap),DrawList,rect,false,false,!gp.ColormapData.IsQual(cmap));
  4382. ImGui::Dummy(size);
  4383. }
  4384. ImDrawList* GetPlotDrawList() {
  4385. return ImGui::GetWindowDrawList();
  4386. }
  4387. void PushPlotClipRect(float expand) {
  4388. ImPlotContext& gp = *GImPlot;
  4389. IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!");
  4390. SetupLock();
  4391. ImRect rect = gp.CurrentPlot->PlotRect;
  4392. rect.Expand(expand);
  4393. ImGui::PushClipRect(rect.Min, rect.Max, true);
  4394. }
  4395. void PopPlotClipRect() {
  4396. SetupLock();
  4397. ImGui::PopClipRect();
  4398. }
  4399. static void HelpMarker(const char* desc) {
  4400. ImGui::TextDisabled("(?)");
  4401. if (ImGui::IsItemHovered()) {
  4402. ImGui::BeginTooltip();
  4403. ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
  4404. ImGui::TextUnformatted(desc);
  4405. ImGui::PopTextWrapPos();
  4406. ImGui::EndTooltip();
  4407. }
  4408. }
  4409. bool ShowStyleSelector(const char* label)
  4410. {
  4411. static int style_idx = -1;
  4412. if (ImGui::Combo(label, &style_idx, "Auto\0Classic\0Dark\0Light\0"))
  4413. {
  4414. switch (style_idx)
  4415. {
  4416. case 0: StyleColorsAuto(); break;
  4417. case 1: StyleColorsClassic(); break;
  4418. case 2: StyleColorsDark(); break;
  4419. case 3: StyleColorsLight(); break;
  4420. }
  4421. return true;
  4422. }
  4423. return false;
  4424. }
  4425. bool ShowColormapSelector(const char* label) {
  4426. ImPlotContext& gp = *GImPlot;
  4427. bool set = false;
  4428. if (ImGui::BeginCombo(label, gp.ColormapData.GetName(gp.Style.Colormap))) {
  4429. for (int i = 0; i < gp.ColormapData.Count; ++i) {
  4430. const char* name = gp.ColormapData.GetName(i);
  4431. if (ImGui::Selectable(name, gp.Style.Colormap == i)) {
  4432. gp.Style.Colormap = i;
  4433. ImPlot::BustItemCache();
  4434. set = true;
  4435. }
  4436. }
  4437. ImGui::EndCombo();
  4438. }
  4439. return set;
  4440. }
  4441. bool ShowInputMapSelector(const char* label) {
  4442. static int map_idx = -1;
  4443. if (ImGui::Combo(label, &map_idx, "Default\0Reversed\0"))
  4444. {
  4445. switch (map_idx)
  4446. {
  4447. case 0: MapInputDefault(); break;
  4448. case 1: MapInputReverse(); break;
  4449. }
  4450. return true;
  4451. }
  4452. return false;
  4453. }
  4454. void ShowStyleEditor(ImPlotStyle* ref) {
  4455. ImPlotContext& gp = *GImPlot;
  4456. ImPlotStyle& style = GetStyle();
  4457. static ImPlotStyle ref_saved_style;
  4458. // Default to using internal storage as reference
  4459. static bool init = true;
  4460. if (init && ref == nullptr)
  4461. ref_saved_style = style;
  4462. init = false;
  4463. if (ref == nullptr)
  4464. ref = &ref_saved_style;
  4465. if (ImPlot::ShowStyleSelector("Colors##Selector"))
  4466. ref_saved_style = style;
  4467. // Save/Revert button
  4468. if (ImGui::Button("Save Ref"))
  4469. *ref = ref_saved_style = style;
  4470. ImGui::SameLine();
  4471. if (ImGui::Button("Revert Ref"))
  4472. style = *ref;
  4473. ImGui::SameLine();
  4474. HelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
  4475. "Use \"Export\" below to save them somewhere.");
  4476. if (ImGui::BeginTabBar("##StyleEditor")) {
  4477. if (ImGui::BeginTabItem("Variables")) {
  4478. ImGui::Text("Item Styling");
  4479. ImGui::SliderFloat("LineWeight", &style.LineWeight, 0.0f, 5.0f, "%.1f");
  4480. ImGui::SliderFloat("MarkerSize", &style.MarkerSize, 2.0f, 10.0f, "%.1f");
  4481. ImGui::SliderFloat("MarkerWeight", &style.MarkerWeight, 0.0f, 5.0f, "%.1f");
  4482. ImGui::SliderFloat("FillAlpha", &style.FillAlpha, 0.0f, 1.0f, "%.2f");
  4483. ImGui::SliderFloat("ErrorBarSize", &style.ErrorBarSize, 0.0f, 10.0f, "%.1f");
  4484. ImGui::SliderFloat("ErrorBarWeight", &style.ErrorBarWeight, 0.0f, 5.0f, "%.1f");
  4485. ImGui::SliderFloat("DigitalBitHeight", &style.DigitalBitHeight, 0.0f, 20.0f, "%.1f");
  4486. ImGui::SliderFloat("DigitalBitGap", &style.DigitalBitGap, 0.0f, 20.0f, "%.1f");
  4487. ImGui::Text("Plot Styling");
  4488. ImGui::SliderFloat("PlotBorderSize", &style.PlotBorderSize, 0.0f, 2.0f, "%.0f");
  4489. ImGui::SliderFloat("MinorAlpha", &style.MinorAlpha, 0.0f, 1.0f, "%.2f");
  4490. ImGui::SliderFloat2("MajorTickLen", (float*)&style.MajorTickLen, 0.0f, 20.0f, "%.0f");
  4491. ImGui::SliderFloat2("MinorTickLen", (float*)&style.MinorTickLen, 0.0f, 20.0f, "%.0f");
  4492. ImGui::SliderFloat2("MajorTickSize", (float*)&style.MajorTickSize, 0.0f, 2.0f, "%.1f");
  4493. ImGui::SliderFloat2("MinorTickSize", (float*)&style.MinorTickSize, 0.0f, 2.0f, "%.1f");
  4494. ImGui::SliderFloat2("MajorGridSize", (float*)&style.MajorGridSize, 0.0f, 2.0f, "%.1f");
  4495. ImGui::SliderFloat2("MinorGridSize", (float*)&style.MinorGridSize, 0.0f, 2.0f, "%.1f");
  4496. ImGui::SliderFloat2("PlotDefaultSize", (float*)&style.PlotDefaultSize, 0.0f, 1000, "%.0f");
  4497. ImGui::SliderFloat2("PlotMinSize", (float*)&style.PlotMinSize, 0.0f, 300, "%.0f");
  4498. ImGui::Text("Plot Padding");
  4499. ImGui::SliderFloat2("PlotPadding", (float*)&style.PlotPadding, 0.0f, 20.0f, "%.0f");
  4500. ImGui::SliderFloat2("LabelPadding", (float*)&style.LabelPadding, 0.0f, 20.0f, "%.0f");
  4501. ImGui::SliderFloat2("LegendPadding", (float*)&style.LegendPadding, 0.0f, 20.0f, "%.0f");
  4502. ImGui::SliderFloat2("LegendInnerPadding", (float*)&style.LegendInnerPadding, 0.0f, 10.0f, "%.0f");
  4503. ImGui::SliderFloat2("LegendSpacing", (float*)&style.LegendSpacing, 0.0f, 5.0f, "%.0f");
  4504. ImGui::SliderFloat2("MousePosPadding", (float*)&style.MousePosPadding, 0.0f, 20.0f, "%.0f");
  4505. ImGui::SliderFloat2("AnnotationPadding", (float*)&style.AnnotationPadding, 0.0f, 5.0f, "%.0f");
  4506. ImGui::SliderFloat2("FitPadding", (float*)&style.FitPadding, 0, 0.2f, "%.2f");
  4507. ImGui::EndTabItem();
  4508. }
  4509. if (ImGui::BeginTabItem("Colors")) {
  4510. static int output_dest = 0;
  4511. static bool output_only_modified = false;
  4512. if (ImGui::Button("Export", ImVec2(75,0))) {
  4513. if (output_dest == 0)
  4514. ImGui::LogToClipboard();
  4515. else
  4516. ImGui::LogToTTY();
  4517. ImGui::LogText("ImVec4* colors = ImPlot::GetStyle().Colors;\n");
  4518. for (int i = 0; i < ImPlotCol_COUNT; i++) {
  4519. const ImVec4& col = style.Colors[i];
  4520. const char* name = ImPlot::GetStyleColorName(i);
  4521. if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) {
  4522. if (IsColorAuto(i))
  4523. ImGui::LogText("colors[ImPlotCol_%s]%*s= IMPLOT_AUTO_COL;\n",name,14 - (int)strlen(name), "");
  4524. else
  4525. ImGui::LogText("colors[ImPlotCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\n",
  4526. name, 14 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
  4527. }
  4528. }
  4529. ImGui::LogFinish();
  4530. }
  4531. ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0");
  4532. ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified);
  4533. static ImGuiTextFilter filter;
  4534. filter.Draw("Filter colors", ImGui::GetFontSize() * 16);
  4535. static ImGuiColorEditFlags alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;
  4536. #if IMGUI_VERSION_NUM < 19173
  4537. if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();
  4538. if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine();
  4539. if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine();
  4540. #else
  4541. if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } ImGui::SameLine();
  4542. if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();
  4543. if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine();
  4544. #endif
  4545. HelpMarker(
  4546. "In the color list:\n"
  4547. "Left-click on colored square to open color picker,\n"
  4548. "Right-click to open edit options menu.");
  4549. ImGui::Separator();
  4550. ImGui::PushItemWidth(-160);
  4551. for (int i = 0; i < ImPlotCol_COUNT; i++) {
  4552. const char* name = ImPlot::GetStyleColorName(i);
  4553. if (!filter.PassFilter(name))
  4554. continue;
  4555. ImGui::PushID(i);
  4556. ImVec4 temp = GetStyleColorVec4(i);
  4557. const bool is_auto = IsColorAuto(i);
  4558. if (!is_auto)
  4559. ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f);
  4560. if (ImGui::Button("Auto")) {
  4561. if (is_auto)
  4562. style.Colors[i] = temp;
  4563. else
  4564. style.Colors[i] = IMPLOT_AUTO_COL;
  4565. BustItemCache();
  4566. }
  4567. if (!is_auto)
  4568. ImGui::PopStyleVar();
  4569. ImGui::SameLine();
  4570. if (ImGui::ColorEdit4(name, &temp.x, ImGuiColorEditFlags_NoInputs | alpha_flags)) {
  4571. style.Colors[i] = temp;
  4572. BustItemCache();
  4573. }
  4574. if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) {
  4575. ImGui::SameLine(175); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; }
  4576. ImGui::SameLine(); if (ImGui::Button("Revert")) {
  4577. style.Colors[i] = ref->Colors[i];
  4578. BustItemCache();
  4579. }
  4580. }
  4581. ImGui::PopID();
  4582. }
  4583. ImGui::PopItemWidth();
  4584. ImGui::Separator();
  4585. ImGui::Text("Colors that are set to Auto (i.e. IMPLOT_AUTO_COL) will\n"
  4586. "be automatically deduced from your ImGui style or the\n"
  4587. "current ImPlot Colormap. If you want to style individual\n"
  4588. "plot items, use Push/PopStyleColor around its function.");
  4589. ImGui::EndTabItem();
  4590. }
  4591. if (ImGui::BeginTabItem("Colormaps")) {
  4592. static int output_dest = 0;
  4593. if (ImGui::Button("Export", ImVec2(75,0))) {
  4594. if (output_dest == 0)
  4595. ImGui::LogToClipboard();
  4596. else
  4597. ImGui::LogToTTY();
  4598. int size = GetColormapSize();
  4599. const char* name = GetColormapName(gp.Style.Colormap);
  4600. ImGui::LogText("static const ImU32 %s_Data[%d] = {\n", name, size);
  4601. for (int i = 0; i < size; ++i) {
  4602. ImU32 col = GetColormapColorU32(i,gp.Style.Colormap);
  4603. ImGui::LogText(" %u%s\n", col, i == size - 1 ? "" : ",");
  4604. }
  4605. ImGui::LogText("};\nImPlotColormap %s = ImPlot::AddColormap(\"%s\", %s_Data, %d);", name, name, name, size);
  4606. ImGui::LogFinish();
  4607. }
  4608. ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0");
  4609. ImGui::SameLine();
  4610. static bool edit = false;
  4611. ImGui::Checkbox("Edit Mode",&edit);
  4612. // built-in/added
  4613. ImGui::Separator();
  4614. for (int i = 0; i < gp.ColormapData.Count; ++i) {
  4615. ImGui::PushID(i);
  4616. int size = gp.ColormapData.GetKeyCount(i);
  4617. bool selected = i == gp.Style.Colormap;
  4618. const char* name = GetColormapName(i);
  4619. if (!selected)
  4620. ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f);
  4621. if (ImGui::Button(name, ImVec2(100,0))) {
  4622. gp.Style.Colormap = i;
  4623. BustItemCache();
  4624. }
  4625. if (!selected)
  4626. ImGui::PopStyleVar();
  4627. ImGui::SameLine();
  4628. ImGui::BeginGroup();
  4629. if (edit) {
  4630. for (int c = 0; c < size; ++c) {
  4631. ImGui::PushID(c);
  4632. ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i,c));
  4633. if (ImGui::ColorEdit4("",&col4.x,ImGuiColorEditFlags_NoInputs)) {
  4634. ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4);
  4635. gp.ColormapData.SetKeyColor(i,c,col32);
  4636. BustItemCache();
  4637. }
  4638. if ((c + 1) % 12 != 0 && c != size -1)
  4639. ImGui::SameLine();
  4640. ImGui::PopID();
  4641. }
  4642. }
  4643. else {
  4644. if (ImPlot::ColormapButton("##",ImVec2(-1,0),i))
  4645. edit = true;
  4646. }
  4647. ImGui::EndGroup();
  4648. ImGui::PopID();
  4649. }
  4650. static ImVector<ImVec4> custom;
  4651. if (custom.Size == 0) {
  4652. custom.push_back(ImVec4(1,0,0,1));
  4653. custom.push_back(ImVec4(0,1,0,1));
  4654. custom.push_back(ImVec4(0,0,1,1));
  4655. }
  4656. ImGui::Separator();
  4657. ImGui::BeginGroup();
  4658. static char name[16] = "MyColormap";
  4659. if (ImGui::Button("+", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0)))
  4660. custom.push_back(ImVec4(0,0,0,1));
  4661. ImGui::SameLine();
  4662. if (ImGui::Button("-", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2)
  4663. custom.pop_back();
  4664. ImGui::SetNextItemWidth(100);
  4665. ImGui::InputText("##Name",name,16,ImGuiInputTextFlags_CharsNoBlank);
  4666. static bool qual = true;
  4667. ImGui::Checkbox("Qualitative",&qual);
  4668. if (ImGui::Button("Add", ImVec2(100, 0)) && gp.ColormapData.GetIndex(name)==-1)
  4669. AddColormap(name,custom.Data,custom.Size,qual);
  4670. ImGui::EndGroup();
  4671. ImGui::SameLine();
  4672. ImGui::BeginGroup();
  4673. for (int c = 0; c < custom.Size; ++c) {
  4674. ImGui::PushID(c);
  4675. if (ImGui::ColorEdit4("##Col1", &custom[c].x, ImGuiColorEditFlags_NoInputs)) {
  4676. }
  4677. if ((c + 1) % 12 != 0)
  4678. ImGui::SameLine();
  4679. ImGui::PopID();
  4680. }
  4681. ImGui::EndGroup();
  4682. ImGui::EndTabItem();
  4683. }
  4684. ImGui::EndTabBar();
  4685. }
  4686. }
  4687. void ShowUserGuide() {
  4688. ImGui::BulletText("Left-click drag within the plot area to pan X and Y axes.");
  4689. ImGui::Indent();
  4690. ImGui::BulletText("Left-click drag on axis labels to pan an individual axis.");
  4691. ImGui::Unindent();
  4692. ImGui::BulletText("Scroll in the plot area to zoom both X and Y axes.");
  4693. ImGui::Indent();
  4694. ImGui::BulletText("Scroll on axis labels to zoom an individual axis.");
  4695. ImGui::Unindent();
  4696. ImGui::BulletText("Right-click drag to box select data.");
  4697. ImGui::Indent();
  4698. ImGui::BulletText("Hold Alt to expand box selection horizontally.");
  4699. ImGui::BulletText("Hold Shift to expand box selection vertically.");
  4700. ImGui::BulletText("Left-click while box selecting to cancel the selection.");
  4701. ImGui::Unindent();
  4702. ImGui::BulletText("Double left-click to fit all visible data.");
  4703. ImGui::Indent();
  4704. ImGui::BulletText("Double left-click axis labels to fit the individual axis.");
  4705. ImGui::Unindent();
  4706. ImGui::BulletText("Right-click open the full plot context menu.");
  4707. ImGui::Indent();
  4708. ImGui::BulletText("Right-click axis labels to open an individual axis context menu.");
  4709. ImGui::Unindent();
  4710. ImGui::BulletText("Click legend label icons to show/hide plot items.");
  4711. }
  4712. void ShowTicksMetrics(const ImPlotTicker& ticker) {
  4713. ImGui::BulletText("Size: %d", ticker.TickCount());
  4714. ImGui::BulletText("MaxSize: [%f,%f]", ticker.MaxSize.x, ticker.MaxSize.y);
  4715. }
  4716. void ShowAxisMetrics(const ImPlotPlot& plot, const ImPlotAxis& axis) {
  4717. ImGui::BulletText("Label: %s", axis.LabelOffset == -1 ? "[none]" : plot.GetAxisLabel(axis));
  4718. ImGui::BulletText("Flags: 0x%08X", axis.Flags);
  4719. ImGui::BulletText("Range: [%f,%f]",axis.Range.Min, axis.Range.Max);
  4720. ImGui::BulletText("Pixels: %f", axis.PixelSize());
  4721. ImGui::BulletText("Aspect: %f", axis.GetAspect());
  4722. ImGui::BulletText(axis.OrthoAxis == nullptr ? "OrtherAxis: NULL" : "OrthoAxis: 0x%08X", axis.OrthoAxis->ID);
  4723. ImGui::BulletText("LinkedMin: %p", (void*)axis.LinkedMin);
  4724. ImGui::BulletText("LinkedMax: %p", (void*)axis.LinkedMax);
  4725. ImGui::BulletText("HasRange: %s", axis.HasRange ? "true" : "false");
  4726. ImGui::BulletText("Hovered: %s", axis.Hovered ? "true" : "false");
  4727. ImGui::BulletText("Held: %s", axis.Held ? "true" : "false");
  4728. if (ImGui::TreeNode("Transform")) {
  4729. ImGui::BulletText("PixelMin: %f", axis.PixelMin);
  4730. ImGui::BulletText("PixelMax: %f", axis.PixelMax);
  4731. ImGui::BulletText("ScaleToPixel: %f", axis.ScaleToPixel);
  4732. ImGui::BulletText("ScaleMax: %f", axis.ScaleMax);
  4733. ImGui::TreePop();
  4734. }
  4735. if (ImGui::TreeNode("Ticks")) {
  4736. ShowTicksMetrics(axis.Ticker);
  4737. ImGui::TreePop();
  4738. }
  4739. }
  4740. void ShowMetricsWindow(bool* p_popen) {
  4741. static bool show_plot_rects = false;
  4742. static bool show_axes_rects = false;
  4743. static bool show_axis_rects = false;
  4744. static bool show_canvas_rects = false;
  4745. static bool show_frame_rects = false;
  4746. static bool show_subplot_frame_rects = false;
  4747. static bool show_subplot_grid_rects = false;
  4748. static bool show_legend_rects = false;
  4749. ImDrawList& fg = *ImGui::GetForegroundDrawList();
  4750. ImPlotContext& gp = *GImPlot;
  4751. // ImGuiContext& g = *GImGui;
  4752. ImGuiIO& io = ImGui::GetIO();
  4753. ImGui::Begin("ImPlot Metrics", p_popen);
  4754. ImGui::Text("ImPlot " IMPLOT_VERSION);
  4755. ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
  4756. ImGui::Text("Mouse Position: [%.0f,%.0f]", io.MousePos.x, io.MousePos.y);
  4757. ImGui::Separator();
  4758. if (ImGui::TreeNode("Tools")) {
  4759. if (ImGui::Button("Bust Plot Cache"))
  4760. BustPlotCache();
  4761. ImGui::SameLine();
  4762. if (ImGui::Button("Bust Item Cache"))
  4763. BustItemCache();
  4764. ImGui::Checkbox("Show Frame Rects", &show_frame_rects);
  4765. ImGui::Checkbox("Show Canvas Rects",&show_canvas_rects);
  4766. ImGui::Checkbox("Show Plot Rects", &show_plot_rects);
  4767. ImGui::Checkbox("Show Axes Rects", &show_axes_rects);
  4768. ImGui::Checkbox("Show Axis Rects", &show_axis_rects);
  4769. ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects);
  4770. ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects);
  4771. ImGui::Checkbox("Show Legend Rects", &show_legend_rects);
  4772. ImGui::TreePop();
  4773. }
  4774. const int n_plots = gp.Plots.GetBufSize();
  4775. const int n_subplots = gp.Subplots.GetBufSize();
  4776. // render rects
  4777. for (int p = 0; p < n_plots; ++p) {
  4778. ImPlotPlot* plot = gp.Plots.GetByIndex(p);
  4779. if (show_frame_rects)
  4780. fg.AddRect(plot->FrameRect.Min, plot->FrameRect.Max, IM_COL32(255,0,255,255));
  4781. if (show_canvas_rects)
  4782. fg.AddRect(plot->CanvasRect.Min, plot->CanvasRect.Max, IM_COL32(0,255,255,255));
  4783. if (show_plot_rects)
  4784. fg.AddRect(plot->PlotRect.Min, plot->PlotRect.Max, IM_COL32(255,255,0,255));
  4785. if (show_axes_rects)
  4786. fg.AddRect(plot->AxesRect.Min, plot->AxesRect.Max, IM_COL32(0,255,128,255));
  4787. if (show_axis_rects) {
  4788. for (int i = 0; i < ImAxis_COUNT; ++i) {
  4789. if (plot->Axes[i].Enabled)
  4790. fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255));
  4791. }
  4792. }
  4793. if (show_legend_rects && plot->Items.GetLegendCount() > 0) {
  4794. fg.AddRect(plot->Items.Legend.Rect.Min, plot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255));
  4795. fg.AddRect(plot->Items.Legend.RectClamped.Min, plot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255));
  4796. }
  4797. }
  4798. for (int p = 0; p < n_subplots; ++p) {
  4799. ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p);
  4800. if (show_subplot_frame_rects)
  4801. fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255));
  4802. if (show_subplot_grid_rects)
  4803. fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255));
  4804. if (show_legend_rects && subplot->Items.GetLegendCount() > 0) {
  4805. fg.AddRect(subplot->Items.Legend.Rect.Min, subplot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255));
  4806. fg.AddRect(subplot->Items.Legend.RectClamped.Min, subplot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255));
  4807. }
  4808. }
  4809. if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) {
  4810. for (int p = 0; p < n_plots; ++p) {
  4811. // plot
  4812. ImPlotPlot& plot = *gp.Plots.GetByIndex(p);
  4813. ImGui::PushID(p);
  4814. if (ImGui::TreeNode("Plot", "Plot [0x%08X]", plot.ID)) {
  4815. int n_items = plot.Items.GetItemCount();
  4816. if (ImGui::TreeNode("Items", "Items (%d)", n_items)) {
  4817. for (int i = 0; i < n_items; ++i) {
  4818. ImPlotItem* item = plot.Items.GetItemByIndex(i);
  4819. ImGui::PushID(i);
  4820. if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) {
  4821. ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show);
  4822. ImGui::Bullet();
  4823. ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color);
  4824. if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs))
  4825. item->Color = ImGui::ColorConvertFloat4ToU32(temp);
  4826. ImGui::BulletText("NameOffset: %d",item->NameOffset);
  4827. ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A");
  4828. ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false");
  4829. ImGui::TreePop();
  4830. }
  4831. ImGui::PopID();
  4832. }
  4833. ImGui::TreePop();
  4834. }
  4835. char buff[16];
  4836. for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {
  4837. ImFormatString(buff,16,"X-Axis %d", i+1);
  4838. if (plot.XAxis(i).Enabled && ImGui::TreeNode(buff, "X-Axis %d [0x%08X]", i+1, plot.XAxis(i).ID)) {
  4839. ShowAxisMetrics(plot, plot.XAxis(i));
  4840. ImGui::TreePop();
  4841. }
  4842. }
  4843. for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {
  4844. ImFormatString(buff,16,"Y-Axis %d", i+1);
  4845. if (plot.YAxis(i).Enabled && ImGui::TreeNode(buff, "Y-Axis %d [0x%08X]", i+1, plot.YAxis(i).ID)) {
  4846. ShowAxisMetrics(plot, plot.YAxis(i));
  4847. ImGui::TreePop();
  4848. }
  4849. }
  4850. ImGui::BulletText("Title: %s", plot.HasTitle() ? plot.GetTitle() : "none");
  4851. ImGui::BulletText("Flags: 0x%08X", plot.Flags);
  4852. ImGui::BulletText("Initialized: %s", plot.Initialized ? "true" : "false");
  4853. ImGui::BulletText("Selecting: %s", plot.Selecting ? "true" : "false");
  4854. ImGui::BulletText("Selected: %s", plot.Selected ? "true" : "false");
  4855. ImGui::BulletText("Hovered: %s", plot.Hovered ? "true" : "false");
  4856. ImGui::BulletText("Held: %s", plot.Held ? "true" : "false");
  4857. ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false");
  4858. ImGui::BulletText("ContextLocked: %s", plot.ContextLocked ? "true" : "false");
  4859. ImGui::TreePop();
  4860. }
  4861. ImGui::PopID();
  4862. }
  4863. ImGui::TreePop();
  4864. }
  4865. if (ImGui::TreeNode("Subplots","Subplots (%d)", n_subplots)) {
  4866. for (int p = 0; p < n_subplots; ++p) {
  4867. // plot
  4868. ImPlotSubplot& plot = *gp.Subplots.GetByIndex(p);
  4869. ImGui::PushID(p);
  4870. if (ImGui::TreeNode("Subplot", "Subplot [0x%08X]", plot.ID)) {
  4871. int n_items = plot.Items.GetItemCount();
  4872. if (ImGui::TreeNode("Items", "Items (%d)", n_items)) {
  4873. for (int i = 0; i < n_items; ++i) {
  4874. ImPlotItem* item = plot.Items.GetItemByIndex(i);
  4875. ImGui::PushID(i);
  4876. if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) {
  4877. ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show);
  4878. ImGui::Bullet();
  4879. ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color);
  4880. if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs))
  4881. item->Color = ImGui::ColorConvertFloat4ToU32(temp);
  4882. ImGui::BulletText("NameOffset: %d",item->NameOffset);
  4883. ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A");
  4884. ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false");
  4885. ImGui::TreePop();
  4886. }
  4887. ImGui::PopID();
  4888. }
  4889. ImGui::TreePop();
  4890. }
  4891. ImGui::BulletText("Flags: 0x%08X", plot.Flags);
  4892. ImGui::BulletText("FrameHovered: %s", plot.FrameHovered ? "true" : "false");
  4893. ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false");
  4894. ImGui::TreePop();
  4895. }
  4896. ImGui::PopID();
  4897. }
  4898. ImGui::TreePop();
  4899. }
  4900. if (ImGui::TreeNode("Colormaps")) {
  4901. ImGui::BulletText("Colormaps: %d", gp.ColormapData.Count);
  4902. ImGui::BulletText("Memory: %d bytes", gp.ColormapData.Tables.Size * 4);
  4903. if (ImGui::TreeNode("Data")) {
  4904. for (int m = 0; m < gp.ColormapData.Count; ++m) {
  4905. if (ImGui::TreeNode(gp.ColormapData.GetName(m))) {
  4906. int count = gp.ColormapData.GetKeyCount(m);
  4907. int size = gp.ColormapData.GetTableSize(m);
  4908. bool qual = gp.ColormapData.IsQual(m);
  4909. ImGui::BulletText("Qualitative: %s", qual ? "true" : "false");
  4910. ImGui::BulletText("Key Count: %d", count);
  4911. ImGui::BulletText("Table Size: %d", size);
  4912. ImGui::Indent();
  4913. static float t = 0.5;
  4914. ImVec4 samp;
  4915. float wid = 32 * 10 - ImGui::GetFrameHeight() - ImGui::GetStyle().ItemSpacing.x;
  4916. ImGui::SetNextItemWidth(wid);
  4917. ImPlot::ColormapSlider("##Sample",&t,&samp,"%.3f",m);
  4918. ImGui::SameLine();
  4919. ImGui::ColorButton("Sampler",samp);
  4920. ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0));
  4921. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0));
  4922. for (int c = 0; c < size; ++c) {
  4923. ImVec4 col = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetTableColor(m,c));
  4924. ImGui::PushID(m*1000+c);
  4925. ImGui::ColorButton("",col,0,ImVec2(10,10));
  4926. ImGui::PopID();
  4927. if ((c + 1) % 32 != 0 && c != size - 1)
  4928. ImGui::SameLine();
  4929. }
  4930. ImGui::PopStyleVar();
  4931. ImGui::PopStyleColor();
  4932. ImGui::Unindent();
  4933. ImGui::TreePop();
  4934. }
  4935. }
  4936. ImGui::TreePop();
  4937. }
  4938. ImGui::TreePop();
  4939. }
  4940. ImGui::End();
  4941. }
  4942. bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1, const ImPlotTime* t2) {
  4943. ImGui::PushID(id);
  4944. ImGui::BeginGroup();
  4945. ImGuiStyle& style = ImGui::GetStyle();
  4946. ImVec4 col_txt = style.Colors[ImGuiCol_Text];
  4947. ImVec4 col_dis = style.Colors[ImGuiCol_TextDisabled];
  4948. ImVec4 col_btn = style.Colors[ImGuiCol_Button];
  4949. ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));
  4950. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0));
  4951. const float ht = ImGui::GetFrameHeight();
  4952. ImVec2 cell_size(ht*1.25f,ht);
  4953. char buff[32];
  4954. bool clk = false;
  4955. tm& Tm = GImPlot->Tm;
  4956. const int min_yr = 1970;
  4957. const int max_yr = 2999;
  4958. // t1 parts
  4959. int t1_mo = 0; int t1_md = 0; int t1_yr = 0;
  4960. if (t1 != nullptr) {
  4961. GetTime(*t1,&Tm);
  4962. t1_mo = Tm.tm_mon;
  4963. t1_md = Tm.tm_mday;
  4964. t1_yr = Tm.tm_year + 1900;
  4965. }
  4966. // t2 parts
  4967. int t2_mo = 0; int t2_md = 0; int t2_yr = 0;
  4968. if (t2 != nullptr) {
  4969. GetTime(*t2,&Tm);
  4970. t2_mo = Tm.tm_mon;
  4971. t2_md = Tm.tm_mday;
  4972. t2_yr = Tm.tm_year + 1900;
  4973. }
  4974. // day widget
  4975. if (*level == 0) {
  4976. *t = FloorTime(*t, ImPlotTimeUnit_Day);
  4977. GetTime(*t, &Tm);
  4978. const int this_year = Tm.tm_year + 1900;
  4979. const int last_year = this_year - 1;
  4980. const int next_year = this_year + 1;
  4981. const int this_mon = Tm.tm_mon;
  4982. const int last_mon = this_mon == 0 ? 11 : this_mon - 1;
  4983. const int next_mon = this_mon == 11 ? 0 : this_mon + 1;
  4984. const int days_this_mo = GetDaysInMonth(this_year, this_mon);
  4985. const int days_last_mo = GetDaysInMonth(this_mon == 0 ? last_year : this_year, last_mon);
  4986. ImPlotTime t_first_mo = FloorTime(*t,ImPlotTimeUnit_Mo);
  4987. GetTime(t_first_mo,&Tm);
  4988. const int first_wd = Tm.tm_wday;
  4989. // month year
  4990. ImFormatString(buff, 32, "%s %d", MONTH_NAMES[this_mon], this_year);
  4991. if (ImGui::Button(buff))
  4992. *level = 1;
  4993. ImGui::SameLine(5*cell_size.x);
  4994. BeginDisabledControls(this_year <= min_yr && this_mon == 0);
  4995. if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size))
  4996. *t = AddTime(*t, ImPlotTimeUnit_Mo, -1);
  4997. EndDisabledControls(this_year <= min_yr && this_mon == 0);
  4998. ImGui::SameLine();
  4999. BeginDisabledControls(this_year >= max_yr && this_mon == 11);
  5000. if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size))
  5001. *t = AddTime(*t, ImPlotTimeUnit_Mo, 1);
  5002. EndDisabledControls(this_year >= max_yr && this_mon == 11);
  5003. // render weekday abbreviations
  5004. ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
  5005. for (int i = 0; i < 7; ++i) {
  5006. ImGui::Button(WD_ABRVS[i],cell_size);
  5007. if (i != 6) { ImGui::SameLine(); }
  5008. }
  5009. ImGui::PopItemFlag();
  5010. // 0 = last mo, 1 = this mo, 2 = next mo
  5011. int mo = first_wd > 0 ? 0 : 1;
  5012. int day = mo == 1 ? 1 : days_last_mo - first_wd + 1;
  5013. for (int i = 0; i < 6; ++i) {
  5014. for (int j = 0; j < 7; ++j) {
  5015. if (mo == 0 && day > days_last_mo) {
  5016. mo = 1;
  5017. day = 1;
  5018. }
  5019. else if (mo == 1 && day > days_this_mo) {
  5020. mo = 2;
  5021. day = 1;
  5022. }
  5023. const int now_yr = (mo == 0 && this_mon == 0) ? last_year : ((mo == 2 && this_mon == 11) ? next_year : this_year);
  5024. const int now_mo = mo == 0 ? last_mon : (mo == 1 ? this_mon : next_mon);
  5025. const int now_md = day;
  5026. const bool off_mo = mo == 0 || mo == 2;
  5027. const bool t1_or_t2 = (t1 != nullptr && t1_mo == now_mo && t1_yr == now_yr && t1_md == now_md) ||
  5028. (t2 != nullptr && t2_mo == now_mo && t2_yr == now_yr && t2_md == now_md);
  5029. if (off_mo)
  5030. ImGui::PushStyleColor(ImGuiCol_Text, col_dis);
  5031. if (t1_or_t2) {
  5032. ImGui::PushStyleColor(ImGuiCol_Button, col_btn);
  5033. ImGui::PushStyleColor(ImGuiCol_Text, col_txt);
  5034. }
  5035. ImGui::PushID(i*7+j);
  5036. ImFormatString(buff,32,"%d",day);
  5037. if (now_yr == min_yr-1 || now_yr == max_yr+1) {
  5038. ImGui::Dummy(cell_size);
  5039. }
  5040. else if (ImGui::Button(buff,cell_size) && !clk) {
  5041. *t = MakeTime(now_yr, now_mo, now_md);
  5042. clk = true;
  5043. }
  5044. ImGui::PopID();
  5045. if (t1_or_t2)
  5046. ImGui::PopStyleColor(2);
  5047. if (off_mo)
  5048. ImGui::PopStyleColor();
  5049. if (j != 6)
  5050. ImGui::SameLine();
  5051. day++;
  5052. }
  5053. }
  5054. }
  5055. // month widget
  5056. else if (*level == 1) {
  5057. *t = FloorTime(*t, ImPlotTimeUnit_Mo);
  5058. GetTime(*t, &Tm);
  5059. int this_yr = Tm.tm_year + 1900;
  5060. ImFormatString(buff, 32, "%d", this_yr);
  5061. if (ImGui::Button(buff))
  5062. *level = 2;
  5063. BeginDisabledControls(this_yr <= min_yr);
  5064. ImGui::SameLine(5*cell_size.x);
  5065. if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size))
  5066. *t = AddTime(*t, ImPlotTimeUnit_Yr, -1);
  5067. EndDisabledControls(this_yr <= min_yr);
  5068. ImGui::SameLine();
  5069. BeginDisabledControls(this_yr >= max_yr);
  5070. if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size))
  5071. *t = AddTime(*t, ImPlotTimeUnit_Yr, 1);
  5072. EndDisabledControls(this_yr >= max_yr);
  5073. // ImGui::Dummy(cell_size);
  5074. cell_size.x *= 7.0f/4.0f;
  5075. cell_size.y *= 7.0f/3.0f;
  5076. int mo = 0;
  5077. for (int i = 0; i < 3; ++i) {
  5078. for (int j = 0; j < 4; ++j) {
  5079. const bool t1_or_t2 = (t1 != nullptr && t1_yr == this_yr && t1_mo == mo) ||
  5080. (t2 != nullptr && t2_yr == this_yr && t2_mo == mo);
  5081. if (t1_or_t2)
  5082. ImGui::PushStyleColor(ImGuiCol_Button, col_btn);
  5083. if (ImGui::Button(MONTH_ABRVS[mo],cell_size) && !clk) {
  5084. *t = MakeTime(this_yr, mo);
  5085. *level = 0;
  5086. }
  5087. if (t1_or_t2)
  5088. ImGui::PopStyleColor();
  5089. if (j != 3)
  5090. ImGui::SameLine();
  5091. mo++;
  5092. }
  5093. }
  5094. }
  5095. else if (*level == 2) {
  5096. *t = FloorTime(*t, ImPlotTimeUnit_Yr);
  5097. int this_yr = GetYear(*t);
  5098. int yr = this_yr - this_yr % 20;
  5099. ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
  5100. ImFormatString(buff,32,"%d-%d",yr,yr+19);
  5101. ImGui::Button(buff);
  5102. ImGui::PopItemFlag();
  5103. ImGui::SameLine(5*cell_size.x);
  5104. BeginDisabledControls(yr <= min_yr);
  5105. if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size))
  5106. *t = MakeTime(yr-20);
  5107. EndDisabledControls(yr <= min_yr);
  5108. ImGui::SameLine();
  5109. BeginDisabledControls(yr + 20 >= max_yr);
  5110. if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size))
  5111. *t = MakeTime(yr+20);
  5112. EndDisabledControls(yr+ 20 >= max_yr);
  5113. // ImGui::Dummy(cell_size);
  5114. cell_size.x *= 7.0f/4.0f;
  5115. cell_size.y *= 7.0f/5.0f;
  5116. for (int i = 0; i < 5; ++i) {
  5117. for (int j = 0; j < 4; ++j) {
  5118. const bool t1_or_t2 = (t1 != nullptr && t1_yr == yr) || (t2 != nullptr && t2_yr == yr);
  5119. if (t1_or_t2)
  5120. ImGui::PushStyleColor(ImGuiCol_Button, col_btn);
  5121. ImFormatString(buff,32,"%d",yr);
  5122. if (yr<1970||yr>3000) {
  5123. ImGui::Dummy(cell_size);
  5124. }
  5125. else if (ImGui::Button(buff,cell_size)) {
  5126. *t = MakeTime(yr);
  5127. *level = 1;
  5128. }
  5129. if (t1_or_t2)
  5130. ImGui::PopStyleColor();
  5131. if (j != 3)
  5132. ImGui::SameLine();
  5133. yr++;
  5134. }
  5135. }
  5136. }
  5137. ImGui::PopStyleVar();
  5138. ImGui::PopStyleColor();
  5139. ImGui::EndGroup();
  5140. ImGui::PopID();
  5141. return clk;
  5142. }
  5143. bool ShowTimePicker(const char* id, ImPlotTime* t) {
  5144. ImPlotContext& gp = *GImPlot;
  5145. ImGui::PushID(id);
  5146. tm& Tm = gp.Tm;
  5147. GetTime(*t,&Tm);
  5148. static const char* nums[] = { "00","01","02","03","04","05","06","07","08","09",
  5149. "10","11","12","13","14","15","16","17","18","19",
  5150. "20","21","22","23","24","25","26","27","28","29",
  5151. "30","31","32","33","34","35","36","37","38","39",
  5152. "40","41","42","43","44","45","46","47","48","49",
  5153. "50","51","52","53","54","55","56","57","58","59"};
  5154. static const char* am_pm[] = {"am","pm"};
  5155. bool hour24 = gp.Style.Use24HourClock;
  5156. int hr = hour24 ? Tm.tm_hour : ((Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12);
  5157. int min = Tm.tm_min;
  5158. int sec = Tm.tm_sec;
  5159. int ap = Tm.tm_hour < 12 ? 0 : 1;
  5160. bool changed = false;
  5161. ImVec2 spacing = ImGui::GetStyle().ItemSpacing;
  5162. spacing.x = 0;
  5163. float width = ImGui::CalcTextSize("888").x;
  5164. float height = ImGui::GetFrameHeight();
  5165. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, spacing);
  5166. ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize,2.0f);
  5167. ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0));
  5168. ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));
  5169. ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered));
  5170. ImGui::SetNextItemWidth(width);
  5171. if (ImGui::BeginCombo("##hr",nums[hr],ImGuiComboFlags_NoArrowButton)) {
  5172. const int ia = hour24 ? 0 : 1;
  5173. const int ib = hour24 ? 24 : 13;
  5174. for (int i = ia; i < ib; ++i) {
  5175. if (ImGui::Selectable(nums[i],i==hr)) {
  5176. hr = i;
  5177. changed = true;
  5178. }
  5179. }
  5180. ImGui::EndCombo();
  5181. }
  5182. ImGui::SameLine();
  5183. ImGui::Text(":");
  5184. ImGui::SameLine();
  5185. ImGui::SetNextItemWidth(width);
  5186. if (ImGui::BeginCombo("##min",nums[min],ImGuiComboFlags_NoArrowButton)) {
  5187. for (int i = 0; i < 60; ++i) {
  5188. if (ImGui::Selectable(nums[i],i==min)) {
  5189. min = i;
  5190. changed = true;
  5191. }
  5192. }
  5193. ImGui::EndCombo();
  5194. }
  5195. ImGui::SameLine();
  5196. ImGui::Text(":");
  5197. ImGui::SameLine();
  5198. ImGui::SetNextItemWidth(width);
  5199. if (ImGui::BeginCombo("##sec",nums[sec],ImGuiComboFlags_NoArrowButton)) {
  5200. for (int i = 0; i < 60; ++i) {
  5201. if (ImGui::Selectable(nums[i],i==sec)) {
  5202. sec = i;
  5203. changed = true;
  5204. }
  5205. }
  5206. ImGui::EndCombo();
  5207. }
  5208. if (!hour24) {
  5209. ImGui::SameLine();
  5210. if (ImGui::Button(am_pm[ap],ImVec2(0,height))) {
  5211. ap = 1 - ap;
  5212. changed = true;
  5213. }
  5214. }
  5215. ImGui::PopStyleColor(3);
  5216. ImGui::PopStyleVar(2);
  5217. ImGui::PopID();
  5218. if (changed) {
  5219. if (!hour24)
  5220. hr = hr % 12 + ap * 12;
  5221. Tm.tm_hour = hr;
  5222. Tm.tm_min = min;
  5223. Tm.tm_sec = sec;
  5224. *t = MkTime(&Tm);
  5225. }
  5226. return changed;
  5227. }
  5228. void StyleColorsAuto(ImPlotStyle* dst) {
  5229. ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle();
  5230. ImVec4* colors = style->Colors;
  5231. style->MinorAlpha = 0.25f;
  5232. colors[ImPlotCol_Line] = IMPLOT_AUTO_COL;
  5233. colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL;
  5234. colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL;
  5235. colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL;
  5236. colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL;
  5237. colors[ImPlotCol_FrameBg] = IMPLOT_AUTO_COL;
  5238. colors[ImPlotCol_PlotBg] = IMPLOT_AUTO_COL;
  5239. colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL;
  5240. colors[ImPlotCol_LegendBg] = IMPLOT_AUTO_COL;
  5241. colors[ImPlotCol_LegendBorder] = IMPLOT_AUTO_COL;
  5242. colors[ImPlotCol_LegendText] = IMPLOT_AUTO_COL;
  5243. colors[ImPlotCol_TitleText] = IMPLOT_AUTO_COL;
  5244. colors[ImPlotCol_InlayText] = IMPLOT_AUTO_COL;
  5245. colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL;
  5246. colors[ImPlotCol_AxisText] = IMPLOT_AUTO_COL;
  5247. colors[ImPlotCol_AxisGrid] = IMPLOT_AUTO_COL;
  5248. colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL;
  5249. colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL;
  5250. colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL;
  5251. colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL;
  5252. colors[ImPlotCol_Selection] = IMPLOT_AUTO_COL;
  5253. colors[ImPlotCol_Crosshairs] = IMPLOT_AUTO_COL;
  5254. }
  5255. void StyleColorsClassic(ImPlotStyle* dst) {
  5256. ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle();
  5257. ImVec4* colors = style->Colors;
  5258. style->MinorAlpha = 0.5f;
  5259. colors[ImPlotCol_Line] = IMPLOT_AUTO_COL;
  5260. colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL;
  5261. colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL;
  5262. colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL;
  5263. colors[ImPlotCol_ErrorBar] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
  5264. colors[ImPlotCol_FrameBg] = ImVec4(0.43f, 0.43f, 0.43f, 0.39f);
  5265. colors[ImPlotCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.35f);
  5266. colors[ImPlotCol_PlotBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f);
  5267. colors[ImPlotCol_LegendBg] = ImVec4(0.11f, 0.11f, 0.14f, 0.92f);
  5268. colors[ImPlotCol_LegendBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f);
  5269. colors[ImPlotCol_LegendText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
  5270. colors[ImPlotCol_TitleText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
  5271. colors[ImPlotCol_InlayText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
  5272. colors[ImPlotCol_AxisText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
  5273. colors[ImPlotCol_AxisGrid] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f);
  5274. colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO
  5275. colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO
  5276. colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO
  5277. colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO
  5278. colors[ImPlotCol_Selection] = ImVec4(0.97f, 0.97f, 0.39f, 1.00f);
  5279. colors[ImPlotCol_Crosshairs] = ImVec4(0.50f, 0.50f, 0.50f, 0.75f);
  5280. }
  5281. void StyleColorsDark(ImPlotStyle* dst) {
  5282. ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle();
  5283. ImVec4* colors = style->Colors;
  5284. style->MinorAlpha = 0.25f;
  5285. colors[ImPlotCol_Line] = IMPLOT_AUTO_COL;
  5286. colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL;
  5287. colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL;
  5288. colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL;
  5289. colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL;
  5290. colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
  5291. colors[ImPlotCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);
  5292. colors[ImPlotCol_PlotBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
  5293. colors[ImPlotCol_LegendBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
  5294. colors[ImPlotCol_LegendBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
  5295. colors[ImPlotCol_LegendText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
  5296. colors[ImPlotCol_TitleText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
  5297. colors[ImPlotCol_InlayText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
  5298. colors[ImPlotCol_AxisText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
  5299. colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f);
  5300. colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO
  5301. colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO
  5302. colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO
  5303. colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO
  5304. colors[ImPlotCol_Selection] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
  5305. colors[ImPlotCol_Crosshairs] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f);
  5306. }
  5307. void StyleColorsLight(ImPlotStyle* dst) {
  5308. ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle();
  5309. ImVec4* colors = style->Colors;
  5310. style->MinorAlpha = 1.0f;
  5311. colors[ImPlotCol_Line] = IMPLOT_AUTO_COL;
  5312. colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL;
  5313. colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL;
  5314. colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL;
  5315. colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL;
  5316. colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
  5317. colors[ImPlotCol_PlotBg] = ImVec4(0.42f, 0.57f, 1.00f, 0.13f);
  5318. colors[ImPlotCol_PlotBorder] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
  5319. colors[ImPlotCol_LegendBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f);
  5320. colors[ImPlotCol_LegendBorder] = ImVec4(0.82f, 0.82f, 0.82f, 0.80f);
  5321. colors[ImPlotCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
  5322. colors[ImPlotCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
  5323. colors[ImPlotCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
  5324. colors[ImPlotCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
  5325. colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
  5326. colors[ImPlotCol_AxisTick] = ImVec4(0.00f, 0.00f, 0.00f, 0.25f);
  5327. colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO
  5328. colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO
  5329. colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO
  5330. colors[ImPlotCol_Selection] = ImVec4(0.82f, 0.64f, 0.03f, 1.00f);
  5331. colors[ImPlotCol_Crosshairs] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);
  5332. }
  5333. //-----------------------------------------------------------------------------
  5334. // [SECTION] Obsolete Functions/Types
  5335. //-----------------------------------------------------------------------------
  5336. #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS
  5337. bool BeginPlot(const char* title, const char* x_label, const char* y1_label, const ImVec2& size,
  5338. ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags,
  5339. const char* y2_label, const char* y3_label)
  5340. {
  5341. if (!BeginPlot(title, size, flags))
  5342. return false;
  5343. SetupAxis(ImAxis_X1, x_label, x_flags);
  5344. SetupAxis(ImAxis_Y1, y1_label, y1_flags);
  5345. if (ImHasFlag(flags, ImPlotFlags_YAxis2))
  5346. SetupAxis(ImAxis_Y2, y2_label, y2_flags);
  5347. if (ImHasFlag(flags, ImPlotFlags_YAxis3))
  5348. SetupAxis(ImAxis_Y3, y3_label, y3_flags);
  5349. return true;
  5350. }
  5351. #endif
  5352. } // namespace ImPlot
  5353. #endif // #ifndef IMGUI_DISABLE