diff --git a/.gitignore b/.gitignore index 2152318d78..a340b1d6b3 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,11 @@ # generated files bin/ gen/ +generated/ +docs/ +finalOutput/ + +build.xml # Local configuration file (sdk path, etc) local.properties @@ -37,3 +42,5 @@ build/ # maven target/ + +.DS_Store diff --git a/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs b/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs new file mode 100644 index 0000000000..9383f623c6 --- /dev/null +++ b/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs @@ -0,0 +1,4 @@ +#org.springsource.ide.eclipse.gradle.core.preferences.GradleProjectPreferences +#Mon Jan 18 23:02:46 CET 2016 +build.family.org.gradle.tooling.model.eclipse.HierarchicalEclipseProject=;MPChartExample;MPChartLib; +org.springsource.ide.eclipse.gradle.rootprojectloc= diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..2e4d5b866c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,49 @@ +# How to contribute + +Bug-fixes and features often come from users of the MPAndroidChart library and improve it greatly. We want to keep it as easy as possible to contribute changes that improve the experience for users all around the world. There are a few guidelines that we +need contributors to follow so that we can have a chance of keeping on +top of things. + +## Simple issues and bug reports + +If you are reporting a bug which can be observed visually, please add to your issue either: + +* Screenshots, if the bug is easily explainable +* A working sample project that we can compile, run, and immediately observe the issue + +## Getting Started with Contributions + +* Make sure you have a [GitHub account](https://github.com/signup/free) +* Submit a ticket for your issue, assuming one does not already exist. + * Clearly describe the issue including steps to reproduce when it is a bug. + * Make sure you fill in the earliest version (or commit number) that you know has the issue. +* Fork the repository on GitHub + +## Making Changes + +* Create a topic branch from where you want to base your work. This is usually the master branch. +* Make commits of logical units. +* Make sure your code conforms to the code style around it. It's easy, just look around! +* If you have made changes back and forth, or have made merges, your commit history might look messy and hard to understand. A single issue or change should still be in one commit. So please squash those commits together and rebase them however you need to - to make our lives easier when reading it later. +* Check for unnecessary whitespace with `git diff --check` before committing. +* Make sure your commit messages are in the proper format. + +```` + First line must be up to 50 chars (Fixes #1234) + + The first line should be a short statement as to what have changed, and should also include an issue number, prefixed with a dash. + The body of the message comes after an empty new line, and describes the changes + more thoroughly, especially if there was a special case handled there, + or maybe some trickery that only code wizards can understand. +```` + +* Make sure you have tested your changes well. +* If your changes could theoretically affect some other component or case, which you do not necessarily use, you still have to test it. +* Create a Pull Request from your topic branch to the relevant branch in the main repo. If you go to the main repo of the framework, you'll see a big green button which pretty much prepares the PR for you. You just have to hit it. + +## Making Trivial Changes + +For changes of a trivial nature to comments and documentation, it is not +always necessary to create a new ticket. In this case, it is +appropriate to start the first line of a commit with '(doc)' instead of +a ticket number. Even the default commit message the GitHub generates is fine with us. diff --git a/MPChartExample/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs b/MPChartExample/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs new file mode 100644 index 0000000000..be975eb2db --- /dev/null +++ b/MPChartExample/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs @@ -0,0 +1,3 @@ +#org.springsource.ide.eclipse.gradle.core.preferences.GradleProjectPreferences +#Mon Jan 18 23:02:46 CET 2016 +org.springsource.ide.eclipse.gradle.rootprojectloc=.. diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 626e85c460..b1ac584a2e 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -1,17 +1,18 @@ + android:versionCode="54" + android:versionName="3.0.1" > + android:minSdkVersion="16" + android:targetSdkVersion="24" /> - + - + + + @@ -44,9 +47,25 @@ + + + + + + + + + + + + + + + + diff --git a/MPChartExample/assets/othersine.txt b/MPChartExample/assets/othersine.txt new file mode 100644 index 0000000000..142857b056 --- /dev/null +++ b/MPChartExample/assets/othersine.txt @@ -0,0 +1,8000 @@ +0.0#0 +0.09983341664682815#1 +0.19866933079506122#2 +0.2955202066613396#3 +0.3894183423086505#4 +0.479425538604203#5 +0.5646424733950354#6 +0.644217687237691#7 +0.7173560908995227#8 +0.7833269096274833#9 +0.8414709848078964#10 +0.8912073600614353#11 +0.9320390859672263#12 +0.963558185417193#13 +0.9854497299884603#14 +0.9974949866040544#15 +0.9995736030415051#16 +0.9916648104524686#17 +0.973847630878195#18 +0.9463000876874144#19 +0.9092974268256815#20 +0.8632093666488735#21 +0.8084964038195899#22 +0.7457052121767197#23 +0.6754631805511504#24 +0.5984721441039558#25 +0.5155013718214634#26 +0.42737988023382895#27 +0.33498815015590383#28 +0.23924932921398112#29 +0.1411200080598659#30 +0.04158066243328916#31 +-0.05837414342758142#32 +-0.15774569414324996#33 +-0.25554110202683294#34 +-0.3507832276896215#35 +-0.44252044329485407#36 +-0.5298361409084948#37 +-0.6118578909427207#38 +-0.6877661591839753#39 +-0.7568024953079294#40 +-0.8182771110644114#41 +-0.8715757724135886#42 +-0.9161659367494552#43 +-0.9516020738895161#44 +-0.977530117665097#45 +-0.9936910036334644#46 +-0.9999232575641008#47 +-0.9961646088358408#48 +-0.9824526126243328#49 +-0.958924274663139#50 +-0.9258146823277331#51 +-0.8834546557201545#52 +-0.8322674422239027#53 +-0.7727644875559894#54 +-0.7055403255703945#55 +-0.6312666378723244#56 +-0.5506855425976414#57 +-0.4646021794137613#58 +-0.37387666483024096#59 +-0.27941549819893097#60 +-0.18216250427210112#61 +-0.0830894028175026#62 +0.016813900484343496#63 +0.11654920485048659#64 +0.21511998808780858#65 +0.3115413635133711#66 +0.40484992061659103#67 +0.4941133511386012#68 +0.5784397643881929#69 +0.6569865987187824#70 +0.7289690401258698#71 +0.7936678638491472#72 +0.8504366206285593#73 +0.8987080958116223#74 +0.9379999767747351#75 +0.9679196720314837#76 +0.9881682338769986#77 +0.9985433453746043#78 +0.9989413418397726#79 +0.9893582466233836#80 +0.9698898108450894#81 +0.9407305566797773#82 +0.9021718337562995#83 +0.8545989080882879#84 +0.7984871126234988#85 +0.734397097874123#86 +0.662969230082194#87 +0.5849171928917747#88 +0.5010208564578985#89 +0.41211848524177114#90 +0.3190983623493673#91 +0.22288991410026324#92 +0.12445442350707933#93 +0.024775425453375525#94 +-0.07515112046179159#95 +-0.17432678122296213#96 +-0.27176062641092535#97 +-0.36647912925191023#98 +-0.457535893775304#99 +-0.5440211108893535#100 +-0.6250706488928668#101 +-0.6998746875935284#102 +-0.7676858097635688#103 +-0.8278264690856417#104 +-0.8796957599716599#105 +-0.9227754216127984#106 +-0.9566350162701817#107 +-0.980936230066487#108 +-0.9954362533063752#109 +-0.9999902065507036#110 +-0.9945525882039916#111 +-0.9791777291513221#112 +-0.9540192499020964#113 +-0.9193285256646855#114 +-0.8754521746884406#115 +-0.822828594968723#116 +-0.7619835839190494#117 +-0.6935250847771416#118 +-0.6181371122370543#119 +-0.5365729180004575#120 +-0.4496474645346253#121 +-0.3582292822368536#122 +-0.26323179136582836#123 +-0.16560417544833742#124 +-0.06632189735122905#125 +0.03362304722110829#126 +0.13323204141991404#127 +0.23150982510150958#128 +0.3274744391376645#129 +0.4201670368266135#130 +0.5086614643723477#131 +0.5920735147071987#132 +0.6695697621965786#133 +0.7403758899524271#134 +0.8037844265516019#135 +0.8591618148564795#136 +0.9059547423084483#137 +0.9436956694440937#138 +0.972007501394968#139 +0.9906073556948657#140 +0.9993093887479164#141 +0.9980266527163638#142 +0.9867719642746191#143 +0.9656577765492868#144 +0.9348950555246957#145 +0.8947911721405201#146 +0.8457468311429532#147 +0.7882520673753391#148 +0.7228813495120019#149 +0.6502878401571452#150 +0.5711968696600193#151 +0.4863986888538323#152 +0.3967405731306479#153 +0.3031183567457395#154 +0.20646748193783482#155 +0.10775365229948292#156 +0.007963183785976422#157 +-0.09190685022764096#158 +-0.19085858137414927#159 +-0.28790331666502617#160 +-0.3820714171839697#161 +-0.4724219863984317#162 +-0.558052271286747#163 +-0.6381066823479201#164 +-0.7117853423690981#165 +-0.7783520785342762#166 +-0.8371417780197293#167 +-0.8875670335814898#168 +-0.9291240127343579#169 +-0.961397491879549#170 +-0.9840650050816383#171 +-0.9969000660415941#172 +-0.9997744310730117#173 +-0.9926593804706357#174 +-0.9756260054681622#175 +-0.9488444979181307#176 +-0.9125824497911917#177 +-0.8672021794855902#178 +-0.8131571116614967#179 +-0.7509872467716855#180 +-0.6813137655555104#181 +-0.6048328224062927#182 +-0.5223085896267406#183 +-0.43456562207190313#184 +-0.34248061846961925#185 +-0.24697366173662777#186 +-0.14899902581420232#187 +-0.049535640878370965#188 +0.05042268780681122#189 +0.14987720966295234#190 +0.24783420798295983#191 +0.3433149288198987#192 +0.4353653603728964#193 +0.5230657651577025#194 +0.6055398697196067#195 +0.6819636200681407#196 +0.7515734153521554#197 +0.8136737375071116#198 +0.8676441006416744#199 +0.9129452507276334#200 +0.949124553647899#201 +0.9758205177669794#202 +0.9927664058359092#203 +0.9997929001426696#204 +0.9968297942787976#205 +0.9839066946186122#206 +0.9611527245021096#207 +0.9287952340772312#208 +0.887157528692338#209 +0.8366556385360404#210 +0.777794161801075#211 +0.71116122290596#212 +0.6374225961502142#213 +0.5573150535176319#214 +0.47163900309416484#215 +0.3812504916549073#216 +0.28705265132769103#217 +0.18998667579539935#218 +0.09102241619980542#219 +-0.008851309290446507#220 +-0.10863659542412214#221 +-0.20733642060680393#222 +-0.30396460881109105#223 +-0.39755568312147854#224 +-0.487174512460553#225 +-0.5719256551096047#226 +-0.6509623056662873#227 +-0.7234947560442817#228 +-0.7887982859754514#229 +-0.846220404175201#230 +-0.8951873678197072#231 +-0.9352099151945603#232 +-0.9658881542360861#233 +-0.986915558120659#234 +-0.9980820279794003#235 +-0.9992759921366252#236 +-0.9904855208971471#237 +-0.9717984457438473#238 +-0.9434014817545319#239 +-0.9055783620065937#240 +-0.8587070026098931#241 +-0.8032557266939103#242 +-0.7397785850778432#243 +-0.6689098203779662#244 +-0.5913575298650613#245 +-0.5078965903905548#246 +-0.4193609160731571#247 +-0.326635126104645#248 +-0.2306457059273127#249 +-0.13235175009768851#250 +-0.0327353793307601#251 +0.06720807252556354#252 +0.16648000353724682#253 +0.2640885213845585#254 +0.3590583540222545#255 +0.4504405942754718#256 +0.5373221810065527#257 +0.6188350221201147#258 +0.6941646682523136#259 +0.7625584504796671#260 +0.823333000738138#261 +0.875881079810939#262 +0.9196776446620603#263 +0.9542850944927288#264 +0.9793576431039386#265 +0.9946447738778491#266 +0.999993742857021#267 +0.9953511049115485#268 +0.9807632477451307#269 +0.9563759284044698#270 +0.9224328169230419#271 +0.8792730616506684#272 +0.8273279005953128#273 +0.7671163526354526#274 +0.6992400316550114#275 +0.624377135416297#276 +0.5432756692321434#277 +0.45674597214408225#278 +0.3656526202825023#279 +0.2709057883077459#280 +0.17345215524576615#281 +0.07426544558423022#282 +-0.025663299860690898#283 +-0.12533562609656307#284 +-0.223755640186928#285 +-0.31993996188432605#286 +-0.4129275492406663#287 +-0.501789301020694#288 +-0.5856373399744128#289 +-0.6636338842130738#290 +-0.734999618048874#291 +-0.7990214786597013#292 +-0.8550597807771457#293 +-0.9025546082102497#294 +-0.941031408343004#295 +-0.9701057337072216#296 +-0.9894870832545573#297 +-0.9989818049469563#298 +-0.9984950306638061#299 +-0.9880316240928376#300 +-0.9676961321337658#301 +-0.9376917403002256#302 +-0.8983182425572844#303 +-0.8499690458792419#304 +-0.7931272394571857#305 +-0.7283607678314815#306 +-0.6563167561776644#307 +-0.5777150444455955#308 +-0.49334099495663#309 +-0.40403764532290903#310 +-0.3106972850942102#311 +-0.21425254029571766#312 +-0.11566705493706401#313 +-0.015925862599924186#314 +0.08397445569192384#315 +0.18303572898076265#316 +0.28026816976919344#317 +0.37470026364963#318 +0.465388476355119#319 +0.5514266812418447#320 +0.6319552130070282#321 +0.7061694571804652#322 +0.7733278895663422#323 +0.8327594853078859#324 +0.8838704235459204#325 +0.9261500206806009#326 +0.9591758329531347#327 +0.9826178773641765#328 +0.9962419287548808#329 +0.9999118601072645#330 +0.9935910026803725#331 +0.9773425123922165#332 +0.9513287387867192#333 +0.9158096028907363#334 +0.8711400001690751#335 +0.8177662545263231#336 +0.7562216587859281#337 +0.6871211462045894#338 +0.6111551462624274#339 +0.529082686119843#340 +0.44172380666903255#341 +0.34995136895646245#342 +0.2546823328438221#343 +0.15686859504819245#344 +0.05748747810470466#345 +-0.04246803471717159#346 +-0.14199922097421971#347 +-0.24011159795399523#348 +-0.33582485921735605#349 +-0.42818266949635647#350 +-0.5162622200801217#351 +-0.5991834492144473#352 +-0.676117835387937#353 +-0.7462966756450723#354 +-0.8090187662120443#355 +-0.8636574086930742#356 +-0.9096666718336256#357 +-0.9465868462850383#358 +-0.974049037868381#359 +-0.9917788534431466#360 +-0.9995991425528098#361 +-0.9974317674536307#362 +-0.9852983838411613#363 +-0.9633202244736941#364 +-0.9317168878546153#365 +-0.8908041440767483#366 +-0.840990779751967#367 +-0.7827745135504952#368 +-0.7167370231604792#369 +-0.6435381333568037#370 +-0.5639092232500512#371 +-0.4786459185881904#372 +-0.38860014212710114#373 +-0.2946716015000064#374 +-0.19779879963620456#375 +-0.09894965755002794#376 +8.881568059804928E-4#377 +0.10071709699276916#378 +0.19953970523905196#379 +0.29636857870964317#380 +0.39023623530819473#381 +0.48020478043849585#382 +0.5653752781372515#383 +0.6448967329450791#384 +0.7179745927718371#385 +0.783878687798465#386 +0.8419505260924545#387 +0.8916098730415694#388 +0.9323605488662917#389 +0.9637953862841635#390 +0.9856002987906811#391 +0.997557418907825#392 +0.9995472750438832#393 +0.9915499852141032#394 +0.9736454556949117#395 +0.9460125826268136#396 +0.9089274645432923#397 +0.862760643685528#398 +0.8079734036668093#399 +0.7451131604791498#400 +0.6748079928936466#401 +0.5977603669050155#402 +0.5147401169235761#403 +0.42657675384429683#404 +0.33415117684813256#405 +0.23838687174859532#406 +0.1402406838267691#407 +0.040693257349552474#408 +-0.059260762703698414#409 +-0.15862266880501766#410 +-0.2563996696333202#411 +-0.35161480971710896#412 +-0.4433167308506074#413 +-0.5305891777504746#414 +-0.6125601529757225#415 +-0.6884106296379023#416 +-0.7573827348470396#417 +-0.8187873221270313#418 +-0.8720108571394337#419 +-0.9165215479157646#420 +-0.9518746583470648#421 +-0.9777169518400896#422 +-0.9937902207405829#423 +-0.9999338662588075#424 +-0.9960865031195645#425 +-0.9822865729035719#426 +-0.9586719599518976#427 +-0.9254786136712014#428 +-0.8830381910052587#429 +-0.831774742628409#430 +-0.7722004759713906#431 +-0.7049106374140062#432 +-0.6305775647800578#433 +-0.549943969560057#434 +-0.46381551598351894#435 +-0.3730527710882991#436 +-0.2785626062192159#437 +-0.18128913586934542#438 +-0.08220428440008934#439 +0.017701925105768793#440 +0.11743126282744855#441 +0.21598726618857098#442 +0.312385196181835#443 +0.4056618765556671#444 +0.49488531755294296#445 +0.5791640280445662#446 +0.6576559230141424#447 +0.7295767373931061#448 +0.7942078621780435#449 +0.8509035245343125#450 +0.8990972401447438#451 +0.9383074733336821#452 +0.9681424484122837#453 +0.9883040641716944#454 +0.9985908724117905#455 +0.9989000907450034#456 +0.989228629563962#457 +0.969673122911827#458 +0.9404289629468755#459 +0.9017883476486434#460 +0.8541373612723012#461 +0.7979521167223996#462 +0.7337939983901085#463 +0.6623040529859501#464 +0.5841965844129685#465 +0.5002520166782486#466 +0.4113090961542109#467 +0.318256511102111#468 +0.2220240121926944#469 +0.12357312274482915#470 +0.023887531502341068#471 +-0.07603673605875032#472 +-0.1752012696875459#473 +-0.2726152501434634#474 +-0.367305349134568#475 +-0.4583254544921258#476 +-0.5447661234106508#477 +-0.6257636692997951#478 +-0.7005087914552633#479 +-0.7682546613239306#480 +-0.8283243845678764#481 +-0.8801177643688576#482 +-0.923117298396518#483 +-0.9568933495206096#484 +-0.9811084386031781#485 +-0.995520616478555#486 +-0.9999858814294422#487 +-0.9944596180044706#488 +-0.9789970428010428#489 +-0.9537526527593436#490 +-0.918978681479984#491 +-0.8750225789892403#492 +-0.8223235401332446#493 +-0.7614081162882599#494 +-0.6928849542333831#495 +-0.6174387147531208#496 +-0.5358232317331488#497 +-0.4488539801013109#498 +-0.3573999278716999#499 +-0.26237485370350366#500 +-0.16472821672669768#501 +-0.06543566986026736#502 +0.03451068858925011#503 +0.13411222764610073#504 +0.2323737616559199#505 +0.32831349385182623#506 +0.42097282614317716#507 +0.5094259371108206#508 +0.5927890325086789#509 +0.6702291758437122#510 +0.740972610802025#511 +0.8043124923661843#512 +0.859615949376982#513 +0.9063304079728589#514 +0.9439891127252716#515 +0.9722157903046471#516 +0.9907284090791111#517 +0.9993419970813273#518 +0.9979704901872514#519 +0.9866275920404088#520 +0.9654266371296195#521 +0.9345794583882479#522 +0.8943942706295563#523 +0.8452725909661725#524 +0.7877052269838246#525 +0.7222673727541837#526 +0.6496128616858338#527 +0.5704676336369813#528 +0.48562248156435645#529 +0.3959251501813905#530 +0.3022718655732663#531 +0.20559838040212833#532 +0.10687062417580104#533 +0.007075051999440675#534 +-0.09279121175779685#535 +-0.19173033639984702#536 +-0.2887537548982128#537 +-0.38289204132693666#538 +-0.47320459704600304#539 +-0.5587890488520287#540 +-0.6387902651930264#541 +-0.712408900360203#542 +-0.7789093812860535#543 +-0.8376272571473031#544 +-0.8879758383378954#545 +-0.929452058477602#546 +-0.9616415008850029#547 +-0.984222539291978#548 +-0.9969695514270638#549 +-0.9997551733586085#550 +-0.9925515720730763#551 +-0.9754307235733183#552 +-0.948563693718144#553 +-0.9122189289888067#554 +-0.8667595742605005#555 +-0.8126398443788057#556 +-0.7504004857950958#557 +-0.6806633736067427#558 +-0.6041252979867081#559 +-0.5215510020864632#560 +-0.43376554097521025#561 +-0.3416460379623573#562 +-0.2461129206713498#563 +-0.14812072443110932#564 +-0.04864855487455499#565 +0.0513096949612317#566 +0.1507552752856959#567 +0.24869455873257396#568 +0.34414896835608744#569 +0.4361647552483109#570 +0.5238225280829925#571 +0.6062464393697752#572 +0.6826129366328042#573 +0.7521589910748351#574 +0.8141897215087521#575 +0.8680853373806932#576 +0.9133073315123875#577 +0.9494038606868752#578 +0.9760142603165708#579 +0.9928726480846032#580 +0.9998105805530361#581 +0.9967587361940206#582 +0.9837476080275863#583 +0.9609071989454651#584 +0.9284657227651701#585 +0.8867473239929686#586 +0.8361688390791232#587 +0.7772356315258688#588 +0.7105365424623492#589 +0.6367380071386995#590 +0.5565773961253213#591 +0.4708556477498852#592 +0.38042926538631583#593 +0.28620175955620214#594 +0.18911462035032636#595 +0.09013791037089226#596 +-0.009739427813336072#597 +-0.10951945285428034#598 +-0.20820519572462254#599 +-0.30481062110277163#600 +-0.3983704795118571#601 +-0.48794995177343276#602 +-0.5726539894116975#603 +-0.6516362576820022#604 +-0.7241075918678563#605 +-0.7893438823539132#606 +-0.8466933096898865#607 +-0.8955828573550356#608 +-0.935524037149826#609 +-0.966117770008547#610 +-0.9870583734654177#611 +-0.9981366159327402#612 +-0.999241807273761#613 +-0.9903629047819286#614 +-0.9715886235159662#615 +-0.9431065498883311#616 +-0.9052012673637889#617 +-0.8582515129955042#618 +-0.8027263932095995#619 +-0.7391806966488114#620 +-0.6682493509077604#621 +-0.5906410785463059#622 +-0.5071313157679993#623 +-0.418554464517864#624 +-0.3257955554139773#625 +-0.2297814048140864#626 +-0.131471354372927#627 +-0.03184768561752508#628 +0.06809419468512197#629 +0.1673557003034234#630 +0.26494504308473243#631 +0.3598871425747261#632 +0.4512333686989858#633 +0.5380710201611258#634 +0.6195324438524475#635 +0.6948037041549939#636 +0.7631327155171924#637 +0.8238367570440802#638 +0.8763092940188087#639 +0.9200260381970413#640 +0.9545501863217811#641 +0.9795367845171353#642 +0.9947361749534073#643 +0.9999964903456082#644 +0.9952651713611683#645 +0.9805894917754141#646 +0.9561160861276317#647 +0.9220894845972212#648 +0.8788496697389755#649 +0.8268286794897758#650 +0.7665462903883387#651 +0.6986048241398573#652 +0.6236831294166234#653 +0.542529799026492#654 +0.4559556902209967#655 +0.3648258228777264#656 +0.27005073650764577#657 +0.17257739244528622#658 +0.07337971212411498#659 +-0.026551154024492412#660 +-0.12621672981873175#661 +-0.2246211897703263#662 +-0.3207813090436296#663 +-0.41373628751317226#664 +-0.5025573497609173#665 +-0.5863570250932251#666 +-0.664298014854665#667 +-0.7356015584394067#668 +-0.7995552144097693#669 +-0.8555199789755651#670 +-0.9029366707087443#671 +-0.9413315176993833#672 +-0.9703208913280009#673 +-0.9896151393559127#674 +-0.9990214800346544#675 +-0.9984459283174868#676 +-0.9878942349269157#677 +-0.9674718288953736#678 +-0.9373827641531779#679 +-0.8979276806891099#680 +-0.849500800655026#681 +-0.7925859894284285#682 +-0.7277519209895582#683 +-0.6556463959189626#684 +-0.5769898687882292#685 +-0.4925682496160574#686 +-0.40322505131513814#687 +-0.30985296158995934#688 +-0.21338492349634555#689 +-0.1147848137828343#690 +-0.015037812152819907#691 +0.08485944232538563#692 +0.18390880930667822#693 +0.2811206202575485#694 +0.37552356689692795#695 +0.46617440618746125#696 +0.552167384908141#697 +0.6326432896410897#698 +0.7067980317480133#699 +0.773890681558078#700 +0.8332508714922635#701 +0.8842854941546171#702 +0.9264846284653002#703 +0.9594266346234597#704 +0.9827823669927427#705 +0.9963184628156941#706 +0.9998996738972894#707 +0.9934902179601922#708 +0.9771541361694416#709 +0.9510546532542999#710 +0.9154525466207135#711 +0.870703540749673#712 +0.8172547529157873#713 +0.7556402257392723#714 +0.6864755912086206#715 +0.610451919489274#716 +0.5283288139789675#717 +0.44092682160167046#718 +0.3491192341739591#719 +0.2538233627618576#720 +0.1559913722118884#721 +0.056600767434670324#722 +-0.04335537350102874#723 +-0.14287832187608665#724 +-0.2409736772882528#725 +-0.3366613033722623#726 +-0.42898512099840763#727 +-0.5170226610991652#728 +-0.5998942816751899#729 +-0.6767719568874018#730 +-0.7468875504174615#731 +-0.8095404904319502#732 +-0.8641047694645521#733 +-0.9100351992757256#734 +-0.9468728581933795#735 +-0.9742496765065195#736 +-0.9918921140961338#737 +-0.9996238935576413#738 +-0.9973677615064493#739 +-0.9851462604682352#740 +-0.9630815036414816#741 +-0.9313939547827625#742 +-0.8904002254057634#743 +-0.8405099113036936#744 +-0.7822215000035158#745 +-0.7161173900433542#746 +-0.6428580718388172#747 +-0.5631755282810933#748 +-0.47786592100586434#749 +-0.38778163540941735#750 +-0.29382276389552614#751 +-0.19692811244968383#752 +-0.09806582040002382#753 +0.001776312910822753#754 +0.10160069789023536#755 +0.2004099222810544#756 +0.29721671697515395#757 +0.3910538204800225#758 +0.480983643475862#759 +0.5661076368981451#760 +0.645575269942848#761 +0.7185925282890948#762 +0.7844298476277866#763 +0.8424294032270155#764 +0.8920116826993384#765 +0.9326812762979901#766 +0.9640318268873135#767 +0.9857500901289341#768 +0.9976190643156452#769 +0.9995201585807343#770 +0.9914343778187229#771 +0.9734425124781813#772 +0.945724331330272#773 +0.9085567852786125#774 +0.8623112401573938#775 +0.807449766166722#776 +0.7445205210199198#777 +0.6741522729328512#778 +0.5970481181797517#779 +0.5139784559876693#780 +0.4257732909620477#781 +0.33331393995507175#782 +0.23752422623900496#783 +0.1393612489694287#784 +0.03980582016685425#785 +-0.06014733523277261#786 +-0.15949951834081932#787 +-0.2572580349851358#788 +-0.3524461143820813#789 +-0.44411266870731736#790 +-0.5313417960509607#791 +-0.6132619318067095#792 +-0.6890545570572891#793 +-0.7579623769449068#794 +-0.8192968873111423#795 +-0.8724452540029098#796 +-0.9168764361087397#797 +-0.9521464919440266#798 +-0.9779030147695946#799 +-0.9938886539233454#800 +-0.9999436861830043#801 +-0.9960076116677604#802 +-0.9821197583330767#803 +-0.9584188890187596#804 +-0.9251418149765318#805 +-0.882621029730285#806 +-0.8312813869106968#807 +-0.7716358552581907#808 +-0.7042803932088473#809 +-0.6298879942747078#810 +-0.5492019627150644#811 +-0.463028486686005#812 +-0.37222858307484785#813 +-0.277709494504018#814 +-0.18041562446265824#815 +-0.0813191011391443#816 +0.018589935762430915#817 +0.1183132281708837#818 +0.2168543739126067#819 +0.3132287824327208#820 +0.406473512498437#821 +0.4956568935895487#822 +0.5798878348422998#823 +0.6583247285347402#824 +0.7301838591528853#825 +0.7947472340170689#826 +0.8513697572274626#827 +0.8994856752490656#828 +0.9386142297340143#829 +0.9683644611000755#830 +0.9884391148695585#831 +0.998637611737797#832 +0.9988580516952584#833 +0.9890982321787556#834 +0.9694556700787299#835 +0.9401266273827171#836 +0.9014041501904432#837 +0.853675140694064#838 +0.7974164913793547#839 +0.7331903200736304#840 +0.6616383534502285#841 +0.5834755151077143#842 +0.4994827822896071#843 +0.41049938261791885#844 +0.31741440880816796#845 +0.22115793514885987#846 +0.12269172450663844#847 +0.022999618709638837#848 +-0.07692229167484431#849 +-0.17607561994804152#850 +-0.2734696588295798#851 +-0.36813127927714384#852 +-0.4591146536701932#853 +-0.5455107062068977#854 +-0.6264561960890486#855 +-0.701142342738765#856 +-0.7688229068666809#857 +-0.8288216466482501#858 +-0.880539074508522#859 +-0.9234584470038253#860 +-0.9571509279514455#861 +-0.9812798732190074#862 +-0.9956041943613665#863 +-0.9999807674966593#864 +-0.9943658633528207#865 +-0.9788155841960154#866 +-0.9534853032753393#867 +-0.9186281123846742#868 +-0.8745922930531441#869 +-0.8218178366312031#870 +-0.7608320480425032#871 +-0.6922442771274011#872 +-0.6167398302207777#873 +-0.5350731227976723#874 +-0.4480601416032317#875 +-0.35657029158288345#876 +-0.2615177090755071#877 +-0.16385212806530994#878 +-0.06454939075379683#879 +0.035398302732936385#880 +0.13499230807988533#881 +0.23323751490702455#882 +0.3291522895832851#883 +0.4217782833853109#884 +0.5101900080011152#885 +0.5935040827033676#886 +0.6708880607976146#887 +0.7415687471544812#888 +0.8048399237198179#889 +0.8600694058120545#890 +0.9067053587025647#891 +0.9442818113658633#892 +0.9724233123080624#893 +0.990848680954091#894 +0.999373817111055#895 +0.9979135404365095#896 +0.9864824415322831#897 +0.9651947361600082#898 +0.9342631240349845#899 +0.8939966636009335#900 +0.8447976840201904#901 +0.7871577652337052#902 +0.7216528262567685#903 +0.6489373707865839#904 +0.5697379476176754#905 +0.4848458912064981#906 +0.3951094149191295#907 +0.30142513596369747#908 +0.20472911668761928#909 +0.1059875117520471#910 +0.006186914633861963#911 +-0.09367550009022263#912 +-0.19260194018240928#913 +-0.2896039653540304#914 +-0.3837123634341776#915 +-0.47398683441733475#916 +-0.5595253856302109#917 +-0.6394733441443728#918 +-0.7130318963856286#919 +-0.7794660696152083#920 +-0.8381120755344199#921 +-0.888383942637911#922 +-0.9297793710472535#923 +-0.9618847513252905#924 +-0.9843792971249118#925 +-0.9970382503801957#926 +-0.9997351270147091#927 +-0.9924429807285888#928 +-0.9752346722370627#929 +-0.9482821412702676#930 +-0.9118546886083012#931 +-0.8663162853168473#932 +-0.8121219360685991#933 +-0.7498131328869748#934 +-0.6800124447368134#935 +-0.6034172970210714#936 +-0.5207930031367345#937 +-0.4329651177163347#938 +-0.3408111879589536#939 +-0.2452519854686876#940 +-0.1472423062091467#941 +-0.047761430497799545#942 +0.05219666163925169#943 +0.15163322198712595#944 +0.2495547133041842#945 +0.3449827364177279#946 +0.43696380606511737#947 +0.524578877803335#948 +0.6069525307972649#949 +0.6832617147353013#950 +0.7527439734759979#951 +0.8147050632577988#952 +0.8685258893532176#953 +0.9136686918587087#954 +0.9496824188138638#955 +0.9762072329635024#956 +0.9929781071325793#957 +0.9998274722901109#958 +0.9966868918435423#959 +0.9835877454345576#960 +0.9606609154040736#961 +0.9281354790591521#962 +0.8863364198082719#963 +0.8356813800345357#964 +0.7766764881510327#965 +0.7099113015330387#966 +0.6360529158556042#967 +0.5558392996940816#968 +0.4700719209860606#969 +0.3796077390286655#970 +0.28535064202453175#971 +0.1882424157296708#972 +0.08925333344150882#973 +-0.010627538651171125#974 +-0.11040222389064579#975 +-0.20907380660312846#976 +-0.3056563929506449#977 +-0.3991849616563692#978 +-0.4887250061782283#979 +-0.5733818719893634#980 +-0.6523096956704291#981 +-0.7247198564972739#982 +-0.7898888560785331#983 +-0.8471655473123978#984 +-0.8959776404332164#985 +-0.9358374211416584#986 +-0.9663466236847766#987 +-0.9872004101957622#988 +-0.9981904165331424#989 +-0.9992068341864074#990 +-0.9902395074463488#991 +-0.9713780348775489#992 +-0.9428108740791415#993 +-0.9048234586787762#994 +-0.8577953463741634#995 +-0.8021964265180238#996 +-0.7385822251390002#997 +-0.6675883543092127#998 +-0.5899241613185386#999 +-0.5063656411109719#1000 +-0.41774768279965196#1001 +-0.3249557277308229#1002 +-0.22891692244659007#1003 +-0.1305908549431452#1004 +-0.03095996678470595#1005 +0.06898026312784167#1006 +0.16823126505305938#1007 +0.26580135578772984#1008 +0.3607156472376163#1009 +0.45202578717704495#1010 +0.5388194348708605#1011 +0.6202293768814718#1012 +0.6954421919788581#1013 +0.7637063785766175#1014 +0.8243398634874052#1015 +0.8767368169727581#1016 +0.9203737059935807#1017 +0.9548145251792146#1018 +0.9797151532489783#1019 +0.9948267913582519#1020 +0.9999984490142912#1021 +0.9951784527233196#1022 +0.9804149622949977#1023 +0.955855489645524#1024 +0.9217454249073299#1025 +0.8784255845719909#1026 +0.8263288061645024#1027 +0.7659756234738067#1028 +0.697969065551239#1029 +0.6229886314435827#1030 +0.5417835008632049#1031 +0.4551650486320175#1032 +0.3639987376924607#1033 +0.2691954716878667#1034 +0.17170249351436012#1035 +0.07249392078295652#1036 +-0.02743898724160609#1037 +-0.1270977339757739#1038 +-0.225486562164981#1039 +-0.3216224031609835#1040 +-0.41454469941883354#1041 +-0.5033250020703501#1042 +-0.5870762476783056#1043 +-0.6649616214810665#1044 +-0.7362029185690774#1045 +-0.8000883194510767#1046 +-0.8559795023191575#1047 +-0.9033180209492709#1048 +-0.9416308845112984#1049 +-0.9705352835370772#1050 +-0.989742414826066#1051 +-0.9990603670714583#1052 +-0.9983960383745241#1053 +-0.987756066488005#1054 +-0.9672467624938841#1055 +-0.9370730485781962#1056 +-0.8975364105162827#1057 +-0.8490318853265837#1058 +-0.79204411419133#1059 +-0.7271425000820525#1060 +-0.6549755184733097#1061 +-0.5762642379901035#1062 +-0.4917951157285321#1063 +-0.40241213923645325#1064 +-0.3090083936688921#1065 +-0.21251713837638628#1066 +-0.11390248208604994#1067 +-0.014149749845864671#1068 +0.08574436201749502#1069 +0.18478174455889376#1070 +0.28197284898938413#1071 +0.37634657392060444#1072 +0.466959968288848#1073 +0.5529076530103934#1074 +0.63333086723003#1075 +0.7074260487756557#1076 +0.7744528630858774#1077 +0.8337416003882263#1078 +0.8846998672178285#1079 +0.926818505417086#1080 +0.9596766794756827#1081 +0.9829460813798933#1082 +0.9963942109577327#1083 +0.9998866989438198#1084 +0.9933886495526604#1085 +0.9769649891458017#1086 +0.9507798175090885#1087 +0.915094768221848#1088 +0.8702663945006491#1089 +0.8167426066374239#1090 +0.7550581966278896#1091 +0.6858294947067065#1092 +0.6097482111795045#1093 +0.5275745250821587#1094 +0.4401294887231446#1095 +0.3482868240002752#1096 +0.2529641924603128#1097 +0.15511402632813187#1098 +0.05571401211876051#1099 +-0.04424267808309759#1100 +-0.1437573100702344#1101 +-0.24183556653499838#1102 +-0.33749748195915497#1103 +-0.42978723410541103#1104 +-0.5177826942772574#1105 +-0.600604640924089#1106 +-0.6774255445323065#1107 +-0.7474778360266712#1108 +-0.8100615760667802#1109 +-0.864551448609587#1110 +-0.910403008860601#1111 +-0.9471581231863048#1112 +-0.9744495466339814#1113 +-0.9920045923218871#1114 +-0.9996478560364346#1115 +-0.9973029688131155#1116 +-0.9849933599899484#1117 +-0.9628420231092787#1118 +-0.9310702870069596#1119 +-0.8899956043677858#1120 +-0.840028379843203#1121 +-0.7816678694236923#1122 +-0.7154971920379454#1123 +-0.6421775032212828#1124 +-0.5624413890680937#1125 +-0.4770855464737418#1126 +-0.3869628228025392#1127 +-0.29297369451879685#1128 +-0.19605726992365072#1129 +-0.0971819058953447#1130 +0.0026644676126004055#1131 +0.1024842186409163#1132 +0.20127998123335095#1133 +0.29806462078761803#1134 +0.3918710971780397#1135 +0.48176212710082145#1136 +0.5668395490989988#1137 +0.6462532976948229#1138 +0.7192098969630194#1139 +0.7849803886799459#1140 +0.8429076158332012#1141 +0.8924127887172654#1142 +0.9330012680089154#1143 +0.9642675070398367#1144 +0.9858991038848767#1145 +0.9976799227788151#1146 +0.9994922536734847#1147 +0.9913179883576629#1148 +0.9732388013883325#1149 +0.9454353340255065#1150 +0.9081853893244696#1151 +0.8618611564194799#1152 +0.8069254917329687#1153 +0.743927294267167#1154 +0.6734960211867176#1155 +0.5963353984907568#1156 +0.5132163896153517#1157 +0.42496949222169395#1158 +0.3324764401379949#1159 +0.2366613933665366#1160 +0.13848170418241593#1161 +0.038918351586070044#1162 +-0.06103386031462717#1163 +-0.16037624205817258#1164 +-0.2581161974044102#1165 +-0.35327714102805435#1166 +-0.4449082562364434#1167 +-0.5320939952156349#1168 +-0.6139632268815225#1169 +-0.6896979409336707#1170 +-0.7585414211438385#1171 +-0.819805806214395#1172 +-0.8728789626610285#1173 +-0.9172306010481774#1174 +-0.9524175744657799#1175 +-0.9780883063067141#1176 +-0.9939863031040413#1177 +-0.9999527173289418#1178 +-0.9959279345427144#1179 +-0.9819521690445431#1180 +-0.9581650620635117#1181 +-0.9248042865096023#1182 +-0.8822031722245435#1183 +-0.8307873754602152#1184 +-0.771070625862084#1185 +-0.7036495934524011#1186 +-0.629197926900574#1187 +-0.5484595226483394#1188 +-0.46224109214241743#1189 +-0.37140410144039926#1190 +-0.27685616372666233#1191 +-0.1795419707414492#1192 +-0.08043385373327268#1193 +0.019477931753509533#1194 +0.1191951001847589#1195 +0.217721310575624#1196 +0.31407212160031445#1197 +0.40728482780441533#1198 +0.49642807863955946#1199 +0.5806111842102433#1200 +0.6589930147528394#1201 +0.730790404926155#1202 +0.7952859789406399#1203 +0.851835318340144#1204 +0.8998734008181136#1205 +0.938920245733708#1206 +0.9685857099197014#1207 +0.988573385864046#1208 +0.9986835633157533#1209 +0.9988152247237061#1210 +0.9889670545706377#1211 +0.9692374525173453#1212 +0.9398235502258059#1213 +0.9010192416847732#1214 +0.8532122467181903#1215 +0.7968802370168715#1216 +0.7325860634008656#1217 +0.6609721320001158#1218 +0.5827539855447597#1219 +0.4987131538986974#1220 +0.409689345271531#1221 +0.3165720561317042#1222 +0.22029168365181864#1223 +0.12181022948763352#1224 +0.022111687775516597#1225 +-0.07780778661170105#1226 +-0.17694983131492942#1227 +-0.27432385179549673#1228 +-0.36895691902833305#1229 +-0.45990349068718106#1230 +-0.5462548586909652#1231 +-0.6271482287145591#1232 +-0.70177534094448#1233 +-0.7693905459437702#1234 +-0.8293182549346921#1235 +-0.8809596900584754#1236 +-0.9237988671657521#1237 +-0.9574077513596155#1238 +-0.9814505337788207#1239 +-0.9956869868889223#1240 +-0.9999748647563899#1241 +-0.9942713243229557#1242 +-0.9786333534792907#1243 +-0.953217201660839#1244 +-0.9182768186551078#1245 +-0.8741613172193351#1246 +-0.8213114848612212#1247 +-0.7602553796358565#1248 +-0.6916030539641876#1249 +-0.616040459190884#1250 +-0.5343225917852477#1251 +-0.4472659496660599#1252 +-0.35574037402427566#1253 +-0.2606603581573747#1254 +-0.1629759101546243#1255 +-0.06366306073028243#1256 +0.03628588895266636#1257 +0.1358722820277203#1258 +0.2341010841741585#1259 +0.3299908256710588#1260 +0.42258340791831867#1261 +0.510953676441162#1262 +0.5942186647278355#1263 +0.6715464165391247#1264 +0.7421642985400877#1265 +0.805366720196939#1266 +0.8605221838044265#1267 +0.9070795942021564#1268 +0.9445737651352686#1269 +0.9726300672417244#1270 +0.9909681712250566#1271 +0.9994048488120352#1272 +0.9978558035090056#1273 +0.9863365128645896#1274 +0.9649620738231349#1275 +0.9339460527140935#1276 +0.8935983513678535#1277 +0.8443221106790897#1278 +0.7866096825562044#1279 +0.7210377105038093#1280 +0.6482613679914285#1281 +0.5690078121768063#1282 +0.48406891839188965#1283 +0.3942933679863122#1284 +0.3005781685838748#1285 +0.20385969147888025#1286 +0.10510431572368364#1287 +0.005298772388643717#1288 +-0.09455971452856225#1289 +-0.1934733920354848#1290 +-0.2904539473629896#1291 +-0.38453238285975366#1292 +-0.4747686978964897#1293 +-0.5602612810415106#1294 +-0.6401559186641225#1295 +-0.7136543299548559#1296 +-0.7800221430834386#1297 +-0.8385962327993722#1298 +-0.8887913461602351#1299 +-0.9301059501856239#1300 +-0.9621272430089094#1301 +-0.9845352784570335#1302 +-0.9971061628469102#1303 +-0.9997142920570979#1304 +-0.9923336065226613#1305 +-0.9750378516137301#1306 +-0.9479998407961381#1307 +-0.9114897289363962#1308 +-0.8658723130035688#1309 +-0.8116033871385435#1310 +-0.749225188509642#1311 +-0.6793609794580729#1312 +-0.6027088200666426#1313 +-0.5200345933741548#1314 +-0.43216435292525485#1315 +-0.3399760691164684#1316 +-0.24439085680621755#1317 +-0.14636377183963595#1318 +-0.04687426844626337#1319 +0.05308358714285112#1320 +0.15251104907633528#1321 +0.25041467102089904#1322 +0.3458162323487072#1323 +0.43776251219453577#1324 +0.5253348137235645#1325 +0.6076581434464698#1326 +0.6839099538651353#1327 +0.753328362095355#1328 +0.8152197623487677#1329 +0.8689657562126182#1330 +0.9140293314822825#1331 +0.9499602278097037#1332 +0.9763994355559533#1333 +0.9930827828968712#1334 +0.9998435753406082#1335 +0.9966142612838874#1336 +0.9834271069652942#1337 +0.9604138740716864#1338 +0.9278045032189735#1339 +0.8859248164614908#1340 +0.8351932617857352#1341 +0.7761167321164042#1342 +0.7092855006098496#1343 +0.6353673228398167#1344 +0.5551007648044826#1345 +0.4692878234191396#1346 +0.37878591322812316#1347 +0.28449929940210755#1348 +0.1873700626194315#1349 +0.08836868610737038#1350 +-0.011515641105471527#1351 +-0.11128490783895244#1352 +-0.2099422525592064#1353 +-0.306501923689571#1354 +-0.3999991289144955#1355 +-0.4894996750654395#1356 +-0.57410930227021#1357 +-0.6529826191020013#1358 +-0.7253315494510825#1359 +-0.7904332067207822#1360 +-0.8476371166714086#1361 +-0.8963717167438338#1362 +-0.9361500669236509#1363 +-0.9665747150848376#1364 +-0.9873416682000192#1365 +-0.9982434297383107#1366 +-0.9991710729020646#1367 +-0.9901153289874269#1368 +-0.9711666799941617#1369 +-0.9425144545594192#1370 +-0.9044449362485765#1371 +-0.857338503104486#1372 +-0.8016658270358069#1373 +-0.7379831710188773#1374 +-0.6669268311019312#1375 +-0.5892067787453151#1376 +-0.5055995670213429#1377 +-0.41694057155269054#1378 +-0.3241156437153132#1379 +-0.2280522595043203#1380 +-0.12971025250041487#1381 +-0.03007222353003439#1382 +0.06986627715730269#1383 +0.1691066970980048#1384 +0.2666574588205459#1385 +0.36154386735978916#1386 +0.45281784908688705#1387 +0.5395674245475895#1388 +0.6209258206594904#1389 +0.6960801312221501#1390 +0.7642794392071385#1391 +0.8248423196727637#1392 +0.8771636483368399#1393 +0.9207206477784876#1394 +0.9550781108573155#1395 +0.9798927491593076#1396 +0.9949166230211789#1397 +0.9999996188615343#1398 +0.9950909490661519#1399 +0.9802396594410367#1400 +0.9555941391629392#1401 +0.9214006381237522#1402 +0.8780008064829905#1403 +0.8258282810123316#1404 +0.7654043523403342#1405 +0.6973327563887936#1406 +0.6222936420429795#1407 +0.5410367753288019#1408 +0.4543740479985197#1409 +0.36317136537672806#1410 +0.2683399945205851#1411 +0.1708274591406018#1412 +0.07160807225693622#1413 +-0.028326798814238986#1414 +-0.1279786378752571#1415 +-0.226351756690739#1416 +-0.32246324357531025#1417 +-0.415352784322254#1418 +-0.5040922573456266#1419 +-0.5877950071643484#1420 +-0.6656247035706814#1421 +-0.7368036979652107#1422 +-0.8006207933645937#1423 +-0.8564383504467267#1424 +-0.9036986586320765#1425 +-0.941929508543436#1426 +-0.9707489101659298#1427 +-0.9898689095649735#1428 +-0.9990984660268033#1429 +-0.9983453608741385#1430 +-0.987617118884721#1431 +-0.9670209331062237#1432 +-0.9367625938187515#1433 +-0.8971444323463876#1434 +-0.848562300262541#1435 +-0.7915016141718754#1436 +-0.7265325055880536#1437 +-0.6543041243681132#1438 +-0.5755381526216753#1439 +-0.4910215939018614#1440 +-0.40159890972594#1441 +-0.30816358199498733#1442 +-0.211649185618078#1443 +-0.1130200605403922#1444 +-0.013261676377251867#1445 +0.08662921407252257#1446 +0.1856545340510954#1447 +0.2828248552946596#1448 +0.37716928407358735#1449 +0.46774516204164157#1450 +0.5536474849665701#1451 +0.6340179452332398#1452 +0.7080535077696084#1453 +0.7750144337077156#1454 +0.8342316716099273#1455 +0.8851135424097409#1456 +0.9271516512734351#1457 +0.9599259673131953#1458 +0.9831090203969006#1459 +0.9964691731214373#1460 +0.999872935257061#1461 +0.9932862975376467#1462 +0.9767750714700341#1463 +0.9505042317672048#1464 +0.9147362679754845#1465 +0.8698285617657631#1466 +0.816229816093974#1467 +0.7544755719094801#1468 +0.6851828572069336#1469 +0.609044021886516#1470 +0.5268198200225961#1471 +0.43933180866049054#1472 +0.34745413909003786#1473 +0.2521048226148663#1474 +0.15423655808690193#1475 +0.05482721285436108#1476 +-0.04512994776555348#1477 +-0.14463618486537203#1478 +-0.24269726501638209#1479 +-0.3383333943203984#1480 +-0.4305890081865166#1481 +-0.518542319016638#1482 +-0.6013145264024473#1483 +-0.6780785978085996#1484 +-0.748067532008433#1485 +-0.8105820227066887#1486 +-0.8649974457768529#1487 +-0.910770100298956#1488 +-0.9474426410394413#1489 +-0.97464864809356#1490 +-0.9921162880319376#1491 +-0.9996710299703442#1492 +-0.9972373894245966#1493 +-0.9848396825265736#1494 +-0.962601783065463#1495 +-0.930745884781808#1496 +-0.8895902812810987#1497 +-0.8395461857492807#1498 +-0.7811136222465291#1499 +-0.7148764296321258#1500 +-0.6414964280395691#1501 +-0.5617068061885679#1502 +-0.4763047956057153#1503 +-0.38614370495060346#1504 +-0.292124394037764#1505 +-0.19518627274318584#1506 +-0.09629791473136161#1507 +0.0035526202125998304#1508 +0.10336765854973708#1509 +0.20214988141145054#1510 +0.2989122894799676#1511 +0.3926880647592674#1512 +0.4825402307009092#1513 +0.5675710141639814#1514 +0.6469308156675612#1515 +0.7198266983078874#1516 +0.7855303105217932#1517 +0.843385163534764#1518 +0.892813190779766#1519 +0.9333205237473006#1520 +0.9645024265563005#1521 +0.9860473399412646#1522 +0.9977399942494521#1523 +0.9994635603440932#1524 +0.9912008169225054#1525 +0.9730343225856573#1526 +0.9451455909399181#1527 +0.9078132769731031#1528 +0.8614103928259461#1529 +0.8064005807780915#1530 +0.7433334806876951#1531 +0.6728392381716484#1532 +0.5956222083988731#1533 +0.5124539184063026#1534 +0.42416535825576057#1535 +0.3316386780559536#1536 +0.23579837381018381#1537 +0.13760205015788235#1538 +0.03803085230559389#1539 +-0.06192033725160367#1540 +-0.16125283926712547#1541 +-0.2589741562157922#1542 +-0.35410788900102547#1543 +-0.44570349281186655#1544 +-0.5328457746525187#1545 +-0.6146640376482388#1546 +-0.6903407807606958#1547 +-0.7591198669881154#1548 +-0.8203140784362564#1549 +-0.8733119827724461#1550 +-0.9175840424553352#1551 +-0.9526879056989698#1552 +-0.9782728263056137#1553 +-0.9940831682058159#1554 +-0.9999609596895136#1555 +-0.9958474718071414#1556 +-0.9817838051698816#1557 +-0.9579104792859435#1558 +-0.9244660285360871#1559 +-0.8817846188169394#1560 +-0.8302927086658156#1561 +-0.7705047882279834#1562 +-0.7030182386411985#1563 +-0.6285073632008448#1564 +-0.5477166499443011#1565 +-0.4614533329725679#1566 +-0.370579326833965#1567 +-0.2760026145588766#1568 +-0.17866817539345065#1569 +-0.07954854287934016#1570 +0.02036591237996836#1571 +0.12007687817485184#1572 +0.21858807549515108#1573 +0.3149152130207141#1574 +0.408095821834904#1575 +0.49719887209586344#1576 +0.5813340755789376#1577 +0.6596607811423243#1578 +0.7313963742354008#1579 +0.7958240965246156#1580 +0.8523002075058284#1581 +0.9002604165466352#1582 +0.9392255210918378#1583 +0.9688061946969714#1584 +0.9887068770494445#1585 +0.998728727109482#1586 +0.9987716098640673#1587 +0.9888350968428918#1588 +0.9690184703994884#1589 +0.9395197317147739#1590 +0.9006336224346992#1591 +0.8527486797091522#1592 +0.7963433540571888#1593 +0.7319812288476018#1594 +0.6603053891601949#1595 +0.5820319962922426#1596 +0.49794313211153723#1597 +0.4088789847528897#1598 +0.31572945373601446#1599 +0.21942525838369095#1600 +0.12092863838194484#1601 +0.021223739399179705#1602 +-0.07869322017202651#1603 +-0.1778239030997941#1604 +-0.275177828368556#1605 +-0.369782267737956#1606 +-0.46069196492188513#1607 +-0.5469985802768322#1608 +-0.6278397666313447#1609 +-0.7024077855739117#1610 +-0.7699575781081692#1611 +-0.8298142090361076#1612 +-0.8813796106874666#1613 +-0.9241385586142015#1614 +-0.9576638195428573#1615 +-0.9816204201482128#1616 +-0.9957689939960187#1617 +-0.9999681732132844#1618 +-0.994176000989335#1619 +-0.9784503507943949#1620 +-0.9529483481270028#1621 +-0.91792480056797#1622 +-0.8737296518272608#1623 +-0.8208044852221184#1624 +-0.7596781115225273#1625 +-0.6909612852488008#1626 +-0.6153406022143034#1627 +-0.5335716392870412#1628 +-0.4464714049153578#1629 +-0.35491017584958534#1630 +-0.2598028016244306#1631 +-0.16209956368483314#1632 +-0.06277668048788872#1633 +0.03717344654927897#1634 +0.13675214879643402#1635 +0.2349644687770656#1636 +0.33082910145460404#1637 +0.42338819910797#1638 +0.511716941829381#1639 +0.5949327780191651#1640 +0.6722042425496124#1641 +0.742759264489685#1642 +0.8058928813825473#1643 +0.8609742829974049#1644 +0.9074531141768128#1645 +0.9448649738034854#1646 +0.9728360549427497#1647 +0.9910868797978722#1648 +0.9994350921598212#1649 +0.9977972794502282#1650 +0.9861898061522987#1651 +0.9647286503023048#1652 +0.9336282446753849#1653 +0.8931993342441357#1654 +0.8438458713175653#1655 +0.78606097938315#1656 +0.7204220259799529#1657 +0.6475848538330251#1658 +0.5682772278896909#1659 +0.48329156373276017#1660 +0.3934770100259633#1661 +0.2997309641011942#1662 +0.20299010546101035#1663 +0.10422103678666807#1664 +0.0044106259636479345#1665 +-0.09544385437604187#1666 +-0.19434469127235002#1667 +-0.29130370025527885#1668 +-0.3853520989574582#1669 +-0.47555018686732337#1670 +-0.5609967345060028#1671 +-0.6408379882143644#1672 +-0.714276200577364#1673 +-0.7805776012525157#1674 +-0.8390797285606031#1675 +-0.8891980485837959#1676 +-0.9304317956353363#1677 +-0.9623689757447501#1678 +-0.9846904831654123#1679 +-0.9971732887736847#1680 +-0.9996926685021967#1681 +-0.9922234495414972#1682 +-0.9748402618584459#1683 +-0.9477167925182539#1684 +-0.9111240502607415#1685 +-0.8654276576705933#1686 +-0.8110841979973509#1687 +-0.7486366531265098#1688 +-0.6787089782840057#1689 +-0.6019998676818485#1690 +-0.5192757733965142#1691 +-0.43136324723315195#1692 +-0.3391406820931682#1693 +-0.24352953536271474#1694 +-0.14548512201507893#1695 +-0.04598706941925612#1696 +0.053970470772899765#1697 +0.1533887558613586#1698 +0.25127443120483245#1699 +0.34664945549199166#1700 +0.4385608730069506#1701 +0.5260903352477752#1702 +0.6083632767611484#1703 +0.6845576535112877#1704 +0.753912156472218#1705 +0.8157338183759043#1706 +0.8694049376121297#1707 +0.9143892500987989#1708 +0.9502372874553822#1709 +0.976590867942398#1710 +0.9931866752949559#1711 +0.9998588896918333#1712 +0.996540844572318#1713 +0.9832656927464438#1714 +0.9601660751430733#1715 +0.9274727955055809#1716 +0.8855125142771431#1717 +0.8347044847175692#1718 +0.775556363863317#1719 +0.7086591401861929#1720 +0.6346812286318964#1721 +0.5543617920388316#1722 +0.4685033556673605#1723 +0.3779637886326811#1724 +0.2836477323602019#1725 +0.18649756170745438#1726 +0.08748396906602378#1727 +-0.012403734475959032#1728 +-0.11216750400318758#1729 +-0.21081053290806362#1730 +-0.30734721265281995#1731 +-0.40081298064423065#1732 +-0.49027395782420097#1733 +-0.5748362796806161#1734 +-0.6536550274460741#1735 +-0.7259426702469165#1736 +-0.7909769338513953#1737 +-0.848108017395044#1738 +-0.8967650859761198#1739 +-0.9364619742492493#1740 +-0.966802044028854#1741 +-0.9874821473667896#1742 +-0.9982956555064372#1743 +-0.9991345234489356#1744 +-0.9899903695030964#1745 +-0.970954559032491#1746 +-0.9422172915629393#1747 +-0.9040657003717193#1748 +-0.8568809835467744#1749 +-0.8011345951814257#1750 +-0.7373835347609137#1751 +-0.666264781807661#1752 +-0.5884889313924426#1753 +-0.5048330941033291#1754 +-0.41613313141356995#1755 +-0.32327530403005167#1756 +-0.2271874166692736#1757 +-0.12882954773931182#1758 +-0.029184456553725577#1759 +0.07075223607464673#1760 +0.16998199574774103#1761 +0.26751335150790145#1762 +0.3623718022879524#1763 +0.45360955380373474#1764 +0.5403149886012937#1765 +0.6216217746371384#1766 +0.6967175213816488#1767 +0.7648518969567075#1768 +0.8253441252037987#1769 +0.8775897877743497#1770 +0.9210668632780755#1771 +0.9553409431481533#1772 +0.9800695721080259#1773 +0.9950056698713242#1774 +0.9999999998864149#1775 +0.995002660458693#1776 +0.9800635833518199#1777 +0.9553320348860447#1778 +0.9210551245184746#1779 +0.8775753358070623#1780 +0.8253271044281046#1781 +0.7648324774385712#1782 +0.6966958971544762#1783 +0.6215981617630594#1784 +0.5402896230123407#1785 +0.4535826889444879#1786 +0.36234370658320497#1787 +0.26748430568064796#1788 +0.1699522900142862#1789 +0.0707221672448598#1790 +-0.029214588042036787#1791 +-0.12885944082227627#1792 +-0.22721677266508764#1793 +-0.32330382962330895#1794 +-0.4161605415859719#1795 +-0.5048591149814937#1796 +-0.5885133029843566#1797 +-0.6662872606004342#1798 +-0.7374038961538791#1799 +-0.8011526357302755#1800 +-0.8568965229963079#1801 +-0.9040785834568934#1802 +-0.9422273895602254#1803 +-0.9709617710460383#1804 +-0.9899946234728495#1805 +-0.9991357768706348#1806 +-0.9982938958563073#1807 +-0.9874773922266732#1808 +-0.966794340910539#1809 +-0.9364514001197477#1810 +-0.8967517464886383#1811 +-0.848092045833332#1812 +-0.790958489798018#1813 +-0.7259219379887585#1814 +-0.6536322141330055#1815 +-0.5748116132557202#1816 +-0.49024768474624164#1817 +-0.4007853634251181#1818 +-0.30731852723467856#1819 +-0.21078106590610934#1820 +-0.11213754984196339#1821 +-0.012373592447542393#1822 +0.08751399779244858#1823 +0.18652717709477906#1824 +0.2836766385012659#1825 +0.37799169670687793#1826 +0.46852998682643804#1827 +0.5543868801930514#1828 +0.6347045231087146#1829 +0.7086804082348972#1830 +0.7755753929805951#1831 +0.8347210847707713#1832 +0.8855265194040249#1833 +0.9274840657715442#1834 +0.960174497939346#1835 +0.9832711839152297#1836 +0.9965433492476736#1837 +0.9998583828478708#1838 +0.9931831619958922#1839 +0.9765843832919562#1840 +0.9502278962460459#1841 +0.9143770461644276#1842 +0.8693900428904013#1843 +0.8157163816899551#1844 +0.7538923520436499#1845 +0.6845356792194047#1846 +0.6083393521658111#1847 +0.5260646993956318#1848 +0.43853378204296184#1849 +0.3466211801001145#1850 +0.25124525390343583#1851 +0.1533589681803935#1852 +0.05394037034102778#1853 +-0.04601718184846958#1854 +-0.145514945568195#1855 +-0.24355877205264906#1856 +-0.3391690397965793#1857 +-0.4313904426092411#1858 +-0.5193015347180733#1859 +-0.6020239375502678#1860 +-0.6787311162011166#1861 +-0.7486566378975624#1862 +-0.8111018299411188#1863 +-0.8654427606145227#1864 +-0.9111364733012091#1865 +-0.9477264115283458#1866 +-0.9748469807281929#1867 +-0.9922272011381739#1868 +-0.9996934153410891#1869 +-0.9971710233926254#1870 +-0.98468522819934#1871 +-0.9623607836995489#1872 +-0.9304207483632138#1873 +-0.8891842564654433#1874 +-0.8390633294023081#1875 +-0.7805587589092471#1876 +-0.7142551033155872#1877 +-0.6408148468309454#1878 +-0.5609717802219951#1879 +-0.47552366901768395#1880 +-0.3853242824997751#1881 +-0.29127486312240225#1882 +-0.1943151215953794#1883 +-0.09541384760541509#1884 +0.0044407700101977065#1885 +0.10425101691979201#1886 +0.20301962212912839#1887 +0.2997597223835154#1888 +0.39350472257923697#1889 +0.48331795366231456#1890 +0.5683020315160732#1891 +0.6476078233265996#1892 +0.7204429318371323#1893 +0.7860796127195201#1894 +0.8438620459549884#1895 +0.8932128885709812#1896 +0.9336390432612995#1897 +0.9647365852513876#1898 +0.9861947981811614#1899 +0.9977992786801687#1900 +0.9994340786151946#1901 +0.9910828636056814#1902 +0.9728290762314595#1903 +0.9448551023020723#1904 +0.9074404485180556#1905 +0.8609589497323792#1906 +0.8058750337161689#1907 +0.7427390807499367#1908 +0.6721819244057498#1909 +0.5949085484667043#1910 +0.5116910429620009#1911 +0.42336088969859254#1912 +0.3308006543698204#1913 +0.23493516825074362#1914 +0.1367222875897472#1915 +0.03714332302553373#1916 +-0.06280676534440063#1917 +-0.16212930927617017#1918 +-0.25983191074247675#1919 +-0.35493835764565523#1920 +-0.4464983778062608#1921 +-0.5335971337685674#1922 +-0.6153643635540208#1923 +-0.6909830760312574#1924 +-0.7596977140214278#1925 +-0.8208217035757736#1926 +-0.8737443139955725#1927 +-0.9179367600513992#1928 +-0.9529574854303443#1929 +-0.9784565746207341#1930 +-0.9941792491522566#1931 +-0.999968413258218#1932 +-0.9957662235245147#1933 +-0.9816146668419067#1934 +-0.9576551408868839#1935 +-0.9241270413228225#1936 +-0.8813653698376505#1937 +-0.8297973869177182#1938 +-0.7699383428022528#1939 +-0.7023863292732867#1940 +-0.6278163037202744#1941 +-0.5469733451889683#1942 +-0.4606652097978839#1943 +-0.36975425990617233#1944 +-0.27514884767398695#1945 +-0.1777942391079601#1946 +-0.07866316927572836#1947 +0.021253876941319732#1948 +0.12095856144556782#1949 +0.21945466798743643#1950 +0.3157580560288433#1951 +0.40890649395014667#1952 +0.49796927335041663#1953 +0.5820565083781266#1954 +0.6603280271764242#1955 +0.7320017666026011#1956 +0.7963615863444993#1957 +0.8527644243577857#1958 +0.9006467221293312#1959 +0.9395300555675856#1960 +0.9690259152579552#1961 +0.9888395883204489#1962 +0.9987731030833557#1963 +0.998727207150748#1964 +0.9887023590996133#1965 +0.9687987238979044#1966 +0.9392151720892897#1967 +0.9002472927444187#1968 +0.8522844400326369#1969 +0.7958058429238293#1970 +0.731375816890965#1971 +0.6596381254564291#1972 +0.5813095479197075#1973 +0.49717271753556175#1974 +0.40806830170125163#1975 +0.3148866022857897#1976 +0.2185586600279604#1977 +0.12004695188501968#1978 +0.020335774281090385#1979 +-0.07957859165734228#1980 +-0.17869783461311994#1981 +-0.27603158787509413#1982 +-0.37060732475493247#1983 +-0.46148007575231387#1984 +-0.5477418703778104#1985 +-0.6285308092938824#1986 +-0.7030396761281532#1987 +-0.7705240029125717#1988 +-0.8303095085612607#1989 +-0.8817988360642393#1990 +-0.9244775210812064#1991 +-0.9579191322991703#1992 +-0.9817895321931679#1993 +-0.9958502156179638#1994 +-0.9999606928726213#1995 +-0.9940798934271551#1996 +-0.9782665762856902#1997 +-0.9526787428859168#1998 +-0.9175720584009519#1999 +-0.8732972972174424#2000 +-0.8202968381138436#2001 +-0.759100244157896#2002 +-0.6903189714875031#2003 +-0.6146402598431213#2004 +-0.5328202658954452#2005 +-0.44567650797790564#2006 +-0.3540796977137181#2007 +-0.25894504015316233#2008 +-0.16122308934724644#2009 +-0.0618902507258409#2010 +0.03806097482262027#2011 +0.13763190769193942#2012 +0.23582766803466082#2013 +0.33166711627264295#2014 +0.4241926563194016#2015 +0.5124798035636668#2016 +0.5956464220140248#2017 +0.6728615383101487#2018 +0.7433536445339318#2019 +0.8064184068615782#2020 +0.8614257030343491#2021 +0.9078259183318809#2022 +0.9451554371407925#2023 +0.9730412752486439#2024 +0.9912048065788938#2025 +0.9994645471305557#2026 +0.9977379683063444#2027 +0.9860423215111407#2028 +0.9644944657816552#2029 +0.9333097001695628#2030 +0.8927996125445465#2031 +0.8433689663113009#2032 +0.7855116561473892#2033 +0.7198057731708851#2034 +0.6469078288450127#2035 +0.5675461953326192#2036 +0.48251382784229224#2037 +0.3926603416820314#2038 +0.29888352318393624#2039 +0.20212035931994485#2040 +0.1033376756377367#2041 +0.003522476059450432#2042 +-0.09632791893524607#2043 +-0.19521583720571825#2044 +-0.29215322336060745#2045 +-0.38617151108069375#2046 +-0.47633130071339214#2047 +-0.5617317454435568#2048 +-0.6415195522570779#2049 +-0.7148975077626172#2050 +-0.7811324436842906#2051 +-0.8395625624367278#2052 +-0.8896040495877838#2053 +-0.9307569071393618#2054 +-0.9626099493421324#2055 +-0.9848449111276216#2056 +-0.9972396281075698#2057 +-0.9996702563670624#2058 +-0.9921125098719891#2059 +-0.97464190312707#2060 +-0.9474329966598855#2061 +-0.9107576528697868#2062 +-0.864982319668668#2063 +-0.8105643690545611#2064 +-0.7480475272018192#2065 +-0.6780564417289145#2066 +-0.6012904404259155#2067 +-0.5185165438023752#2068 +-0.4305618012719435#2069 +-0.338305027548012#2070 +-0.24266802181759525#2071 +-0.1446063574285605#2072 +-0.04509983411660629#2073 +0.054857311829817915#2074 +0.15426634164985503#2075 +0.2521339931777998#2076 +0.34748240519032947#2077 +0.43935888787260957#2078 +0.5268454417800063#2079 +0.6090679301850868#2080 +0.6852048131628486#2081 +0.7544953561460856#2082 +0.8162472309337178#2083 +0.8698434332053228#2084 +0.9147484474243518#2085 +0.9505135975323528#2086 +0.9767815299718332#2087 +0.993289784244882#2088 +0.9998734153317062#2089 +0.9964666417667453#2090 +0.9831035029053308#2091 +0.9599175188136995#2092 +0.9271403561806276#2093 +0.8850995135804554#2094 +0.8342150492155885#2095 +0.7749953838337934#2096 +0.7080322207561459#2097 +0.633994633773039#2098 +0.5536223819800354#2099 +0.4677185183495166#2100 +0.37714136589083663#2101 +0.2827959415705366#2102 +0.18562491368197392#2103 +0.08659918301533971#2104 +-0.013291818062099673#2105 +-0.11305001168715346#2106 +-0.21167864696479485#2107 +-0.308192259173622#2108 +-0.4016265162036029#2109 +-0.49104785384375327#2110 +-0.5755628036471371#2111 +-0.6543269201722473#2112 +-0.7265532184027201#2113 +-0.7915200370414769#2114 +-0.8485782491118543#2115 +-0.8971577478197825#2116 +-0.9367731428724192#2117 +-0.967028610337507#2118 +-0.9876218475852627#2119 +-0.9983470937963259#2120 +-0.9990971858558507#2121 +-0.9898646290919259#2122 +-0.970741672159859#2123 +-0.9419193853241061#2124 +-0.9036857513473486#2125 +-0.8564227880619233#2126 +-0.8006027313739196#2127 +-0.7367833168381062#2128 +-0.6656022069486314#2129 +-0.5877706198261639#2130 +-0.5040662229615295#2131 +-0.4153253630192043#2132 +-0.32243470933790386#2133 +-0.22632239462364384#2134 +-0.1279487413545418#2135 +-0.02829666655605602#2136 +0.07163813918102345#2137 +0.17085716031182666#2138 +0.26836903317466254#2139 +0.3631994513690255#2140 +0.45440090070308614#2141 +0.5410621264422896#2142 +0.6223172382654424#2143 +0.6973543619545768#2144 +0.7654237513737662#2145 +0.8258452796846828#2146 +0.8780152349491458#2147 +0.9214123522192472#2148 +0.9556030218444039#2149 +0.9802456219556541#2150 +0.995093931838447#2151 +0.9999995920886321#2152 +0.9949135869705855#2153 +0.9798867341662368#2154 +0.9550691770215901#2155 +0.9207088843640407#2156 +0.8771491728798204#2157 +0.824825276807153#2158 +0.7642599992196163#2159 +0.6960584883506457#2160 +0.6209021911524217#2161 +0.5395420445031802#2162 +0.4527909720941514#2163 +0.3615157619647542#2164 +0.2666284058430285#2165 +0.16907698682575256#2166 +0.06983620644553508#2167 +-0.030102354224705447#2168 +-0.1297401421220481#2169 +-0.22808160940569652#2170 +-0.3241441606419197#2171 +-0.41696797057282303#2172 +-0.5056255743730492#2173 +-0.5892311345717338#2174 +-0.6669492920476956#2175 +-0.7380035126616422#2176 +-0.8016838461286017#2177 +-0.8573540196064917#2178 +-0.9044577951240342#2179 +-0.9425245273266959#2180 +-0.9711738660094966#2181 +-0.99011955645053#2182 +-0.9991722995735218#2183 +-0.9982416433616265#2184 +-0.9873368866240788#2185 +-0.9665669860855675#2186 +-0.9361394677266565#2187 +-0.896358353252788#2188 +-0.8476211224098967#2189 +-0.7904147414981781#2190 +-0.7253107977657867#2191 +-0.6529597882979938#2192 +-0.5740846204653375#2193 +-0.48947338887213754#2194 +-0.3999715009757185#2195 +-0.3064732300545506#2196 +-0.2099127799252589#2197 +-0.11125495068689388#2198 +-0.01148549875726277#2199 +0.08839871247934979#2200 +0.18739967300159813#2201 +0.28452819793731066#2202 +0.3788138111717516#2203 +0.4693144420241625#2204 +0.5551258381065974#2205 +0.6353906003148769#2206 +0.709306749677019#2207 +0.7761357404620275#2208 +0.8352098394847063#2209 +0.8859387978749215#2210 +0.9278157486492024#2211 +0.9604222711580919#2212 +0.9834325718069646#2213 +0.9966167392779312#2214 +0.999843041727728#2215 +0.9930792430087505#2216 +0.9763929247619841#2217 +0.9499508111635871#2218 +0.9140171030720339#2219 +0.8689508382204706#2220 +0.8152023038303673#2221 +0.7533085374904468#2222 +0.6838879612546183#2223 +0.6076342025732385#2224 +0.5253091637969104#2225 +0.43773540950004725#2226 +0.34578794768754867#2227 +0.2503854870040551#2228 +0.15248125730085532#2229 +0.05305348527830788#2230 +-0.046904379631989734#2231 +-0.1463935914855311#2232 +-0.24442008696423675#2233 +-0.3400044177285349#2234 +-0.43219153674140776#2235 +-0.5200603407826888#2236 +-0.6027328738079627#2237 +-0.6793830991951469#2238 +-0.7492451532293688#2239 +-0.8116209973600432#2240 +-0.8658873927713292#2241 +-0.9115021275783626#2242 +-0.9480094344291783#2243 +-0.9750445443814343#2244 +-0.9923373315531067#2245 +-0.9997150121310117#2246 +-0.9971038707695518#2247 +-0.9845299971300822#2248 +-0.9621190252016383#2249 +-0.9300948780076469#2250 +-0.8887775302410945#2251 +-0.8385798111831653#2252 +-0.780003279849526#2253 +-0.7136332135784356#2254 +-0.6401327601330475#2255 +-0.5602363117481685#2256 +-0.4747421673258055#2257 +-0.3845045560964199#2258 +-0.2904251024428272#2259 +-0.19344381716740122#2260 +-0.09452970521486317#2261 +0.005328916304815557#2262 +0.10513429305428211#2263 +0.2038892027003272#2264 +0.3006069188298006#2265 +0.3943210699937632#2266 +0.4840952953715645#2267 +0.569032600578643#2268 +0.64828432013791#2269 +0.7210585970646649#2270 +0.7866282948398333#2271 +0.8443382627177061#2272 +0.8936118817756269#2273 +0.9339568262996617#2274 +0.9649699829403923#2275 +0.9863414784882509#2276 +0.9978577760242008#2277 +0.9994038085100443#2278 +0.9909641285002334#2279 +0.9726230624876389#2280 +0.9445638683411081#2281 +0.9070669042534167#2282 +0.8605068274948806#2283 +0.8053488509617559#2284 +0.7421440949227586#2285 +0.6715240804075154#2286 +0.5941944192571897#2287 +0.5109277638842079#2288 +0.42255608718475995#2289 +0.3299623697406341#2290 +0.23407177736911827#2291 +0.135842417171973#2292 +0.03625576444597857#2293 +-0.0636931438937977#2294 +-0.16300565139394135#2295 +-0.26068946030786144#2296 +-0.3557685463068644#2297 +-0.44729291059261544#2298 +-0.534348071971104#2299 +-0.6160642040464467#2300 +-0.6916248262387088#2301 +-0.7602749617879662#2302 +-0.8213286812325286#2303 +-0.8741759559893822#2304 +-0.9182887535581434#2305 +-0.9532263134472572#2306 +-0.9786395511071335#2307 +-0.9942745458675742#2308 +-0.9999750780291754#2309 +-0.9956841897589236#2310 +-0.9814447541940361#2311 +-0.9573990470677456#2312 +-0.9237873251372041#2313 +-0.8809454256173832#2314 +-0.8293014106066361#2315 +-0.7693712900317081#2316 +-0.7017538658471201#2317 +-0.6271247490039753#2318 +-0.5462296089686645#2319 +-0.45987672324004214#2320 +-0.3689289013078397#2321 +-0.2742948637454506#2322 +-0.17692016257434437#2323 +-0.07777773362082588#2324 +0.022141824737131294#2325 +0.12184014930142922#2326 +0.2203210873689061#2327 +0.3166006499598618#2328 +0.4097168435106797#2329 +0.4987392817955214#2330 +0.5827784820379505#2331 +0.6609947523288111#2332 +0.7326065815502183#2333 +0.7968984479763155#2334 +0.8532279685298386#2335 +0.901032317261481#2336 +0.9398338489207327#2337 +0.9692448714293358#2338 +0.9889715195723757#2339 +0.9988166912023702#2340 +0.9986820166187732#2341 +0.9885688414455068#2342 +0.968578213185931#2343 +0.9389098715895922#2344 +0.8998602529186711#2345 +0.8518195280548398#2346 +0.7952677040407854#2347 +0.7307698280085084#2348 +0.6589703414151602#2349 +0.5805866409970266#2350 +0.49640191077847906#2351 +0.407257296756089#2352 +0.31404350244587664#2353 +0.21769188926820557#2354 +0.1191651706923382#2355 +0.019447793121681437#2356 +-0.08046390036926145#2357 +-0.17957162516554395#2358 +-0.27688512964165996#2359 +-0.37143208942845196#2360 +-0.4622678225568002#2361 +-0.5484847284075876#2362 +-0.6292213561570731#2363 +-0.7036710121087649#2364 +-0.7710898199101781#2365 +-0.8308041531194559#2366 +-0.8822173658581055#2367 +-0.9248157542993909#2368 +-0.9581736894271621#2369 +-0.9819578697802895#2370 +-0.9959306516906896#2371 +-0.9999524237403014#2372 +-0.993983001712226#2373 +-0.9780820300981395#2374 +-0.9524083861502476#2375 +-0.917218592432299#2376 +-0.8728642537309242#2377 +-0.8197885439368322#2378 +-0.7585217779977886#2379 +-0.6896761131869558#2380 +-0.6139394326297725#2381 +-0.532068472203148#2382 +-0.44488125948072355#2383 +-0.35324894027176074#2384 +-0.2580870744201778#2385 +-0.16034648783323305#2386 +-0.061003772143360675#2387 +0.03894847307260204#2388 +0.13851155802027681#2389 +0.23669068126604687#2390 +0.33250486946414387#2391 +0.4249967789180523#2392 +0.5132422610422692#2393 +0.5963595961494876#2394 +0.6735183033022544#2395 +0.7439474382039768#2396 +0.8069432962194937#2397 +0.861876443559176#2398 +0.9081980063732902#2399 +0.9454451549180705#2400 +0.9732457279975278#2401 +0.9913219514750999#2402 +0.9994932137010042#2403 +0.9976778701241392#2404 +0.9858940590574525#2405 +0.9642595204459123#2406 +0.9329904194478973#2407 +0.8923991865843892#2408 +0.8428913960364823#2409 +0.7849617132822316#2410 +0.7191889525627104#2411 +0.6462302935614438#2412 +0.5668147150822461#2413 +0.4817357113339816#2414 +0.39184336359872296#2415 +0.2980358465005815#2416 +0.20125045374175918#2417 +0.1024542329737048#2418 +0.0026343233766439834#2419 +-0.09721190750880472#2420 +-0.19608682914840989#2421 +-0.29300251600885235#2422 +-0.3869906185830895#2423 +-0.47711203881853564#2424 +-0.5624663132743792#2425 +-0.6422006102546296#2426 +-0.7155182510205144#2427 +-0.7816866699410908#2428 +-0.840044734046876#2429 +-0.890009348851936#2430 +-0.9310812844412447#2431 +-0.9628501636109706#2432 +-0.9849985622218451#2433 +-0.9973051807962356#2434 +-0.999647055669374#2435 +-0.9920007876016487#2436 +-0.9744427755760723#2437 +-0.9471484534448972#2438 +-0.9103905370525548#2439 +-0.8645362993490856#2440 +-0.8100439007202268#2441 +-0.7474578112002859#2442 +-0.6774033703075353#2443 +-0.6005805388584559#2444 +-0.5177569051906351#2445 +-0.42976001567382827#2446 +-0.33746910614018294#2447 +-0.2418063168504405#2448 +-0.14372747877327008#2449 +-0.0442125632381852#2450 +0.05574410961404525#2451 +0.155143805749565#2452 +0.2529933562617591#2453 +0.34831508078667095#2454 +0.44015655616202054#2455 +0.5276001327246129#2456 +0.6097721031624384#2457 +0.6858514323093241#2458 +0.7550779606569172#2459 +0.8167599996172166#2460 +0.8702812426463021#2461 +0.9151069231755982#2462 +0.9507891578226558#2463 +0.9769714214938605#2464 +0.9933921096653152#2465 +0.9998871522487686#2466 +0.9963916529257024#2467 +0.9829405375698944#2468 +0.959668205279632#2469 +0.9268071855063493#2470 +0.8846858146972121#2471 +0.8337249556658711#2472 +0.774433792470347#2473 +0.7074047428142367#2474 +0.633307538804846#2475 +0.5528825352113574#2476 +0.4669333120847054#2477 +0.3763186456513355#2478 +0.28194392770502347#2479 +0.18475211923135468#2480 +0.08571432865325745#2481 +-0.014179891163352991#2482 +-0.11393243019470806#2483 +-0.212546594044612#2484 +-0.3090370625853853#2485 +-0.402439734950877#2486 +-0.49182136251362973#2487 +-0.5762888735966745#2488 +-0.6549982967505166#2489 +-0.7271631934368789#2490 +-0.7920625158626148#2491 +-0.8490478114509101#2492 +-0.8975497019650812#2493 +-0.9370835725477036#2494 +-0.9672544138320761#2495 +-0.9877607687452395#2496 +-0.9983977445674013#2497 +-0.9990590601522629#2498 +-0.9897381078531023#2499 +-0.9705280195441958#2500 +-0.9416207360779147#2501 +-0.9033050894751768#2502 +-0.8559639170113676#2503 +-0.8000702360328346#2504 +-0.7361825177239204#2505 +-0.664939107047496#2506 +-0.5870518446130994#2507 +-0.5032989542008693#2508 +-0.4145172670067797#2509 +-0.32159386030194986#2510 +-0.22545719404978#2511 +-0.12706783404090483#2512 +-0.027408854237334586#2513 +0.07252398577761245#2514 +0.17173219009991203#2515 +0.26922450314584817#2516 +0.36402681395014#2517 +0.4551918891607088#2518 +0.5418088374812179#2519 +0.6230122109958052#2520 +0.6979906524385796#2521 +0.7659950020072227#2522 +0.8263457827200938#2523 +0.8784399895256259#2524 +0.9217571143294733#2525 +0.9558643467393337#2526 +0.9804208985633203#2527 +0.9951814088529242#2528 +0.9999983954685079#2529 +0.9948237286720926#2530 +0.9797091120237905#2531 +0.9548055657769237#2532 +0.9203619179335726#2533 +0.8767223180374318#2534 +0.8243227985453299#2535 +0.7636869181350534#2536 +0.6954205304801045#2537 +0.620205730760064#2538 +0.5387940403910272#2539 +0.45199889807203436#2540 +0.3606875321744773#2541 +0.26577229568287974#2542 +0.16820155026545985#2543 +0.068950190557828#2544 +-0.03099009666195487#2545 +-0.13062074107985552#2546 +-0.2289462662303628#2547 +-0.32498423596827036#2548 +-0.41777507064588915#2549 +-0.5063916349156925#2550 +-0.5899485013602381#2551 +-0.6676107973902402#2552 +-0.738602547015509#2553 +-0.8022144241405411#2554 +-0.8578108399163944#2555 +-0.9048362933343682#2556 +-0.9428209216084585#2557 +-0.9713851948889993#2558 +-0.9902437083994649#2559 +-0.9992080341066545#2560 +-0.9981886034313139#2561 +-0.9871956021877676#2562 +-0.966338868810637#2563 +-0.9358267968855071#2564 +-0.8959642529491038#2565 +-0.8471495303636348#2566 +-0.7898703697011723#2567 +-0.7246990854010827#2568 +-0.6522868473933305#2569 +-0.5733571748237859#2570 +-0.48869870689008327#2571 +-0.39915732301944756#2572 +-0.30562769112106847#2573 +-0.20904432836008902#2574 +-0.11037226377100239#2575 +-0.010597396006535125#2576 +0.0892833574357961#2577 +0.18827202108378255#2578 +0.2853795329315549#2579 +0.37963562682020546#2580 +0.4700985270165206#2581 +0.5558643581247975#2582 +0.6360761763110162#2583 +0.7099325316023618#2584 +0.7766954757104277#2585 +0.8356979353665813#2586 +0.8863503774975581#2587 +0.9281466996450561#2588 +0.9606692867742049#2589 +0.9835931839449478#2590 +0.9966893431543875#2591 +0.9998269119087179#2592 +0.992974540658088#2593 +0.9762006960309414#2594 +0.9496729767380968#2595 +0.9136564389818304#2596 +0.8685109481019182#2597 +0.8146875829201173#2598 +0.7527241287096857#2599 +0.6832397038226994#2600 +0.6069285736641322#2601 +0.5245532138214233#2602 +0.43693669166044735#2603 +0.34495444250846585#2604 +0.2495255225937167#2605 +0.15160342613938146#2606 +0.052166558364490814#2607 +-0.047791540417606526#2608 +-0.14727212192563383#2609 +-0.24528120907307063#2610 +-0.3408395274586362#2611 +-0.4329922899524019#2612 +-0.520818736613183#2613 +-0.6034413346175079#2614 +-0.6800345462775168#2615 +-0.7498330775406523#2616 +-0.8121395245548596#2617 +-0.8663313418973455#2618 +-0.911867062842657#2619 +-0.9482917095192162#2620 +-0.9752413388978184#2621 +-0.9924466791900753#2622 +-0.9997358203231163#2623 +-0.9970359316082094#2624 +-0.9843739894409296#2625 +-0.9618765077619307#2626 +-0.9297682739714707#2627 +-0.888370102928013#2628 +-0.8380956314722081#2629 +-0.7794471855043118#2630 +-0.713010760909837#2631 +-0.6394501684823696#2632 +-0.5595004013455463#2633 +-0.4739602911447193#2634 +-0.38368452638521455#2635 +-0.28957511266730906#2636 +-0.19257236014443638#2637 +-0.0936454882549586#2638 +0.006217058398080305#2639 +0.10601748625869184#2640 +0.2047586224413277#2641 +0.3014538781527306#2642 +0.3951371063610338#2643 +0.48487225521753735#2644 +0.5697627207773633#2645 +0.6489603055696946#2646 +0.7216736935065267#2647 +0.7871763564514449#2648 +0.8448138134486051#2649 +0.8940101700801013#2650 +0.934273872612625#2651 +0.9652026194398814#2652 +0.986487380747257#2653 +0.9979154862355749#2654 +0.9993727500524254#2655 +0.9908446116994578#2656 +0.9724162815160672#2657 +0.9442718892858502#2658 +0.9066926444726718#2659 +0.860054026468659#2660 +0.8048220329282305#2661 +0.7415485236735729#2662 +0.6708657066937176#2663 +0.5934798213312947#2664 +0.5101640817724733#2665 +0.42175095134640345#2666 +0.32912382482680763#2667 +0.23320820184341287#2668 +0.13496243959558005#2669 +0.035368177263958724#2670 +-0.06457947220371922#2671 +-0.16388186493227264#2672 +-0.26154680423856425#2673 +-0.3565984543327818#2674 +-0.4480870905470817#2675 +-0.5350985886705324#2676 +-0.6167635585760615#2677 +-0.6922660308792223#2678 +-0.7608516098345607#2679 +-0.8218350110085325#2680 +-0.8746069084150397#2681 +-0.918640022699266#2682 +-0.9534943895386959#2683 +-0.9788217556211913#2684 +-0.9943690582769701#2685 +-0.9999809539971505#2686 +-0.9956013705747425#2687 +-0.9812740673596052#2688 +-0.9571421980294875#2689 +-0.9234468802458005#2690 +-0.8805247864856478#2691 +-0.8288047801217232#2692 +-0.7688036303612542#2693 +-0.7011208488589045#2694 +-0.6264326995944929#2695 +-0.54548544186685#2696 +-0.4590878739175849#2697 +-0.3681032516864081#2698 +-0.2734406634431365#2699 +-0.17604594647820584#2700 +-0.07689223660911715#2701 +0.023029755070976092#2702 +0.12272164105102494#2703 +0.22118733296007248#2704 +0.31744299415299343#2705 +0.4105268698810389#2706 +0.4995088968273724#2707 +0.5834999959922931#2708 +0.6616609560767109#2709 +0.7332108186040404#2710 +0.797434680999148#2711 +0.8536908396585681#2712 +0.9014172016407891#2713 +0.9401369009131222#2714 +0.9694630630394689#2715 +0.9891026707018031#2716 +0.9988594914323538#2717 +0.9986360383035573#2718 +0.988434543985213#2719 +0.9683569384363839#2720 +0.9386038304549402#2721 +0.8994725032607627#2722 +0.8513539441400785#2723 +0.7947289378297411#2724 +0.730163262675065#2725 +0.6583020375596216#2726 +0.5798632760906015#2727 +0.4956307124441966#2728 +0.4064459705527783#2729 +0.31320015487677#2730 +0.2168249467834381#2731 +0.11828329549464342#2732 +0.018559796616524828#2733 +-0.08134914561433308#2734 +-0.18044527407266456#2735 +-0.2777384529997385#2736 +-0.37225656111256417#2737 +-0.4630552047184104#2738 +-0.5492271537844082#2739 +-0.6299114066801492#2740 +-0.7043017930213666#2741 +-0.7716550286579311#2742 +-0.8312981423233815#2743 +-0.8826351997413644#2744 +-0.9251532580039342#2745 +-0.9584274907275325#2746 +-0.9821254327777841#2747 +-0.9960103021512207#2748 +-0.9999433658227904#2749 +-0.9938853259203851#2750 +-0.9778967123761879#2751 +-0.9521372781315993#2752 +-0.9168644029386548#2753 +-0.8724305217066197#2754 +-0.8192796030888767#2755 +-0.7579427134948966#2756 +-0.6890327108502228#2757 +-0.6132381211226626#2758 +-0.531316258798414#2759 +-0.4440856600460539#2760 +-0.3524179041737139#2761 +-0.2572289050967394#2762 +-0.15946975982860834#2763 +-0.06011724543396326#2764 +0.039835940604938974#2765 +0.13939109909332772#2766 +0.23755350779614662#2767 +0.3333423603738149#2768 +0.425800566274961#2769 +0.5140043136688397#2770 +0.5970722998677733#2771 +0.6741745370122875#2772 +0.7445406450354447#2773 +0.8074675490458203#2774 +0.862326504219411#2775 +0.908569378010082#2776 +0.9457341269087781#2777 +0.9734494130295364#2778 +0.9914383143948933#2779 +0.9995210918487465#2780 +0.997616984950588#2781 +0.9857450189071303#2782 +0.9640238144787292#2783 +0.932670402759958#2784 +0.8919980566766509#2785 +0.8424131608663822#2786 +0.7844111512175009#2787 +0.7185715646375037#2788 +0.6455522485118246#2789 +0.5660827877102146#2790 +0.4809572148158928#2791 +0.39102607641444626#2792 +0.29718793471350063#2793 +0.20038038940616668#2794 +0.10157070948483571#2795 +0.001746168609144461#2796 +-0.09809581940608277#2797 +-0.1969576664199707#2798 +-0.2938515775365379#2799 +-0.38780942082477593#2800 +-0.4778924005728819#2801 +-0.5632004374246865#2802 +-0.6428811616750538#2803 +-0.7161384298662196#2804 +-0.7822402795900505#2805 +-0.8405262430144722#2806 +-0.8904139460597256#2807 +-0.9314049272876644#2808 +-0.9630896183636759#2809 +-0.9851514363280947#2810 +-0.9973699467884874#2811 +-0.9996230664272368#2812 +-0.9918882828176945#2813 +-0.9742428793609076#2814 +-0.9468631630954215#2815 +-0.9100227030956302#2816 +-0.8640895970600148#2817 +-0.8095227934006172#2818 +-0.746867505582215#2819 +-0.6767497645296058#2820 +-0.5998701635335436#2821 +-0.5169968581541643#2822 +-0.4289578910645463#2823 +-0.33663291852203614#2824 +-0.24094442113370063#2825 +-0.14284848673503256#2826 +-0.04332525747633915#2827 +0.056630863433631995#2828 +0.15602114747584864#2829 +0.25385251978622037#2830 +0.34914748163137366#2831 +0.44095387725287755#2832 +0.5283544074928413#2833 +0.610475795143881#2834 +0.6864975104463086#2835 +0.7556599695502599#2836 +0.8172721240264376#2837 +0.8707183655935856#2838 +0.9154646770729435#2839 +0.9510639681113735#2840 +0.9771605423603803#2841 +0.9934936514764484#2842 +0.9999001004322978#2843 +0.9963158781076524#2844 +0.9827767968671937#2845 +0.9594181347352508#2846 +0.9264732837424994#2847 +0.8842714179499392#2848 +0.8332342044504877#2849 +0.7738715902107717#2850 +0.7067767068496035#2851 +0.6326199442629082#2852 +0.5521422523094861#2853 +0.4661477374849479#2854 +0.3754956285554135#2855 +0.281091691427704#2856 +0.1838791790358073#2857 +0.08482940666935936#2858 +-0.015067953087655503#2859 +-0.11481475883822152#2860 +-0.21341437347118883#2861 +-0.3098816222298437#2862 +-0.4032526362524221#2863 +-0.4925944832311636#2864 +-0.5770144889635445#2865 +-0.6556691566578302#2866 +-0.7277725948741954#2867 +-0.7926043698922097#2868 +-0.8495167040464284#2869 +-0.8979409481066987#2870 +-0.9373932630332972#2871 +-0.9674794543366784#2872 +-0.9878989107385114#2873 +-0.9984476077802041#2874 +-0.9990201463678514#2875 +-0.9896108058851415#2876 +-0.9703136013518633#2877 +-0.9413213440569057#2878 +-0.902923715051594#2879 +-0.8555043707523807#2880 +-0.7995371095727526#2881 +-0.7355811378860999#2882 +-0.6642754826204825#2883 +-0.5863326063127988#2884 +-0.5025312884186264#2885 +-0.41370884400533103#2886 +-0.3207527575766946#2887 +-0.22459181562111544#2888 +-0.12618682648403282#2889 +-0.026521020288540302#2890 +0.07340977517499168#2891 +0.17260708443102024#2892 +0.27007976075572954#2893 +0.3648538893874657#2894 +0.4559825185611017#2895 +0.5425551211370541#2896 +0.623706692287481#2897 +0.6986263923385901#2898 +0.7665656484126308#2899 +0.8268456339206427#2900 +0.878864051173344#2901 +0.9221011493405477#2902 +0.9561249176296516#2903 +0.9805954017946731#2904 +0.995268100846702#2905 +0.9999964100269597#2906 +0.9947330856330885#2907 +0.979530717062608#2908 +0.9545412013570396#2909 +0.920014225496878#2910 +0.876294771611817#2911 +0.8238196700333464#2912 +0.7631132346304763#2913 +0.6947820240388727#2914 +0.6195087811274721#2915 +0.5380456112574195#2916 +0.45120646749391313#2917 +0.35985901785623386#2918 +0.26491597586570953#2919 +0.16732598101391277#2920 +0.06806412027043987#2921 +-0.03187781466376874#2922 +-0.13150123701126137#2923 +-0.22981074246706656#2924 +-0.3258240549494714#2925 +-0.41858184117793273#2926 +-0.507157296014103#2927 +-0.5906654027924112#2928 +-0.6682717761140383#2929 +-0.7392009987500107#2930 +-0.8027443693538329#2931 +-0.8582669835710771#2932 +-0.9052140777938206#2933 +-0.9431165721752354#2934 +-0.9715957575203611#2935 +-0.9903670792231963#2936 +-0.9992429804422601#2937 +-0.9981347761065544#2938 +-0.987053539027478#2939 +-0.9661099892629551#2940 +-0.9355133878392158#2941 +-0.8955694458837944#2942 +-0.8466772700609979#2943 +-0.7893253748300435#2944 +-0.724086801370063#2945 +-0.6516133919420634#2946 +-0.5726292768965259#2947 +-0.4879236394023083#2948 +-0.3983428301892924#2949 +-0.3047819110916564#2950 +-0.2081757118858963#2951 +-0.1094894897807127#2952 +-0.009709284886053158#2953 +0.090167931973725#2954 +0.18914422066277686#2955 +0.28623064282173216#2956 +0.38045714301288247#2957 +0.47088224119345734#2958 +0.5566024396729995#2959 +0.6367612505636312#2960 +0.7105577535239129#2961 +0.7772545982901459#2962 +0.8361853720364713#2963 +0.8867612579515394#2964 +0.9284769185014516#2965 +0.9609155445953574#2966 +0.9837530202041103#2967 +0.9967611608204933#2968 +0.9998099934033869#2969 +0.9928690550254331#2970 +0.9760076972485352#2971 +0.9493943931859762#2972 +0.9132950541747605#2973 +0.8680703728774336#2974 +0.8141722193602262#2975 +0.7521391261567218#2976 +0.6825909074287959#2977 +0.6062224659883935#2978 +0.523796850058337#2979 +0.4361376291467138#2980 +0.3441206652125872#2981 +0.24866536134282327#2982 +0.1507254753803607#2983 +0.05127959029111491#2984 +-0.04867866351354105#2985 +-0.1481505362033963#2986 +-0.2461421377075628#2987 +-0.3416743683355274#2988 +-0.43379270161761196#2989 +-0.521576721617934#2990 +-0.6041493194261889#2991 +-0.6806854569399482#2992 +-0.7504204103726574#2993 +-0.8126574111209275#2994 +-0.8667746076460986#2995 +-0.9122312788092578#2996 +-0.9485732365781204#2997 +-0.9754373641237138#2998 +-0.9925552439637022#2999 +-0.9997558399011433#3000 +-0.9969672059616304#3001 +-0.9842172052536875#3002 +-0.9616332315697985#3003 +-0.9294409365097438#3004 +-0.8879619748444056#3005 +-0.8376107906476223#3006 +-0.778890476308#3007 +-0.712387745796065#3008 +-0.6387670724122129#3009 +-0.5587640495891363#3010 +-0.4731780410854005#3011 +-0.38286419400700217#3012 +-0.2887248944601604#3013 +-0.1917007512076325#3014 +-0.09276119741688045#3015 +0.007105195595686342#3016 +0.10690059584252641#3017 +0.20562788067234633#3018 +0.30230059969002754#3019 +0.39595283104289725#3020 +0.48564883259258895#3021 +0.5704923915411738#3022 +0.649635779093189#3023 +0.7222882206815403#3024 +0.7877237971255806#3025 +0.8452886977756067#3026 +0.8944077531727492#3027 +0.9345901819520811#3028 +0.9654344945677839#3029 +0.9866325048439754#3030 +0.9979724092691066#3031 +0.9993409032666355#3032 +0.9907243132969028#3033 +0.9722087334786201#3034 +0.9439791653648948#3035 +0.9063176694688648#3036 +0.8596005470082881#3037 +0.8042945800281633#3038 +0.7409523674688342#3039 +0.670206803780042#3040 +0.5927647552487915#3041 +0.5093999972250692#3042 +0.42094548281432353#3043 +0.32828502028537193#3044 +0.23234444235032745#3045 +0.13408235555017892#3046 +0.0344805621751064#3047 +-0.06546574957946144#3048 +-0.16475794920433068#3049 +-0.26240394186248667#3050 +-0.3574280810727624#3051 +-0.4488809170469727#3052 +-0.5358486832783511#3053 +-0.6174624265944348#3054 +-0.6929066894499258#3055 +-0.7614276577089293#3056 +-0.8223406925066218#3057 +-0.8750371709344767#3058 +-0.9189905671991826#3059 +-0.9537617134943233#3060 +-0.9790031880199306#3061 +-0.9944627863062665#3062 +-0.9999860411575177#3063 +-0.9955177660369557#3064 +-0.9811026064725701#3065 +-0.9568845939737097#3066 +-0.9231057069158529#3067 +-0.8801034527726663#3068 +-0.8283074958528935#3069 +-0.7682353642366102#3070 +-0.7004872788057198#3071 +-0.6257401560353084#3072 +-0.5447408444679848#3073 +-0.45829866245011713#3074 +-0.36727731169044214#3075 +-0.27258624743809257#3076 +-0.17517159150637399#3077 +-0.07600667893635255#3078 +0.0239176672451336#3079 +0.12360303600163591#3080 +0.22205340408014085#3081 +0.3182850879461725#3082 +0.41133657242450644#3083 +0.5002781178409661#3084 +0.5842210496739159#3085 +0.6623266378963246#3086 +0.7338144772889486#3087 +0.7979702849913142#3088 +0.8541530373799495#3089 +0.901801374964536#3090 +0.9404392113063745#3091 +0.969680489916708#3092 +0.9892330416055432#3093 +0.9989015037396196#3094 +0.9985892722412621#3095 +0.9882994668243924#3096 +0.9681348998233783#3097 +0.9382970489261745#3098 +0.8990840440758637#3099 +0.8508876886548145#3100 +0.7941895447147971#3101 +0.7295561213681458#3102 +0.6576332144159718#3103 +0.5791394537699889#3104 +0.49485912313998487#3105 +0.40563432373024166#3106 +0.31235656024266445#3107 +0.21595783325649234#3108 +0.11740132698658906#3109 +0.017671785465154324#3110 +-0.08223432669513268#3111 +-0.18131878064613588#3112 +-0.2785915572769423#3113 +-0.37308073915756135#3114 +-0.4638422216166124#3115 +-0.5499691459231215#3116 +-0.6306009603191937#3117 +-0.704932018368717#3118 +-0.7722196287102416#3119 +-0.8317914757835613#3120 +-0.8830523373845538#3121 +-0.9254900319286898#3122 +-0.9586805360001208#3123 +-0.9822922210534869#3124 +-0.9960891669367201#3125 +-0.9999335191272192#3126 +-0.9937868661286703#3127 +-0.9777106232660219#3128 +-0.951865419043858#3129 +-0.9165094901994781#3130 +-0.8719961014867792#3131 +-0.8187700159716086#3132 +-0.7573630511062297#3133 +-0.6883887649851336#3134 +-0.6125363258753749#3135 +-0.530563626275056#3136 +-0.4432897103020118#3137 +-0.3515865900757242#3138 +-0.2563705328604733#3139 +-0.1585929060257105#3140 +-0.05923067129778288#3141 +0.040723376718695616#3142 +0.14027053021635935#3143 +0.23841614694337318#3144 +0.3341795883400279#3145 +0.4266040177550728#3146 +0.5147659608412467#3147 +0.597784532605696#3148 +0.6748302389216413#3149 +0.7451332645594977#3150 +0.807991164926182#3151 +0.8627758846592893#3152 +0.908940032948667#3153 +0.9460223528844458#3154 +0.9736523301836125#3155 +0.99155389524625#3156 +0.9995481815517231#3157 +0.9975553128338268#3158 +0.985595201178036#3159 +0.9637873480665278#3160 +0.9323496503588747#3161 +0.8915962231386506#3162 +0.8419342611793482#3163 +0.783859970388803#3164 +0.7179536098837859#3165 +0.6448736942327173#3166 +0.5653504137957747#3167 +0.4801783389041825#3168 +0.3902084807761133#3169 +0.29633978849390213#3170 +0.19951016700196894#3171 +0.10068710587064418#3172 +8.58012460191664E-4#3173 +-0.09897965392714189#3174 +-0.19782834833075857#3175 +-0.2947004072712111#3176 +-0.3886279171572114#3177 +-0.4786723853582849#3178 +-0.5639341173129099#3179 +-0.6435612059791769#3180 +-0.7167580438083482#3181 +-0.7827932721924921#3182 +-0.8410070889579382#3183 +-0.8908178408904955#3184 +-0.931727835422099#3185 +-0.9633283134104351#3186 +-0.9853035333251733#3187 +-0.9974339260329675#3188 +-0.9995982886596559#3189 +-0.991774995609316#3190 +-0.9740422146400699#3191 +-0.9465771258376843#3192 +-0.9096541512907214#3193 +-0.8636422131557419#3194 +-0.8090010475090663#3195 +-0.7462766108158684#3196 +-0.6760956249136447#3197 +-0.5991593150147811#3198 +-0.5162364032960245#3199 +-0.4281554280805988#3200 +-0.33579646535715757#3201 +-0.24008233535141982#3202 +-0.14196938201151837#3203 +-0.04243791753539556#3204 +0.057517572584630666#3205 +0.1568983661321727#3206 +0.25471148306902575#3207 +0.34997960706347403#3208 +0.44175085051201957#3209 +0.5291082654856638#3210 +0.6111790055705121#3211 +0.6871430470606158#3212 +0.7562413823637792#3213 +0.8177836037545257#3214 +0.8711548016998724#3215 +0.9158217088321214#3216 +0.9513380281801238#3217 +0.9773488924210882#3218 +0.9935944095975676#3219 +0.9999122598719884#3220 +0.9962393173728123#3221 +0.9826122809273776#3222 +0.959167307379348#3223 +0.9261386511545373#3224 +0.8838563236681221#3225 +0.8327427959596674#3226 +0.7733087775021505#3227 +0.7061481133617248#3228 +0.6319318506941176#3229 +0.5514015338632693#3230 +0.4653617951751706#3231 +0.374672315257837#3232 +0.2802392334166466#3233 +0.18300609378993019#3234 +0.08394441776783579#3235 +-0.015956003128259934#3236 +-0.1156969969154514#3237 +-0.21428198455380562#3238 +-0.310725937434704#3239 +-0.40406521946109236#3240 +-0.4933672153808272#3241 +-0.5777396491699932#3242 +-0.6563394993599873#3243 +-0.7283814222293667#3244 +-0.7931455986987139#3245 +-0.8499849265249375#3246 +-0.8983314859329812#3247 +-0.9377022140824873#3248 +-0.9677037316720184#3249 +-0.9880362734550029#3250 +-0.9984966833949899#3251 +-0.9989804445336107#3252 +-0.9894827232894784#3253 +-0.9700984177537353#3254 +-0.941021209499695#3255 +-0.9025416283805849#3256 +-0.8550441496512888#3257 +-0.7990033524186914#3258 +-0.7349791778041159#3259 +-0.6636113341967335#3260 +-0.5856129054987965#3261 +-0.5017632262270028#3262 +-0.4129000946596174#3263 +-0.3199114018330162#3264 +-0.22372626002794793#3265 +-0.12530571938674848#3266 +-0.025633165417999926#3267 +0.07429550666640745#3268 +0.1734818426070331#3269 +0.27093480532180136#3270 +0.36568067702093243#3271 +0.4567727882732294#3272 +0.5433009768141075#3273 +0.6244006815860823#3274 +0.69926158114707#3275 +0.7671356901343803#3276 +0.8273448328872091#3277 +0.8792874195536663#3278 +0.9224444569777169#3279 +0.9563847343072394#3280 +0.9807691315103211#3281 +0.9953540077505189#3282 +0.9999936357655596#3283 +0.994641657925974#3284 +0.9793515494252069#3285 +0.9542760839731598#3286 +0.9196658073317829#3287 +0.8758665339446406#3288 +0.8233158916733028#3289 +0.7625389491644119#3290 +0.6941429695373308#3291 +0.6188113428117887#3292 +0.5372967577007027#3293 +0.4504136809933635#3294 +0.3590302196725045#3295 +0.264059447076288#3296 +0.16645027977133223#3297 +0.0671779962920478#3298 +-0.03276550752009386#3299 +-0.1323816292119314#3300 +-0.23067503742423134#3301 +-0.3266636169136169#3302 +-0.4193882815234355#3303 +-0.5079225570556044#3304 +-0.5913818382945464#3305 +-0.668932227690092#3306 +-0.7397988673861511#3307 +-0.8032736813442769#3308 +-0.8587224502053834#3309 +-0.9055911481999364#3310 +-0.9434114787903057#3311 +-0.9718055537349715#3312 +-0.9904896688229197#3313 +-0.9992771385523388#3314 +-0.9980801614304436#3315 +-0.9869106972569683#3316 +-0.9658803476258001#3317 +-0.9351992408387446#3318 +-0.8951739323729925#3319 +-0.8462043418801258#3320 +-0.7887797573211552#3321 +-0.7234739461629501#3322 +-0.6509394224833727#3323 +-0.5719009272663036#3324 +-0.4871481870292994#3325 +-0.3975280231372775#3326 +-0.30393589064335974#3327 +-0.20730693119798013#3328 +-0.10860662942263#3329 +-0.008821166106667043#3330 +0.09105243538514393#3331 +0.19001627104051966#3332 +0.2870815269266859#3333 +0.3812783590923355#3334 +0.47166558392780156#3335 +0.557340082160572#3336 +0.6374458225245284#3337 +0.711182414941391#3338 +0.777813107753807#3339 +0.836672149104373#3340 +0.8871714389081266#3341 +0.9288064049541939#3342 +0.9611610444245324#3343 +0.9839120804565766#3344 +0.9968321922187865#3345 +0.9997922862252552#3346 +0.9927627861951471#3347 +0.9758139285691194#3348 +0.9491150607300238#3349 +0.9129329489398365#3350 +0.867629112899349#3351 +0.813656213562821#3352 +0.751553530299355#3353 +0.6819415725917019#3354 +0.605515880110623#3355 +0.5230400731124144#3356 +0.4353382225977257#3357 +0.34328661646652237#3358 +0.24780500393905253#3359 +0.14984740572566616#3360 +0.05039258176723505#3361 +-0.04956574821064165#3362 +-0.14902883361665542#3363 +-0.2470028721795537#3364 +-0.34250893969192686#3365 +-0.4345927710972995#3366 +-0.5223342951911364#3367 +-0.6048568276681833#3368 +-0.6813358306622516#3369 +-0.7510071512560216#3370 +-0.8131746566443966#3371 +-0.8672171896633807#3372 +-0.9125947751871345#3373 +-0.9488540153809398#3374 +-0.9756326199024913#3375 +-0.9926630257872393#3376 +-0.9997750708490869#3377 +-0.9968976938847057#3378 +-0.9840596446935896#3379 +-0.9613891968195613#3380 +-0.9291128658839244#3381 +-0.8875531463162514#3382 +-0.8371252890966461#3383 +-0.7783331527052134#3384 +-0.7117641687346805#3385 +-0.6380834724681006#3386 +-0.5580272570669699#3387 +-0.4723954177725101#3388 +-0.38204355961682934#3389 +-0.28787444850026606#3390 +-0.19082899105292786#3391 +-0.09187683340666573#3392 +0.00799332718855293#3393 +0.10778362110074621#3394 +0.20649697669942857#3395 +0.3031470827657546#3396 +0.39676824338818645#3397 +0.48642502687682576#3398 +0.5712216122876458#3399 +0.6503107401692458#3400 +0.7229021780992223#3401 +0.7882706164253177#3402 +0.8457629153197103#3403 +0.8948046307362744#3404 +0.9349057540656031#3405 +0.9656656081390587#3406 +0.9867768506625949#3407 +0.9980285450793656#3408 +0.9993082681780738#3409 +0.9906032333885376#3410 +0.972000418540872#3411 +0.9436856968117622#3412 +0.9059419795411228#3413 +0.8591463894755079#3414 +0.8037664926822882#3415 +0.7403556267840615#3416 +0.6695473721920379#3417 +0.592049221580004#3418 +0.508635510851393#3419 +0.4201396822308988#3420 +0.32744595678526595#3421 +0.23148049957867578#3422 +0.1332021657375744#3423 +0.03359291988720454#3424 +-0.06635197531433547#3425 +-0.1656339035115813#3426 +-0.2632608724962278#3427 +-0.35825742586536535#3428 +-0.4496743894594147#3429 +-0.5365983551965742#3430 +-0.6181608075444406#3431 +-0.6935468014401158#3432 +-0.7620031049518906#3433 +-0.8228457253237202#3434 +-0.8754667432047455#3435 +-0.9193403867784948#3436 +-0.9540282851010776#3437 +-0.9791838481587489#3438 +-0.9945557298807602#3439 +-0.9999903395062136#3440 +-0.9954333762121745#3441 +-0.9809303716695452#3442 +-0.9566262351056599#3443 +-0.9227638054191856#3444 +-0.8796814248141186#3445 +-0.8278095581963233#3446 +-0.7676664921104855#3447 +-0.6998531561922808#3448 +-0.6250471188780957#3449 +-0.5439958173651854#3450 +-0.45750908946626917#3451 +-0.366451081977802#3452 +-0.27173161641083377#3453 +-0.17429709835521742#3454 +-0.07512106130779558#3455 +0.02480556055249268#3456 +0.12448433345136849#3457 +0.22291930003944738#3458 +0.3191269306688547#3459 +0.4121459504963556#3460 +0.5010469442238331#3461 +0.5849416425087233#3462 +0.6629917972576633#3463 +0.7344175571243525#3464 +0.7985052595264196#3465 +0.854614561326041#3466 +0.9021848369269034#3467 +0.9407407798598458#3468 +0.969897151887982#3469 +0.9893626321798219#3470 +0.9989427280907218#3471 +0.9985417184690987#3472 +0.9881636100705354#3473 +0.9679120975236041#3474 +0.9379895272474138#3475 +0.8986948756730775#3476 +0.8504207619700415#3477 +0.793649525125126#3478 +0.7289484045708101#3479 +0.6569638725163257#3480 +0.5784151746110389#3481 +0.49408714347967225#3482 +0.40482235693414975#3483 +0.3115127192146192#3484 +0.21509054937710928#3485 +0.11651926586970504#3486 +0.016783760373878607#3487 +-0.08311944290763014#3488 +-0.1821921441912403#3489 +-0.2794444417948066#3490 +-0.3739046229080092#3491 +-0.4646288726255491#3492 +-0.550710704233698#3493 +-0.6312900165258971#3494 +-0.7055616876497018#3495 +-0.7727836196181932#3496 +-0.8322841531077579#3497 +-0.8834687784560297#3498 +-0.9258260758059158#3499 +-0.9589328250437571#3500 +-0.9824582344748051#3501 +-0.9961672459844911#3502 +-0.9999228836614071#3503 +-0.9936876224153341#3504 +-0.9775237629155394#3505 +-0.9515928091030836#3506 +-0.9161538544968271#3507 +-0.8715609934166348#3508 +-0.8182597829899804#3509 +-0.7567827912924092#3510 +-0.6877442761033722#3511 +-0.6118340474455396#3512 +-0.5298105752310752#3513 +-0.4424934108809914#3514 +-0.35075499863825743#3515 +-0.2555119583933158#3516 +-0.15771592712113114#3517 +-0.05834405043910549#3518 +0.041610780708929006#3519 +0.14114985069081507#3520 +0.23927859802253518#3521 +0.3350165526978016#3522 +0.427407132720259#3523 +0.5155272019545877#3524 +0.5984962937976203#3525 +0.6754854085095959#3526 +0.7457252963055302#3527 +0.8085141434447857#3528 +0.8632245845219801#3529 +0.9093099708947372#3530 +0.9463098326162236#3531 +0.9738544792986461#3532 +0.9916686939374035#3533 +0.999574482788422#3534 +0.9974928538228081#3535 +0.9854446059890913#3536 +0.9635501213970051#3537 +0.9320281624992393#3538 +0.8911936862893262#3539 +0.841454697355472#3540 +0.7833081712335805#3541 +0.7173350887919758#3542 +0.6441946312626129#3543 +0.5646175939201048#3544 +0.4793990842169069#3545 +0.38939057733247995#3546 +0.2954914085147568#3547 +0.19863978721962608#3548 +0.09980342283217974#3549 +-3.01443655808235E-5#3550 +-0.0998634103708049#3551 +-0.19869887419005808#3552 +-0.2955490045395186#3553 +-0.38944610693113085#3554 +-0.47945199255605286#3555 +-0.5646673523571102#3556 +-0.6442407426276232#3557 +-0.7173770923554741#3558 +-0.783345647309845#3559 +-0.8414872714959365#3560 +-0.8912210330239467#3561 +-0.9320500085884833#3562 +-0.9635662485619702#3563 +-0.9854548530924759#3564 +-0.9974971184789425#3565 +-0.9995727223862718#3566 +-0.9916609260663253#3567 +-0.9738407815726398#3568 +-0.9462903418984395#3569 +-0.9092848819299848#3570 +-0.8631941479909019#3571 +-0.8084786634591391#3572 +-0.7456851273696038#3573 +-0.6754409519781194#3574 +-0.5984479938655606#3575 +-0.5154755412189004#3576 +-0.42735262735793816#3577 +-0.33495974730841055#3578 +-0.23922006018674727#3579 +-0.14109016529933546#3580 +-0.04155054411846003#3581 +0.05840423636446346#3582 +0.15777546102350673#3583 +0.2555702454296358#3584 +0.350811456423722#3585 +0.4425474753080707#3586 +0.5298617061058852#3587 +0.6118817338852807#3588 +0.6877880416409017#3589 +0.7568221986369429#3590 +0.8182944383963581#3591 +0.8715905506194918#3592 +0.9161780181703624#3593 +0.9516113378118588#3594 +0.9775364715268212#3595 +0.9936943839488795#3596 +0.9999236305582081#3597 +0.9961619707818016#3598 +0.9824469898807066#3599 +0.9589157234105169#3600 +0.9258032880074003#3601 +0.8834405321803889#3602 +0.8322507305824411#3603 +0.7727453547900252#3604 +0.7055189628481972#3605 +0.6312432586431493#3606 +0.5506603804590156#3607 +0.4645754857774538#3608 +0.3738487064102398#3609 +0.2793865543465253#3610 +0.1821328641846953#3611 +0.08305936264905144#3612 +-0.016844040582407176#3613 +-0.11657914372826583#3614 +-0.2151494266059326#3615 +-0.31157000753189684#3616 +-0.4048774839339524#3617 +-0.4941395583512405#3618 +-0.5784643536423021#3619 +-0.6570093243266599#3620 +-0.7289896750207507#3621 +-0.7936862018539796#3622 +-0.8504524785160569#3623 +-0.8987213151350112#3624 +-0.9380104254509017#3625 +-0.9679272456607052#3626 +-0.9881728567860709#3627 +-0.9985449713729435#3628 +-0.9989399546809362#3629 +-0.9893538601673995#3630 +-0.9698824689199713#3631 +-0.9407203326436097#3632 +-0.9021588297642675#3633 +-0.8545832540719774#3634 +-0.7984689649926625#3635 +-0.7343766379538854#3636 +-0.6629466623013117#3637 +-0.5848927427400513#3638 +-0.5009947682331652#3639 +-0.41209101960894357#3640 +-0.3190697937359677#3641 +-0.22286052795443187#3642 +-0.12442451344547048#3643 +-0.024745290327437247#3644 +0.07518117955184238#3645 +0.1743564639366335#3646 +0.27178963616835333#3647 +0.36650717619718665#3648 +0.45756269767262103#3649 +0.5440464039230273#3650 +0.6250941783432632#3651 +0.6998962183621539#3652 +0.7677051267220971#3653 +0.8278433792254051#3654 +0.8797100943321279#3655 +0.9227870369697753#3656 +0.9566437965668558#3657 +0.9809420875730321#3658 +0.9954391294965175#3659 +0.9999900726864973#3660 +0.9945494456229591#3661 +0.9791716092530895#3662 +0.9540102138346597#3663 +0.9193166637134389#3664 +0.8754376053740758#3665 +0.8228114638630091#3666 +0.7619640621903281#3667 +0.6935033674800697#3668 +0.6181134163636822#3669 +0.5365474803121163#3670 +0.44962053919628586#3671 +0.35820113827759426#3672 +0.26320270999078516#3673 +0.1655744472289955#3674 +0.0662918193221285#3675 +-0.033653174530243454#3676 +-0.13326191698696957#3677 +-0.2315391504196944#3678 +-0.3275029211980919#3679 +-0.4201943910459491#3680 +-0.508687417436272#3681 +-0.5920978073012736#3682 +-0.6695921515972314#3683 +-0.7403961524521645#3684 +-0.8038023596942209#3685 +-0.8591772394599432#3686 +-0.9059675042552137#3687 +-0.943705641221003#3688 +-0.9720145833673183#3689 +-0.9906114771019258#3690 +-0.9993105084099446#3691 +-0.9980247594460628#3692 +-0.9867670769889152#3693 +-0.9656499440803186#3694 +-0.9348843561318997#3695 +-0.894777712728688#3696 +-0.845730746194075#3697 +-0.788233517604903#3698 +-0.7228605202631786#3699 +-0.6502649395489006#3700 +-0.5711721265076578#3701 +-0.4863723503827509#3702 +-0.396712902506141#3703 +-0.3030896304435386#3704 +-0.20643798698165444#3705 +-0.10772368339317456#3706 +-0.00793304036894502#3707 +0.09193686697233655#3708 +0.19088817152911786#3709 +0.28793218457521963#3710 +0.3820992744107705#3711 +0.4724485546016347#3712 +0.558077285005646#3713 +0.6381298916537022#3714 +0.7118065153620489#3715 +0.778371003660846#3716 +0.837158266186306#3717 +0.8875809200437595#3718 +0.9291351587433763#3719 +0.9614057860680744#3720 +0.9840703645768764#3721 +0.996902437293236#3722 +0.9997737903882905#3723 +0.9926557342510572#3724 +0.9756193901455429#3725 +0.9488349795905833#3726 +0.9125701235626947#3727 +0.8671871685157403#3728 +0.8131395659349391#3729 +0.7509673415995165#3730 +0.6812916998236274#3731 +0.6048088165881917#3732 +0.5222828835806185#3733 +0.43453847264407314#3734 +0.3424522969281879#3735 +0.24694445106107385#3736 +0.1489692178679391#3737 +0.04950553349254367#3738 +-0.0504527938091558#3739 +-0.14990701347259025#3740 +-0.2478634118100764#3741 +-0.34334324086950585#3742 +-0.43539249776035166#3743 +-0.523091456735199#3744 +-0.6055638587853923#3745 +-0.6819856669313974#3746 +-0.7515932997279102#3747 +-0.8136912607172517#3748 +-0.8676590876000732#3749 +-0.9129575516895527#3750 +-0.9491340457061896#3751 +-0.9758271060801286#3752 +-0.9927700245756657#3753 +-0.9997935131517774#3754 +-0.9968273954322683#3755 +-0.9839013078849232#3756 +-0.961144403703719#3757 +-0.9287840623528019#3758 +-0.8871436176660437#3759 +-0.8366391272022538#3760 +-0.7777752151355816#3761 +-0.7111400302175752#3762 +-0.6373993691892722#3763 +-0.5572900243602457#3764 +-0.47161242182339913#3765 +-0.38122262386203104#3766 +-0.28702377545847707#3767 +-0.18995708036798664#3768 +-0.09099239692192086#3769 +0.008881452476101726#3770 +0.10866656133680092#3771 +0.20736590983701175#3772 +0.3039933267121866#3773 +0.3975833427536857#3774 +0.48720083745796805#3775 +0.5719503824415534#3776 +0.6509851882654402#3777 +0.7235155652752691#3778 +0.7888168139193128#3779 +0.8462364657068425#3780 +0.8952008024576104#3781 +0.9352205887042604#3782 +0.9658959599713982#3783 +0.9869204180892522#3784 +0.9980838936220705#3785 +0.9992748448124834#3786 +0.9904813720698726#3787 +0.9717913368671467#3788 +0.943391483857947#3789 +0.9055655749857984#3790 +0.8586915542285684#3791 +0.8032377713071723#3792 +0.7397583020899774#3793 +0.6688874124498796#3794 +0.5913332208893615#3795 +0.5078706232544888#3796 +0.41933355023176294#3797 +0.326606634988362#3798 +0.23061637420995554#3799 +0.13232187085208025#3800 +0.032705251100445504#3801 +-0.06723814870926637#3802 +-0.1665097271630519#3803 +-0.2641175954638214#3804 +-0.3590864880563847#3805 +-0.45046750715849865#3806 +-0.5373476038338433#3807 +-0.61885870087518#3808 +-0.6941863663448576#3809 +-0.7625779511095188#3810 +-0.8233501090614465#3811 +-0.8758956248869896#3812 +-0.9196894811612573#3813 +-0.954294104148681#3814 +-0.9793637358951378#3815 +-0.9946478889271355#3816 +-0.9999938490398477#3817 +-0.9953482011669674#3818 +-0.9807573630863935#3819 +-0.9563671216291374#3820 +-0.9224211760254981#3821 +-0.8792587029429098#3822 +-0.8273109675447968#3823 +-0.7670970144316195#3824 +-0.6992184815187981#3825 +-0.624353588669538#3826 +-0.5432503611461462#3827 +-0.45671915558887405#3828 +-0.3656245632002365#3829 +-0.27087677103551244#3830 +-0.17342246771455624#3831 +-0.07423538442204156#3832 +0.025693434292662947#3833 +0.12536553270503523#3834 +0.22378502015495372#3835 +0.31996852165697554#3836 +0.4129550034581306#3837 +0.5018153753695055#3838 +0.5856617739282951#3839 +0.663656433636034#3840 +0.7350200576345289#3841 +0.7990396041824644#3842 +0.8550754111327816#3843 +0.902567587225408#3844 +0.9410416063356517#3845 +0.970113048782383#3846 +0.9894914423224154#3847 +0.9989831644531418#3848 +0.9984933770245784#3849 +0.988026973830809#3850 +0.9676885317128129#3851 +0.9376812656612382#3852 +0.8983049983593889#3853 +0.8499531644540819#3854 +0.7931088794867078#3855 +0.7283401127624386#3856 +0.6562940123886756#3857 +0.5776904391850793#3858 +0.4933147740722138#3859 +0.40401007080500034#3860 +0.31066863245827525#3861 +0.21422309582942212#3862 +0.11563711283978116#3863 +0.01589572204319201#3864 +-0.08400449355362567#3865 +-0.18306536401904885#3866 +-0.2802971058805568#3867 +-0.37472821171400944#3868 +-0.4654151571246921#3869 +-0.5514518281311794#3870 +-0.6319785747567164#3871 +-0.7061908003676237#3872 +-0.7733470009368971#3873 +-0.8327761739073366#3874 +-0.8838845226272941#3875 +-0.9261613893705334#3876 +-0.9591843576594301#3877 +-0.9826234729107834#3878 +-0.9962445392329431#3879 +-0.9999114594337439#3880 +-0.9935875948586621#3881 +-0.9773361314721403#3882 +-0.951319448524317#3883 +-0.9157974961112352#3884 +-0.8711251978394099#3885 +-0.8177489045464752#3886 +-0.7562019345111574#3887 +-0.6870992447133262#3888 +-0.6111312863871298#3889 +-0.5290571062604954#3890 +-0.44169676241113176#3891 +-0.34992313051729185#3892 +-0.25465318237253#3893 +-0.15683882380665107#3894 +-0.05745738355731774#3895 +0.042498151875634736#3896 +0.14202905982306696#3897 +0.24014086035331167#3898 +0.33585325278691963#3899 +0.42820991053700425#3900 +0.5162880364083785#3901 +0.5992075828820926#3902 +0.6761400452593388#3903 +0.7463167398065342#3904 +0.809036484189094#3905 +0.8636726034535386#3906 +0.909679191556477#3907 +0.9465965658773408#3908 +0.9740558602151771#3909 +0.991782710377798#3910 +0.9995999955380961#3911 +0.9974296079668007#3912 +0.9852932334590895#3913 +0.9633121346572913#3914 +0.9317059394346487#3915 +0.8907904464462075#3916 +0.8409744697730445#3917 +0.7827557541871054#3918 +0.7167160018499767#3919 +0.6435150601371714#3920 +0.56388432866127#3921 +0.47861945136875983#3922 +0.38857236672872686#3923 +0.29464279544528593#3924 +0.19776925074571347#3925 +0.0989196610665116#3926 +-9.183011675747517E-4#3927 +-0.10074708803994468#3928 +-0.19956924331117867#3929 +-0.2963973686720675#3930 +-0.3902639895011276#3931 +-0.48023122155121406#3932 +-0.5654001419788951#3933 +-0.6449197710843588#3934 +-0.7179955750192778#3935 +-0.7838974045063832#3936 +-0.8419667902496881#3937 +-0.891623522142032#3938 +-0.9323714465326797#3939 +-0.9638034236305925#3940 +-0.9856053955106381#3941 +-0.9975595240765649#3942 +-0.9995463676272517#3943 +-0.9915460742787029#3944 +-0.9736385803175122#3945 +-0.9460028115039091#3946 +-0.9089148953047087#3947 +-0.8627454019189386#3948 +-0.8079556416629041#3949 +-0.7450930557099968#3950 +-0.6747857462394504#3951 +-0.5977362006469884#3952 +-0.5147142725229772#3953 +-0.4265494895298316#3954 +-0.3341227650358171#3955 +-0.23835759631986517#3956 +-0.14021083729202993#3957 +-0.04066313792551283#3958 +0.059290854073709855#3959 +0.1586524314579784#3960 +0.25642880619063935#3961 +0.351643029055936#3962 +0.44334375101263535#3963 +0.5306147287591743#3964 +0.6125839795338587#3965 +0.6884324936783796#3966 +0.7574024179115897#3967 +0.8188046275489766#3968 +0.8720256120087151#3969 +0.9165336048066015#3970 +0.9518838967909858#3971 +0.9777232795296128#3972 +0.993793574451522#3973 +0.9999342124819873#3974 +0.9960838383956331#3975 +0.9822809238575657#3976 +0.9586633830272128#3977 +0.9254671945656303#3978 +0.8830240438147254#3979 +0.8317580087069611#3980 +0.7721813225188363#3981 +0.7048892558053088#3982 +0.6305541686531807#3983 +0.5499187926813633#3984 +0.4637888099120561#3985 +0.37302480266230326#3986 +0.27853365488995285#3987 +0.18125949090892593#3988 +0.08217424201115783#3989 +-0.017732064749593147#3990 +-0.11746119858080742#3991 +-0.21601669894331174#3992 +-0.31241383185560057#3993 +-0.40568942903026955#3994 +-0.49491151153316204#3995 +-0.579188601808808#3996 +-0.6576786310294751#3997 +-0.7295973527685441#3998 +-0.7942261789315666#3999 +-0.850919359650971#4000 +-0.8991104354052828#4001 +-0.9383178968954163#4002 +-0.9681499961264257#4003 +-0.9883086606239754#4004 +-0.9985924716759744#4005 +-0.9988986768417671#4006 +-0.9892242166205548#4007 +-0.9696657550209166#4008 +-0.9404187137259883#4009 +-0.9017753195046023#4010 +-0.8541216843780113#4011 +-0.7979339477162029#4012 +-0.7337735188107056#4013 +-0.6622814674585256#4014 +-0.5841721186046438#4015 +-0.5002259150432908#4016 +-0.41128161949152625#4017 +-0.3182279339494287#4018 +-0.2219946200834764#4019 +-0.12354320935531402#4020 +-0.023857395717228277#4021 +0.07606679313265759#4022 +0.1752309477299004#4023 +0.2726442526210754#4024 +0.36733338626426704#4025 +0.4583522461361782#4026 +0.5447914018758033#4027 +0.6257871820119776#4028 +0.7005303034832253#4029 +0.7682739577265967#4030 +0.8283412725419662#4031 +0.8801320751753131#4032 +0.9231288890464878#4033 +0.9569021042041466#4034 +0.9811142698463743#4035 +0.9955234660175518#4036 +0.9999857207925835#4037 +0.9944564487967827#4038 +0.978990896688197#4039 +0.9537435911512637#4040 +0.9189667949172583#4041 +0.8750079862384708#4042 +0.8223063870003694#4043 +0.761388574161709#4044 +0.6928632183716221#4045 +0.6174150023336926#4046 +0.5357977796827074#4047 +0.4488270427283285#4048 +0.35737177432550127#4049 +0.26234576528501463#4050 +0.1646984840777792#4051 +0.06540559005971848#4052 +-0.034540814994005645#4053 +-0.13414209964198492#4054 +-0.2324030807718237#4055 +-0.3283419671408341#4056 +-0.421000169109596#4057 +-0.5094518765527665#4058 +-0.5928133092478203#4059 +-0.6702515473148938#4060 +-0.7409928534768989#4061 +-0.8043304039866315#4062 +-0.8596313509760083#4063 +-0.9063431456627741#4064 +-0.9439990592352849#4065 +-0.9722228462525142#4066 +-0.9907325039641293#4067 +-0.9993430899887548#4068 +-0.9979685701971142#4069 +-0.9866226783366094#4070 +-0.9654187788082579#4071 +-0.9345687339670693#4072 +-0.8943807872634282#4073 +-0.8452564833764284#4074 +-0.7876866561121733#4075 +-0.7222465241546325#4076 +-0.6495899436706947#4077 +-0.5704428751954832#4078 +-0.48559613007466035#4079 +-0.3958974689388687#4080 +-0.30224313115974216#4081 +-0.20556887992236214#4082 +-0.10684065238883424#4083 +-0.0070449083734608125#4084 +0.09282122603764317#4085 +0.19175992144079657#4086 +0.28878261509631514#4087 +0.382919888320631#4088 +0.47323115259733173#4089 +0.5588140476266991#4090 +0.6388134574115429#4091 +0.712430054293582#4092 +0.7789282855711821#4093 +0.8376437228988098#4094 +0.8879897010354305#4095 +0.9294631796096705#4096 +0.9616497693329252#4097 +0.9842278724401516#4098 +0.996971895988431#4099 +0.9997545059070827#4100 +0.9925478992776083#4101 +0.9754240821312622#4102 +0.9485541499885891#4103 +0.9122065783295403#4104 +0.8667445400752233#4105 +0.8126222768841236#4106 +0.7503805605196052#4107 +0.6806412896372068#4108 +0.6041012759788476#4109 +0.5215252820602374#4110 +0.433738379916617#4111 +0.34161770725571405#4112 +0.24608370338771093#4113 +0.148090912499914#4114 +0.04861844616676457#4115 +-0.0513397996093618#4116 +-0.15078507507847264#4117 +-0.24872375592031773#4118 +-0.34417727121014857#4119 +-0.43619188097592587#4120 +-0.5238482056528556#4121 +-0.606270412220094#4122 +-0.6826349652347802#4123 +-0.7521788553259561#4124 +-0.8142072229319841#4125 +-0.8681003011075967#4126 +-0.9133196080303462#4127 +-0.9494133273329757#4128 +-0.9760208225032105#4129 +-0.9928762402445782#4130 +-0.9998111667946672#4131 +-0.9967563106597709#4132 +-0.983742194952588#4133 +-0.9608988524153701#4134 +-0.928454526175744#4135 +-0.8867333892168279#4136 +-0.836152305347941#4137 +-0.7772166640392184#4138 +-0.710515330737084#4139 +-0.6367147631153636#4140 +-0.5565523520505097#4141 +-0.47082905385571255#4142 +-0.38040138739018#4143 +-0.28617287600582364#4144 +-0.18908501984059176#4145 +-0.09010788866030936#4146 +0.009769570757759355#4147 +0.10954941585420723#4148 +0.20823467939966173#4149 +0.30483933086178727#4150 +0.39839812849642603#4151 +0.48797626372403896#4152 +0.572678701428026#4153 +0.651659122849752#4154 +0.7241283817258269#4155 +0.7893623891767129#4156 +0.8467093485634559#4157 +0.8955962680242483#4158 +0.9355346856197043#4159 +0.9661255498830958#4160 +0.987063207010698#4161 +0.9981384548535612#4162 +0.9992406331962297#4163 +0.9903587294370351#4164 +0.9715814886223723#4165 +0.943096526735531#4166 +0.905188456099808#4167 +0.8582360416262537#4168 +0.8027084163198831#4169 +0.7391603938577652#4170 +0.6682269250741585#4171 +0.5906167537416616#4172 +0.5071053350377174#4173 +0.41852708745281125#4174 +0.32576705555673624#4175 +0.22975206692580843#4176 +0.13144147158809297#4177 +0.03181755651504257#4178 +-0.06812426906522044#4179 +-0.16738541946787286#4180 +-0.26497411008946486#4181 +-0.35991526699183735#4182 +-0.45126026951859527#4183 +-0.5380964285991342#4184 +-0.619556106036139#4185 +-0.694825383659648#4186 +-0.7631521957283619#4187 +-0.8238538433219311#4188 +-0.8763238156428974#4189 +-0.9200378500720962#4190 +-0.9545591704274494#4191 +-0.9795428510871975#4192 +-0.9947392633726951#4193 +-0.9999965697556514#4194 +-0.9952622409685223#4195 +-0.9805835808595879#4196 +-0.9561072537485367#4197 +-0.9220778190050644#4198 +-0.8788352874924923#4199 +-0.8268117242916139#4200 +-0.7665269316492288#4201 +-0.6985832552859171#4202 +-0.6236595659567079#4203 +-0.5425044763989001#4204 +-0.4559288614410496#4205 +-0.3647977560097223#4206 +-0.27002171198645003#4207 +-0.17254770027431887#4208 +-0.07334964897773245#4209 +0.026581287765269457#4210 +0.1262466330675262#4211 +0.2246505637437602#4212 +0.32080986024666885#4213 +0.413763730671632#4214 +0.5025834106718274#4215 +0.5863814433645755#4216 +0.6643205465071567#4217 +0.7356219783442121#4218 +0.7995733185379454#4219 +0.8555355864366431#4220 +0.9029496255581272#4221 +0.9413416904964934#4222 +0.9703281804296068#4223 +0.9896194719317152#4224 +0.9990228127949825#4225 +0.9984442479458342#4226 +0.9878895582129948#4227 +0.9674642025673584#4228 +0.9373722644108115#4229 +0.8979144124423419#4230 +0.8494848964757872#4231 +0.7925676082260158#4232 +0.7277312464228656#4233 +0.6556236345614223#4234 +0.576965248063798#4235 +0.49254201552687205#4236 +0.4031974659835432#4237 +0.3098243006394673#4238 +0.21335547329769788#4239 +0.11475486859267976#4240 +0.015007671173599317#4241 +-0.08488947793497129#4242 +-0.1839384394407459#4243 +-0.28114954886159205#4244 +-0.3755515049258966#4245 +-0.46620107449380255#4246 +-0.5521925170309506#4247 +-0.6326666344685011#4248 +-0.7068193560262241#4249 +-0.7739097722219453#4250 +-0.83326753779418#4251 +-0.8842995695703985#4252 +-0.9264959723580396#4253 +-0.9594351336487255#4254 +-0.9827879362310783#4255 +-0.9963210466211054#4256 +-0.9998992464532412#4257 +-0.9934867835375585#4258 +-0.9771477290838324#4259 +-0.9510453375231913#4260 +-0.9154404153238058#4261 +-0.8706887150988698#4262 +-0.8172373810440856#4263 +-0.7556204812206672#4264 +-0.686453671323811#4265 +-0.6104280432544994#4266 +-0.5283032199576698#4267 +-0.4408997655208472#4268 +-0.34909098636902386#4269 +-0.2537942054755377#4270 +-0.15596159677414934#4271 +-0.05657067135184257#4272 +0.043385489518834315#4273 +0.14290815691957492#4274 +0.24100293325553057#4275 +0.33668968794737386#4276 +0.42901235057205933#4277 +0.5170484636024555#4278 +0.5999183992980321#4279 +0.6767941486544775#4280 +0.7469075945959657#4281 +0.8095581867470729#4282 +0.8641199411005575#4283 +0.910047694642637#4284 +0.9468825524416159#4285 +0.9742564727743516#4286 +0.9918959444774944#4287 +0.9996247197806206#4288 +0.9973655753156946#4289 +0.9851410837074361#4290 +0.9630733880351154#4291 +0.9313829814192799#4292 +0.8903865039273795#4293 +0.8404935788108798#4294 +0.7822027196851367#4295 +0.7160963495461387#4296 +0.6428349813924542#4297 +0.5631506185976866#4298 +0.4778394409747333#4299 +0.38775384961027715#4300 +0.2937939499548949#4301 +0.19689855826693#4302 +0.09803582127077107#4303 +-0.0018064572451918896#4304 +-0.10163068623749626#4305 +-0.20043945500755345#4306 +-0.2972454989996491#4307 +-0.3910815642220373#4308 +-0.48101007172909493#4309 +-0.5661324856002233#4310 +-0.6455982908137508#4311 +-0.7186134913118861#4312 +-0.7844485433468682#4313 +-0.8424456448409379#4314 +-0.8920253079272591#4315 +-0.9326921490011307#4316 +-0.9640398384292128#4317 +-0.9857551604609085#4318 +-0.9976211427766078#4319 +-0.9995192244033847#4320 +-0.9914304403370466#4321 +-0.973435611034188#4322 +-0.9457145348809033#4323 +-0.9085441917067469#4324 +-0.8622959752938333#4325 +-0.8074319825329326#4326 +-0.7445003963040872#4327 +-0.6741300082144792#4328 +-0.5970239359205429#4329 +-0.5139525978087609#4330 +-0.42574601522981265#4331 +-0.33328551919960797#4332 +-0.23749494443110458#4333 +-0.1393313986832356#4334 +-0.0397756996565601#4335 +0.06017742501298571#4336 +0.15952927674381348#4337 +0.2572871646747842#4338 +0.35247432430415254#4339 +0.4441396769975931#4340 +0.5313673328515297#4341 +0.6132857419622988#4342 +0.6890764026646912#4343 +0.7579820397300296#4344 +0.8193141708099325#4345 +0.872459985524356#4346 +0.9168884684603441#4347 +0.9521557049025041#4348 +0.9779093162821043#4349 +0.9938919810272516#4350 +0.9999440056349789#4351 +0.9960049202759392#4352 +0.9821140829889516#4353 +0.9584102864284875#4354 +0.9251303710943455#4355 +0.8826068588996672#4356 +0.8312646307218977#4357 +0.7716166811335055#4358 +0.7042589927297871#4359 +0.6298645812677813#4360 +0.5491767711152941#4361 +0.4630017681995254#4362 +0.3722006046639412#4363 +0.27768053571971557#4364 +0.18038597465155876#4365 +0.08128905655235824#4366 +-0.018620074929324236#4367 +-0.11834316077729126#4368 +-0.21688380088181902#4369 +-0.3132574097401875#4370 +-0.4065010541095631#4371 +-0.49568307431765746#4372 +-0.5799123930982074#4373 +-0.6583474189404683#4374 +-0.7302044549933965#4375 +-0.7947655295055287#4376 +-0.8513855695613934#4377 +-0.8994988464368491#4378 +-0.9386246281734907#4379 +-0.9683719828934694#4380 +-0.9884436848615913#4381 +-0.9986391842666134#4382 +-0.9988566110486644#4383 +-0.9890937927512102#4384 +-0.9694482762275201#4385 +-0.9401163529847545#4386 +-0.9013910979041106#4387 +-0.8536594409334871#4388 +-0.7973983010113463#4389 +-0.7331698208503304#4390 +-0.6616157501930958#4391 +-0.5834510336610189#4392 +-0.4994566672638683#4393 +-0.4104718949458387#4394 +-0.31738582313747726#4395 +-0.2211285370981292#4396 +-0.12266180781147178#4397 +-0.022969482287764426#4398 +0.07695234671026101#4399 +0.17610529329702104#4400 +0.27349865400582685#4401 +0.3681593065704404#4402 +0.45914143304108707#4403 +0.5455359700847644#4404 +0.6264796920455684#4405 +0.7011638360101038#4406 +0.768842182699172#4407 +0.8288385124441429#4408 +0.880553361750354#4409 +0.9234700129381941#4410 +0.9571596570153528#4411 +0.9812856781945266#4412 +0.9956070172470954#4413 +0.9999805800872513#4414 +0.9943626675208029#4415 +0.9788094118730793#4416 +0.9534762161332899#4417 +0.9186162012192256#4418 +0.874577676876719#4419 +0.8218006614837998#4420 +0.7608124855325126#4421 +0.6922225227169522#4422 +0.6167161012727448#4423 +0.5350476564038567#4424 +0.44803319221541943#4425 +0.35654212847044664#4426 +0.2614886136349538#4427 +0.16382239100866292#4428 +0.06451930920389547#4429 +-0.03542842821118951#4430 +-0.13502217648266945#4431 +-0.2332668277991315#4432 +-0.3291807540799851#4433 +-0.4218056150787603#4434 +-0.5102159338020652#4435 +-0.5935283435697821#4436 +-0.6709104143229327#4437 +-0.7415889699896632#4438 +-0.8048578138049752#4439 +-0.8600847843953653#4440 +-0.9067180721263033#4441 +-0.944291732601697#4442 +-0.9724303422262768#4443 +-0.9908527493140615#4444 +-0.9993748832630681#4445 +-0.997911593727921#4446 +-0.9864775014139562#4447 +-0.9651868519919665#4448 +-0.9342523745932234#4449 +-0.8939831562903168#4450 +-0.8447815538012954#4451 +-0.7871391732743425#4452 +-0.721631958321646#4453 +-0.6489144353812071#4454 +-0.5697131739050296#4455 +-0.4848195267173279#4456 +-0.39508172307869294#4457 +-0.30139639345970826#4458 +-0.20469961070567458#4459 +-0.10595753710616392#4460 +-0.006156770820793085#4461 +0.09370551188346128#4462 +0.1926315200878997#4463 +0.28963281781913364#4464 +0.3837402001745981#4465 +0.47401337729756166#4466 +0.5595503694425562#4467 +0.6394965192588389#4468 +0.7130530312441288#4469 +0.7794849530452186#4470 +0.8381285188589453#4471 +0.8883977815606836#4472 +0.9297904672943264#4473 +0.9618929940266256#4474 +0.9843846039221565#4475 +0.9970405682495806#4476 +0.9997344327968439#4477 +0.9924392813598633#4478 +0.9752280046803405#4479 +0.948272572145566#4480 +0.911842313527146#4481 +0.8663012279269533#4482 +0.8121043468184239#4483 +0.7497931875224875#4484 +0.6799903425454984#4485 +0.6033932588407144#4486 +0.5207672691488844#4487 +0.4329379450464886#4488 +0.340782848107444#4489 +0.245222761597941#4490 +0.14721249031441808#4491 +0.04773132048965235#4492 +-0.052226764911569364#4493 +-0.15166301774166763#4494 +-0.2495839038316188#4495 +-0.345011030055953#4496 +-0.4369909201134505#4497 +-0.5246045413471654#4498 +-0.6069764874149429#4499 +-0.6832837250602186#4500 +-0.7527638175882598#4501 +-0.8147225428815908#4502 +-0.8685408298379123#4503 +-0.9136809439239166#4504 +-0.9496918600409943#4505 +-0.9762137690189293#4506 +-0.9929816727101918#4507 +-0.9998280317638305#4508 +-0.9966844396232869#4509 +-0.9835823060220958#4510 +-0.9606525431482109#4511 +-0.9281242576126963#4512 +-0.8863224612922014#4513 +-0.835664823917724#4514 +-0.7766574998567214#4515 +-0.7098900707859835#4516 +-0.6360296547864077#4517 +-0.5558142407196545#4518 +-0.47004531448738884#4519 +-0.37957985084908624#4520 +-0.2853217508135146#4521 +-0.18821281015864433#4522 +-0.08922330931955144#4523 +0.010657681332957976#4524 +0.11043218395654948#4525 +0.20910328470207334#4526 +0.30568509454720927#4527 +0.39921259997368597#4528 +0.4887513050633646#4529 +0.5734065686725494#4530 +0.6523325433905668#4531 +0.7247406269674924#4532 +0.7899073417671557#4533 +0.84718156351653#4534 +0.8959910271242357#4535 +0.9358480445641681#4536 +0.9663543776930457#4537 +0.9872052173142973#4538 +0.9981922287307987#4539 +0.9992056333562993#4540 +0.990235305586768#4541 +0.9713708739720819#4542 +0.942800825677183#4543 +0.9048106231806318#4544 +0.8577798520278825#4545 +0.8021784281379878#4546 +0.7385619025590685#4547 +0.6675659105858819#4548 +0.5898998207020697#4549 +0.5063396468047552#4550 +0.41772029453019976#4551 +0.32492721915266787#4552 +0.2288875784080196#4553 +0.13056096864009153#4554 +0.03092983683122936#4555 +-0.06901033568320586#4556 +-0.16826097973527826#4557 +-0.2658304156975201#4558 +-0.36074376201796365#4559 +-0.45205267591435566#4560 +-0.538844828901757#4561 +-0.6202530224771886#4562 +-0.6954638528804155#4563 +-0.7637258383554432#4564 +-0.8243569277078173#4565 +-0.8767513151347024#4566 +-0.9203854932362104#4567 +-0.9548234837282926#4568 +-0.9797211935936381#4569 +-0.9948298531453608#4570 +-0.9999985016514786#4571 +-0.9951754956846467#4572 +-0.9804090251262118#4573 +-0.9558466316688474#4574 +-0.9217337346287321#4575 +-0.8784111787968661#4576 +-0.8263118288305887#4577 +-0.7659562442130079#4578 +-0.6979474779947189#4579 +-0.622965051287066#4580 +-0.5417581637118176#4581 +-0.45513820764619767#4582 +-0.36397066105846365#4583 +-0.2691664399381365#4584 +-0.17167279672454178#4585 +-0.07246385567355651#4586 +0.02746912026995325#4587 +0.12712763384380946#4588 +0.2255159301231064#4589 +0.32165094577426767#4590 +0.41457213149891764#4591 +0.5033510495249554#4592 +0.5871006502498729#4593 +0.6649841353471638#4594 +0.736223318778593#4595 +0.8001064021718566#4596 +0.8559950868746282#4597 +0.9033309516237009#4598 +0.941641032105658#4599 +0.9705425466599525#4600 +0.9897467209067285#4601 +0.9990616730849682#4602 +0.9983943312716201#4603 +0.9877513633254895#4604 +0.9672391102641967#4605 +0.9370625237398807#4606 +0.8975231182300396#4607 +0.849015958404538#4608 +0.7920257117700172#4609 +0.7271218060323789#4610 +0.6549527395633749#4611 +0.5762396018192422#4612 +0.49176886845321643#4613 +0.4023845431107794#4614 +0.30897972442422345#4615 +0.21248768246633692#4616 +0.11387253382433511#4617 +0.014119608465615191#4618 +-0.0857743953535712#4619 +-0.18481136976762977#4620 +-0.282001770065486#4621 +-0.37637450189423827#4622 +-0.46698662411293085#4623 +-0.5529327703487394#4624 +-0.6333541951184943#4625 +-0.7074473541296843#4626 +-0.7744719330294112#4627 +-0.833758244380689#4628 +-0.8847139189579444#4629 +-0.9268298245045077#4630 +-0.9596851528138249#4631 +-0.9829516243059571#4632 +-0.9963967680886273#4633 +-0.9998862447295328#4634 +-0.9933851885315454#4635 +-0.9769585558992319#4636 +-0.9507704763159315#4637 +-0.9150826124162126#4638 +-0.8702515455393219#4639 +-0.8167252128863131#4640 +-0.7550384318796021#4641 +-0.6858075564440695#4642 +-0.6097243186023826#4643 +-0.5275489169172812#4644 +-0.44010242083882695#4645 +-0.3482585668498676#4646 +-0.25293502837991955#4647 +-0.1550842467156025#4648 +-0.05568391452213869#4649 +0.044272792938575674#4650 +0.14378714128688722#4651 +0.2418648160491692#4652 +0.33752585751936537#4653 +0.4298144521924413#4654 +0.5178084829369766#4655 +0.6006287424847309#4656 +0.6774477181790405#4657 +0.7474978602077449#4658 +0.8100792507071913#4659 +0.8645665971101664#4660 +0.9104154798625334#4661 +0.947167792083456#4662 +0.9744563168179221#4663 +0.9920083961471714#4664 +0.9996486554964923#4665 +0.9973007559200001#4666 +0.9849881568541509#4667 +0.9628338817188064#4668 +0.9310592887078893#4669 +0.8899818590514812#4670 +0.8400120248483158#4671 +0.7816490681639204#4672 +0.7154761323692573#4673 +0.6421543955649226#4674 +0.5624164643081216#4675 +0.4770590536501172#4676 +0.3869350266227958#4677 +0.29294487271317216#4678 +0.19602771047009768#4679 +0.09715190414215104#4680 +-0.002694611897834578#4681 +-0.10251420426645728#4682 +-0.2013095085927397#4683 +-0.2980933948532388#4684 +-0.3918988304489386#4685 +-0.4817885424753208#4686 +-0.5668643826434057#4687 +-0.6462763012805672#4688 +-0.7192308407458727#4689 +-0.784999063396549#4690 +-0.8429238348919543#4691 +-0.89242639006269#4692 +-0.933012115740859#4693 +-0.9642754927713421#4694 +-0.985904147825149#4695 +-0.9976819745304649#4696 +-0.9994912927360821#4697 +-0.991314024332571#4698 +-0.9732318738827741#4699 +-0.9454255122568215#4700 +-0.9081727714285188#4701 +-0.8618458684701038#4702 +-0.8069076864822988#4703 +-0.7439071496193778#4704 +-0.6734737384204668#4705 +-0.596311200248075#4706 +-0.5131905176770778#4707 +-0.42494220509167996#4708 +-0.3324480104602216#4709 +-0.2366321052009449#4710 +-0.13845185016667366#4711 +-0.03888823001163328#4712 +0.06106394848294375#4713 +0.16040599618933662#4714 +0.25814532020497766#4715 +0.35330534151262716#4716 +0.44493525263509953#4717 +0.5321195177892803#4718 +0.6139870206170349#4719 +0.6897197680919065#4720 +0.7585610636350439#4721 +0.8198230677772865#4722 +0.872893670823771#4723 +0.9172426088516656#4724 +0.9524267619320054#4725 +0.9780945816375459#4726 +0.9939896035984427#4727 +0.9999530100094578#4728 +0.9959252164849727#4729 +0.9819464674064724#4730 +0.9581564338139892#4731 +0.9247928178592397#4732 +0.882188977764299#4733 +0.8307705970164385#4734 +0.7710514310794344#4735 +0.7036281741187975#4736 +0.6291744970309135#4737 +0.5484343163461312#4738 +0.4622143612606979#4739 +0.37137611306529994#4740 +0.2768271975087733#4741 +0.17951231610164317#4742 +0.080403806970907#4743 +-0.019508070421117876#4744 +-0.1192250296220056#4745 +-0.21775073173746473#4746 +-0.31410074052022424#4747 +-0.40731235853160475#4748 +-0.49645424609610067#4749 +-0.5806357269395575#4750 +-0.6590156875320847#4751 +-0.7308109812164121#4752 +-0.7953042531504141#4753 +-0.8518511078795672#4754 +-0.8998865479233226#4755 +-0.938930619043168#4756 +-0.968593205786728#4757 +-0.9885779293924083#4758 +-0.9986851091080121#4759 +-0.9988137573348106#4760 +-0.9889625886622468#4761 +-0.9692300327113339#4762 +-0.9398132506584176#4763 +-0.9010061652658754#4764 +-0.8531965241030328#4765 +-0.7968620253006231#4766 +-0.7325655445489706#4767 +-0.6609495110301554#4768 +-0.5827294884779846#4769 +-0.49868702550169774#4770 +-0.4096618466106099#4771 +-0.3165434619643894#4772 +-0.22026227968157627#4773 +-0.12178030950921329#4774 +-0.022081550739451946#4775 +0.07783783958610788#4776 +0.17697949994829187#4777 +0.27435283964863644#4778 +0.36898493646420194#4779 +0.4599302577648192#4780 +0.5462801079625782#4781 +0.6271717078977682#4782 +0.7017968154430443#4783 +0.7694098011915946#4784 +0.8293350985397009#4785 +0.8809739537249303#4786 +0.9238104083758077#4787 +0.957416454797311#4788 +0.981456312482278#4789 +0.9956897831192412#4790 +0.9999746505745556#4791 +0.9942681018689966#4792 +0.978627154950896#4793 +0.9532080889916499#4794 +0.918264882895897#4795 +0.8741466776282583#4796 +0.8212942877122281#4797 +0.7602357967571705#4798 +0.691581281021455#4799 +0.6160167137321476#4800 +0.5342971110672784#4801 +0.44723898828376585#4802 +0.3557122013668739#4803 +0.2606312557167434#4804 +0.16294616871272874#4805 +0.0636329774537779#4806 +-0.036316013481625595#4807 +-0.13590214681479854#4808 +-0.23413039082027445#4809 +-0.33001928135389064#4810 +-0.4226107283180879#4811 +-0.5109795885814625#4812 +-0.5942429097031241#4813 +-0.6715687521016191#4814 +-0.7421845015202269#4815 +-0.8053845887332195#4816 +-0.8605375393603357#4817 +-0.9070922833500508#4818 +-0.9445836610893715#4819 +-0.9726370711249284#4820 +-0.9909722130568702#4821 +-0.9994058882078054#4822 +-0.9978538300834279#4823 +-0.9863315463354743#4824 +-0.9649541638143936#4825 +-0.9339352782599134#4826 +-0.893584820123014#4827 +-0.8443059578433111#4828 +-0.7865910695232777#4829 +-0.7210168232490024#4830 +-0.6482384152132938#4831 +-0.5689830232119021#4832 +-0.484042540923356#4833 +-0.3942656655690927#4834 +-0.30054941801136287#4835 +-0.203830180017291#4836 +-0.10507433824178496#4837 +-0.005268628411524885#4838 +0.09458972381224616#4839 +0.19350296678289045#4840 +0.29048279207301586#4841 +0.38456020932559115#4842 +0.4747952280852878#4843 +0.56028624987239#4844 +0.6401790766567751#4845 +0.7136754457222707#4846 +0.7800410056438436#4847 +0.8386126536842945#4848 +0.8888051612976158#4849 +0.9301170215391718#4850 +0.9621354599573143#4851 +0.9845405588992577#4852 +0.997108454022515#4853 +0.9997135710734083#4854 +0.9923298805835022#4855 +0.9750311579474484#4856 +0.947990246283629#4857 +0.9114773294428521#4858 +0.865857232420625#4859 +0.811585776146395#4860 +0.7492052230714963#4861 +0.6793388590619841#4862 +0.6026847657322923#4863 +0.5200088454444972#4864 +0.4321371686650891#4865 +0.33994772014193364#4866 +0.24436162637089454#4867 +0.1463339520043707#4868 +0.04684415716099188#4869 +-0.053113689016120146#4870 +-0.15254084076963417#4871 +-0.2504438548654753#4872 +-0.34584451674923133#4873 +-0.4377896145426254#4874 +-0.5253604632215139#4875 +-0.607682083812971#4876 +-0.6839319458959563#4877 +-0.7533481860533897#4878 +-0.815237220159576#4879 +-0.8689806734435207#4880 +-0.9140415590852341#4881 +-0.9499696436105324#4882 +-0.9764059454750843#4883 +-0.9930863218893389#4884 +-0.999844108045964#4885 +-0.9966117823795099#4886 +-0.9834216412195708#4887 +-0.960405476096536#4888 +-0.9277932569241824#4889 +-0.8859108342163137#4890 +-0.8351766832961389#4891 +-0.7760977230291721#4892 +-0.7092642508574933#4893 +-0.6353440447428335#4894 +-0.555075690949919#4895 +-0.4692612043366593#4896 +-0.37875801488679633#4897 +-0.28447040055293665#4898 +-0.18734045201016314#4899 +-0.08833865959742333#4900 +0.011545783501135819#4901 +0.11131486494748018#4902 +0.20997172505907102#4903 +0.3065306171012951#4904 +0.400026756542992#4905 +0.4895259608645748#4906 +0.5741339836009652#4907 +0.6530054493566748#4908 +0.7253523005173126#4909 +0.7904516712607705#4910 +0.8476531101935705#4911 +0.8963850794461777#4912 +0.9361606652904673#4913 +0.9665824432207433#4914 +0.9873464488880291#4915 +0.9982452152113649#4916 +0.9991698453203011#4917 +0.990111100616431#4918 +0.9711594930824132#4919 +0.942504380916159#4920 +0.9044320765263129#4921 +0.8573229857933078#4922 +0.8016478071795532#4923 +0.7379628286659907#4924 +0.6669043695064731#4925 +0.589182422336119#4926 +0.5055735591595948#4927 +0.4169131721003451#4928 +0.3240871264386389#4929 +0.2280229093385172#4930 +0.12968036270263658#4931 +0.030042092749259296#4932 +-0.06989634786427318#4933 +-0.16913640727460627#4934 +-0.2666865116125137#4935 +-0.36157197248122797#4936 +-0.4528447257207117#4937 +-0.5395928041513566#4938 +-0.6209494496485853#4939 +-0.696101773503521#4940 +-0.76429887853826#4941 +-0.8248593618222606#4942 +-0.8771781230251833#4943 +-0.9207324103793711#4944 +-0.9550870438427141#4945 +-0.9798987632737787#4946 +-0.9949196581736731#4947 +-0.9999996447258055#4948 +-0.9950879653837672#4949 +-0.9802336960239586#4950 +-0.9555852555956543#4951 +-0.9213889231679229#4952 +-0.8779863771905777#4953 +-0.8258112815560504#4954 +-0.7653849525731279#4955 +-0.6973111501467191#4956 +-0.6222700452084615#4957 +-0.5410114236736072#4958 +-0.45434719482800395#4959 +-0.3631432789988919#4960 +-0.26831095556522977#4961 +-0.17079775775536865#4962 +-0.07157800520824754#4963 +0.028356931106371913#4964 +0.12800853433893156#4965 +0.2263811186103717#4966 +0.32249177757630465#4967 +0.4153802053023024#4968 +0.5041182913233582#4969 +0.5878193940168606#4970 +0.6656471996326018#4971 +0.7368240784633209#4972 +0.8006388546636893#4973 +0.856453912084273#4974 +0.9037115651213302#4975 +0.9419396309270177#4976 +0.9707561473043234#4977 +0.9898731891470789#4978 +0.9990997452924452#4979 +0.9983436270413116#4980 +0.9876123892773043#4981 +0.9670132549808855#4982 +0.9367520438927767#4983 +0.8971311160311438#4984 +0.8485463506102431#4985 +0.7914831905461722#4986 +0.7265117920717187#4987 +0.6542813279237506#4988 +0.5755135010238178#4989 +0.49099533346112295#4990 +0.40157130282796105#4991 +0.3081349044787646#4992 +0.2116197240198723#4993 +0.1129901092307529#4994 +0.013231534619763643#4995 +-0.08665924511138251#4996 +-0.1856841543111132#4997 +-0.2828537688199873#4998 +-0.3771972019698358#4999 +-0.46777180536241775#5000 +-0.5536725875006172#5001 +-0.6340412561643325#5002 +-0.7080747941826249#5003 +-0.7750334829158495#5004 +-0.8342482932797833#5005 +-0.8851275704630837#5006 +-0.9271629455465875#5007 +-0.9599344149575082#5008 +-0.9831145370062989#5009 +-0.9964717035757779#5010 +-0.9998724542728749#5011 +-0.9932828099207633#5012 +-0.9767686120675627#5013 +-0.9504948651193542#5014 +-0.9147240876706985#5015 +-0.8698136895056156#5016 +-0.8162124004771674#5017 +-0.754455786947096#5018 +-0.6851609005837718#5019 +-0.6090201129858933#5020 +-0.5267941977343409#5021 +-0.4393047289940346#5022 +-0.34742587261645447#5023 +-0.25207565176341384#5024 +-0.1542067743230842#5025 +-0.05479711376769998#5026 +0.04516006143493376#5027 +0.14466601223164102#5028 +0.24272650805435486#5029 +0.3383617608433046#5030 +0.43061621476543416#5031 +0.5185680938124212#5032 +0.6013386118818538#5033 +0.6781007533174772#5034 +0.748087536176257#5035 +0.8105996756584224#5036 +0.8650125711300333#5037 +0.910782546926049#5038 +0.9474522845778579#5039 +0.9746553921882468#5040 +0.9921200652981245#5041 +0.9996718026668301#5042 +0.9972351498308482#5043 +0.9848344530198655#5044 +0.9625936158972921#5045 +0.9307348615558133#5046 +0.8895765121377056#5047 +0.8395298082652133#5048 +0.7810948000601897#5049 +0.7148553508085741#5050 +0.6414733031914381#5051 +0.5616818663716927#5052 +0.4762782900104988#5053 +0.38611589841218874#5054 +0.2920955643898892#5055 +0.19515670804216095#5056 +0.0962679103779054#5057 +-0.003582764424906281#5058 +-0.10339764142988875#5059 +-0.20217940338041937#5060 +-0.2989410555639972#5061 +-0.3927157875371532#5062 +-0.48256663317581633#5063 +-0.5675958325311052#5064 +-0.6469538019499782#5065 +-0.7198476228342582#5066 +-0.785548964221163#5067 +-0.8434013600255303#5068 +-0.8928267682319421#5069 +-0.9333313464994675#5070 +-0.9645103864710906#5071 +-0.9860523574858352#5072 +-0.9977420192901507#5073 +-0.9994625726473769#5074 +-0.9911968263571076#5075 +-0.9730273690239829#5076 +-0.9451357438596512#5077 +-0.9078006347630092#5078 +-0.8613950818028046#5079 +-0.8063827539245794#5080 +-0.7433133161238353#5081 +-0.6728169373750934#5082 +-0.5955979941918066#5083 +-0.5124280327290739#5084 +-0.4241380597494972#5085 +-0.33161023947830387#5086 +-0.23576907931001329#5087 +-0.13757219243615187#5088 +-0.03800072969078855#5089 +0.06195042378427414#5090 +0.16128258910297158#5091 +0.2590032721042872#5092 +0.3541360800258346#5093 +0.4457304772975865#5094 +0.5328712829790856#5095 +0.6146878149448826#5096 +0.6903625894525244#5097 +0.7591394891698853#5098 +0.8203313180496092#5099 +0.8733266675648592#5100 +0.9175960257012121#5101 +0.9526970676656734#5102 +0.9782790754497961#5103 +0.9940864420880884#5104 +0.9999612255983212#5105 +0.9958447270856055#5106 +0.9817780772423467#5107 +0.9579018253839627#5108 +0.9244545351265829#5109 +0.8817704007382551#5110 +0.8302759079802887#5111 +0.7704855728025053#5112 +0.7029968004699444#5113 +0.6284839164869312#5114 +0.5476914289595397#5115 +0.46142658971669825#5116 +0.37055132851675693#5117 +0.2759736409302586#5118 +0.17863851594834093#5119 +0.07951849396516575#5120 +-0.02039605052444625#5121 +-0.12010680441925582#5122 +-0.21861749082633866#5123 +-0.31494382353041833#5124 +-0.4081233416563674#5125 +-0.4972250262601245#5126 +-0.5813586027622296#5127 +-0.6596834362771361#5128 +-0.7314169309591104#5129 +-0.7958423494412308#5130 +-0.8523159742382354#5131 +-0.9002735395588507#5132 +-0.9392358692630565#5133 +-0.968813664631682#5134 +-0.9887113941105229#5135 +-0.9987302461639416#5136 +-0.9987701157340123#5137 +-0.9888306044571701#5138 +-0.9690110246445276#5139 +-0.9395094069860908#5140 +-0.9006205218935647#5141 +-0.8527329342518376#5142 +-0.796325121007094#5143 +-0.7319606903833317#5144 +-0.6602827504952915#5145 +-0.5820074836247582#5146 +-0.49791699036393944#5147 +-0.4088514751248763#5148 +-0.3157008510946928#5149 +-0.2193958485171965#5150 +-0.12089871514394081#5151 +-0.02119360177276821#5152 +0.07872327106164467#5153 +0.17785356699406288#5154 +0.27520680887564847#5155 +0.36981027529422345#5156 +0.4607187196850812#5157 +0.5470238149222044#5158 +0.6278632290226549#5159 +0.702429241282698#5160 +0.7699768127560778#5161 +0.8298310304368901#5162 +0.8813938507672423#5163 +0.9241500750907953#5164 +0.9576724973474368#5165 +0.9816261725750176#5166 +0.9957717635686961#5167 +0.9999679322591741#5168 +0.9941727519159652#5169 +0.978444126065427#5170 +0.9529392099378654#5171 +0.917912840224423#5172 +0.8737149888330982#5173 +0.8207872660851259#5174 +0.7596585082906251#5175 +0.6909394937909978#5176 +0.6153168402636389#5177 +0.533546144265068#5178 +0.44644443155990476#5179 +0.3548819936695015#5180 +0.2597736922067416#5181 +0.16206981788121674#5182 +0.06274659550858111#5183 +-0.03720357010510971#5184 +-0.13678200994417533#5185 +-0.23499376915399928#5186 +-0.33085754830104785#5187 +-0.4234155081924356#5188 +-0.5117428402885212#5189 +-0.5949570070841342#5190 +-0.6722265601315991#5191 +-0.7427794475987832#5192 +-0.8059107283557985#5193 +-0.8609896155137471#5194 +-0.9074657790388062#5195 +-0.9448748444680102#5196 +-0.9728430327853834#5197 +-0.9910908950983123#5198 +-0.9994361047985073#5199 +-0.9977952793092036#5200 +-0.9861848132163057#5201 +-0.9647207144591039#5202 +-0.9336174452172925#5203 +-0.893185779075762#5204 +-0.8438296958776669#5205 +-0.7860423452913707#5206 +-0.7204011194219737#5207 +-0.6475618837002585#5208 +-0.5682524236921068#5209 +-0.4832651733056984#5210 +-0.39344929705384485#5211 +-0.2997022054828736#5212 +-0.20296058854309376#5213 +-0.1041910564924412#5214 +-0.004380481846299932#5215 +0.0954738611264548#5216 +0.19437426083829612#5217 +0.2913325371874285#5218 +0.38537991512671593#5219 +0.47557670434371857#5220 +0.5610216883356741#5221 +0.6408611290668913#5222 +0.7142972972369935#5223 +0.7805964429283947#5224 +0.8390961269929299#5225 +0.8892118399248504#5226 +0.9304428420865926#5227 +0.9623771669337133#5228 +0.9846957372484245#5229 +0.9971755532536798#5230 +0.9996919207532333#5231 +0.9922196970348302#5232 +0.9748335420878756#5233 +0.9477071726255013#5234 +0.9111116263645896#5235 +0.8654125539065005#5236 +0.8110665652771305#5237 +0.7486166676304692#5238 +0.6786868397006105#5239 +0.6019757972125022#5240 +0.5192500115453864#5241 +0.4313360514041404#5242 +0.3391123240180043#5243 +0.2435002983859096#5244 +0.14545529826283898#5245 +0.04595695688065448#5246 +-0.054000571223331524#5247 +-0.15341854346986936#5248 +-0.25130360834348353#5249 +-0.346677730632457#5250 +-0.4385879636333711#5251 +-0.5261159706795642#5252 +-0.6083872008575425#5253 +-0.6845796272306204#5254 +-0.7539319602603454#5255 +-0.8157512543599176#5256 +-0.8694198315774354#5257 +-0.9144014532298141#5258 +-0.9502466778224503#5259 +-0.9765973517200701#5260 +-0.993190187699463#5261 +-0.9998593956283852#5262 +-0.996538338985758#5263 +-0.9832602006717591#5264 +-0.9601576514552531#5265 +-0.927461524371324#5266 +-0.8854985083138918#5267 +-0.8346878838682738#5268 +-0.7755373339981713#5269 +-0.7086378714453144#5270 +-0.6346579335255096#5271 +-0.5543367033239355#5272 +-0.46847672402209806#5273 +-0.3779358801516458#5274 +-0.28361882589570886#5275 +-0.18646794608333994#5276 +-0.08745394019181343#5277 +0.012433876561680912#5278 +0.11219745813066402#5279 +0.2108399997855538#5280 +0.3073758978570232#5281 +0.4008405975620669#5282 +0.4903002305165539#5283 +0.5748609456394255#5284 +0.6536778402172297#5285 +0.7259634018927463#5286 +0.7909953772281428#5287 +0.848123988222581#5288 +0.896778424679212#5289 +0.9364725475519793#5290 +0.9668097462862708#5291 +0.9874869016204777#5292 +0.9982974142534595#5293 +0.9991332691164677#5294 +0.989986114624008#5295 +0.9709473461201218#5296 +0.94220719268632#5297 +0.9040528164354815#5298 +0.856865443282945#5299 +0.8011165538631789#5300 +0.7373631726511333#5301 +0.6662423023578131#5302 +0.5884645592097554#5303 +0.5048070727065923#5304 +0.41610572079997543#5305 +0.32324677807738733#5306 +0.2271580603994272#5307 +0.12879965447042646#5308 +0.029154324969461867#5309 +-0.07078230490945918#5310 +-0.17001170139524382#5311 +-0.2675423971590831#5312 +-0.36239989772826614#5313 +-0.45363641831282586#5314 +-0.540340353757865#5315 +-0.6216453870009271#5316 +-0.6967391450257175#5317 +-0.7648713158247487#5318 +-0.8253611452688975#5319 +-0.877604238977637#5320 +-0.9210786012279#5321 +-0.9553498505627952#5322 +-0.9800755599875371#5323 +-0.9950086783867864#5324 +-0.9999999989777303#5325 +-0.9949996501349354#5326 +-0.9800575936911434#5327 +-0.9553231257351534#5328 +-0.9210433848946539#5329 +-0.8775608830087473#5330 +-0.825310082862874#5331 +-0.7648130571802731#5332 +-0.696674272243908#5333 +-0.6215745482691752#5334 +-0.5402642568733623#5335 +-0.45355582361048796#5336 +-0.36231561048371774#5337 +-0.2674552595426102#5338 +-0.16992258405710617#5339 +-0.07069209828064123#5340 +0.0292447195741433#5341 +0.1288893338579619#5342 +0.22724612852302042#5343 +0.3233323549894589#5344 +0.4161879514443078#5345 +0.5048851354617692#5346 +0.5885376740985312#5347 +0.6663097388403877#5348 +0.7374242569244644#5349 +0.8011706755933992#5350 +0.8569120617036162#5351 +0.9040914657507545#5352 +0.9422374867250122#5353 +0.9709689821942126#5354 +0.9899988765529969#5355 +0.9991370293873787#5356 +0.9982921352949088#5357 +0.9874726361780741#5358 +0.9667866368955992#5359 +0.9364408251144325#5360 +0.8967384061548997#5361 +0.84807607346337#5362 +0.7909400449824686#5363 +0.7259012050221171#5364 +0.6536094001722174#5365 +0.5747869462503366#5366 +0.4902214111608231#5367 +0.4007577457766425#5368 +0.307289841469558#5369 +0.21075159864302492#5370 +0.11210759550806611#5371 +0.012343450336634304#5372 +-0.087544026510359#5373 +-0.18655679238266815#5374 +-0.28370554445296553#5375 +-0.3780196045036721#5376 +-0.4685566176228442#5377 +-0.5544119679029521#5378 +-0.6347278170640023#5379 +-0.708701675690067#5380 +-0.775594421438261#5381 +-0.8347376841048699#5382 +-0.8855405237594921#5383 +-0.9274953352214845#5384 +-0.9601829198831353#5385 +-0.9832766742035843#5386 +-0.9965458530234417#5387 +-0.9998578750941465#5388 +-0.9931796477859774#5389 +-0.9765778977386692#5390 +-0.9502185041508856#5391 +-0.9143648413700989#5392 +-0.8693751473431697#5393 +-0.8156989442211995#5394 +-0.7538725468827897#5395 +-0.684513704253056#5396 +-0.6083154269605701#5397 +-0.5260390630042369#5398 +-0.43850669061575864#5399 +-0.34659290432568596#5400 +-0.2512160763039714#5401 +-0.15332918028882067#5402 +-0.05391026978811178#5403 +0.046047294307954255#5404 +0.14554476906050678#5405 +0.24358800859131027#5406 +0.33919739725975856#5407 +0.4314176376585382#5408 +0.5193272956295426#5409 +0.6020480069293945#5410 +0.6787532535546172#5411 +0.7486766220363146#5412 +0.8111194611902097#5413 +0.865457862808335#5414 +0.9111488955436093#5415 +0.9477360297003892#5416 +0.9748536987282782#5417 +0.9922309518422593#5418 +0.9996941612733737#5419 +0.9971687570999949#5420 +0.9846799723258353#5421 +0.9623525907601151#5422 +0.9304097002189882#5423 +0.8891704635058256#5424 +0.8390469294419863#5425 +0.7805399158111991#5426 +0.7142340053538154#5427 +0.6407917048093058#5428 +0.5609468253679145#5429 +0.47549715067181214#5430 +0.3852964656246555#5431 +0.291246025655054#5432 +0.19428555167024208#5433 +0.09538384067540505#5434 +-0.004470914125755297#5435 +-0.1042809970308588#5436 +-0.20304913868434413#5437 +-0.2997884804632161#5438 +-0.3935324348421947#5439 +-0.483344343216756#5440 +-0.568326834686291#5441 +-0.6476307922875124#5442 +-0.7204638370904717#5443 +-0.7860982453869009#5444 +-0.8438782198649527#5445 +-0.8932264421191625#5446 +-0.9336498410251193#5447 +-0.9647445193431539#5448 +-0.9861997893260463#5449 +-0.9978012770082968#5450 +-0.9994330641599263#5451 +-0.9910788465031126#5452 +-0.9728220966191456#5453 +-0.9448452299179868#5454 +-0.9074277820037917#5455 +-0.8609436156475553#5456 +-0.8058571852738867#5457 +-0.742718896285927#5458 +-0.6721596055965009#5459 +-0.5948843183143766#5460 +-0.5116651435662637#5461 +-0.42333357983764397#5462 +-0.3307722069147612#5463 +-0.23490586743913935#5464 +-0.13669242618562055#5465 +-0.0371131993941631#5466 +0.06283685021764836#5467 +0.16215905479318588#5468 +0.25986101969588604#5469 +0.3549665391884163#5470 +0.44652535035771196#5471 +0.533622627827888#5472 +0.6153881243929944#5473 +0.6910048662394316#5474 +0.7597173158782414#5475 +0.8208389212259487#5476 +0.8737589754060353#5477 +0.9179487187301777#5478 +0.952966621890268#5479 +0.9784627975733101#5480 +0.9941824964197943#5481 +0.9999686523950879#5482 +0.9957634521413347#5483 +0.9816089126294156#5484 +0.9576464613392653#5485 +0.9241155231632417#5486 +0.8813511281517454#5487 +0.8297805640037017#5488 +0.7699191067491161#5489 +0.7023648722813098#5490 +0.6277928401806244#5491 +0.5469481095415738#5492 +0.4606384541889888#5493 +0.3697262516689739#5494 +0.27511986665753085#5495 +0.1777645748809813#5496 +0.07863311823337614#5497 +-0.02128401453896417#5498 +-0.12098848447359085#5499 +-0.2194840774648326#5500 +-0.3157866581058347#5501 +-0.40893400284423276#5502 +-0.4979954142018187#5503 +-0.5820810199960956#5504 +-0.6603506646489725#5505 +-0.7320223037435822#5506 +-0.796379817953585#5507 +-0.8527801682707603#5508 +-0.9006598210382154#5509 +-0.9395403785924068#5510 +-0.9690333592544564#5511 +-0.988844078910672#5512 +-0.9987745953988022#5513 +-0.9987256862806896#5514 +-0.9886978402400751#5515 +-0.9687912521998316#5516 +-0.9392048222074141#5517 +-0.9002341680913335#5518 +-0.8522686717455321#5519 +-0.7957875885542126#5520 +-0.7313552588304589#5521 +-0.6596154691143745#5522 +-0.5812850196707813#5523 +-0.497146562457916#5524 +-0.4080407811277736#5525 +-0.3148579911929501#5526 +-0.2185292442883396#5527 +-0.12001702541096315#5528 +-0.02030563608803385#5529 +0.0796086404385363#5530 +0.17872749374496208#5531 +0.27606056101334203#5532 +0.3706353224095644#5533 +0.4615068181800175#5534 +0.5477670903770854#5535 +0.6285542548748294#5536 +0.703061113030274#5537 +0.7705432169454224#5538 +0.830326307744572#5539 +0.8818130525461197#5540 +0.9244890128152635#5541 +0.9579277844637908#5542 +0.9817952583387776#5543 +0.9958529585308029#5544 +0.9999604251464057#5545 +0.994076617736911#5546 +0.9782603253610263#5547 +0.9526695791840002#5548 +0.9175600734824577#5549 +0.8732826108317093#5550 +0.8202795970023783#5551 +0.7590806205881805#5552 +0.6902971615317548#5553 +0.6146164814192049#5554 +0.5327947565895087#5555 +0.44564952267049884#5556 +0.35405150603310986#5557 +0.2589159237813043#5558 +0.16119333920530024#5559 +0.06186016406738981#5560 +-0.03809109738163067#5561 +-0.13766176517685647#5562 +-0.23585696211936405#5563 +-0.33169555426032#5564 +-0.424219954067078#5565 +-0.5125056883212689#5566 +-0.595670635149608#5567 +-0.6728838378940628#5568 +-0.7433738077561016#5569 +-0.8064362322577487#5570 +-0.8614410124990496#5571 +-0.9078385588979957#5572 +-0.9451652825079584#5573 +-0.973048227045201#5574 +-0.9912087953447836#5575 +-0.9994655330113421#5576 +-0.9977359414514271#5577 +-0.9860373021721781#5578 +-0.9644865041102175#5579 +-0.9332988757160339#5580 +-0.8927860334632826#5581 +-0.8433527682799882#5582 +-0.7854930010113974#5583 +-0.7197848473261622#5584 +-0.6468848413756778#5585 +-0.5675213759218636#5586 +-0.48248742447746074#5587 +-0.3926326181768149#5588 +-0.2988547565424325#5589 +-0.20209083696892502#5590 +-0.10330769255477226#5591 +-0.0034923318255946968#5592 +0.09635792312877281#5593 +0.19524540156693176#5594 +0.2921820524921821#5595 +0.3861993169314748#5596 +0.4763578054565078#5597 +0.5617566842523725#5598 +0.6415426759512564#5599 +0.7149185852978462#5600 +0.7811512644608015#5601 +0.8395789384035385#5602 +0.8896178171216423#5603 +0.9307679286796158#5604 +0.9626181147651897#5605 +0.9848501388472685#5606 +0.9972418658901542#5607 +0.9996694818533945#5608 +0.9921087308007481#5609 +0.9746351572574807#5610 +0.9474233513944414#5611 +0.9107452045807868#5612 +0.8649671927352955#5613 +0.8105467146201293#5614 +0.7480275216635965#5615 +0.6780342849756218#5616 +0.6012663538405035#5617 +0.5184907680500401#5618 +0.4305345938954794#5619 +0.3382766603945284#5620 +0.24263877832231104#5621 +0.14457652978281252#5622 +0.045069720348370305#5623 +-0.05488741083372423#5624 +-0.15429612515013374#5625 +-0.2521631635875603#5626 +-0.34751067104847855#5627 +-0.4393859667560341#5628 +-0.5268710631254516#5629 +-0.6090918379925355#5630 +-0.6852267685533883#5631 +-0.7545151397487074#5632 +-0.8162646450771994#5633 +-0.8698583038932945#5634 +-0.9147606260738101#5635 +-0.9505229624582529#5636 +-0.9767879876029256#5637 +-0.993293270058646#5638 +-0.9998738944990372#5639 +-0.996464109499956#5640 +-0.9830979845059883#5641 +-0.9599090694198203#5642 +-0.927129060215757#5643 +-0.885085483910135#5644 +-0.8341984260196418#5645 +-0.7749763332056944#5646 +-0.7080109330434688#5647 +-0.6339713216755684#5648 +-0.5535972784245394#5649 +-0.4676918741624206#5650 +-0.37711344729204793#5651 +-0.28276702751346355#5652 +-0.1855952930663155#5653 +-0.08656915180049524#5654 +0.013321959814158985#5655 +0.1130799628100045#5656 +0.21170810819671795#5657 +0.30822093614772517#5658 +0.4016541223890385#5659 +0.4910741134086396#5660 +0.5755874542145792#5661 +0.6543497154419204#5662 +0.7265739306118212#5663 +0.791538459240455#5664 +0.8485941972321822#5665 +0.897171062513109#5666 +0.9367836911027245#5667 +0.9670362867103555#5668 +0.9876265754008688#5669 +0.9983488258159133#5670 +0.9990959047736466#5671 +0.9898603477080745#5672 +0.970734433252527#5673 +0.941909261222057#5674 +0.9036728432072584#5675 +0.8564072248576555#5676 +0.8005846686078619#5677 +0.7367629349874418#5678 +0.6655797096620707#5679 +0.5877462318891539#5680 +0.5040401880502716#5681 +0.41529794126592323#5682 +0.3224061747316915#5683 +0.22629303227285133#5684 +0.12791884463807107#5685 +0.028266534192014796#5686 +-0.07166820612001427#5687 +-0.17088686140685108#5688 +-0.26839807166219604#5689 +-0.3632275371060979#5690 +-0.45442775306629446#5691 +-0.5410874771316942#5692 +-0.6223408339853317#5693 +-0.6973759669443137#5694 +-0.7654431497634311#5695 +-0.825862277651974#5696 +-0.8780296626559881#5697 +-0.9214240655087582#5698 +-0.9556119036812614#5699 +-0.9802515835954748#5700 +-0.9950969137144907#5701 +-0.9999995644069737#5702 +-0.9949105500078052#5703 +-0.9798807182666571#5704 +-0.9550602422940854#5705 +-0.9206971200814494#5706 +-0.8771346965869603#5707 +-0.8248082331463521#5708 +-0.7642405584854947#5709 +-0.6960368447885882#5710 +-0.620878561017742#5711 +-0.5395166639003695#5712 +-0.4527640946178#5713 +-0.3614876561657189#5714 +-0.26659935254516054#5715 +-0.1690472763199991#5716 +-0.06980613558944763#5717 +0.030132484973073366#5718 +0.12977003170621854#5719 +0.22811095917882387#5720 +0.3241726773507713#5721 +0.4169953692878687#5722 +0.5056515813353825#5723 +0.5892554899283811#5724 +0.6669717524479807#5725 +0.738023853688666#5726 +0.8017018645415418#5727 +0.8573695353713178#5728 +0.904470653212348#5729 +0.942534599264724#5730 +0.9711810511617585#5731 +0.9901237830253536#5732 +0.9991735253403632#5733 +0.9982398560730228#5734 +0.9873321041380214#5735 +0.9665592561870703#5736 +0.936128867650305#5737 +0.8963449889110358#5738 +0.8476051273348236#5739 +0.7903962755072824#5740 +0.7252900453651407#5741 +0.6529369568387209#5742 +0.574059938071828#5743 +0.48944710216270515#5744 +0.3999438725984719#5745 +0.3064445360631003#5746 +0.20988330702048066#5747 +0.11122499335230866#5748 +0.011455356316654522#5749 +-0.08842873885267832#5750 +-0.18742928329404956#5751 +-0.28455709629262965#5752 +-0.37884170884712265#5753 +-0.4693410602752329#5754 +-0.5551509109725986#5755 +-0.6354138772760167#5756 +-0.7093279981575924#5757 +-0.7761547481542365#5758 +-0.8352264164699689#5759 +-0.8859527785214759#5760 +-0.9278269932670445#5761 +-0.9604306673947122#5762 +-0.9834380357699366#5763 +-0.9966192163731373#5764 +-0.9998425072048462#5765 +-0.9930757022085512#5766 +-0.9763864130629667#5767 +-0.9499413936284902#5768 +-0.9140048737977496#5769 +-0.8689359193978601#5770 +-0.8151848445233696#5771 +-0.7532887121466815#5772 +-0.6838659679623627#5773 +-0.6076102610821948#5774 +-0.5252835133225399#5775 +-0.43770830633340774#5776 +-0.3457596626345197#5777 +-0.2503563026795348#5778 +-0.15245146530496573#5779 +-0.053023383282823196#5780 +0.04693449085788134#5781 +0.14642341108041368#5782 +0.24444931698057448#5783 +0.3400327661096653#5784 +0.43221872023967534#5785 +0.5200860877895621#5786 +0.602756927067857#5787 +0.6794052183758368#5788 +0.7492651173233087#5789 +0.8116386068926013#5790 +0.8659024717938725#5791 +0.9115145254262775#5792 +0.9480190272272615#5793 +0.9750512362816127#5794 +0.9923410556921204#5795 +0.9997157312984891#5796 +0.9971015777798035#5797 +0.984524714893898#5798 +0.9621108064973708#5799 +0.9300838049538668#5800 +0.8887637134760895#5801 +0.8385633887594791#5802 +0.779984415854583#5803 +0.713612096495033#5804 +0.6401096009560984#5805 +0.5602113418765098#5806 +0.4747156362501377#5807 +0.3844767289064784#5808 +0.2903962571786933#5809 +0.19341424204141744#5810 +0.09449969573191108#5811 +-0.005359060299902684#5812 +-0.10516427037266961#5813 +-0.2039187138185583#5814 +-0.30063566888253596#5815 +-0.39434877171997756#5816 +-0.48412167198476425#5817 +-0.5690573885324254#5818 +-0.6483072717592313#5819 +-0.7210794830285081#5820 +-0.7866469064605475#5821 +-0.8443544140341247#5822 +-0.8936254114091304#5823 +-0.9339675990666196#5824 +-0.9649778912028727#5825 +-0.9863464432295038#5826 +-0.997859747638167#5827 +-0.9994027672970033#5828 +-0.990960084863636#5829 +-0.9726160568301596#5830 +-0.9445539706609553#5831 +-0.9070542134449333#5832 +-0.8604914703604248#5833 +-0.8053309809447339#5834 +-0.7421238905744688#5835 +-0.6715017436031232#5836 +-0.5941701731786572#5837 +-0.5109018507903337#5838 +-0.4225287659906093#5839 +-0.32993391343054546#5840 +-0.23404247026913375#5841 +-0.13581255210894666#5842 +-0.036225639821747355#5843 +0.06372322708394657#5844 +0.16303539256871574#5845 +0.26071856230327317#5846 +0.35579671834539367#5847 +0.4473198711885637#5848 +0.534373551743106#5849 +0.61608794840904#5850 +0.6916465979460678#5851 +0.7602945433043837#5852 +0.8213458769059611#5853 +0.8741905940063404#5854 +0.9183006876603954#5855 +0.953235424393193#5856 +0.9786457478631875#5857 +0.9942777665178029#5858 +0.9999752903939005#5859 +0.9956813917162614#5860 +0.9814389737010982#5861 +0.9573903418813012#5862 +0.9237757822365931#5863 +0.8809311603354478#5864 +0.8292845654773529#5865 +0.7693520333660359#5866 +0.7017323900512924#5867 +0.6271012686570413#5868 +0.5462043586784855#5869 +0.4598499552991679#5870 +0.3689008831726845#5871 +0.27426587536395725#5872 +0.17689049358883693#5873 +0.07774768047399928#5874 +-0.022171961764168652#5875 +-0.12187006908946504#5876 +-0.22035049096930778#5877 +-0.31662924358157246#5878 +-0.40974434145568117#5879 +-0.4987654093134345#5880 +-0.5828029780712501#5881 +-0.6610173721212264#5882 +-0.7326270990922569#5883 +-0.7969166582634752#5884 +-0.8532436896109454#5885 +-0.9010453920566842#5886 +-0.9398441467909955#5887 +-0.9692522894817371#5888 +-0.9889759836881826#5889 +-0.9988181567776074#5890 +-0.998680469009892#5891 +-0.9885642961156978#5892 +-0.9685707155506215#5893 +-0.9388994965626704#5894 +-0.8998471041639711#5895 +-0.851803736950367#5896 +-0.7952494283660311#5897 +-0.7307492503679691#5898 +-0.6589476674138139#5899 +-0.580562097185996#5900 +-0.4963757423914076#5901 +-0.4072297652588474#5902 +-0.31401488292408225#5903 +-0.21766246767865804#5904 +-0.11913524100583359#5905 +-0.01941765438575316#5906 +0.08049394701832663#5907 +0.17960127951156024#5908 +0.27691409538820333#5909 +0.3714600771593562#5910 +0.46229455262790753#5911 +0.5485099337408607#5912 +0.6292447849091506#5913 +0.7036924301872969#5914 +0.7711090133127998#5915 +0.8308209300720284#5916 +0.8822315587308598#5917 +0.9248272212818289#5918 +0.9581823159449805#5919 +0.9819635696401684#5920 +0.9959333679415077#5921 +0.9999521292421726#5922 +0.9939796994076727#5923 +0.9780757529826917#5924 +0.9523991969427621#5925 +0.917206582948294#5926 +0.8728495439651891#5927 +0.8197712808644788#5928 +0.7585021341057248#5929 +0.6896542847504532#5930 +0.6139156377513494#5931 +0.5320429486333605#5932 +0.4448542622426411#5933 +0.3532207391128595#5934 +0.2580579511171135#5935 +0.1603167333764215#5936 +0.060973683829497964#5937 +-0.038978594611030024#5938 +-0.13854141181881474#5939 +-0.23671996903540732#5940 +-0.3325332985706153#5941 +-0.42502406530739834#5942 +-0.5132681320779048#5943 +-0.5963837933365737#5944 +-0.6735405848704926#5945 +-0.7439675815232989#5946 +-0.806961100024507#5947 +-0.8618917299601419#5948 +-0.908210622633538#5949 +-0.9454549749800931#5950 +-0.9732526537425062#5951 +-0.9913259137032742#5952 +-0.9994941728230944#5953 +-0.997675816556909#5954 +-0.985889013319461#5955 +-0.9642515329525#5956 +-0.9329795700074526#5957 +-0.8923855836009295#5958 +-0.8428751754265166#5959 +-0.7849430371167282#5960 +-0.7191680074477368#5961 +-0.6462072887736614#5962 +-0.5667898804778864#5963 +-0.4817092950521987#5964 +-0.3918156295822699#5965 +-0.29800707185858066#5966 +-0.20122092598092017#5967 +-0.10242424712565217#5968 +-0.0026041790500589026#5969 +0.09724190912177542#5970 +0.19611638828156605#5971 +0.29303133731710534#5972 +0.3870184140934526#5973 +0.47713853080745516#5974 +0.5624912370426562#5975 +0.6422237167722069#5976 +0.7155393094147028#5977 +0.7817054698033725#5978 +0.8400610875352369#5979 +0.8900230925677216#5980 +0.9310922810617849#5981 +0.9628583032616626#5982 +0.9850037635739842#5983 +0.997307391879625#5984 +0.9996462543915937#5985 +0.9919969819687958#5986 +0.9744360036127667#5987 +0.9471387828143523#5988 +0.9103780643805088#5989 +0.8645211492583496#5990 +0.8100262245854944#5991 +0.7474377856356479#5992 +0.6773811954018097#5993 +0.6005564361759669#5994 +0.5177311155574154#5995 +0.42973279677136517#5996 +0.33744072993074997#5997 +0.2417770668597405#5998 +0.14369764725754003#5999 +0.04418244826406843#6000 +-0.055774207147682296#6001 +-0.15517358511811483#6002 +-0.25302251991961405#6003 +-0.34834333734020045#6004 +-0.4401836232810802#6005 +-0.5276257399634938#6006 +-0.6097959946620711#6007 +-0.6858733693537384#6008 +-0.7550977240584129#6009 +-0.8167773919064145#6010 +-0.8702960900451931#6011 +-0.9151190773338755#6012 +-0.9507984972999824#6013 +-0.9769778529732608#6014 +-0.9933955688855672#6015 +-0.9998876046464816#6016 +-0.9963890939806623#6017 +-0.9829349928502283#6018 +-0.9596597301863407#6019 +-0.926795864719758#6020 +-0.8846717613308728#6021 +-0.8337083101363698#6022 +-0.7744147210943076#6023 +-0.70738343614654#6024 +-0.6332842097346686#6025 +-0.5528574168350533#6026 +-0.46690665537678444#6027 +-0.37629071695680905#6028 +-0.28191500607817316#6029 +-0.18472249364751417#6030 +-0.0856842951214665#6031 +0.014210032557972981#6032 +0.11396237828930539#6033 +0.21257604960772433#6034 +0.3090657313067615#6035 +0.40246733038212795#6036 +0.491847608930326#6037 +0.5763135087532935#6038 +0.6550210745007133#6039 +0.7271838861928992#6040 +0.7920809168692764#6041 +0.8490637368514327#6042 +0.8975629926381219#6043 +0.9370940956972457#6044 +0.9672620643142826#6045 +0.9877654701190156#6046 +0.9983994498581685#6047 +0.9990577523213141#6048 +0.9897337999678465#6049 +0.9705207546475926#6050 +0.9416105867584041#6051 +0.903292157141399#6052 +0.8559483308789219#6053 +0.8000521518331991#6054 +0.7361621161484359#6055 +0.6649165919419564#6056 +0.5870274409409932#6057 +0.5032729057956179#6058 +0.4144898341354351#6059 +0.3215653170646921#6060 +0.22542782564119876#6061 +0.1270379339004295#6062 +0.027378721117284403#6063 +-0.07255405079706279#6064 +-0.17176188661902594#6065 +-0.26925353444682204#6066 +-0.3640548899618095#6067 +-0.4552187293568439#6068 +-0.5418341736834483#6069 +-0.6230357900531704#6070 +-0.6980122387569291#6071 +-0.7660143799031949#6072 +-0.8263627585761534#6073 +-0.8784543937246257#6074 +-0.9217688029494135#6075 +-0.9558732029913825#6076 +-0.980426833958729#6077 +-0.9951843640871785#6078 +-0.9999983410138783#6079 +-0.994820665072666#6080 +-0.9797030698900342#6081 +-0.9547966054798351#6082 +-0.9203501290014732#6083 +-0.876707818261429#6084 +-0.8243057328023874#6085 +-0.7636674569404289#6086 +-0.6953988682836173#6087 +-0.620182084003217#6088 +-0.5387686453443945#6089 +-0.45197200847452446#6090 +-0.36065941669805746#6091 +-0.26574323524809457#6092 +-0.1681718352345661#6093 +-0.06892011783359091#6094 +0.031020226602815813#6095 +0.1306506271889307#6096 +0.22897560989552862#6097 +0.3250127439973229#6098 +0.4178024581960238#6099 +0.5064176283395591#6100 +0.5899728409401348#6101 +0.6676332399331268#6102 +0.7386228682829117#6103 +0.8022324210890692#6104 +0.8578263327264827#6105 +0.9048491272069747#6106 +0.9428309683117653#6107 +0.9713923540396625#6108 +0.9902479084656121#6109 +0.9992092331226073#6110 +0.9981867894168958#6111 +0.98719079326801#6112 +0.9663311130346763#6113 +0.935816171746493#6114 +0.8959508646099132#6115 +0.8471335125961273#6116 +0.789851882549586#6117 +0.7246783135829253#6118 +0.6522639984537427#6119 +0.5733324770618192#6120 +0.4886724070776113#6121 +0.39912968393550297#6122 +0.3055989889262417#6123 +0.20901484983722315#6124 +0.1103423034597536#6125 +0.010567253260429845#6126 +-0.08931338144040267#6127 +-0.188301626356961#6128 +-0.2854084236672018#6129 +-0.37966351435164025#6130 +-0.47012513270074757#6131 +-0.5558894161266145#6132 +-0.6360994362591523#6133 +-0.7099537610911038#6134 +-0.7767144626217417#6135 +-0.8357144899895256#6136 +-0.8863643344238125#6137 +-0.9281579194216271#6138 +-0.9606776572967937#6139 +-0.9835986215780596#6140 +-0.9966917935669896#6141 +-0.9998263506170976#6142 +-0.9929709732704858#6143 +-0.9761941581915149#6144 +-0.9496635337707765#6145 +-0.9136441852376241#6146 +-0.8684960060161877#6147 +-0.8146701017892441#6148 +-0.7527042831993508#6149 +-0.683217692222682#6150 +-0.6069046159070638#6151 +-0.5245275492852929#6152 +-0.4369095767768165#6153 +-0.34492614820028883#6154 +-0.24949633156836795#6155 +-0.151573630063937#6156 +-0.052136454951487186#6157 +0.04782165038481744#6158 +0.14730193759821267#6159 +0.2453104325426728#6160 +0.34086786673401337#6161 +0.4330194618768823#6162 +0.520844469693879#6163 +0.6034653717379834#6164 +0.6800566472668097#6165 +0.7498530215729831#6166 +0.8121571123560498#6167 +0.8663463977358996#6168 +0.9118794362856127#6169 +0.9483012769352218#6170 +0.9752480046924157#6171 +0.9924503767608481#6172 +0.9997365127251596#6173 +0.997033611923271#6174 +0.9843686808465345#6175 +0.9618682632997994#6176 +0.9297571760175435#6177 +0.888356262369377#6178 +0.8380791865991503#6179 +0.7794283006285677#6180 +0.7129896247228424#6181 +0.6394269921699185#6182 +0.5594754164776916#6183 +0.47393374736200167#6184 +0.38365668890433663#6185 +0.28954625963117797#6186 +0.1925427798430516#6187 +0.09361547624491363#6188 +-0.00624720224670175#6189 +-0.10604746075851858#6190 +-0.2047881280970657#6191 +-0.30148262015362104#6192 +-0.3951647975265046#6193 +-0.4848986188666166#6194 +-0.5697874934931841#6195 +-0.6489832398314691#6196 +-0.7216945601626925#6197 +-0.7871949470092712#6198 +-0.844829942157383#6199 +-0.8940236757871044#6200 +-0.9342846203732933#6201 +-0.9652105018661431#6202 +-0.9864923190805144#6203 +-0.9979174311336345#6204 +-0.9993716820825086#6205 +-0.9908405415323671#6206 +-0.9724092498195667#6207 +-0.9442619663183269#6208 +-0.9066799293811373#6209 +-0.8600386462981047#6210 +-0.8048041413522367#6211 +-0.7415282994588526#6212 +-0.6708433519139391#6213 +-0.593455559348028#6214 +-0.5101381550034353#6215 +-0.4217236188433001#6216 +-0.32909535968697506#6217 +-0.23317888848111895#6218 +-0.13493257090025113#6219 +-0.03533805167372523#6220 +0.06460955368391785#6221 +0.16391160173822988#6222 +0.2615758992499447#6223 +0.35662661684184926#6224 +0.4481140391633548#6225 +0.5351240541323454#6226 +0.6167872864409377#6227 +0.6922877840661794#6228 +0.7608711709929451#6229 +0.8218521846897158#6230 +0.8746215230252763#6231 +0.9186519322142014#6232 +0.9535034749623935#6233 +0.978827926175101#6234 +0.9943722523069567#6235 +0.9999811395895223#6236 +0.9955985458751219#6237 +0.9812682605914571#6238 +0.9571334672121197#6239 +0.9234353126146541#6240 +0.8805104976206695#6241 +0.8287879127925286#6242 +0.768784353100621#6243 +0.7010993542788487#6244 +0.6264092024617534#6245 +0.5454601769570099#6246 +0.45906109366927184#6247 +0.36807522367901085#6248 +0.27341166772324016#6249 +0.17601627276145895#6250 +0.07686218138548857#6251 +-0.02305989149962678#6252 +-0.12275155757146444#6253 +-0.22121673065631767#6254 +-0.317471579292981#6255 +-0.4105543568514991#6256 +-0.4995350109875823#6257 +-0.5835244764181962#6258 +-0.6616835581679836#6259 +-0.7332313165280581#6260 +-0.7974528699474296#6261 +-0.8537065378931546#6262 +-0.9014302523101099#6263 +-0.9401471736192033#6264 +-0.969470455140827#6265 +-0.9891071083390048#6266 +-0.9988609302659951#6267 +-0.9986344639572882#6268 +-0.9884299721893809#6269 +-0.9683494148708616#6270 +-0.9385934302927073#6271 +-0.8994593304168023#6272 +-0.8513381302330933#6273 +-0.7947106408670623#6274 +-0.7301426654738955#6275 +-0.6582793459203868#6276 +-0.57983871674066#6277 +-0.49560453077245464#6278 +-0.40641842815784546#6279 +-0.31317152695315215#6280 +-0.21679551937188488#6281 +-0.11825336262412364#6282 +-0.018529657366386256#6283 +0.08137919010266577#6284 +0.18047492360459536#6285 +0.27776741132694516#6286 +0.37228453889301355#6287 +0.46308192240736856#6288 +0.5492523444275588#6289 +0.6299348185809126#6290 +0.7043231922557696#6291 +0.7716742014118969#6292 +0.8313148970290903#6293 +0.8826493689913352#6294 +0.9251647002237049#6295 +0.9584360915902255#6296 +0.9821311063464223#6297 +0.9960129917373818#6298 +0.9999430445530186#6299 +0.9938819970047018#6300 +0.9778904090760181#6301 +0.9521280634274347#6302 +0.9168523689007734#6303 +0.8724157885751502#6304 +0.8192623180723986#6305 +0.7579230492995809#6306 +0.6890108639542089#6307 +0.6132143098129139#6308 +0.5312907209896665#6309 +0.4440586509036509#6310 +0.3523896935640783#6311 +0.25719977489095747#6312 +0.15944000108606712#6313 +0.06008715549418129#6314 +-0.03986606109322982#6315 +-0.13942094917616493#6316 +-0.23758278922136963#6317 +-0.3333707805711021#6318 +-0.42582784127909545#6319 +-0.514030170956996#6320 +-0.5970964810824756#6321 +-0.6741968005428316#6322 +-0.7445607684319929#6323 +-0.807485331242046#6324 +-0.8623417675414878#6325 +-0.9085819699519408#6326 +-0.9457439216558984#6327 +-0.9734563127160425#6328 +-0.9914422500813985#6329 +-0.9995220242111722#6330 +-0.9976149046730771#6331 +-0.9857399467751279#6332 +-0.9640158011713017#6333 +-0.9326595283434245#6334 +-0.8919844298045866#6335 +-0.8423969176939883#6336 +-0.7843924540411868#6337 +-0.7185506002732748#6338 +-0.645529226428678#6339 +-0.5660579379371954#6340 +-0.4809307856437185#6341 +-0.390998331914669#6342 +-0.2971591520999911#6343 +-0.20035085626528476#6344 +-0.10154072090196273#6345 +-0.0017160242202876055#6346 +0.09812581840815446#6347 +0.19698722029514487#6348 +0.29388039099226304#6349 +0.3878372059665269#6350 +0.47791887978070685#6351 +0.5632253461270936#6352 +0.6429042509925226#6353 +0.7161594690979219#6354 +0.7822590585189381#6355 +0.840542574007695#6356 +0.8904276659433977#6357 +0.9314158989772433#6358 +0.963097732233666#6359 +0.9851566113073892#6360 +0.9973721311704039#6361 +0.9996222383861532#6362 +0.9918844506271235#6363 +0.9742360813108307#6364 +0.946853467109708#6365 +0.9100102060533641#6366 +0.8640744238275117#6367 +0.8095050955838005#6368 +0.7468474600118203#6369 +0.6767275714943461#6370 +0.5998460447788914#6371 +0.5169710546667434#6372 +0.4289306606642735#6373 +0.33660453328606976#6374 +0.2409151646779356#6375 +0.142818651380304#6376 +0.04329514132764921#6377 +-0.05666095946568067#6378 +-0.15605092268165224#6379 +-0.25388167666176487#6380 +-0.3491757288507966#6381 +-0.4409809325792998#6382 +-0.5283800005983845#6383 +-0.6104996703106945#6384 +-0.6865194291216177#6385 +-0.7556797127299064#6386 +-0.8172894944430965#6387 +-0.8707331896877957#6388 +-0.9154768067272556#6389 +-0.9510732821302911#6390 +-0.9771669476813053#6391 +-0.9934970840995313#6392 +-0.9999005260599038#6393 +-0.9963132924870508#6394 +-0.9827712258330508#6395 +-0.9594096339514985#6396 +-0.9264619381461588#6397 +-0.8842573409024587#6398 +-0.8332175366050724#6399 +-0.7738524981070234#6400 +-0.7067553812495116#6401 +-0.6325965982448198#6402 +-0.5521171191390967#6403 +-0.466121068284588#6404 +-0.3754676897949175#6405 +-0.2810627622619315#6406 +-0.1838495485154199#6407 +-0.08479937085272225#6408 +0.015098094092590981#6409 +0.11484470387249736#6410 +0.21344382333392106#6411 +0.30991028266773873#6412 +0.4032802208998583#6413 +0.49262071647146216#6414 +0.5770391086828398#6415 +0.6556919168640254#6416 +0.7277932681548336#6417 +0.7926227496867044#6418 +0.8495326067099487#6419 +0.8979542147450874#6420 +0.9374037610906888#6421 +0.9674870789199537#6422 +0.9879035856653547#6423 +0.9984492863402916#6424 +0.9990188117895656#6425 +0.9896064715031475#6426 +0.9703063104738733#6427 +0.9413111695309624#6428 +0.9029107585381975#6429 +0.8554887617087299#6430 +0.799519003959252#6431 +0.7355607166080547#6432 +0.6642529497205526#6433 +0.5863081869322719#6434 +0.5025052265478807#6435 +0.41368140004596415#6436 +0.32072420573967675#6437 +0.2245624411869641#6438 +0.12615692295238418#6439 +0.026490886445597783#6440 +-0.07343983824183033#6441 +-0.17263677634152869#6442 +-0.2701087848381528#6443 +-0.3648819556427663#6444 +-0.4560093465605342#6445 +-0.5425804428241162#6446 +-0.623730254656246#6447 +-0.698647959961658#6448 +-0.7665850057934409#6449 +-0.8268625876466449#6450 +-0.8788784318485121#6451 +-0.9221128132779292#6452 +-0.9561337482870396#6453 +-0.9806013109390582#6454 +-0.9952710294358666#6455 +-0.9999963287994093#6456 +-0.9947299954004215#6457 +-0.9795246487014079#6458 +-0.9545322155003655#6459 +-0.9200024119284395#6460 +-0.876280248368885#6461 +-0.8238025822273635#6462 +-0.7630937529971512#6463 +-0.694760343232246#6464 +-0.6194851177749968#6465 +-0.5380202017954916#6466 +-0.45117956580547686#6467 +-0.35983089273406754#6468 +-0.264886908326737#6469 +-0.16729626149137494#6470 +-0.06803404571198197#6471 +0.031907943763099904#6472 +0.13153111961146466#6473 +0.2298400799910786#6474 +0.32585255426645#6475 +0.4186092175321232#6476 +0.5071832758700237#6477 +0.5906897265679296#6478 +0.6682942007740298#6479 +0.7392213002346851#6480 +0.8027623448174664#6481 +0.8582824534087788#6482 +0.9052268874360858#6483 +0.943126593632353#6484 +0.9716028906612442#6485 +0.9903712527758591#6486 +0.999244152705944#6487 +0.9981329353683882#6488 +0.9870487036795089#6489 +0.9661022076183817#6490 +0.9355027376496583#6491 +0.8955560335624265#6492 +0.8466612296193012#6493 +0.7893068665388091#6494 +0.7240660101580189#6495 +0.6515905255481277#6496 +0.572604563794148#6497 +0.4878973265166385#6498 +0.39831518042998615#6499 +0.3047532007259689#6500 +0.20814622777831115#6501 +0.10945952652668677#6502 +0.009679141868516105#6503 +-0.0901979535757059#6504 +-0.18917382088327842#6505 +-0.28625952590513565#6506 +-0.3804850203689661#6507 +-0.47090883428089403#6508 +-0.5566274827824501#6509 +-0.6367844934726242#6510 +-0.7105789639969848#6511 +-0.7772735643992611#6512 +-0.8362019042785367#6513 +-0.8867751911418572#6514 +-0.9284881134241899#6515 +-0.9609238893945486#6516 +-0.9837584315012794#6517 +-0.9967635845477477#6518 +-0.9998094053436452#6519 +-0.9928654610543938#6520 +-0.9760011332759688#6521 +-0.9493849247969268#6522 +-0.9132827759742418#6523 +-0.8680554075451663#6524 +-0.8141547164248637#6525 +-0.7521192605018084#6526 +-0.6825688775453889#6527 +-0.6061984919918056#6528 +-0.5237711714888175#6529 +-0.43611050257604106#6530 +-0.3440923616804886#6531 +-0.24863616364883562#6532 +-0.15069567525819083#6533 +-0.051249485493732934#6534 +0.04870877218895102#6535 +0.14818034792090215#6536 +0.2461713545983377#6537 +0.3417026984740565#6538 +0.4338198619385158#6539 +0.5216024407442641#6540 +0.6041733403809366#6541 +0.6807075397136739#6542 +0.7504403343215857#6543 +0.8126749771715466#6544 +0.8667896402842373#6545 +0.912243627833765#6546 +0.9485827786016253#6547 +0.9754440038054716#6548 +0.9925589149622076#6549 +0.9997565055369927#6550 +0.9969648595840104#6551 +0.9842118703068278#6552 +0.9616249613587246#6553 +0.9294298136676707#6554 +0.8879481105070949#6555 +0.8375943233429495#6556 +0.7788715705718298#6557 +0.7123665905282638#6558 +0.6387438789892236#6559 +0.5587390497519747#6560 +0.47315148462417583#6561 +0.3828363462650965#6562 +0.28869603368300556#6563 +0.1916711657625737#6564 +0.09273118291190512#6565 +-0.0071353392655659825#6566 +-0.10693056749172508#6567 +-0.20565738083405258#6568 +-0.30232933360837705#6569 +-0.395980511618076#6570 +-0.48567518324943976#6571 +-0.5705171489926438#6572 +-0.6496586959710069#6573 +-0.7223090680078381#6574 +-0.7877423666007652#6575 +-0.8453048038596203#6576 +-0.8944212349389244#6577 +-0.9346009046950666#6578 +-0.9654423511494763#6579 +-0.9866374167640076#6580 +-0.9979743274491972#6581 +-0.9993398085409632#6582 +-0.9907202166036044#6583 +-0.9722016757505011#6584 +-0.9439692171204415#6585 +-0.9063049301076473#6586 +-0.8595851438177926#6587 +-0.8042766669119777#6588 +-0.7409321234088941#6589 +-0.6701844310483026#6590 +-0.5927404773861927#6591 +-0.5093740568079891#6592 +-0.4209181390308347#6593 +-0.3282565463455203#6594 +-0.2323151227563079#6595 +-0.13405248325368307#6596 +-0.0344504356502464#6597 +0.06549582931840771#6598 +0.1647876816105547#6599 +0.2624330298596137#6600 +0.35745623402314036#6601 +0.44890785365562746#6602 +0.5358741344035932#6603 +0.6174861379370337#6604 +0.6929284240939841#6605 +0.7614471984890684#6606 +0.8223578441778256#6607 +0.8750517621229162#6608 +0.9190024521145261#6609 +0.9537707733864255#6610 +0.9790093323653442#6611 +0.9944659537127232#6612 +0.9999861999773392#6613 +0.995514914683267#6614 +0.9810967734351546#6615 +0.9568758375343491#6616 +0.9230941145659948#6617 +0.8800891403392386#6618 +0.8282906063409999#6619 +0.7682160664006705#6620 +0.7004657654633318#6621 +0.625716642140678#6622 +0.5447155649641743#6623 +0.4582718699215725#6624 +0.36724927383925204#6625 +0.272557244409198#6626 +0.17514191308845256#6627 +0.07597662166634576#6628 +-0.023947803044919447#6629 +-0.12363294922425093#6630 +-0.22208279584255267#6631 +-0.3183136645756067#6632 +-0.411364048392728#6633 +-0.5003042186171828#6634 +-0.5842455144677996#6635 +-0.6623492222637417#6636 +-0.7338349555743653#6637 +-0.7979884525824719#6638 +-0.8541687127522828#6639 +-0.9018144014949061#6640 +-0.9404494588379959#6641 +-0.969687856059632#6642 +-0.9892374527597048#6643 +-0.9989029158302245#6644 +-0.9985876711591678#6645 +-0.9882948685670818#6646 +-0.9681273503351187#6647 +-0.9382866236389569#6648 +-0.8990708471557118#6649 +-0.8508718519609921#6650 +-0.7941712264823142#6651 +-0.7295355046267261#6652 +-0.6576105051612804#6653 +-0.5791148789053921#6654 +-0.4948329282094064#6655 +-0.4056067704647689#6656 +-0.3123279239454181#6657 +-0.2159284000518886#6658 +-0.117371390961479#6659 +-0.017641645730405324#6660 +0.08226436899325389#6661 +0.18134842533491713#6662 +0.2786205081564525#6663 +0.3731087069601824#6664 +0.46386892689730536#6665 +0.5499943218515493#6666 +0.6306243553458014#6667 +0.7049533987381323#6668 +0.7722387807968807#6669 +0.8318082082261052#6670 +0.8830664829979679#6671 +0.9255014493746805#6672 +0.958689111199342#6673 +0.9822978683253825#6674 +0.9960918298556162#6675 +0.9999331710861101#6676 +0.9937835106050681#6677 +0.977704293787209#6678 +0.9518561788518946#6679 +0.9164974316193079#6680 +0.871981345003749#6681 +0.8187527090276192#6682 +0.7573433666265443#6683 +0.6883668996505666#6684 +0.6125124981571216#6685 +0.5305380742518008#6686 +0.4432626892811246#6687 +0.3515583700423137#6688 +0.25634139577978515#6689 +0.15856314302582344#6690 +0.05920057976075344#6691 +-0.04075349612817637#6692 +-0.14030037655510802#6693 +-0.2384454219966389#6694 +-0.3342079996011555#6695 +-0.42663128134813255#6696 +-0.514791804357429#6697 +-0.5978086978251298#6698 +-0.674852484393442#6699 +-0.7451533680142645#6700 +-0.8080089254968401#6701 +-0.8627911248880876#6702 +-0.908952600560277#6703 +-0.9460321223074467#6704 +-0.973659203805159#6705 +-0.9915578043873877#6706 +-0.9995490871536059#6707 +-0.9975532058479801#6708 +-0.9855901026567658#6709 +-0.9637793089525735#6710 +-0.9323387509764054#6711 +-0.8915825723906932#6712 +-0.8419179954596638#6713 +-0.783841252219086#6714 +-0.7179326262898007#6715 +-0.6448506548755989#6716 +-0.5653255488771634#6717 +-0.4801518968661259#6718 +-0.39018072581871516#6719 +-0.2963109979355221#6720 +-0.1994806285083495#6721 +-0.10065711458064946#6722 +-8.278680368774796E-4#6723 +0.0990096502906622#6724 +0.1978578969207362#6725 +0.2947292128479025#6726 +0.388655691904816#6727 +0.4786988517607058#6728 +0.5639590109266028#6729 +0.6435842780753824#6730 +0.7167790638583078#6731 +0.7828120301708148#6732 +0.8410233974411052#6733 +0.8908315369295339#6734 +0.9317387821707139#6735 +0.963336401492333#6736 +0.9853086819269133#6737 +0.997436083711423#6738 +0.9995974338560171#6739 +0.9917711368644981#6740 +0.9740353905093757#6741 +0.9465674045055719#6742 +0.9096416298895278#6743 +0.863627016795169#6744 +0.8089833280261255#6745 +0.746256545257776#6746 +0.6760734137688245#6747 +0.59913518020965#6748 +0.5162105859775773#6749 +0.42812818620694754#6750 +0.33576807112009877#6751 +0.24005307245678428#6752 +0.14193954284447627#6753 +0.04240780023904047#6754 +-0.05754766708822902#6755 +-0.15692813714868228#6756 +-0.2547406331362906#6757 +-0.3500078449236578#6758 +-0.4417778940217574#6759 +-0.5291338444351459#6760 +-0.6112028643833304#6761 +-0.6871649473473994#6762 +-0.7562611053041018#6763 +-0.8178009522832872#6764 +-0.8711696024763078#6765 +-0.9158338139717653#6766 +-0.9513473167324222#6767 +-0.977355271577897#6768 +-0.9935978156204607#6769 +-0.9999126587291108#6770 +-0.9962367050789155#6771 +-0.9826066835836388#6772 +-0.9591587809125754#6773 +-0.9261272807583686#6774 +-0.8838422229517972#6775 +-0.8327261058128828#6776 +-0.7732896646873357#6777 +-0.7061267688478077#6778 +-0.6319084877484257#6779 +-0.5513763859206331#6780 +-0.4653351135055202#6781 +-0.37464436645559573#6782 +-0.28021029673700776#6783 +-0.18297645835863166#6784 +-0.08391437969231084#6785 +0.0159861437174899#6786 +0.11572693886358196#6787 +0.21431142869078854#6788 +0.31075458956445545#6789 +0.40409279330100306#6790 +0.4933934354222034#6791 +0.5777642534308487#6792 +0.6563622420026807#6793 +0.72840207601693#6794 +0.793163957265329#6795 +0.8500008064378751#6796 +0.8983447285254006#6797 +0.9377126870387825#6798 +0.967711330349872#6799 +0.9880409219309375#6800 +0.9984983352229706#6801 +0.9989790832091183#6802 +0.9894783624144172#6803 +0.9700911009005274#6804 +0.9410110097759191#6805 +0.9025286476985094#6806 +0.8550285177095982#6807 +0.7989852254065796#6808 +0.7349587368406961#6809 +0.6635887835213552#6810 +0.585588470430354#6811 +0.5017371509126227#6812 +0.41287263963522164#6813 +0.3198828414201331#6814 +0.22369687959278223#6815 +0.1252758124888965#6816 +0.02560303087729906#6817 +-0.07432556775558767#6818 +-0.17351152988422644#6819 +-0.27096382216154746#6820 +-0.3657087334965602#6821 +-0.4567996040537086#6822 +-0.5433262839650238#6823 +-0.6244242272467491#6824 +-0.699283130057029#6825 +-0.7671550269840464#6826 +-0.8273617644691722#6827 +-0.8793017766931565#6828 +-0.9224560962229421#6829 +-0.9563935393627088#6830 +-0.980775014398831#6831 +-0.9953569096921918#6832 +-0.9999935277651537#6833 +-0.9946385410625938#6834 +-0.9793454548415207#6835 +-0.9542670725642333#6836 +-0.9196539691366354#6837 +-0.8758519872466048#6838 +-0.823298781818177#6839 +-0.7625194471082125#6840 +-0.6941212701381572#6841 +-0.6187876628828645#6842 +-0.5372713338440499#6843 +-0.45038676723575394#6844 +-0.3590020849273077#6845 +-0.26403037245657784#6846 +-0.16642055578109807#6847 +-0.06714791992357462#6848 +0.03279563575367421#6849 +0.13241150827926804#6850 +0.23070436878356018#6851 +0.32669210749569116#6852 +0.4194156466597768#6853 +0.5079485233228161#6854 +0.59140614624627#6855 +0.6689546344493088#6856 +0.7398191490719298#6857 +0.8032916353087174#6858 +0.858737897058408#6859 +0.9056039336016954#6860 +0.9434214749932913#6861 +0.9718126608604276#6862 +0.9904938158587981#6863 +0.9992782840628278#6864 +0.9980782939699812#6865 +0.986905835484602#6866 +0.9658725401187522#6867 +0.9351885656070448#6868 +0.8951604960800272#6869 +0.846188278776893#6870 +0.7887612279048725#6871 +0.72345313557342#6872 +0.6509165386531266#6873 +0.571876198843009#6874 +0.4871218610911878#6875 +0.3975003627244203#6876 +0.3039071721294589#6877 +0.20727744152893346#6878 +0.1085766632494626#6879 +0.008791022841475713#6880 +-0.09108245456081744#6881 +-0.19004586618499503#6882 +-0.2871104023350624#6883 +-0.3813062262510775#6884 +-0.4716921643974707#6885 +-0.5573651103579016#6886 +-0.637469048376044#6887 +-0.7112036063820617#6888 +-0.7778320530457624#6889 +-0.8366886589525186#6890 +-0.8871853483515171#6891 +-0.9288175750142688#6892 +-0.9611693634937438#6893 +-0.9839174654135346#6894 +-0.9968345892587814#6895 +-0.9997916713978559#6896 +-0.9927591656435055#6897 +-0.9758073384685907#6898 +-0.9491055669267142#6899 +-0.9129206462926903#6900 +-0.86761412433235#6901 +-0.813638688836776#6902 +-0.7515336445155336#6903 +-0.6819195244422828#6904 +-0.605491889893427#6905 +-0.5230143805297617#6906 +-0.43531108436140914#6907 +-0.3432583037328287#6908 +-0.24777579959945775#6909 +-0.1498176015802779#6910 +-0.050362475609222065#6911 +0.04959585557050026#6912 +0.14905864135557212#6913 +0.24703208246845434#6914 +0.34253726067126045#6915 +0.43461991979320214#6916 +0.5223600003428128#6917 +0.6048808324382549#6918 +0.6813578952029908#6919 +0.7510270551058309#6920 +0.813192200930588#6921 +0.867232199089246#6922 +0.9126070997834519#6923 +0.9488635320044169#6924 +0.975639233466172#6925 +0.9926666702105814#6926 +0.9997757097182173#6927 +0.9968953208162551#6928 +0.9840542833984735#6929 +0.9613809008660682#6930 +0.9291017181624794#6931 +0.8875392582112024#6932 +0.8371087993733477#6933 +0.7783142261235303#6934 +0.7117429944027603#6935 +0.6380602619528687#6936 +0.558002242280222#6937 +0.47236884865372675#6938 +0.38201570163586274#6939 +0.28784558000485344#6940 +0.19079940048753272#6941 +0.09184681643043616#6942 +-0.008023470655912756#6943 +-0.10781358987567453#6944 +-0.20652647134383265#6945 +-0.30317580857889714#6946 +-0.39679591335123815#6947 +-0.4864513645206622#6948 +-0.5712463544552357#6949 +-0.6503336396450295#6950 +-0.7229230060792073#6951 +-0.7882891648032128#6952 +-0.8457789987662547#6953 +-0.8948180885509868#6954 +-0.9349164517824474#6955 +-0.9656734388699845#6956 +-0.9867817361655272#6957 +-0.998030436539974#6958 +-0.9993071466975093#6959 +-0.9905991101722632#6960 +-0.9719933347867018#6961 +-0.9436757232982262#6962 +-0.9059292159202714#6963 +-0.8591309632772218#6964 +-0.8037485580400412#6965 +-0.7403353628948707#6966 +-0.6695249815259856#6967 +-0.592024927857224#6968 +-0.508609556806733#6969 +-0.42011232718859376#6970 +-0.3274174740678562#6971 +-0.23145117377605856#6972 +-0.1331722898634756#6973 +-0.03356279245148251#6974 +0.0663820532883018#6975 +0.16566363149461852#6976 +0.2632899534561562#6977 +0.3582855692348461#6978 +0.449701314039203#6979 +0.5366237919651687#6980 +0.618184502346058#6981 +0.6935685175241308#6982 +0.7620226253383701#6983 +0.8228628549714151#6984 +0.8754813109598781#6985 +0.9193522470848716#6986 +0.9540373194544375#6987 +0.9791899662908198#6988 +0.9945588706611886#6989 +0.9999904715533596#6990 +0.9954304982066668#6991 +0.9809245123674635#6992 +0.9566174530512137#6993 +0.92275218835976#6994 +0.8796670888235313#6995 +0.8277926465150534#6996 +0.767647173714462#6997 +0.699831624104531#6998 +0.6250235882401227#6999 +0.5439705232873457#7000 +0.45748228467862745#7001 +0.3664230343049359#7002 +0.2717026060958192#7003 +0.17426741525953252#7004 +0.07509100201512046#7005 +-0.024835695699641874#7006 +-0.12451424335256278#7007 +-0.22294868584484095#7008 +-0.3191554987651938#7009 +-0.4121734154406648#7010 +-0.5010730315954676#7011 +-0.5849660916512891#7012 +-0.6630143638834097#7013 +-0.7344380157550146#7014 +-0.7985234057461226#7015 +-0.8546302138237558#7016 +-0.9021978393080471#7017 +-0.9407510022089238#7018 +-0.9699044920666614#7019 +-0.9893670168474638#7020 +-0.9989441134371765#7021 +-0.9985400906524421#7022 +-0.9881589853553737#7023 +-0.9679045221185625#7024 +-0.9379790768434352#7025 +-0.8986816546871441#7026 +-0.8504049025018744#7027 +-0.7936311856372888#7028 +-0.7289277683054031#7029 +-0.6569411456640912#7030 +-0.5783905842511721#7031 +-0.4940609353109204#7032 +-0.40479479281987163#7033 +-0.3114840745663333#7034 +-0.21506111040267262#7035 +-0.11648932671361892#7036 +-0.016753620178294085#7037 +0.0831494829918418#7038 +0.1822217840134876#7039 +0.27947338520378323#7040 +0.37393258071074015#7041 +0.46465556547691106#7042 +0.5507358654275436#7043 +0.6313133946598948#7044 +0.7055830491372643#7045 +0.7728027510223979#7046 +0.8323008632739383#7047 +0.8834829004217286#7048 +0.9258374684691203#7049 +0.9589413745727422#7050 +0.9824638554455033#7051 +0.9961698822340214#7052 +0.9999225088492355#7053 +0.9936842402864599#7054 +0.9775174072630763#7055 +0.9515835434306101#7056 +0.9161417713838799#7057 +0.8715462135936842#7058 +0.8182424541321317#7059 +0.756763086543882#7060 +0.6877223923475002#7061 +0.6118102033375776#7062 +0.5297850090134684#7063 +0.44246637800293476#7064 +0.35072676920333273#7065 +0.2554828144607054#7066 +0.15768615988737594#7067 +0.05831395732856547#7068 +-0.041640899015840796#7069 +-0.14117969326193175#7070 +-0.2393078666807508#7071 +-0.33504495500035825#7072 +-0.427434384880738#7073 +-0.5155530316784099#7074 +-0.5985204430027234#7075 +-0.6755076359051054#7076 +-0.7457453798026574#7077 +-0.8085318823758662#7078 +-0.863239801645478#7079 +-0.9093225141661847#7080 +-0.9463195767073986#7081 +-0.9738613268498107#7082 +-0.99167257653009#7083 +-0.9995753616290475#7084 +-0.9974907201302877#7085 +-0.9854394810825754#7086 +-0.9635420564828655#7087 +-0.932017238159432#7088 +-0.8911800116762435#7089 +-0.8414384091013278#7090 +-0.783289432085226#7091 +-0.7173140859847869#7092 +-0.6441715746496963#7093 +-0.5645927138755155#7094 +-0.47937262933382613#7095 +-0.38936281193935457#7096 +-0.2954626100342169#7097 +-0.1986102433965698#7098 +-0.09977342885872108#7099 +6.0288799573731514E-5#7100 +0.09989340407211203#7101 +0.19872841747153056#7102 +0.2955778022144536#7103 +0.3894738712626798#7104 +0.47947844613219254#7105 +0.5646922308624522#7106 +0.6442637974843658#7107 +0.7173980932071098#7108 +0.7833643843228061#7109 +0.8415035574561832#7110 +0.8912347052075476#7111 +0.9320609303874993#7112 +0.9635743108493957#7113 +0.9854599753126002#7114 +0.9974992494522351#7115 +0.9995718408207521#7116 +0.9916570407703045#7117 +0.9738339313667114#7118 +0.9462805952275968#7119 +0.909272336179741#7120 +0.8631789285142464#7121 +0.8084609223240518#7122 +0.745665041839642#7123 +0.675418722741259#7124 +0.5984238430289884#7125 +0.5154497100897922#7126 +0.4273253740323974#7127 +0.3349313440926573#7128 +0.23919079087632442#7129 +0.14106032234351765#7130 +0.041520425698197#7131 +-0.05843432931587226#7132 +-0.15780522782723821#7133 +-0.2555993886656267#7134 +-0.3508396849023918#7135 +-0.4425745069797916#7136 +-0.5298872708791288#7137 +-0.6119055763252834#7138 +-0.6878099235218842#7139 +-0.7568419013223837#7140 +-0.8183117650235371#7141 +-0.8716053280664775#7142 +-0.9161900987857895#7143 +-0.9516206008902112#7144 +-0.9775428245144819#7145 +-0.9936977633688959#7146 +-0.9999240026445323#7147 +-0.9961593318166703#7148 +-0.9824413662317869#7149 +-0.95890717126745#7150 +-0.9257918928203727#7151 +-0.8834264078063425#7152 +-0.8322340181474527#7153 +-0.7727262212792206#7154 +-0.7054975994372918#7155 +-0.6312198787882831#7156 +-0.55063521776397#7157 +-0.46454879165956#7158 +-0.3738207475882999#7159 +-0.27935761017584615#7160 +-0.1821032238658627#7161 +-0.08302932233833335#7162 +0.01687418073215605#7163 +0.11660908256663223#7164 +0.21517886499393998#7165 +0.3115986513309033#7166 +0.4049050469445867#7167 +0.4941657651730115#7168 +0.5784889424253095#7169 +0.6570320493879117#7170 +0.7290103092989471#7171 +0.7937045391782332#7172 +0.8504683356658855#7173 +0.8987345336710154#7174 +0.9380208732978393#7175 +0.967934818427143#7176 +0.98817747880743#7177 +0.9985465964675143#7178 +0.998938566611311#7179 +0.989349472802711#7180 +0.9698751260973173#7181 +0.9407101077300469#7182 +0.9021458249237525#7183 +0.8545675992445777#7184 +0.7984508165962385#7185 +0.7343561773212153#7186 +0.6629240938682737#7187 +0.5848682920029681#7188 +0.5009686794957195#7189 +0.4120635535411761#7190 +0.31904122476974855#7191 +0.22283114154142777#7192 +0.12439460320500637#7193 +0.02471515511274884#7194 +-0.0752112386396513#7195 +-0.17438614655709378#7196 +-0.271818645742533#7197 +-0.3665352228710099#7198 +-0.4575895012129941#7199 +-0.5440716965178346#7200 +-0.6251177072772582#7201 +-0.6999177485420057#7202 +-0.7677244430253657#7203 +-0.8278602886499732#7204 +-0.8797244279246147#7205 +-0.9227986515136628#7206 +-0.9566525760134608#7207 +-0.9809479442010262#7208 +-0.9954420047884096#7209 +-0.9999899379133214#7210 +-0.9945463021313241#7211 +-0.9791654884517249#7212 +-0.9540011768805893#7213 +-0.9193048009009206#7214 +-0.8754230352324109#7215 +-0.822794331972237#7216 +-0.7619445397266382#7217 +-0.6934816495054661#7218 +-0.618089719876988#7219 +-0.5365220420807936#7220 +-0.44959361339073317#7221 +-0.3581729939315602#7222 +-0.26317362831327207#7223 +-0.16554471879451196#7224 +-0.06626174116736495#7225 +0.03368330187430715#7226 +0.1332917924978703#7227 +0.23156847559120272#7228 +0.32753140302278777#7229 +0.42022174494285497#7230 +0.5087133700942921#7231 +0.5921220994100276#7232 +0.6696145404379987#7233 +0.7404164143230488#7234 +0.803820292145306#7235 +0.8591926633161057#7236 +0.9059802654063811#7237 +0.9437156121619712#7238 +0.9720216644717411#7239 +0.9906155976177484#7240 +0.9993116271663351#7241 +0.9980228652647771#7242 +0.9867621887959865#7243 +0.9656421107169549#7244 +0.9348736558664785#7245 +0.8947642524747242#7246 +0.8457146604419769#7247 +0.7882149670781883#7248 +0.7228396903125782#7249 +0.6502420383003954#7250 +0.5711473827829529#7251 +0.48634601141296413#7252 +0.39668523146155205#7253 +0.3030609038040782#7254 +0.20640849177440834#7255 +0.1076937143245037#7256 +0.007902896879877103#7257 +-0.09196688369802286#7258 +-0.19091776157422172#7259 +-0.2879610522857915#7260 +-0.38212713135018883#7261 +-0.472475122432569#7262 +-0.5581022982711118#7263 +-0.6381531004294191#7264 +-0.711827687753602#7265 +-0.7783899281206974#7266 +-0.8371747536275085#7267 +-0.8875948057292506#7268 +-0.9291463039319765#7269 +-0.9614140794007434#7270 +-0.9840757231893756#7271 +-0.9969048076440806#7272 +-0.9997731487937185#7273 +-0.9926520871216694#7274 +-0.9756127739222505#7275 +-0.9488254603805024#7276 +-0.9125577964786259#7277 +-0.8671721567258328#7278 +-0.8131220194320353#7279 +-0.7509474357024735#7280 +-0.6812696334255883#7281 +-0.6047848101693116#7282 +-0.5222571770050993#7283 +-0.43451132276352#7284 +-0.3424239750152328#7285 +-0.24691524009890903#7286 +-0.14893940972284261#7287 +-0.0494754259976481#7288 +0.050482899829713684#7289 +0.14993681720940477#7290 +0.24789261547406108#7291 +0.3433715526673036#7292 +0.4354196348098376#7293 +0.5231171478919449#7294 +0.6055878473518524#7295 +0.6820077132217451#7296 +0.7516131834628998#7297 +0.8137087832251761#7298 +0.8676740738018253#7299 +0.9129698518479584#7300 +0.949143536922132#7301 +0.9758336935205153#7302 +0.9927736424209699#7303 +0.9997941252536848#7304 +0.9968249956748588#7305 +0.9838959202457798#7306 +0.961136082014351#7307 +0.9287728897607787#7308 +0.8871297058042116#7309 +0.8366226150733376#7310 +0.7777562677233152#7311 +0.7111188368382388#7312 +0.6373761416001066#7313 +0.5572649946436438#7314 +0.4715858400680154#7315 +0.3811947556639786#7316 +0.2869948992675788#7317 +0.18992748470559684#7318 +0.0909623774981151#7319 +-0.008911595717163887#7320 +-0.10869652721381882#7321 +-0.20739539894084763#7322 +-0.3040220443974627#7323 +-0.3976110020827836#7324 +-0.48722716206801436#7325 +-0.5719751093057461#7326 +-0.6510080703211262#7327 +-0.7235363738925117#7328 +-0.7888353411852869#7329 +-0.8462525265032307#7330 +-0.8952142363102442#7331 +-0.935231261386525#7332 +-0.9659037648453803#7333 +-0.9869252771712309#7334 +-0.9980857583617049#7335 +-0.9992736965779115#7336 +-0.9904772223338747#7337 +-0.971784227092513#7338 +-0.9433814850831956#7339 +-0.9055527871153815#7340 +-0.85867610503466#7341 +-0.8032198151530112#7342 +-0.7397380183875204#7343 +-0.6688650038671767#7344 +-0.5913089113255638#7345 +-0.5078446556027223#7346 +-0.4193061839522201#7347 +-0.32657814351586195#7348 +-0.23058704222187348#7349 +-0.13229199142394524#7350 +-0.03267512277762666#7351 +0.06726822489452655#7352 +0.16653945069945214#7353 +0.2641466693636113#7354 +0.35911462182276815#7355 +0.45049441968818166#7356 +0.5373730262257255#7357 +0.6188823791171251#7358 +0.6942080638516992#7359 +0.7625974510869405#7360 +0.82336721667212#7361 +0.875910169197324#7362 +0.9197013168493114#7363 +0.9543031129561722#7364 +0.9793698278090396#7365 +0.994651003079058#7366 +0.999993954314214#7367 +0.995345296511911#7368 +0.9807514775242675#7369 +0.9563583139665331#7370 +0.9224095342656689#7371 +0.8792443434064714#7372 +0.827294033707491#7373 +0.7670776754907508#7374 +0.6991969307026712#7375 +0.624330041306784#7376 +0.5432250525142298#7377 +0.4566923385632795#7378 +0.36559650572781904#7379 +0.27084775345726186#7380 +0.1733927799645225#7381 +0.07420532313040953#7382 +-0.025723568763404207#7383 +-0.1253954392612152#7384 +-0.22381439998014888#7385 +-0.3199970811976842#7386 +-0.4129824573568625#7387 +-0.5018414493159796#7388 +-0.5856862074002571#7389 +-0.6636789825023087#7390 +-0.7350404965942983#7391 +-0.7990577290163985#7392 +-0.855091040743531#7393 +-0.9025805654470686#7394 +-0.9410518034941227#7395 +-0.9701203629910278#7396 +-0.9894958005000788#7397 +-0.9989845230543534#7398 +-0.9984917224746438#7399 +-0.9880223226614443#7400 +-0.9676809303969649#7401 +-0.9376707901487422#7402 +-0.8982917533181031#7403 +-0.8499372822240805#7404 +-0.793090518757983#7405 +-0.7283194569893225#7406 +-0.6562712679568256#7407 +-0.57766583334934#7408 +-0.4932885526859621#7409 +-0.40398249586366014#7410 +-0.31063997948154537#7411 +-0.21419365110837466#7412 +-0.11560717057633567#7413 +-0.015865581410547242#7414 +0.0840345314002315#7415 +0.18309499895138165#7416 +0.28032604179616866#7417 +0.3747561594947966#7418 +0.4654418375256672#7419 +0.5514769745705953#7420 +0.6320019359796627#7421 +0.7062121429564829#7422 +0.7733661116435763#7423 +0.8327928617839718#7424 +0.8838986209341382#7425 +0.9261727572419648#7426 +0.9591928815114351#7427 +0.9826290675758504#7428 +0.996247148811028#7429 +0.9999110578508053#7430 +0.9935841861271838#7431 +0.9773297496510405#7432 +0.9513101573786427#7433 +0.9157853884750424#7434 +0.8711103946881975#7435 +0.8177315537884366#7436 +0.7561822095093318#7437 +0.6870773425534116#7438 +0.6111074259082679#7439 +0.529031525868704#7440 +0.44166971769722985#7441 +0.34989489170312094#7442 +0.2546240316109867#7443 +0.15680905236250883#7444 +0.05742728889700527#7445 +-0.04252826905621956#7446 +-0.14205889860301116#7447 +-0.2401701225933894#7448 +-0.33588164610850085#7449 +-0.42823715124340533#7450 +-0.5163138523194657#7451 +-0.5992317160538159#7452 +-0.6761622545610236#7453 +-0.7463368033301797#7454 +-0.809054201466604#7455 +-0.8636877974597326#7456 +-0.9096917104778677#7457 +-0.9466062846290039#7458 +-0.9740626816905584#7459 +-0.9917865664189706#7460 +-0.9996008476167711#7461 +-0.9974274475692897#7462 +-0.9852880821713702#7463 +-0.963304043949328#7464 +-0.9316949901461209#7465 +-0.8907767479787866#7466 +-0.840958158997289#7467 +-0.782736994074895#7468 +-0.7166949798461515#7469 +-0.6434919862866445#7470 +-0.5638594335103286#7471 +-0.47859298366152303#7472 +-0.38854459092177623#7473 +-0.2946139890653031#7474 +-0.19773970161652513#7475 +-0.09888966443324891#7476 +9.484455884677701E-4#7477 +0.10077707905537892#7478 +0.19959878126084157#7479 +0.29642615842253006#7480 +0.3902917433947197#7481 +0.4802576622802053#7482 +0.5654250053562614#7483 +0.6449428086834523#7484 +0.7180165566560236#7485 +0.7839161205392026#7486 +0.8419830536741678#7487 +0.8916371704594106#7488 +0.9323823433734814#7489 +0.963811460117186#7490 +0.985610491345106#7491 +0.9975616283430137#7492 +0.9995454593005464#7493 +0.9915421624345439#7494 +0.973631704041753#7495 +0.9459930395020244#7496 +0.908902325215311#7497 +0.8627301593382061#7498 +0.8079378788896648#7499 +0.7450729502240094#7500 +0.6747634989280851#7501 +0.597712033798026#7502 +0.5146884276035842#7503 +0.4265222247738995#7504 +0.33409435286377454#7505 +0.23832832061674333#7506 +0.1401809905709772#7507 +0.040633018405099953#7508 +-0.0593209454491911#7509 +-0.15868219402545106#7510 +-0.25645794257236754#7511 +-0.35167124813082457#7512 +-0.4433707708250161#7513 +-0.5306402793360133#7514 +-0.6126078055822383#7515 +-0.6884543571363001#7516 +-0.7574221003266068#7517 +-0.8188219322609055#7518 +-0.8720403661145942#7519 +-0.9165456608882823#7520 +-0.9518931343880851#7521 +-0.9777296063431139#7522 +-0.993796927265996#7523 +-0.99993455779722#7524 +-0.9960811727613489#7525 +-0.9822752739079014#7526 +-0.9586548052145977#7527 +-0.9254557745967325#7528 +-0.8830098957940993#7529 +-0.8317412739969522#7530 +-0.772162168327135#7531 +-0.7048678735142673#7532 +-0.6305307719075828#7533 +-0.5498936152537572#7534 +-0.4637621033669758#7535 +-0.37299683384271926#7536 +-0.2785047032510652#7537 +-0.18122984572594025#7538 +-0.0821441994889431#7539 +0.017762204436085696#7540 +0.1174911342857932#7541 +0.21604613155912197#7542 +0.31244246730126723#7543 +0.4057169811898851#7544 +0.4949377051146552#7545 +0.5792131750945708#7546 +0.6577013384913591#7547 +0.7296179675210964#7548 +0.794244494998994#7549 +0.8509351940251821#7550 +0.8991236298744447#7551 +0.9383283196247547#7552 +0.9681575429754743#7553 +0.9883132561871131#7554 +0.9985940700358535#7555 +0.998897262028104#7556 +0.9892198027697#7557 +0.9696583862346088#7558 +0.9404084636307043#7559 +0.9017622905159063#7560 +0.8541060066772513#7561 +0.7979157779497825#7562 +0.7337530385249249#7563 +0.6622588812856302#7564 +0.5841476522182073#7565 +0.5001998129033588#7566 +0.4112541424020529#7567 +0.3181993564524092#7568 +0.22196522771581453#7569 +0.12351329579583167#7570 +0.023827259852323673#7571 +-0.07609685019538392#7572 +-0.17526062567021322#7573 +-0.2726732549068054#7574 +-0.367361423114162#7575 +-0.4583790374153017#7576 +-0.5448166798945504#7577 +-0.6258106942007409#7578 +-0.7005518149159866#7579 +-0.7682932534682305#7580 +-0.8283581597958007#7581 +-0.8801463852094901#7582 +-0.923140478879876#7583 +-0.9569108580349618#7584 +-0.9811201002092328#7585 +-0.995526314657395#7586 +-0.9999855592467439#7587 +-0.9944532786793728#7588 +-0.9789847496739821#7589 +-0.953734528659178#7590 +-0.9189549074967269#7591 +-0.8749933926646701#7592 +-0.822289233087465#7593 +-0.7613690313059284#7594 +-0.6928414818387202#7595 +-0.6173912893079209#7596 +-0.5357723270967809#7597 +-0.44880010489607236#7598 +-0.3573436204008309#7599 +-0.2623166765726389#7600 +-0.16466875122249675#7601 +-0.06537551014239087#7602 +0.03457094142478756#7603 +0.13417197157288324#7604 +0.23243239973237922#7605 +0.3283704401856845#7606 +0.42102751174548914#7607 +0.5094778155811228#7608 +0.5928375854944425#7609 +0.6702739182195506#7610 +0.7410130955169058#7611 +0.8043483149102156#7612 +0.859646751823142#7613 +0.9063558825532837#7614 +0.9440090049063705#7615 +0.9722299013303181#7616 +0.9907365979566459#7617 +0.9993441819901644#7618 +0.9979666492964995#7619 +0.9866177637269746#7620 +0.9654109195947578#7621 +0.9345580086763673#7622 +0.8943673030590836#7623 +0.8452403749881539#7624 +0.7876680844896596#7625 +0.722225674859393#7626 +0.6495670250219955#7627 +0.5704181161888862#7628 +0.48556977809397517#7629 +0.39586978728437555#7630 +0.30221439641738246#7631 +0.2055393792001831#7632 +0.10681068044830018#7633 +0.007014764684294179#7634 +-0.0928512402896644#7635 +-0.19178950636318762#7636 +-0.28881147508631094#7637 +-0.38294773501875135#7638 +-0.4732577077685739#7639 +-0.55883904594057#7640 +-0.6388366490931539#7641 +-0.7124512076193162#7642 +-0.7789471891840013#7643 +-0.8376601879200635#7644 +-0.8880035629520688#7645 +-0.9294742999180047#7646 +-0.9616580369225101#7647 +-0.984233204703965#7648 +-0.9969742396482555#7649 +-0.9997538375458441#7650 +-0.9925442255733512#7651 +-0.9754174397904251#7652 +-0.948544605379246#7653 +-0.9121942268182728#7654 +-0.866729505074249#7655 +-0.8126047086182022#7656 +-0.7503606345250428#7657 +-0.6806192050079543#7658 +-0.6040772533772201#7659 +-0.5214995615121297#7660 +-0.4337112184132438#7661 +-0.3415893761858385#7662 +-0.2460544858260183#7663 +-0.14806110037862266#7664 +-0.048588337358735884#7665 +0.05136990426687065#7666 +0.15081487478967512#7667 +0.24875295293634997#7668 +0.3442055738040776#7669 +0.43621900635758876#7670 +0.5238738827944053#7671 +0.6062943845640199#7672 +0.6826569932573459#7673 +0.7521987189304415#7674 +0.8142247236478194#7675 +0.8681152640734142#7676 +0.9133318837411375#7677 +0.9494227931338964#7678 +0.976027383815107#7679 +0.9928798315089906#7680 +0.9998117521288687#7681 +0.9967538842152956#7682 +0.9837367809736671#7683 +0.9608905049966914#7684 +0.9284433287219556#7685 +0.8867194536091866#7686 +0.836135770826432#7687 +0.7771976958113149#7688 +0.7104941183270491#7689 +0.6366915184705867#7690 +0.5565273074237976#7691 +0.4708024594846968#7692 +0.3803735089970247#7693 +0.286143992142218#7694 +0.18905541910455304#7695 +0.09007786681260743#7696 +-0.009799713748746178#7697 +-0.10957937880967696#7698 +-0.2082641629396677#7699 +-0.30486804039654375#7700 +-0.3984257771697518#7701 +-0.48800257527952967#7702 +-0.5727034129693166#7703 +-0.6516819874672904#7704 +-0.7241491709639128#7705 +-0.7893808953161512#7706 +-0.8467253866970188#7707 +-0.8956096779042066#7708 +-0.9355453332589703#7709 +-0.9661333288939775#7710 +-0.9870680396678903#7711 +-0.9981402928707507#7712 +-0.9992394582085561#7713 +-0.9903545531845869#7714 +-0.9715743528328836#7715 +-0.9430865027074515#7716 +-0.9051756439899129#7717 +-0.8582205694489098#7718 +-0.8026904386679721#7719 +-0.7391400903580423#7720 +-0.6682044985924814#7721 +-0.5905924283560221#7722 +-0.507079353799328#7723 +-0.4184997099576176#7724 +-0.32573855535162055#7725 +-0.22972272877539926#7726 +-0.13141158862949134#7727 +-0.03178742732889281#7728 +0.0681543434380497#7729 +0.16741513853418957#7730 +0.26500317690618225#7731 +0.3599433911329309#7732 +0.45128716997694385#7733 +0.5381218365942502#7734 +0.6195797676997338#7735 +0.6948470625722005#7736 +0.763171675281344#7737 +0.8238709288820886#7738 +0.8763383364969607#7739 +0.9200496611324916#7740 +0.9545681536819657#7741 +0.979548916778125#7742 +0.994742350893655#7743 +0.999996648257155#7744 +0.9952593096662085#7745 +0.9805776690420603#7746 +0.956098420484722#7747 +0.9220661525540147#7748 +0.8788209044215302#7749 +0.8267947683116299#7750 +0.7665075721787701#7751 +0.6985616857584132#7752 +0.6236360018877475#7753 +0.542479153232871#7754 +0.4559020321986561#7755 +0.3647696887598858#7756 +0.2699926871678532#7757 +0.1725180078933547#7758 +0.07331958571085657#7759 +-0.026611421535832028#7760 +-0.1262765362551009#7761 +-0.22467993756558144#7762 +-0.3208384112092187#7763 +-0.4137911735031304#7764 +-0.5026094711725734#7765 +-0.58640586114666#7766 +-0.6643430775961726#7767 +-0.7356423976169658#7768 +-0.7995914219718132#7769 +-0.855551193148098#7770 +-0.9029625796100673#7771 +-0.9413518624563137#7772 +-0.9703354686624472#7773 +-0.9896238036159622#7774 +-0.9990241446498792#7775 +-0.9984425666639266#7776 +-0.987884880593096#7777 +-0.9674565753467003#7778 +-0.9373617637980615#7779 +-0.8979011433561516#7780 +-0.8494689914964797#7781 +-0.7925492262708868#7782 +-0.7277105711583345#7783 +-0.6556008725678979#7784 +-0.5769406267715956#7785 +-0.4925157809438046#7786 +-0.40316988023689265#7787 +-0.3097956393568956#7788 +-0.2133260228532663#7789 +-0.11472492324549403#7790 +-0.014977530127669912#7791 +0.08491951352027674#7792 +0.1839680694597875#7793 +0.28117847726101386#7794 +0.3755794426626941#7795 +0.46622774242334464#7796 +0.5522176486961006#7797 +0.6326899787619682#7798 +0.7068406796995446#7799 +0.773928862216024#7800 +0.8332842033681063#7801 +0.8843136442072667#7802 +0.9265073154287307#7803 +0.9594436318170264#7804 +0.9827935045861007#7805 +0.9963236295256865#7806 +0.9998988180998523#7807 +0.993483348206165#7808 +0.97714132109913#7809 +0.951036020911645#7810 +0.9154282831739184#7811 +0.8706738886310726#7812 +0.817220008399544#7813 +0.7556007359811027#7814 +0.6864317507771306#7815 +0.6104041664235594#7816 +0.5282776254118724#7817 +0.44087270899243347#7818 +0.3490627381978821#7819 +0.2537650479080564#7820 +0.15593182114310442#7821 +0.056540575165497#7822 +-0.04341560554933505#7823 +-0.1429379918848091#7824 +-0.2410321890543877#7825 +-0.3367180722655825#7826 +-0.4290395798028944#7827 +-0.5170742656804435#7828 +-0.5999425164173386#7829 +-0.6768163398448185#7830 +-0.7469276381303027#7831 +-0.8095758823570365#7832 +-0.8641351119774621#7833 +-0.9100601892040956#7834 +-0.9468922458461004#7835 +-0.9742632681685688#7836 +-0.9918997739641114#7837 +-0.9996255450966729#7838 +-0.9973633882148972#7839 +-0.9851359060425772#7840 +-0.9630652715397107#7841 +-0.9313720071906685#7842 +-0.8903727816164261#7843 +-0.8404772455263794#7844 +-0.782183938623869#7845 +-0.7160753083622596#7846 +-0.642811890322518#7847 +-0.5631257083600311#7848 +-0.47781296046421884#7849 +-0.3877260634114115#7850 +-0.2937651356981923#7851 +-0.19686900385491865#7852 +-0.0980058220013664#7853 +0.0018366016292064157#7854 +0.10166067454339997#7855 +0.2004689876021066#7856 +0.29727428080292906#7857 +0.3911093076557798#7858 +0.4810364995900806#7859 +0.5661573338300011#7860 +0.6456213111370226#7861 +0.7186344537171913#7862 +0.7844672383847822#7863 +0.8424618857168215#7864 +0.8920389323676491#7865 +0.9327030208751224#7866 +0.9640478491086351#7867 +0.9857602299057003#7868 +0.9976232203345528#7869 +0.9995182893162105#7870 +0.9914265019478351#7871 +0.9734287086940224#7872 +0.9457047375556851#7873 +0.908531597288111#7874 +0.8622807096210473#7875 +0.807414198135553#7876 +0.7444802708779339#7877 +0.6741077428461579#7878 +0.5969997530782538#7879 +0.5139267391194702#7880 +0.42571873906499613#7881 +0.3332570980936882#7882 +0.2374656623583771#7883 +0.13930154822049173#7884 +0.03974557905975634#7885 +-0.06020751478880264#7886 +-0.15955903505155#7887 +-0.25731629417926616#7888 +-0.3525025339530001#7889 +-0.4441666849293199#7890 +-0.5313928692118094#7891 +-0.6133095516002608#7892 +-0.6890982476823034#7893 +-0.7580017018590971#7894 +-0.819331453592961#7895 +-0.8724747162774904#7896 +-0.9169004999987684#7897 +-0.9521649170110635#7898 +-0.9779156169164552#7899 +-0.9938953072335382#7900 +-0.9999443241788476#7901 +-0.996002227974605#7902 +-0.9821084067429994#7903 +-0.9584016829530904#7904 +-0.9251189263525859#7905 +-0.8825926872436216#7906 +-0.8312478737500688#7907 +-0.7715975062760166#7908 +-0.7042375915754759#7909 +-0.6298411676499076#7910 +-0.5491515789749879#7911 +-0.46297504924832533#7912 +-0.37217262586877564#7913 +-0.27765157663545714#7914 +-0.18035632462780476#7915 +-0.08125901184234507#7916 +0.018650214128785595#7917 +0.1183730933252825#7918 +0.21691322770221497#7919 +0.313286036809926#7920 +0.40652859539642616#7921 +0.4957092546382107#7922 +0.5799369508673419#7923 +0.6583701087850726#7924 +0.7302250502040435#7925 +0.7947838243016816#7926 +0.8514013811474965#7927 +0.8995120168287606#7928 +0.9386350257770081#7929 +0.9683795038191755#7930 +0.9884482539628825#7931 +0.9986407558905398#7932 +0.9988551694920795#7933 +0.9890893524176707#7934 +0.9694408814833709#7935 +0.940106077715835#7936 +0.9013780447775112#7937 +0.8536437403717337#7938 +0.7973801098892623#7939 +0.7331493209275945#7940 +0.6615931462981596#7941 +0.5834265516445285#7942 +0.49943055174204004#7943 +0.410444406856334#7944 +0.31735723713220004#7945 +0.221099138798995#7946 +0.1226318909565677#7947 +0.022939345796415475#7948 +-0.07698240172419506#7949 +-0.17613496653377583#7950 +-0.2735276489802294#7951 +-0.3681873335742909#7952 +-0.45916821203782765#7953 +-0.5455612335075115#7954 +-0.626503187470553#7955 +-0.7011853286788058#7956 +-0.7688614578639504#7957 +-0.8288553775139227#7958 +-0.8805676482149329#7959 +-0.9234815780519405#7960 +-0.957168385223473#7961 +-0.9812914822876503#7962 +-0.9956098392326427#7963 +-0.9999803917688753#7964 +-0.9943594707801187#7965 +-0.9788032386508632#7966 +-0.9534671281103374#7967 +-0.9186042892000581#7968 +-0.8745630598822943#7969 +-0.8217834855622946#7970 +-0.7607929223000569#7971 +-0.6922007676428981#7972 +-0.6166923717266009#7973 +-0.535022189483404#7974 +-0.44800624237770903#7975 +-0.35651396498934845#7976 +-0.2614595179106613#7977 +-0.1637926537560355#7978 +-0.06448922754773154#7979 +0.03545855370492522#7980 +0.13505204481000171#7981 +0.23329614052560674#7982 +0.32920921832252986#7983 +0.42183294643207214#7984 +0.5102418591802964#7985 +0.5935526039351231#7986 +0.6709327672738326#7987 +0.7416091921828252#7988 +0.80487570318693#7989 +0.8601001622213218#7990 +0.9067307847461078#7991 +0.9443016529950545#7992 +0.9724373712718963#7993 +0.9908568167800427#7994 +0.9993759485086353#7995 +0.9979096461094927#7996 +0.9864725603914919#7997 +0.9651789669345293#7998 +0.9342416242857009#7999 \ No newline at end of file diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 250cf844f9..96c367bb0b 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -1,13 +1,15 @@ apply plugin: 'com.android.application' +apply plugin: 'realm-android' android { - compileSdkVersion 21 - buildToolsVersion '21.1.1' + compileSdkVersion 24 + buildToolsVersion '23.0.3' defaultConfig { minSdkVersion 16 - targetSdkVersion 21 - versionCode 36 - versionName '2.0.7' + targetSdkVersion 24 + versionCode 54 + versionName '3.0.1' + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" sourceSets { main { @@ -25,7 +27,6 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } - } lintOptions { @@ -38,8 +39,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.1.0' - + classpath 'com.android.tools.build:gradle:2.2.3' + //classpath 'io.realm:realm-gradle-plugin:0.88.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -47,11 +48,18 @@ buildscript { repositories { maven { url "https://jitpack.io" } + maven { // this is for realm-db + url 'http://oss.jfrog.org/artifactory/oss-snapshot-local' + } } dependencies { //compile fileTree(dir: 'libs', include: ['*.jar']) - compile project(':MPChartLib') // remove this if you only imported the example project - compile 'com.android.support:appcompat-v7:21.0.3' - //compile 'com.github.PhilJay:MPAndroidChart:v2.0.7' + //compile project(':MPChartLib-Realm') // clone "https://github.com/PhilJay/MPAndroidChart-Realm" to get this or uncomment the gradle dependency below: + compile 'com.github.PhilJay:MPAndroidChart-Realm:v2.0.2@aar' + + compile project(':MPChartLib') + compile 'com.android.support:appcompat-v7:24.2.1' + //compile 'io.realm:realm-android:0.87.5' // dependency for realm-database API (http://realm.io) + //compile 'com.github.PhilJay:MPAndroidChart:v2.2.5' } diff --git a/MPChartExample/libs/android-support-v4.jar b/MPChartExample/libs/android-support-v4.jar deleted file mode 100644 index c31cede47e..0000000000 Binary files a/MPChartExample/libs/android-support-v4.jar and /dev/null differ diff --git a/MPChartExample/project.properties b/MPChartExample/project.properties index a8d309b24e..b32d807be6 100644 --- a/MPChartExample/project.properties +++ b/MPChartExample/project.properties @@ -11,5 +11,5 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-19 +target=android-23 android.library.reference.1=../MPChartLib diff --git a/MPChartExample/res/drawable-hdpi/star.png b/MPChartExample/res/drawable-hdpi/star.png new file mode 100644 index 0000000000..c7811ef2b8 Binary files /dev/null and b/MPChartExample/res/drawable-hdpi/star.png differ diff --git a/MPChartExample/res/drawable-nodpi/radar_marker.png b/MPChartExample/res/drawable-nodpi/radar_marker.png new file mode 100644 index 0000000000..a84b93b539 Binary files /dev/null and b/MPChartExample/res/drawable-nodpi/radar_marker.png differ diff --git a/MPChartExample/res/drawable/fade_red.xml b/MPChartExample/res/drawable/fade_red.xml new file mode 100644 index 0000000000..54ac10ba03 --- /dev/null +++ b/MPChartExample/res/drawable/fade_red.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/MPChartExample/res/drawable/new_background.xml b/MPChartExample/res/drawable/new_background.xml new file mode 100644 index 0000000000..c2d3b9cfc5 --- /dev/null +++ b/MPChartExample/res/drawable/new_background.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_age_distribution.xml b/MPChartExample/res/layout/activity_age_distribution.xml new file mode 100644 index 0000000000..b023d3ab2d --- /dev/null +++ b/MPChartExample/res/layout/activity_age_distribution.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/MPChartExample/res/layout/activity_barchart.xml b/MPChartExample/res/layout/activity_barchart.xml index f140d6e78c..ccb26bb81c 100644 --- a/MPChartExample/res/layout/activity_barchart.xml +++ b/MPChartExample/res/layout/activity_barchart.xml @@ -30,12 +30,12 @@ android:layout_marginBottom="35dp" android:layout_toLeftOf="@+id/tvXMax" android:layout_marginRight="5dp" - android:max="500" + android:max="1500" android:paddingBottom="12dp" /> + android:textAppearance="?android:attr/textAppearanceSmall" /> + android:textAppearance="?android:attr/textAppearanceSmall" /> diff --git a/MPChartExample/res/layout/activity_barchart_noseekbar.xml b/MPChartExample/res/layout/activity_barchart_noseekbar.xml new file mode 100644 index 0000000000..5180e07b78 --- /dev/null +++ b/MPChartExample/res/layout/activity_barchart_noseekbar.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_barchart_sinus.xml b/MPChartExample/res/layout/activity_barchart_sinus.xml new file mode 100644 index 0000000000..78b849081f --- /dev/null +++ b/MPChartExample/res/layout/activity_barchart_sinus.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/MPChartExample/res/layout/activity_bubblechart.xml b/MPChartExample/res/layout/activity_bubblechart.xml new file mode 100644 index 0000000000..1cc55dfb42 --- /dev/null +++ b/MPChartExample/res/layout/activity_bubblechart.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + diff --git a/MPChartExample/res/layout/activity_bubblechart_noseekbar.xml b/MPChartExample/res/layout/activity_bubblechart_noseekbar.xml new file mode 100644 index 0000000000..6d7a7763df --- /dev/null +++ b/MPChartExample/res/layout/activity_bubblechart_noseekbar.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_candlechart.xml b/MPChartExample/res/layout/activity_candlechart.xml index 08ede1e0d7..f9384c9158 100644 --- a/MPChartExample/res/layout/activity_candlechart.xml +++ b/MPChartExample/res/layout/activity_candlechart.xml @@ -30,7 +30,7 @@ android:layout_marginBottom="35dp" android:layout_toLeftOf="@+id/tvXMax" android:layout_marginRight="5dp" - android:max="500" + android:max="3000" android:paddingBottom="12dp" /> + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_horizontalbarchart.xml b/MPChartExample/res/layout/activity_horizontalbarchart.xml index c4f255598e..5b713fbdd7 100644 --- a/MPChartExample/res/layout/activity_horizontalbarchart.xml +++ b/MPChartExample/res/layout/activity_horizontalbarchart.xml @@ -7,6 +7,7 @@ android:id="@+id/chart1" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@android:color/white" android:layout_above="@+id/seekBar1" /> + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_linechart.xml b/MPChartExample/res/layout/activity_linechart.xml index 0389e9e298..7cadd8dd65 100644 --- a/MPChartExample/res/layout/activity_linechart.xml +++ b/MPChartExample/res/layout/activity_linechart.xml @@ -18,7 +18,7 @@ android:layout_margin="8dp" android:layout_toLeftOf="@+id/tvYMax" android:layout_marginRight="5dp" - android:max="200" + android:max="150" android:paddingBottom="12dp" /> + + + + + + + + + diff --git a/MPChartExample/res/layout/activity_performance_linechart.xml b/MPChartExample/res/layout/activity_performance_linechart.xml index b7aedf6dda..d7cd5747fe 100644 --- a/MPChartExample/res/layout/activity_performance_linechart.xml +++ b/MPChartExample/res/layout/activity_performance_linechart.xml @@ -1,7 +1,8 @@ + android:layout_height="match_parent" + android:background="@android:color/white"> + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_piechart_noseekbar.xml b/MPChartExample/res/layout/activity_piechart_noseekbar.xml new file mode 100644 index 0000000000..52c62806b0 --- /dev/null +++ b/MPChartExample/res/layout/activity_piechart_noseekbar.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_radarchart_noseekbar.xml b/MPChartExample/res/layout/activity_radarchart_noseekbar.xml new file mode 100644 index 0000000000..d8e3b35aab --- /dev/null +++ b/MPChartExample/res/layout/activity_radarchart_noseekbar.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_realm_wiki.xml b/MPChartExample/res/layout/activity_realm_wiki.xml new file mode 100644 index 0000000000..d4e27933cf --- /dev/null +++ b/MPChartExample/res/layout/activity_realm_wiki.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_scatterchart_noseekbar.xml b/MPChartExample/res/layout/activity_scatterchart_noseekbar.xml new file mode 100644 index 0000000000..548a0c7a66 --- /dev/null +++ b/MPChartExample/res/layout/activity_scatterchart_noseekbar.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_scrollview.xml b/MPChartExample/res/layout/activity_scrollview.xml new file mode 100644 index 0000000000..95c78fedeb --- /dev/null +++ b/MPChartExample/res/layout/activity_scrollview.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MPChartExample/res/layout/frag_simple_line.xml b/MPChartExample/res/layout/frag_simple_line.xml index ef1b69a35d..7a2757476b 100644 --- a/MPChartExample/res/layout/frag_simple_line.xml +++ b/MPChartExample/res/layout/frag_simple_line.xml @@ -6,7 +6,7 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/MPChartExample/res/layout/frag_simple_pie.xml b/MPChartExample/res/layout/frag_simple_pie.xml index ef63ff3382..ed490ddbe6 100644 --- a/MPChartExample/res/layout/frag_simple_pie.xml +++ b/MPChartExample/res/layout/frag_simple_pie.xml @@ -6,7 +6,7 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/MPChartExample/res/layout/frag_simple_scatter.xml b/MPChartExample/res/layout/frag_simple_scatter.xml index 53c3391526..2e42332db3 100644 --- a/MPChartExample/res/layout/frag_simple_scatter.xml +++ b/MPChartExample/res/layout/frag_simple_scatter.xml @@ -6,7 +6,7 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/MPChartExample/res/layout/list_item.xml b/MPChartExample/res/layout/list_item.xml index eb95ab8fc0..c9c11e93ba 100644 --- a/MPChartExample/res/layout/list_item.xml +++ b/MPChartExample/res/layout/list_item.xml @@ -22,6 +22,24 @@ android:layout_below="@+id/tvName" android:layout_marginTop="3dp" android:text="Small Text" - android:textSize="12dp" /> + android:textSize="12dp" + android:layout_marginRight="10dp" + android:layout_toLeftOf="@+id/tvNew" + android:layout_toStartOf="@+id/tvNew" /> + + diff --git a/MPChartExample/res/layout/radar_markerview.xml b/MPChartExample/res/layout/radar_markerview.xml new file mode 100644 index 0000000000..9ab1b84121 --- /dev/null +++ b/MPChartExample/res/layout/radar_markerview.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/MPChartExample/res/menu/bar.xml b/MPChartExample/res/menu/bar.xml index 52eadc6458..4bbfedd4db 100644 --- a/MPChartExample/res/menu/bar.xml +++ b/MPChartExample/res/menu/bar.xml @@ -6,12 +6,12 @@ android:title="Toggle Values"> + android:id="@+id/actionToggleIcons" + android:title="Toggle Icons"> + android:id="@+id/actionToggleHighlight" + android:title="Toggle Highlight"> - - - - @@ -45,5 +37,13 @@ android:id="@+id/actionTogglePinch" android:title="Toggle PinchZoom"> + + + + \ No newline at end of file diff --git a/MPChartExample/res/menu/bubble.xml b/MPChartExample/res/menu/bubble.xml new file mode 100644 index 0000000000..b7950291e9 --- /dev/null +++ b/MPChartExample/res/menu/bubble.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MPChartExample/res/menu/candle.xml b/MPChartExample/res/menu/candle.xml index 972e172826..cdf1c4e4dd 100644 --- a/MPChartExample/res/menu/candle.xml +++ b/MPChartExample/res/menu/candle.xml @@ -5,10 +5,6 @@ android:id="@+id/actionToggleHighlight" android:title="Toggle Highlight"> - - @@ -33,5 +29,13 @@ android:id="@+id/actionTogglePinch" android:title="Toggle PinchZoom"> + + + + \ No newline at end of file diff --git a/MPChartExample/res/menu/combined.xml b/MPChartExample/res/menu/combined.xml index 9b034d7f4f..7e37ba0d83 100644 --- a/MPChartExample/res/menu/combined.xml +++ b/MPChartExample/res/menu/combined.xml @@ -9,5 +9,8 @@ android:id="@+id/actionToggleBarValues" android:title="Toggle bar Values"> - + + \ No newline at end of file diff --git a/MPChartExample/res/menu/draw.xml b/MPChartExample/res/menu/draw.xml index e519fd1cff..50f35239e7 100644 --- a/MPChartExample/res/menu/draw.xml +++ b/MPChartExample/res/menu/draw.xml @@ -17,18 +17,10 @@ android:id="@+id/actionToggleHighlight" android:title="Toggle Highlight"> - - - - @@ -37,4 +29,8 @@ android:id="@+id/actionTogglePinch" android:title="Toggle PinchZoom"> + + \ No newline at end of file diff --git a/MPChartExample/res/menu/line.xml b/MPChartExample/res/menu/line.xml index d58bcd309e..f9f5be9615 100644 --- a/MPChartExample/res/menu/line.xml +++ b/MPChartExample/res/menu/line.xml @@ -5,6 +5,10 @@ android:id="@+id/actionToggleValues" android:title="Toggle Values"> + + @@ -13,17 +17,21 @@ android:id="@+id/actionToggleCircles" android:title="Toggle Circles"> - + android:id="@+id/actionToggleStepped" + android:title="Toggle Stepped"> + android:id="@+id/actionToggleHorizontalCubic" + android:title="Toggle Horizontal Cubic"> + + - - - + + \ No newline at end of file diff --git a/MPChartExample/res/menu/pie.xml b/MPChartExample/res/menu/pie.xml index 3013da3f2c..0e5323a590 100644 --- a/MPChartExample/res/menu/pie.xml +++ b/MPChartExample/res/menu/pie.xml @@ -5,6 +5,10 @@ android:id="@+id/actionToggleValues" android:title="Toggle Y-Values"> + + @@ -37,5 +41,9 @@ android:id="@+id/actionSave" android:title="Save to Gallery"> + + \ No newline at end of file diff --git a/MPChartExample/res/menu/radar.xml b/MPChartExample/res/menu/radar.xml index cbde056f4f..14690f446c 100644 --- a/MPChartExample/res/menu/radar.xml +++ b/MPChartExample/res/menu/radar.xml @@ -5,6 +5,10 @@ android:id="@+id/actionToggleValues" android:title="Toggle Values"> + + @@ -13,6 +17,22 @@ android:id="@+id/actionToggleHighlight" android:title="Toggle Highlight"> + + + + + + + + diff --git a/MPChartExample/res/menu/realm.xml b/MPChartExample/res/menu/realm.xml new file mode 100644 index 0000000000..f954443b30 --- /dev/null +++ b/MPChartExample/res/menu/realm.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/MPChartExample/res/menu/realtime.xml b/MPChartExample/res/menu/realtime.xml index 03f4e7c8a9..a4b2d22a78 100644 --- a/MPChartExample/res/menu/realtime.xml +++ b/MPChartExample/res/menu/realtime.xml @@ -5,4 +5,12 @@ android:id="@+id/actionAdd" android:title="Feed new Entry"> + + + + \ No newline at end of file diff --git a/MPChartExample/res/menu/scatter.xml b/MPChartExample/res/menu/scatter.xml index 3020bdb786..b7950291e9 100644 --- a/MPChartExample/res/menu/scatter.xml +++ b/MPChartExample/res/menu/scatter.xml @@ -6,12 +6,12 @@ android:title="Toggle Values"> + android:id="@+id/actionToggleIcons" + android:title="Toggle Icons"> + android:id="@+id/actionToggleHighlight" + android:title="Toggle Highlight"> - - @@ -41,5 +37,9 @@ android:id="@+id/actionTogglePinch" android:title="Toggle PinchZoom"> + + \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index e15f57b7e2..ba01acd794 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -16,9 +16,8 @@ import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -48,7 +47,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (BarChart) findViewById(R.id.chart1); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn @@ -62,7 +61,6 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setSpaceBetweenLabels(0); xAxis.setDrawGridLines(false); mChart.getAxisLeft().setDrawGridLines(false); @@ -75,14 +73,6 @@ protected void onCreate(Bundle savedInstanceState) { mChart.animateY(2500); mChart.getLegend().setEnabled(false); - - // Legend l = mChart.getLegend(); - // l.setPosition(LegendPosition.BELOW_CHART_CENTER); - // l.setFormSize(8f); - // l.setFormToTextSpace(4f); - // l.setXEntrySpace(6f); - - // mChart.setDrawLegend(false); } @Override @@ -97,18 +87,18 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + for (IDataSet set : mChart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); mChart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); + + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } break; } case R.id.actionTogglePinch: { @@ -120,28 +110,14 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); break; } - case R.id.actionToggleAdjustXLegend: { - XAxis xAxis = mChart.getXAxis(); - - if (xAxis.isAdjustXLabelsEnabled()) - xAxis.setAdjustXLabels(false); - else - xAxis.setAdjustXLabels(true); + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : mChart.getData().getDataSets()) + ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); mChart.invalidate(); break; @@ -159,18 +135,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.animateXY(3000, 3000); break; } - case R.id.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - break; - } case R.id.actionSave: { if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", @@ -194,25 +158,31 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { float mult = (mSeekBarY.getProgress() + 1); - float val1 = (float) (Math.random() * mult) + mult / 3; - yVals1.add(new BarEntry((int) val1, i)); + float val = (float) (Math.random() * mult) + mult / 3; + yVals1.add(new BarEntry(i, val)); } - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add((int) yVals1.get(i).getVal() + ""); + BarDataSet set1; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set1.setValues(yVals1); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(yVals1, "Data Set"); + set1.setColors(ColorTemplate.VORDIPLOM_COLORS); + set1.setDrawValues(false); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + mChart.setData(data); + mChart.setFitBars(true); } - BarDataSet set1 = new BarDataSet(yVals1, "Data Set"); - set1.setColors(ColorTemplate.VORDIPLOM_COLORS); - set1.setDrawValues(false); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - - BarData data = new BarData(xVals, dataSets); - - mChart.setData(data); mChart.invalidate(); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 352db3a3e7..5772359773 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -2,9 +2,7 @@ package com.xxmassdeveloper.mpchartexample; import android.annotation.SuppressLint; -import android.graphics.PointF; import android.graphics.RectF; -import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -23,17 +21,21 @@ import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.ValueFormatter; -import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter; +import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; +import com.xxmassdeveloper.mpchartexample.custom.XYMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -44,8 +46,6 @@ public class BarChartActivity extends DemoBase implements OnSeekBarChangeListene protected BarChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - - private Typeface mTf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -63,10 +63,10 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (BarChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); - mChart.setDrawBarShadow(true); + mChart.setDrawBarShadow(false); mChart.setDrawValueAboveBar(true); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn @@ -75,33 +75,54 @@ protected void onCreate(Bundle savedInstanceState) { // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); - // draw shadows for each bar that show the maximum value - // mChart.setDrawBarShadow(true); - - // mChart.setDrawXLabels(false); - mChart.setDrawGridBackground(false); // mChart.setDrawYLabels(false); - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + IAxisValueFormatter xAxisFormatter = new DayAxisValueFormatter(mChart); XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTf); + xAxis.setTypeface(mTfLight); xAxis.setDrawGridLines(false); - - ValueFormatter custom = new MyValueFormatter(); + xAxis.setGranularity(1f); // only intervals of 1 day + xAxis.setLabelCount(7); + xAxis.setValueFormatter(xAxisFormatter); + + IAxisValueFormatter custom = new MyAxisValueFormatter(); YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTf); - leftAxis.setLabelCount(8); + leftAxis.setTypeface(mTfLight); + leftAxis.setLabelCount(8, false); leftAxis.setValueFormatter(custom); + leftAxis.setPosition(YAxisLabelPosition.OUTSIDE_CHART); + leftAxis.setSpaceTop(15f); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) YAxis rightAxis = mChart.getAxisRight(); rightAxis.setDrawGridLines(false); - rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(8); + rightAxis.setTypeface(mTfLight); + rightAxis.setLabelCount(8, false); rightAxis.setValueFormatter(custom); + rightAxis.setSpaceTop(15f); + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + + Legend l = mChart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setForm(LegendForm.SQUARE); + l.setFormSize(9f); + l.setTextSize(11f); + l.setXEntrySpace(4f); + // l.setExtra(ColorTemplate.VORDIPLOM_COLORS, new String[] { "abc", + // "def", "ghj", "ikl", "mno" }); + // l.setCustom(ColorTemplate.VORDIPLOM_COLORS, new String[] { "abc", + // "def", "ghj", "ikl", "mno" }); + + XYMarkerView mv = new XYMarkerView(this, xAxisFormatter); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart setData(12, 50); @@ -112,13 +133,6 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setOnSeekBarChangeListener(this); mSeekBarX.setOnSeekBarChangeListener(this); - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); - l.setForm(LegendForm.SQUARE); - l.setFormSize(9f); - l.setTextSize(11f); - l.setXEntrySpace(4f); - // mChart.setDrawLegend(false); } @@ -133,20 +147,26 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + for (IDataSet set : mChart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); mChart.invalidate(); break; } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); + case R.id.actionToggleIcons: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + mChart.invalidate(); break; } + case R.id.actionToggleHighlight: { + if (mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } case R.id.actionTogglePinch: { if (mChart.isPinchZoomEnabled()) mChart.setPinchZoom(false); @@ -156,18 +176,15 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); break; } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.notifyDataSetChanged(); + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : mChart.getData().getDataSets()) + ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + mChart.invalidate(); break; } @@ -184,29 +201,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.animateXY(3000, 3000); break; } - case R.id.actionToggleAdjustXLegend: { - XAxis xLabels = mChart.getXAxis(); - - if (xLabels.isAdjustXLabelsEnabled()) - xLabels.setAdjustXLabels(false); - else - xLabels.setAdjustXLabels(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - break; - } case R.id.actionSave: { if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", @@ -223,68 +217,90 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); + tvX.setText("" + (mSeekBarX.getProgress() + 2)); tvY.setText("" + (mSeekBarY.getProgress())); - setData(mSeekBarX.getProgress(), mSeekBarY.getProgress()); + setData(mSeekBarX.getProgress() + 1 , mSeekBarY.getProgress()); mChart.invalidate(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub - } @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub - } private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add(mMonths[i % 12]); - } + float start = 1f; ArrayList yVals1 = new ArrayList(); - for (int i = 0; i < count; i++) { + for (int i = (int) start; i < start + count + 1; i++) { float mult = (range + 1); float val = (float) (Math.random() * mult); - yVals1.add(new BarEntry(val, i)); + + if (Math.random() * 100 < 25) { + yVals1.add(new BarEntry(i, val, getResources().getDrawable(R.drawable.star))); + } else { + yVals1.add(new BarEntry(i, val)); + } } - BarDataSet set1 = new BarDataSet(yVals1, "DataSet"); - set1.setBarSpacePercent(35f); + BarDataSet set1; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); + set1.setValues(yVals1); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(yVals1, "The year 2017"); + + set1.setDrawIcons(false); - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + set1.setColors(ColorTemplate.MATERIAL_COLORS); - BarData data = new BarData(xVals, dataSets); -// data.setValueFormatter(new MyValueFormatter()); - data.setValueTextSize(10f); - data.setValueTypeface(mTf); + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); - mChart.setData(data); + BarData data = new BarData(dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(mTfLight); + data.setBarWidth(0.9f); + + mChart.setData(data); + } } + protected RectF mOnValueSelectedRectF = new RectF(); + @SuppressLint("NewApi") @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { if (e == null) return; - RectF bounds = mChart.getBarBounds((BarEntry) e); - PointF position = mChart.getPosition(e, AxisDependency.LEFT); + RectF bounds = mOnValueSelectedRectF; + mChart.getBarBounds((BarEntry) e, bounds); + MPPointF position = mChart.getPosition(e, AxisDependency.LEFT); Log.i("bounds", bounds.toString()); Log.i("position", position.toString()); + + Log.i("x-index", + "low: " + mChart.getLowestVisibleX() + ", high: " + + mChart.getHighestVisibleX()); + + MPPointF.recycleInstance(position); } - public void onNothingSelected() { - }; + @Override + public void onNothingSelected() { } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 7f131a43d5..671ab5abec 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -2,7 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -13,18 +12,19 @@ import android.widget.TextView; import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.LargeValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.LargeValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -36,8 +36,6 @@ public class BarChartActivityMultiDataset extends DemoBase implements OnSeekBarC private BarChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -47,6 +45,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_barchart); tvX = (TextView) findViewById(R.id.tvXMax); + tvX.setTextSize(10); tvY = (TextView) findViewById(R.id.tvYMax); mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); @@ -57,7 +56,9 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (BarChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); + +// mChart.setDrawBorders(true); // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); @@ -69,31 +70,40 @@ protected void onCreate(Bundle savedInstanceState) { // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - - // define an offset to change the original position of the marker - // (optional) - // mv.setOffsets(-mv.getMeasuredWidth() / 2, -mv.getMeasuredHeight()); - - // set the marker to the chart - mChart.setMarkerView(mv); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart mSeekBarX.setProgress(10); mSeekBarY.setProgress(100); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART_INSIDE); - l.setTypeface(tf); - - XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(true); + l.setTypeface(mTfLight); + l.setYOffset(0f); + l.setXOffset(10f); + l.setYEntrySpace(0f); + l.setTextSize(8f); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setTypeface(mTfLight); + xAxis.setGranularity(1f); + xAxis.setCenterAxisLabels(true); + xAxis.setValueFormatter(new IAxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return String.valueOf((int) value); + } + }); YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); + leftAxis.setTypeface(mTfLight); leftAxis.setValueFormatter(new LargeValueFormatter()); leftAxis.setDrawGridLines(false); - leftAxis.setSpaceTop(25f); + leftAxis.setSpaceTop(35f); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); } @@ -109,7 +119,7 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : mChart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); mChart.invalidate(); @@ -124,37 +134,23 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); break; } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : mChart.getData().getDataSets()) + ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + mChart.invalidate(); break; } - case R.id.actionToggleAdjustXLegend: { - XAxis xAxis = mChart.getXAxis(); - - if (xAxis.isAdjustXLabelsEnabled()) - xAxis.setAdjustXLabels(false); - else - xAxis.setAdjustXLabels(true); - - mChart.invalidate(); + case R.id.actionToggleHighlight: { + if (mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } break; } case R.id.actionSave: { @@ -171,7 +167,6 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); break; } @@ -182,76 +177,90 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); + float groupSpace = 0.08f; + float barSpace = 0.03f; // x4 DataSet + float barWidth = 0.2f; // x4 DataSet + // (0.2 + 0.03) * 4 + 0.08 = 1.00 -> interval per "group" - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - xVals.add((i + 1990) + ""); - } + int groupCount = mSeekBarX.getProgress() + 1; + int startYear = 1980; + int endYear = startYear + groupCount; + + tvX.setText(startYear + "-" + endYear); + tvY.setText("" + (mSeekBarY.getProgress())); ArrayList yVals1 = new ArrayList(); ArrayList yVals2 = new ArrayList(); ArrayList yVals3 = new ArrayList(); + ArrayList yVals4 = new ArrayList(); - float mult = mSeekBarY.getProgress() * 10000000f; + float randomMultiplier = mSeekBarY.getProgress() * 100000f; - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mult) + 3; - yVals1.add(new BarEntry(val, i)); + for (int i = startYear; i < endYear; i++) { + yVals1.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + yVals2.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + yVals3.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + yVals4.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); } - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mult) + 3; - yVals2.add(new BarEntry(val, i)); + BarDataSet set1, set2, set3, set4; + + if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { + + set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); + set2 = (BarDataSet) mChart.getData().getDataSetByIndex(1); + set3 = (BarDataSet) mChart.getData().getDataSetByIndex(2); + set4 = (BarDataSet) mChart.getData().getDataSetByIndex(3); + set1.setValues(yVals1); + set2.setValues(yVals2); + set3.setValues(yVals3); + set4.setValues(yVals4); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + + } else { + // create 4 DataSets + set1 = new BarDataSet(yVals1, "Company A"); + set1.setColor(Color.rgb(104, 241, 175)); + set2 = new BarDataSet(yVals2, "Company B"); + set2.setColor(Color.rgb(164, 228, 251)); + set3 = new BarDataSet(yVals3, "Company C"); + set3.setColor(Color.rgb(242, 247, 158)); + set4 = new BarDataSet(yVals4, "Company D"); + set4.setColor(Color.rgb(255, 102, 0)); + + BarData data = new BarData(set1, set2, set3, set4); + data.setValueFormatter(new LargeValueFormatter()); + data.setValueTypeface(mTfLight); + + mChart.setData(data); } - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mult) + 3; - yVals3.add(new BarEntry(val, i)); - } + // specify the width each bar should have + mChart.getBarData().setBarWidth(barWidth); - // create 3 datasets with different types - BarDataSet set1 = new BarDataSet(yVals1, "Company A"); - // set1.setColors(ColorTemplate.createColors(getApplicationContext(), - // ColorTemplate.FRESH_COLORS)); - set1.setColor(Color.rgb(104, 241, 175)); - BarDataSet set2 = new BarDataSet(yVals2, "Company B"); - set2.setColor(Color.rgb(164, 228, 251)); - BarDataSet set3 = new BarDataSet(yVals3, "Company C"); - set3.setColor(Color.rgb(242, 247, 158)); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - dataSets.add(set2); - dataSets.add(set3); - - BarData data = new BarData(xVals, dataSets); - data.setValueFormatter(new LargeValueFormatter()); - - // add space between the dataset groups in percent of bar-width - data.setGroupSpace(80f); - data.setValueTypeface(tf); - - mChart.setData(data); + // restrict the x-axis range + mChart.getXAxis().setAxisMinimum(startYear); + + // barData.getGroupWith(...) is a helper that calculates the width each group needs based on the provided parameters + mChart.getXAxis().setAxisMaximum(startYear + mChart.getBarData().getGroupWidth(groupSpace, barSpace) * groupCount); + mChart.groupBars(startYear, groupSpace, barSpace); mChart.invalidate(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub - } @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub - } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - Log.i("Activity", "Selected: " + e.toString() + ", dataSet: " + dataSetIndex); + public void onValueSelected(Entry e, Highlight h) { + Log.i("Activity", "Selected: " + e.toString() + ", dataSet: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java new file mode 100644 index 0000000000..56add4458e --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -0,0 +1,229 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; +import android.widget.Toast; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.Legend.LegendForm; +import com.github.mikephil.charting.components.Legend.LegendPosition; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.FileUtils; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +public class BarChartActivitySinus extends DemoBase implements OnSeekBarChangeListener { + + protected BarChart mChart; + private SeekBar mSeekBarX; + private TextView tvX; + + private List mSinusData; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart_sinus); + + mSinusData = FileUtils.loadBarEntriesFromAssets(getAssets(), "othersine.txt"); + + tvX = (TextView) findViewById(R.id.tvValueCount); + + mSeekBarX = (SeekBar) findViewById(R.id.seekbarValues); + + mChart = (BarChart) findViewById(R.id.chart1); + + mChart.setDrawBarShadow(false); + mChart.setDrawValueAboveBar(true); + + mChart.getDescription().setEnabled(false); + + // if more than 60 entries are displayed in the chart, no values will be + // drawn + mChart.setMaxVisibleValueCount(60); + + // scaling can now only be done on x- and y-axis separately + mChart.setPinchZoom(false); + + // draw shadows for each bar that show the maximum value + // mChart.setDrawBarShadow(true); + + // mChart.setDrawXLabels(false); + + mChart.setDrawGridBackground(false); + // mChart.setDrawYLabels(false); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setEnabled(false); + + YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.setTypeface(mTfLight); + leftAxis.setLabelCount(6, false); + leftAxis.setAxisMinimum(-2.5f); + leftAxis.setAxisMaximum(2.5f); + leftAxis.setGranularityEnabled(true); + leftAxis.setGranularity(0.1f); + + YAxis rightAxis = mChart.getAxisRight(); + rightAxis.setDrawGridLines(false); + rightAxis.setTypeface(mTfLight); + rightAxis.setLabelCount(6, false); + rightAxis.setAxisMinimum(-2.5f); + rightAxis.setAxisMaximum(2.5f); + rightAxis.setGranularity(0.1f); + + mSeekBarX.setOnSeekBarChangeListener(this); + mSeekBarX.setProgress(150); // set data + + Legend l = mChart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setForm(LegendForm.SQUARE); + l.setFormSize(9f); + l.setTextSize(11f); + l.setXEntrySpace(4f); + + mChart.animateXY(2000, 2000); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.actionToggleValues: { + for (IBarDataSet set : mChart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + mChart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if (mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (mChart.isPinchZoomEnabled()) + mChart.setPinchZoom(false); + else + mChart.setPinchZoom(true); + + mChart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : mChart.getData().getDataSets()) + ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + mChart.invalidate(); + break; + } + case R.id.animateX: { + mChart.animateX(1500); + break; + } + case R.id.animateY: { + mChart.animateY(1500); + break; + } + case R.id.animateXY: { + + mChart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { + Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", + Toast.LENGTH_SHORT).show(); + } else + Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) + .show(); + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText("" + (mSeekBarX.getProgress())); + + setData(mSeekBarX.getProgress()); + mChart.invalidate(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + private void setData(int count) { + + ArrayList entries = new ArrayList(); + + for (int i = 0; i < count; i++) { + entries.add(mSinusData.get(i)); + } + + BarDataSet set; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set = (BarDataSet) mChart.getData().getDataSetByIndex(0); + set.setValues(entries); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + set = new BarDataSet(entries, "Sinus Function"); + set.setColor(Color.rgb(240, 120, 124)); + } + + BarData data = new BarData(set); + data.setValueTextSize(10f); + data.setValueTypeface(mTfLight); + data.setDrawValues(false); + data.setBarWidth(0.8f); + + mChart.setData(data); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java new file mode 100644 index 0000000000..743b7e735d --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -0,0 +1,173 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.graphics.Typeface; +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +public class BarChartPositiveNegative extends DemoBase { + + protected BarChart mChart; + private Typeface mTf; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart_noseekbar); + + mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + mChart = (BarChart) findViewById(R.id.chart1); + mChart.setBackgroundColor(Color.WHITE); + mChart.setExtraTopOffset(-30f); + mChart.setExtraBottomOffset(10f); + mChart.setExtraLeftOffset(70f); + mChart.setExtraRightOffset(70f); + + mChart.setDrawBarShadow(false); + mChart.setDrawValueAboveBar(true); + + mChart.getDescription().setEnabled(false); + + // scaling can now only be done on x- and y-axis separately + mChart.setPinchZoom(false); + + mChart.setDrawGridBackground(false); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setPosition(XAxisPosition.BOTTOM); + xAxis.setTypeface(mTf); + xAxis.setDrawGridLines(false); + xAxis.setDrawAxisLine(false); + xAxis.setTextColor(Color.LTGRAY); + xAxis.setTextSize(13f); + xAxis.setLabelCount(5); + xAxis.setCenterAxisLabels(true); + xAxis.setGranularity(1f); + + YAxis left = mChart.getAxisLeft(); + left.setDrawLabels(false); + left.setSpaceTop(25f); + left.setSpaceBottom(25f); + left.setDrawAxisLine(false); + left.setDrawGridLines(false); + left.setDrawZeroLine(true); // draw a zero line + left.setZeroLineColor(Color.GRAY); + left.setZeroLineWidth(0.7f); + mChart.getAxisRight().setEnabled(false); + mChart.getLegend().setEnabled(false); + + // THIS IS THE ORIGINAL DATA YOU WANT TO PLOT + final List data = new ArrayList<>(); + data.add(new Data(0f, -224.1f, "12-29")); + data.add(new Data(1f, 238.5f, "12-30")); + data.add(new Data(2f, 1280.1f, "12-31")); + data.add(new Data(3f, -442.3f, "01-01")); + data.add(new Data(4f, -2280.1f, "01-02")); + + xAxis.setValueFormatter(new IAxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return data.get(Math.min(Math.max((int) value, 0), data.size()-1)).xAxisValue; + } + }); + + setData(data); + } + + private void setData(List dataList) { + + ArrayList values = new ArrayList(); + List colors = new ArrayList(); + + int green = Color.rgb(110, 190, 102); + int red = Color.rgb(211, 74, 88); + + for (int i = 0; i < dataList.size(); i++) { + + Data d = dataList.get(i); + BarEntry entry = new BarEntry(d.xValue, d.yValue); + values.add(entry); + + // specific colors + if (d.yValue >= 0) + colors.add(red); + else + colors.add(green); + } + + BarDataSet set; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set.setValues(values); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + set = new BarDataSet(values, "Values"); + set.setColors(colors); + set.setValueTextColors(colors); + + BarData data = new BarData(set); + data.setValueTextSize(13f); + data.setValueTypeface(mTf); + data.setValueFormatter(new ValueFormatter()); + data.setBarWidth(0.8f); + + mChart.setData(data); + mChart.invalidate(); + } + } + + /** + * Demo class representing data. + */ + private class Data { + + public String xAxisValue; + public float yValue; + public float xValue; + + public Data(float xValue, float yValue, String xAxisValue) { + this.xAxisValue = xAxisValue; + this.yValue = yValue; + this.xValue = xValue; + } + } + + private class ValueFormatter implements IValueFormatter + { + + private DecimalFormat mFormat; + + public ValueFormatter() { + mFormat = new DecimalFormat("######.0"); + } + + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return mFormat.format(value); + } + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java new file mode 100644 index 0000000000..0ecc1e9c93 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -0,0 +1,252 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.BubbleChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.Legend.LegendPosition; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BubbleData; +import com.github.mikephil.charting.data.BubbleDataSet; +import com.github.mikephil.charting.data.BubbleEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class BubbleChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private BubbleChart mChart; + private SeekBar mSeekBarX, mSeekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_bubblechart); + + tvX = (TextView) findViewById(R.id.tvXMax); + tvY = (TextView) findViewById(R.id.tvYMax); + + mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarX.setOnSeekBarChangeListener(this); + + mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarY.setOnSeekBarChangeListener(this); + + mChart = (BubbleChart) findViewById(R.id.chart1); + mChart.getDescription().setEnabled(false); + + mChart.setOnChartValueSelectedListener(this); + + mChart.setDrawGridBackground(false); + + mChart.setTouchEnabled(true); + + // enable scaling and dragging + mChart.setDragEnabled(true); + mChart.setScaleEnabled(true); + + mChart.setMaxVisibleValueCount(200); + mChart.setPinchZoom(true); + + mSeekBarX.setProgress(10); + mSeekBarY.setProgress(50); + + Legend l = mChart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); + l.setTypeface(mTfLight); + + YAxis yl = mChart.getAxisLeft(); + yl.setTypeface(mTfLight); + yl.setSpaceTop(30f); + yl.setSpaceBottom(30f); + yl.setDrawZeroLine(false); + + mChart.getAxisRight().setEnabled(false); + + XAxis xl = mChart.getXAxis(); + xl.setPosition(XAxis.XAxisPosition.BOTTOM); + xl.setTypeface(mTfLight); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bubble, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.actionToggleValues: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + mChart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + mChart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (mChart.isPinchZoomEnabled()) + mChart.setPinchZoom(false); + else + mChart.setPinchZoom(true); + + mChart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); + break; + } + case R.id.actionSave: { + mChart.saveToPath("title" + System.currentTimeMillis(), ""); + break; + } + case R.id.animateX: { + mChart.animateX(3000); + break; + } + case R.id.animateY: { + mChart.animateY(3000); + break; + } + case R.id.animateXY: { + + mChart.animateXY(3000, 3000); + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + int count = mSeekBarX.getProgress(); + int range = mSeekBarY.getProgress(); + + tvX.setText("" + count); + tvY.setText("" + range); + + ArrayList yVals1 = new ArrayList(); + ArrayList yVals2 = new ArrayList(); + ArrayList yVals3 = new ArrayList(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range); + float size = (float) (Math.random() * range); + + yVals1.add(new BubbleEntry(i, val, size, getResources().getDrawable(R.drawable.star))); + } + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range); + float size = (float) (Math.random() * range); + + yVals2.add(new BubbleEntry(i, val, size, getResources().getDrawable(R.drawable.star))); + } + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range); + float size = (float) (Math.random() * range); + + yVals3.add(new BubbleEntry(i, val, size)); + } + + // create a dataset and give it a type + BubbleDataSet set1 = new BubbleDataSet(yVals1, "DS 1"); + set1.setDrawIcons(false); + set1.setColor(ColorTemplate.COLORFUL_COLORS[0], 130); + set1.setDrawValues(true); + + BubbleDataSet set2 = new BubbleDataSet(yVals2, "DS 2"); + set2.setDrawIcons(false); + set2.setIconsOffset(new MPPointF(0, 15)); + set2.setColor(ColorTemplate.COLORFUL_COLORS[1], 130); + set2.setDrawValues(true); + + BubbleDataSet set3 = new BubbleDataSet(yVals3, "DS 3"); + set3.setColor(ColorTemplate.COLORFUL_COLORS[2], 130); + set3.setDrawValues(true); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); // add the datasets + dataSets.add(set2); + dataSets.add(set3); + + // create a data object with the datasets + BubbleData data = new BubbleData(dataSets); + data.setDrawValues(false); + data.setValueTypeface(mTfLight); + data.setValueTextSize(8f); + data.setValueTextColor(Color.WHITE); + data.setHighlightCircleWidth(1.5f); + + mChart.setData(data); + mChart.invalidate(); + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + Log.i("VAL SELECTED", + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); + } + + @Override + public void onNothingSelected() { + // TODO Auto-generated method stub + + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index 420a04d107..bd8dde108f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -2,6 +2,7 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; +import android.graphics.Paint; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; @@ -19,6 +20,8 @@ import com.github.mikephil.charting.data.CandleData; import com.github.mikephil.charting.data.CandleDataSet; import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -46,8 +49,9 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setOnSeekBarChangeListener(this); mChart = (CandleStickChart) findViewById(R.id.chart1); + mChart.setBackgroundColor(Color.WHITE); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn @@ -60,32 +64,23 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setSpaceBetweenLabels(2); + xAxis.setDrawGridLines(false); YAxis leftAxis = mChart.getAxisLeft(); // leftAxis.setEnabled(false); - leftAxis.setLabelCount(7); + leftAxis.setLabelCount(7, false); leftAxis.setDrawGridLines(false); leftAxis.setDrawAxisLine(false); - leftAxis.setStartAtZero(true); YAxis rightAxis = mChart.getAxisRight(); rightAxis.setEnabled(false); // rightAxis.setStartAtZero(false); // setting data - mSeekBarX.setProgress(15); + mSeekBarX.setProgress(40); mSeekBarY.setProgress(100); mChart.getLegend().setEnabled(false); - - // Legend l = mChart.getLegend(); - // l.setPosition(LegendPosition.BELOW_CHART_CENTER); - // l.setFormSize(8f); - // l.setFormToTextSpace(4f); - // l.setXEntrySpace(6f); - - // mChart.setDrawLegend(false); } @Override @@ -98,14 +93,27 @@ public boolean onCreateOptionsMenu(Menu menu) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); + case R.id.actionToggleValues: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + mChart.invalidate(); break; } + case R.id.actionToggleIcons: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + mChart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } case R.id.actionTogglePinch: { if (mChart.isPinchZoomEnabled()) mChart.setPinchZoom(false); @@ -115,19 +123,15 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); break; } - case R.id.actionToggleAdjustXLegend: { - XAxis xAxis = mChart.getXAxis(); - - if (xAxis.isAdjustXLabelsEnabled()) - xAxis.setAdjustXLabels(false); - else - xAxis.setAdjustXLabels(true); + case R.id.actionToggleMakeShadowSameColorAsCandle: { + for (ICandleDataSet set : mChart.getData().getDataSets()) { + //TODO: set.setShadowColorSameAsCandle(!set.getShadowColorSameAsCandle()); + } mChart.invalidate(); break; @@ -161,10 +165,12 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - int prog = (mSeekBarX.getProgress() + 1) * 2; + int prog = (mSeekBarX.getProgress() + 1); tvX.setText("" + prog); tvY.setText("" + (mSeekBarY.getProgress())); + + mChart.resetTracking(); ArrayList yVals1 = new ArrayList(); @@ -180,20 +186,30 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { boolean even = i % 2 == 0; - yVals1.add(new CandleEntry(i, val + high, val - low, even ? val + open : val - open, - even ? val - close : val + close)); - } - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < prog; i++) { - xVals.add("" + (1990 + i)); + yVals1.add(new CandleEntry( + i, val + high, + val - low, + even ? val + open : val - open, + even ? val - close : val + close, + getResources().getDrawable(R.drawable.star) + )); } CandleDataSet set1 = new CandleDataSet(yVals1, "Data Set"); - set1.setAxisDependency(AxisDependency.LEFT); - set1.setColor(Color.rgb(80, 80, 80)); - CandleData data = new CandleData(xVals, set1); + set1.setDrawIcons(false); + set1.setAxisDependency(AxisDependency.LEFT); +// set1.setColor(Color.rgb(80, 80, 80)); + set1.setShadowColor(Color.DKGRAY); + set1.setShadowWidth(0.7f); + set1.setDecreasingColor(Color.RED); + set1.setDecreasingPaintStyle(Paint.Style.FILL); + set1.setIncreasingColor(Color.rgb(122, 242, 84)); + set1.setIncreasingPaintStyle(Paint.Style.STROKE); + set1.setNeutralColor(Color.BLUE); + //set1.setHighlightLineWidth(1f); + + CandleData data = new CandleData(set1); mChart.setData(data); mChart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 44e06e7bf8..fadfbf175f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -8,22 +8,30 @@ import android.view.WindowManager; import com.github.mikephil.charting.charts.CombinedChart; +import com.github.mikephil.charting.charts.CombinedChart.DrawOrder; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.BubbleData; +import com.github.mikephil.charting.data.BubbleDataSet; +import com.github.mikephil.charting.data.BubbleEntry; import com.github.mikephil.charting.data.CandleData; import com.github.mikephil.charting.data.CandleDataSet; import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.CombinedData; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -41,24 +49,53 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_combined); mChart = (CombinedChart) findViewById(R.id.chart1); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); + mChart.setBackgroundColor(Color.WHITE); mChart.setDrawGridBackground(false); + mChart.setDrawBarShadow(false); + mChart.setHighlightFullBarEnabled(false); + + // draw bars behind lines + mChart.setDrawOrder(new DrawOrder[]{ + DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.CANDLE, DrawOrder.LINE, DrawOrder.SCATTER + }); + + Legend l = mChart.getLegend(); + l.setWordWrapEnabled(true); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); YAxis rightAxis = mChart.getAxisRight(); rightAxis.setDrawGridLines(false); + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setDrawGridLines(false); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTH_SIDED); + xAxis.setAxisMinimum(0f); + xAxis.setGranularity(1f); + xAxis.setValueFormatter(new IAxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mMonths[(int) value % mMonths.length]; + } + }); - CombinedData data = new CombinedData(mMonths); + CombinedData data = new CombinedData(); data.setData(generateLineData()); data.setData(generateBarData()); -// data.setData(generateScatterData()); -// data.setData(generateCandleData()); + data.setData(generateBubbleData()); + data.setData(generateScatterData()); + data.setData(generateCandleData()); + data.setValueTypeface(mTfLight); + + xAxis.setAxisMaximum(data.getXMax() + 0.25f); mChart.setData(data); mChart.invalidate(); @@ -71,21 +108,20 @@ private LineData generateLineData() { ArrayList entries = new ArrayList(); for (int index = 0; index < itemcount; index++) - entries.add(new Entry(getRandom(15, 10), index)); + entries.add(new Entry(index + 0.5f, getRandom(15, 5))); LineDataSet set = new LineDataSet(entries, "Line DataSet"); set.setColor(Color.rgb(240, 238, 70)); set.setLineWidth(2.5f); set.setCircleColor(Color.rgb(240, 238, 70)); - set.setCircleSize(5f); + set.setCircleRadius(5f); set.setFillColor(Color.rgb(240, 238, 70)); - set.setDrawCubic(true); + set.setMode(LineDataSet.Mode.CUBIC_BEZIER); set.setDrawValues(true); set.setValueTextSize(10f); set.setValueTextColor(Color.rgb(240, 238, 70)); set.setAxisDependency(YAxis.AxisDependency.LEFT); - d.addDataSet(set); return d; @@ -93,35 +129,54 @@ private LineData generateLineData() { private BarData generateBarData() { - BarData d = new BarData(); + ArrayList entries1 = new ArrayList(); + ArrayList entries2 = new ArrayList(); - ArrayList entries = new ArrayList(); + for (int index = 0; index < itemcount; index++) { + entries1.add(new BarEntry(0, getRandom(25, 25))); - for (int index = 0; index < itemcount; index++) - entries.add(new BarEntry(getRandom(15, 30), index)); + // stacked + entries2.add(new BarEntry(0, new float[]{getRandom(13, 12), getRandom(13, 12)})); + } - BarDataSet set = new BarDataSet(entries, "Bar DataSet"); - set.setColor(Color.rgb(60, 220, 78)); - set.setValueTextColor(Color.rgb(60, 220, 78)); - set.setValueTextSize(10f); - d.addDataSet(set); + BarDataSet set1 = new BarDataSet(entries1, "Bar 1"); + set1.setColor(Color.rgb(60, 220, 78)); + set1.setValueTextColor(Color.rgb(60, 220, 78)); + set1.setValueTextSize(10f); + set1.setAxisDependency(YAxis.AxisDependency.LEFT); - set.setAxisDependency(YAxis.AxisDependency.LEFT); + BarDataSet set2 = new BarDataSet(entries2, ""); + set2.setStackLabels(new String[]{"Stack 1", "Stack 2"}); + set2.setColors(new int[]{Color.rgb(61, 165, 255), Color.rgb(23, 197, 255)}); + set2.setValueTextColor(Color.rgb(61, 165, 255)); + set2.setValueTextSize(10f); + set2.setAxisDependency(YAxis.AxisDependency.LEFT); + + float groupSpace = 0.06f; + float barSpace = 0.02f; // x2 dataset + float barWidth = 0.45f; // x2 dataset + // (0.45 + 0.02) * 2 + 0.06 = 1.00 -> interval per "group" + + BarData d = new BarData(set1, set2); + d.setBarWidth(barWidth); + + // make this BarData object grouped + d.groupBars(0, groupSpace, barSpace); // start at x = 0 return d; } - private ScatterData generateScatterData() { + protected ScatterData generateScatterData() { ScatterData d = new ScatterData(); ArrayList entries = new ArrayList(); - for (int index = 0; index < itemcount; index++) - entries.add(new Entry(getRandom(20, 30), index)); + for (float index = 0; index < itemcount; index += 0.5f) + entries.add(new Entry(index + 0.25f, getRandom(10, 55))); ScatterDataSet set = new ScatterDataSet(entries, "Scatter DataSet"); - set.setColor(Color.GREEN); + set.setColors(ColorTemplate.MATERIAL_COLORS); set.setScatterShapeSize(7.5f); set.setDrawValues(false); set.setValueTextSize(10f); @@ -130,18 +185,19 @@ private ScatterData generateScatterData() { return d; } - private CandleData generateCandleData() { + protected CandleData generateCandleData() { CandleData d = new CandleData(); ArrayList entries = new ArrayList(); - for (int index = 0; index < itemcount; index++) - entries.add(new CandleEntry(index, 20f, 10f, 13f, 17f)); + for (int index = 0; index < itemcount; index += 2) + entries.add(new CandleEntry(index + 1f, 90, 70, 85, 75f)); CandleDataSet set = new CandleDataSet(entries, "Candle DataSet"); - set.setColor(Color.rgb(80, 80, 80)); - set.setBodySpace(0.3f); + set.setDecreasingColor(Color.rgb(142, 150, 175)); + set.setShadowColor(Color.DKGRAY); + set.setBarSpace(0.3f); set.setValueTextSize(10f); set.setDrawValues(false); d.addDataSet(set); @@ -149,38 +205,66 @@ private CandleData generateCandleData() { return d; } - private float getRandom(float range, float startsfrom) { - return (float) (Math.random() * range) + startsfrom; - } + protected BubbleData generateBubbleData() { - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.combined, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.actionToggleLineValues: { - for (DataSet set : mChart.getData().getDataSets()) { - if(set instanceof LineDataSet) - set.setDrawValues(!set.isDrawValuesEnabled()); - } + BubbleData bd = new BubbleData(); - mChart.invalidate(); - break; - } - case R.id.actionToggleBarValues: { - for (DataSet set : mChart.getData().getDataSets()) { - if(set instanceof BarDataSet) - set.setDrawValues(!set.isDrawValuesEnabled()); + ArrayList entries = new ArrayList(); + + for (int index = 0; index < itemcount; index++) { + float y = getRandom(10, 105); + float size = getRandom(100, 105); + entries.add(new BubbleEntry(index + 0.5f, y, size)); } - mChart.invalidate(); - break; - } + BubbleDataSet set = new BubbleDataSet(entries, "Bubble DataSet"); + set.setColors(ColorTemplate.VORDIPLOM_COLORS); + set.setValueTextSize(10f); + set.setValueTextColor(Color.WHITE); + set.setHighlightCircleWidth(1.5f); + set.setDrawValues(true); + bd.addDataSet(set); + + return bd; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.combined, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.actionToggleLineValues: { + for (IDataSet set : mChart.getData().getDataSets()) { + if (set instanceof LineDataSet) + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + mChart.invalidate(); + break; + } + case R.id.actionToggleBarValues: { + for (IDataSet set : mChart.getData().getDataSets()) { + if (set instanceof BarDataSet) + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + mChart.invalidate(); + break; + } + case R.id.actionRemoveDataSet: { + + int rnd = (int) getRandom(mChart.getData().getDataSetCount(), 0); + mChart.getData().removeDataSet(mChart.getData().getDataSetByIndex(rnd)); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + mChart.invalidate(); + break; + } + } + return true; } - return true; - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 026d45512d..cb979e80e9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -2,7 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Typeface; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; @@ -15,24 +14,23 @@ import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.formatter.IFillFormatter; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.List; public class CubicLineChartActivity extends DemoBase implements OnSeekBarChangeListener { private LineChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -54,13 +52,11 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarX.setOnSeekBarChangeListener(this); mChart = (LineChart) findViewById(R.id.chart1); - // if enabled, the chart will always start at zero on the y-axis + mChart.setViewPortOffsets(0, 0, 0, 0); + mChart.setBackgroundColor(Color.rgb(104, 241, 175)); // no description text - mChart.setDescription(""); - - // enable value highlighting - mChart.setHighlightEnabled(true); + mChart.getDescription().setEnabled(false); // enable touch gestures mChart.setTouchEnabled(true); @@ -73,15 +69,18 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + mChart.setMaxHighlightDistance(300); XAxis x = mChart.getXAxis(); - x.setTypeface(tf); + x.setEnabled(false); YAxis y = mChart.getAxisLeft(); - y.setTypeface(tf); - y.setLabelCount(5); + y.setTypeface(mTfLight); + y.setLabelCount(6, false); + y.setTextColor(Color.WHITE); + y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); + y.setDrawGridLines(false); + y.setAxisLineColor(Color.WHITE); mChart.getAxisRight().setEnabled(false); @@ -107,26 +106,28 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + for (IDataSet set : mChart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); mChart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } break; } case R.id.actionToggleFilled: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawFilledEnabled()) set.setDrawFilled(false); else @@ -136,10 +137,12 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleCircles: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; if (set.isDrawCirclesEnabled()) set.setDrawCircles(false); else @@ -149,21 +152,44 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleCubic: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); + } + mChart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); } mChart.invalidate(); break; } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); + case R.id.actionToggleHorizontalCubic: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.HORIZONTAL_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.HORIZONTAL_BEZIER); + } mChart.invalidate(); break; } @@ -176,6 +202,11 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); + break; + } case R.id.animateX: { mChart.animateX(3000); break; @@ -188,30 +219,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.animateXY(3000, 3000); break; } - case R.id.actionToggleAdjustXLegend: { - XAxis xLabels = mChart.getXAxis(); - - if (xLabels.isAdjustXLabelsEnabled()) - xLabels.setAdjustXLabels(false); - else - xLabels.setAdjustXLabels(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - // the angle of filtering is 35° - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 35); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - break; - } case R.id.actionSave: { if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", @@ -253,40 +260,55 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((1990 +i) + ""); - } - - ArrayList vals1 = new ArrayList(); + ArrayList yVals = new ArrayList(); for (int i = 0; i < count; i++) { float mult = (range + 1); float val = (float) (Math.random() * mult) + 20;// + (float) // ((mult * // 0.1) / 10); - vals1.add(new Entry(val, i)); + yVals.add(new Entry(i, val)); + } + + LineDataSet set1; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); + set1.setValues(yVals); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(yVals, "DataSet 1"); + + set1.setMode(LineDataSet.Mode.CUBIC_BEZIER); + set1.setCubicIntensity(0.2f); + //set1.setDrawFilled(true); + set1.setDrawCircles(false); + set1.setLineWidth(1.8f); + set1.setCircleRadius(4f); + set1.setCircleColor(Color.WHITE); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setColor(Color.WHITE); + set1.setFillColor(Color.WHITE); + set1.setFillAlpha(100); + set1.setDrawHorizontalHighlightIndicator(false); + set1.setFillFormatter(new IFillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return -10; + } + }); + + // create a data object with the datasets + LineData data = new LineData(set1); + data.setValueTypeface(mTfLight); + data.setValueTextSize(9f); + data.setDrawValues(false); + + // set data + mChart.setData(data); } - - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(vals1, "DataSet 1"); - set1.setDrawCubic(true); - set1.setCubicIntensity(0.2f); - //set1.setDrawFilled(true); - set1.setDrawCircles(false); - set1.setLineWidth(2f); - set1.setCircleSize(5f); - set1.setHighLightColor(Color.rgb(244, 117, 117)); - set1.setColor(Color.rgb(104, 241, 175)); - set1.setFillColor(ColorTemplate.getHoloBlue()); - - // create a data object with the datasets - LineData data = new LineData(xVals, set1); - data.setValueTypeface(tf); - data.setValueTextSize(9f); - data.setDrawValues(false); - - // set data - mChart.setData(data); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index 35b8215648..970ba12909 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java @@ -16,12 +16,14 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.listener.OnDrawListener; -import com.github.mikephil.charting.utils.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.List; /** * This Activity demonstrates drawing into the Chart with the finger. Both line, @@ -47,14 +49,6 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setOnChartValueSelectedListener(this); mChart.setOnDrawListener(this); - // enable drawing with the finger - // mChart.setDrawingEnabled(true); - - // mChart.setLineWidth(5f); - // mChart.setCircleSize(5f); - - mChart.setHighlightEnabled(true); - // if disabled, drawn datasets with the finger will not be automatically // finished // mChart.setAutoFinish(true); @@ -80,23 +74,16 @@ protected void onCreate(Bundle savedInstanceState) { } private void initWithDummyData() { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < 24; i++) { - xVals.add((i) + ":00"); - } ArrayList yVals = new ArrayList(); // create a dataset and give it a type (0) LineDataSet set1 = new LineDataSet(yVals, "DataSet"); set1.setLineWidth(3f); - set1.setCircleSize(5f); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets + set1.setCircleRadius(5f); // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); + LineData data = new LineData(set1); mChart.setData(data); } @@ -112,35 +99,23 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; set.setDrawValues(!set.isDrawValuesEnabled()); + } mChart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionToggleAdjustXLegend: { - XAxis xLabels = mChart.getXAxis(); - - if (xLabels.isAdjustXLabelsEnabled()) - xLabels.setAdjustXLabels(false); - else - xLabels.setAdjustXLabels(true); - - mChart.invalidate(); + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } break; } case R.id.actionTogglePinch: { @@ -152,6 +127,11 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); + break; + } case R.id.actionSave: { // mChart.saveToGallery("title"+System.currentTimeMillis()); mChart.saveToPath("title" + System.currentTimeMillis(), ""); @@ -162,10 +142,10 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); } @Override @@ -180,11 +160,11 @@ public void onEntryAdded(Entry entry) { /** callback when a DataSet has been drawn (when lifting the finger) */ @Override - public void onDrawFinished(DataSet dataSet) { + public void onDrawFinished(DataSet dataSet) { Log.i(Chart.LOG_TAG, "DataSet drawn. " + dataSet.toSimpleString()); // prepare the legend again - mChart.getLegendRenderer().computeLegend(mChart.getData(), mChart.getLegend()); + mChart.getLegendRenderer().computeLegend(mChart.getData()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index 203188bfcb..2875b89d7e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -13,9 +13,10 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -34,10 +35,12 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); - mChart.setDescription(""); - + mChart.getDescription().setEnabled(false); + // add an empty data object mChart.setData(new LineData()); +// mChart.getXAxis().setDrawLabels(false); +// mChart.getXAxis().setDrawGridLines(false); mChart.invalidate(); } @@ -47,55 +50,49 @@ protected void onCreate(Bundle savedInstanceState) { private void addEntry() { LineData data = mChart.getData(); - - if(data != null) { - LineDataSet set = data.getDataSetByIndex(0); - // set.addEntry(...); // can be called as well + ILineDataSet set = data.getDataSetByIndex(0); + // set.addEntry(...); // can be called as well - if (set == null) { - set = createSet(); - data.addDataSet(set); - } + if (set == null) { + set = createSet(); + data.addDataSet(set); + } - // add a new x-value first - data.addXValue(set.getEntryCount() + ""); - - // choose a random dataSet - int randomDataSetIndex = (int) (Math.random() * data.getDataSetCount()); - - data.addEntry(new Entry((float) (Math.random() * 10) + 50f, set.getEntryCount()), randomDataSetIndex); + // choose a random dataSet + int randomDataSetIndex = (int) (Math.random() * data.getDataSetCount()); + float yValue = (float) (Math.random() * 10) + 50f; - // let the chart know it's data has changed - mChart.notifyDataSetChanged(); - -// mChart.setVisibleXRange(6); -// mChart.setVisibleYRange(30, AxisDependency.LEFT); + data.addEntry(new Entry(data.getDataSetByIndex(randomDataSetIndex).getEntryCount(), yValue), randomDataSetIndex); + data.notifyDataChanged(); + + // let the chart know it's data has changed + mChart.notifyDataSetChanged(); + + mChart.setVisibleXRangeMaximum(6); + //mChart.setVisibleYRangeMaximum(15, AxisDependency.LEFT); // // // this automatically refreshes the chart (calls invalidate()) -// mChart.moveViewTo(data.getXValCount()-7, 55f, AxisDependency.LEFT); + mChart.moveViewTo(data.getEntryCount() - 7, 50f, AxisDependency.LEFT); - // redraw the chart - mChart.invalidate(); - } } private void removeLastEntry() { LineData data = mChart.getData(); - - if(data != null) { - - LineDataSet set = data.getDataSetByIndex(0); + + if (data != null) { + + ILineDataSet set = data.getDataSetByIndex(0); if (set != null) { - Entry e = set.getEntryForXIndex(set.getEntryCount() - 1); + Entry e = set.getEntryForXValue(set.getEntryCount() - 1, Float.NaN); data.removeEntry(e, 0); // or remove by index - // mData.removeEntry(xIndex, dataSetIndex); - + // mData.removeEntryByXValue(xIndex, dataSetIndex); + data.notifyDataChanged(); mChart.notifyDataSetChanged(); mChart.invalidate(); } @@ -105,28 +102,20 @@ private void removeLastEntry() { private void addDataSet() { LineData data = mChart.getData(); - - if(data != null) { + + if (data != null) { int count = (data.getDataSetCount() + 1); - // create 10 y-vals ArrayList yVals = new ArrayList(); - - if(data.getXValCount() == 0) { - // add 10 x-entries - for (int i = 0; i < 10; i++) { - data.addXValue("" + (i+1)); - } - } - for (int i = 0; i < data.getXValCount(); i++) { - yVals.add(new Entry((float) (Math.random() * 50f) + 50f * count, i)); + for (int i = 0; i < data.getEntryCount(); i++) { + yVals.add(new Entry(i, (float) (Math.random() * 50f) + 50f * count)); } LineDataSet set = new LineDataSet(yVals, "DataSet " + count); set.setLineWidth(2.5f); - set.setCircleSize(4.5f); + set.setCircleRadius(4.5f); int color = mColors[count % mColors.length]; @@ -137,26 +126,27 @@ private void addDataSet() { set.setValueTextColor(color); data.addDataSet(set); + data.notifyDataChanged(); mChart.notifyDataSetChanged(); - mChart.invalidate(); + mChart.invalidate(); } } private void removeDataSet() { LineData data = mChart.getData(); - - if(data != null) { + + if (data != null) { data.removeDataSet(data.getDataSetByIndex(data.getDataSetCount() - 1)); mChart.notifyDataSetChanged(); - mChart.invalidate(); + mChart.invalidate(); } } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show(); } @@ -209,7 +199,7 @@ private LineDataSet createSet() { LineDataSet set = new LineDataSet(null, "DataSet 1"); set.setLineWidth(2.5f); - set.setCircleSize(4.5f); + set.setCircleRadius(4.5f); set.setColor(Color.rgb(240, 99, 99)); set.setCircleColor(Color.rgb(240, 99, 99)); set.setHighLightColor(Color.rgb(190, 190, 190)); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java new file mode 100644 index 0000000000..d824167d6b --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java @@ -0,0 +1,150 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IFillFormatter; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class FilledLineActivity extends DemoBase { + + private LineChart mChart; + private int mFillColor = Color.argb(150, 51, 181, 229); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart_noseekbar); + + mChart = (LineChart) findViewById(R.id.chart1); + mChart.setBackgroundColor(Color.WHITE); + mChart.setGridBackgroundColor(mFillColor); + mChart.setDrawGridBackground(true); + + mChart.setDrawBorders(true); + + // no description text + mChart.getDescription().setEnabled(false); + + // if disabled, scaling can be done on x- and y-axis separately + mChart.setPinchZoom(false); + + Legend l = mChart.getLegend(); + l.setEnabled(false); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setEnabled(false); + + YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.setAxisMaximum(900f); + leftAxis.setAxisMinimum(-250f); + leftAxis.setDrawAxisLine(false); + leftAxis.setDrawZeroLine(false); + leftAxis.setDrawGridLines(false); + + mChart.getAxisRight().setEnabled(false); + + // add data + setData(100, 60); + + mChart.invalidate(); + } + + private void setData(int count, float range) { + + ArrayList yVals1 = new ArrayList(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range) + 50;// + (float) + // ((mult * + // 0.1) / 10); + yVals1.add(new Entry(i, val)); + } + + ArrayList yVals2 = new ArrayList(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range) + 450;// + (float) + // ((mult * + // 0.1) / 10); + yVals2.add(new Entry(i, val)); + } + + LineDataSet set1, set2; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); + set2 = (LineDataSet)mChart.getData().getDataSetByIndex(1); + set1.setValues(yVals1); + set2.setValues(yVals2); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(yVals1, "DataSet 1"); + + set1.setAxisDependency(YAxis.AxisDependency.LEFT); + set1.setColor(Color.rgb(255, 241, 46)); + set1.setDrawCircles(false); + set1.setLineWidth(2f); + set1.setCircleRadius(3f); + set1.setFillAlpha(255); + set1.setDrawFilled(true); + set1.setFillColor(Color.WHITE); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setDrawCircleHole(false); + set1.setFillFormatter(new IFillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return mChart.getAxisLeft().getAxisMinimum(); + } + }); + + // create a dataset and give it a type + set2 = new LineDataSet(yVals2, "DataSet 2"); + set2.setAxisDependency(YAxis.AxisDependency.LEFT); + set2.setColor(Color.rgb(255, 241, 46)); + set2.setDrawCircles(false); + set2.setLineWidth(2f); + set2.setCircleRadius(3f); + set2.setFillAlpha(255); + set2.setDrawFilled(true); + set2.setFillColor(Color.WHITE); + set2.setDrawCircleHole(false); + set2.setHighLightColor(Color.rgb(244, 117, 117)); + set2.setFillFormatter(new IFillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return mChart.getAxisLeft().getAxisMaximum(); + } + }); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); // add the datasets + dataSets.add(set2); + + // create a data object with the datasets + LineData data = new LineData(dataSets); + data.setDrawValues(false); + + // set data + mChart.setData(data); + } + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java new file mode 100644 index 0000000000..15da39a8f3 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java @@ -0,0 +1,137 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.graphics.Point; +import android.graphics.Typeface; +import android.os.Bundle; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StyleSpan; +import android.view.Display; +import android.view.WindowManager; +import android.widget.RelativeLayout; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.Legend.LegendPosition; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class HalfPieChartActivity extends DemoBase { + + private PieChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_piechart_half); + + mChart = (PieChart) findViewById(R.id.chart1); + mChart.setBackgroundColor(Color.WHITE); + + moveOffScreen(); + + mChart.setUsePercentValues(true); + mChart.getDescription().setEnabled(false); + + mChart.setCenterTextTypeface(mTfLight); + mChart.setCenterText(generateCenterSpannableText()); + + mChart.setDrawHoleEnabled(true); + mChart.setHoleColor(Color.WHITE); + + mChart.setTransparentCircleColor(Color.WHITE); + mChart.setTransparentCircleAlpha(110); + + mChart.setHoleRadius(58f); + mChart.setTransparentCircleRadius(61f); + + mChart.setDrawCenterText(true); + + mChart.setRotationEnabled(false); + mChart.setHighlightPerTapEnabled(true); + + mChart.setMaxAngle(180f); // HALF CHART + mChart.setRotationAngle(180f); + mChart.setCenterTextOffset(0, -20); + + setData(4, 100); + + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); + + Legend l = mChart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setXEntrySpace(7f); + l.setYEntrySpace(0f); + l.setYOffset(0f); + + // entry label styling + mChart.setEntryLabelColor(Color.WHITE); + mChart.setEntryLabelTypeface(mTfRegular); + mChart.setEntryLabelTextSize(12f); + } + + private void setData(int count, float range) { + + ArrayList values = new ArrayList(); + + for (int i = 0; i < count; i++) { + values.add(new PieEntry((float) ((Math.random() * range) + range / 5), mParties[i % mParties.length])); + } + + PieDataSet dataSet = new PieDataSet(values, "Election Results"); + dataSet.setSliceSpace(3f); + dataSet.setSelectionShift(5f); + + dataSet.setColors(ColorTemplate.MATERIAL_COLORS); + //dataSet.setSelectionShift(0f); + + PieData data = new PieData(dataSet); + data.setValueFormatter(new PercentFormatter()); + data.setValueTextSize(11f); + data.setValueTextColor(Color.WHITE); + data.setValueTypeface(mTfLight); + mChart.setData(data); + + mChart.invalidate(); + } + + private SpannableString generateCenterSpannableText() { + + SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda"); + s.setSpan(new RelativeSizeSpan(1.7f), 0, 14, 0); + s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0); + s.setSpan(new RelativeSizeSpan(.8f), 14, s.length() - 15, 0); + s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0); + return s; + } + + private void moveOffScreen() { + + Display display = getWindowManager().getDefaultDisplay(); + int height = display.getHeight(); // deprecated + + int offset = (int)(height * 0.65); /* percent to move */ + + RelativeLayout.LayoutParams rlParams = + (RelativeLayout.LayoutParams)mChart.getLayoutParams(); + rlParams.setMargins(0, 0, 0, -offset); + mChart.setLayoutParams(rlParams); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index db15dfc0db..d68b75cc15 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -2,9 +2,7 @@ package com.xxmassdeveloper.mpchartexample; import android.annotation.SuppressLint; -import android.graphics.PointF; import android.graphics.RectF; -import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -24,15 +22,15 @@ import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; +import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.List; public class HorizontalBarChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { @@ -41,8 +39,6 @@ public class HorizontalBarChartActivity extends DemoBase implements OnSeekBarCha private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - private Typeface tf; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -64,7 +60,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawValueAboveBar(true); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn @@ -76,34 +72,31 @@ protected void onCreate(Bundle savedInstanceState) { // draw shadows for each bar that show the maximum value // mChart.setDrawBarShadow(true); - // mChart.setDrawXLabels(false); - mChart.setDrawGridBackground(false); - // mChart.setDrawYLabels(false); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - XAxis xl = mChart.getXAxis(); xl.setPosition(XAxisPosition.BOTTOM); - xl.setTypeface(tf); + xl.setTypeface(mTfLight); xl.setDrawAxisLine(true); - xl.setDrawGridLines(true); - xl.setGridLineWidth(0.3f); - // xl.setEnabled(false); + xl.setDrawGridLines(false); + xl.setGranularity(10f); YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(tf); + yl.setTypeface(mTfLight); yl.setDrawAxisLine(true); yl.setDrawGridLines(true); - yl.setGridLineWidth(0.3f); + yl.setAxisMinimum(0f); // this replaces setStartAtZero(true) +// yl.setInverted(true); YAxis yr = mChart.getAxisRight(); - yr.setTypeface(tf); + yr.setTypeface(mTfLight); yr.setDrawAxisLine(true); yr.setDrawGridLines(false); + yr.setAxisMinimum(0f); // this replaces setStartAtZero(true) +// yr.setInverted(true); setData(12, 50); + mChart.setFitBars(true); mChart.animateY(2500); // setting data @@ -114,11 +107,12 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarX.setOnSeekBarChangeListener(this); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); l.setFormSize(8f); l.setXEntrySpace(4f); - - // mChart.setDrawLegend(false); } @Override @@ -132,20 +126,38 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + IBarDataSet set = (BarDataSet) iSet; set.setDrawValues(!set.isDrawValuesEnabled()); + } mChart.invalidate(); break; } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); + case R.id.actionToggleIcons: { + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + IBarDataSet set = (BarDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + mChart.invalidate(); break; } + case R.id.actionToggleHighlight: { + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } case R.id.actionTogglePinch: { if (mChart.isPinchZoomEnabled()) mChart.setPinchZoom(false); @@ -155,17 +167,15 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); break; } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : mChart.getData().getDataSets()) + ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + mChart.invalidate(); break; } @@ -182,29 +192,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.animateXY(3000, 3000); break; } - case R.id.actionToggleAdjustXLegend: { - XAxis xLabels = mChart.getXAxis(); - - if (xLabels.isAdjustXLabelsEnabled()) - xLabels.setAdjustXLabels(false); - else - xLabels.setAdjustXLabels(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - break; - } case R.id.actionSave: { if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", @@ -224,7 +211,8 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress() + 1)); tvY.setText("" + (mSeekBarY.getProgress())); - setData(mSeekBarX.getProgress(), mSeekBarY.getProgress()); + setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + mChart.setFitBars(true); mChart.invalidate(); } @@ -242,47 +230,61 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add(mMonths[i % 12]); - } - + float barWidth = 9f; + float spaceForBar = 10f; ArrayList yVals1 = new ArrayList(); for (int i = 0; i < count; i++) { - float mult = (range + 1); - float val = (float) (Math.random() * mult); - yVals1.add(new BarEntry(val, i)); + float val = (float) (Math.random() * range); + yVals1.add(new BarEntry(i * spaceForBar, val, + getResources().getDrawable(R.drawable.star))); } - BarDataSet set1 = new BarDataSet(yVals1, "DataSet"); - set1.setBarSpacePercent(35f); + BarDataSet set1; - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set1.setValues(yVals1); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(yVals1, "DataSet 1"); - BarData data = new BarData(xVals, dataSets); - data.setValueTextSize(10f); - data.setValueTypeface(tf); + set1.setDrawIcons(false); - mChart.setData(data); + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(mTfLight); + data.setBarWidth(barWidth); + mChart.setData(data); + } } + protected RectF mOnValueSelectedRectF = new RectF(); @SuppressLint("NewApi") @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { if (e == null) return; - RectF bounds = mChart.getBarBounds((BarEntry) e); - PointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(dataSetIndex) + RectF bounds = mOnValueSelectedRectF; + mChart.getBarBounds((BarEntry) e, bounds); + + MPPointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(h.getDataSetIndex()) .getAxisDependency()); Log.i("bounds", bounds.toString()); Log.i("position", position.toString()); + + MPPointF.recycleInstance(position); } + @Override public void onNothingSelected() { }; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index a2284d3e5b..f87a9a8098 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -16,18 +16,19 @@ import com.github.mikephil.charting.components.Legend.LegendForm; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; +import com.github.mikephil.charting.utils.EntryXComparator; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public class InvertedLineChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { @@ -57,12 +58,10 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); + mChart.setDrawGridBackground(false); // no description text - mChart.setDescription(""); - - // enable value highlighting - mChart.setHighlightEnabled(true); + mChart.getDescription().setEnabled(false); // enable touch gestures mChart.setTouchEnabled(true); @@ -80,19 +79,16 @@ protected void onCreate(Bundle savedInstanceState) { // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - - // set the marker to the chart - mChart.setMarkerView(mv); - - // enable/disable highlight indicators (the lines that indicate the - // highlighted Entry) - mChart.setHighlightIndicatorEnabled(false); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart XAxis xl = mChart.getXAxis(); xl.setAvoidFirstLastClipping(true); + xl.setAxisMinimum(0f); YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setInverted(true); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) YAxis rightAxis = mChart.getAxisRight(); rightAxis.setEnabled(false); @@ -110,7 +106,6 @@ protected void onCreate(Bundle savedInstanceState) { Legend l = mChart.getLegend(); // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); // dont forget to refresh the drawing @@ -128,26 +123,33 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; set.setDrawValues(!set.isDrawValuesEnabled()); + } mChart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } break; } case R.id.actionToggleFilled: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; if (set.isDrawFilledEnabled()) set.setDrawFilled(false); else @@ -157,10 +159,12 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleCircles: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; if (set.isDrawCirclesEnabled()) set.setDrawCircles(false); else @@ -169,12 +173,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); - break; - } case R.id.animateX: { mChart.animateX(3000); break; @@ -197,28 +195,9 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleAdjustXLegend: { - XAxis xLabels = mChart.getXAxis(); - - if (xLabels.isAdjustXLabelsEnabled()) - xLabels.setAdjustXLabels(false); - else - xLabels.setAdjustXLabels(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - // the angle of filtering is 35° - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 35); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); break; } case R.id.actionSave: { @@ -249,10 +228,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); } @Override @@ -275,29 +254,25 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((i % 30) + "/" + (i % 12) + "/14"); - } - - ArrayList yVals = new ArrayList(); + ArrayList entries = new ArrayList(); for (int i = 0; i < count; i++) { - float mult = (range + 1); - float val = (float) (Math.random() * mult) + 3;// + (float) - // ((mult * - // 0.1) / 10); - yVals.add(new Entry(val, i)); + float xVal = (float) (Math.random() * range); + float yVal = (float) (Math.random() * range); + entries.add(new Entry(xVal, yVal)); } + // sort by x-value + Collections.sort(entries, new EntryXComparator()); + // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals, "DataSet 1"); + LineDataSet set1 = new LineDataSet(entries, "DataSet 1"); set1.setLineWidth(1.5f); - set1.setCircleSize(4f); + set1.setCircleRadius(4f); // create a data object with the datasets - LineData data = new LineData(xVals, set1); + LineData data = new LineData(set1); // set data mChart.setData(data); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 023218ed75..b18309a26a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -2,7 +2,11 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -13,6 +17,7 @@ import android.widget.TextView; import android.widget.Toast; +import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendForm; @@ -20,19 +25,20 @@ import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.listener.ChartTouchListener; import com.github.mikephil.charting.listener.OnChartGestureListener; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; +import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.List; public class LineChartActivity1 extends DemoBase implements OnSeekBarChangeListener, OnChartGestureListener, OnChartValueSelectedListener { @@ -63,13 +69,10 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); mChart.setOnChartGestureListener(this); mChart.setOnChartValueSelectedListener(this); + mChart.setDrawGridBackground(false); // no description text - mChart.setDescription(""); - mChart.setNoDataTextDescription("You need to provide data for the chart."); - - // enable value highlighting - mChart.setHighlightEnabled(true); + mChart.getDescription().setEnabled(false); // enable touch gestures mChart.setTouchEnabled(true); @@ -77,6 +80,8 @@ protected void onCreate(Bundle savedInstanceState) { // enable scaling and dragging mChart.setDragEnabled(true); mChart.setScaleEnabled(true); + // mChart.setScaleXEnabled(true); + // mChart.setScaleYEnabled(true); // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(true); @@ -87,36 +92,81 @@ protected void onCreate(Bundle savedInstanceState) { // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart + + // x-axis limit line + LimitLine llXAxis = new LimitLine(10f, "Index 10"); + llXAxis.setLineWidth(4f); + llXAxis.enableDashedLine(10f, 10f, 0f); + llXAxis.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM); + llXAxis.setTextSize(10f); + + XAxis xAxis = mChart.getXAxis(); + xAxis.enableGridDashedLine(10f, 10f, 0f); + //xAxis.setValueFormatter(new MyCustomXAxisValueFormatter()); + //xAxis.addLimitLine(llXAxis); // add x-axis limit line + + + Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + + LimitLine ll1 = new LimitLine(150f, "Upper Limit"); + ll1.setLineWidth(4f); + ll1.enableDashedLine(10f, 10f, 0f); + ll1.setLabelPosition(LimitLabelPosition.RIGHT_TOP); + ll1.setTextSize(10f); + ll1.setTypeface(tf); + + LimitLine ll2 = new LimitLine(-30f, "Lower Limit"); + ll2.setLineWidth(4f); + ll2.enableDashedLine(10f, 10f, 0f); + ll2.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM); + ll2.setTextSize(10f); + ll2.setTypeface(tf); - // set the marker to the chart - mChart.setMarkerView(mv); + YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines + leftAxis.addLimitLine(ll1); + leftAxis.addLimitLine(ll2); + leftAxis.setAxisMaximum(200f); + leftAxis.setAxisMinimum(-50f); + //leftAxis.setYOffset(20f); + leftAxis.enableGridDashedLine(10f, 10f, 0f); + leftAxis.setDrawZeroLine(false); + + // limit lines are drawn behind data (and not on top) + leftAxis.setDrawLimitLinesBehindData(true); + + mChart.getAxisRight().setEnabled(false); + + //mChart.getViewPortHandler().setMaximumScaleY(2f); + //mChart.getViewPortHandler().setMaximumScaleX(2f); - // enable/disable highlight indicators (the lines that indicate the - // highlighted Entry) - mChart.setHighlightIndicatorEnabled(false); - // add data setData(45, 100); - mChart.animateX(2500); -// mChart.setVisibleYRange(30, AxisDependency.LEFT); -// // restrain the maximum scale-out factor -// mChart.setScaleMinima(3f, 3f); -// -// // center the view to a specific position inside the chart -// mChart.centerViewPort(10, 50, AxisDependency.LEFT); +// mChart.setVisibleXRange(20); +// mChart.setVisibleYRange(20f, AxisDependency.LEFT); +// mChart.centerViewTo(20, 50, AxisDependency.LEFT); + + mChart.animateX(2500); + //mChart.invalidate(); // get the legend (only possible after setting data) Legend l = mChart.getLegend(); // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); // // dont forget to refresh the drawing // mChart.invalidate(); } + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.line, menu); @@ -128,26 +178,46 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; set.setDrawValues(!set.isDrawValuesEnabled()); + } mChart.invalidate(); break; } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); + case R.id.actionToggleIcons: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + mChart.invalidate(); break; } + case R.id.actionToggleHighlight: { + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } case R.id.actionToggleFilled: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; if (set.isDrawFilledEnabled()) set.setDrawFilled(false); else @@ -157,10 +227,12 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleCircles: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; if (set.isDrawCirclesEnabled()) set.setDrawCircles(false); else @@ -170,21 +242,44 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleCubic: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); + } + mChart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); } mChart.invalidate(); break; } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); + case R.id.actionToggleHorizontalCubic: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.HORIZONTAL_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.HORIZONTAL_BEZIER); + } mChart.invalidate(); break; } @@ -197,55 +292,23 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); + break; + } case R.id.animateX: { mChart.animateX(3000); break; } case R.id.animateY: { - mChart.animateY(3000); + mChart.animateY(3000, Easing.EasingOption.EaseInCubic); break; } case R.id.animateXY: { mChart.animateXY(3000, 3000); break; } - case R.id.actionToggleAdjustXLegend: { - XAxis xLabels = mChart.getXAxis(); - - if (xLabels.isAdjustXLabelsEnabled()) - xLabels.setAdjustXLabels(false); - else - xLabels.setAdjustXLabels(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - // the angle of filtering is 35° - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 35); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - - // - // for(int i = 0; i < 10; i++) { - // mChart.addEntry(new Entry((float) (Math.random() * 100), - // i+2), 0); - // mChart.invalidate(); - // } - // - // Toast.makeText(getApplicationContext(), "valcount: " + - // mChart.getDataOriginal().getYValCount() + ", valsum: " + - // mChart.getDataOriginal().getYValueSum(), - // Toast.LENGTH_SHORT).show(); - // - break; - } case R.id.actionSave: { if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", @@ -268,7 +331,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvY.setText("" + (mSeekBarY.getProgress())); setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); - + // redraw mChart.invalidate(); } @@ -287,67 +350,74 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); + ArrayList values = new ArrayList(); + for (int i = 0; i < count; i++) { - xVals.add((i) + ""); + + float val = (float) (Math.random() * range) + 3; + values.add(new Entry(i, val, getResources().getDrawable(R.drawable.star))); } - ArrayList yVals = new ArrayList(); + LineDataSet set1; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); + set1.setValues(values); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(values, "DataSet 1"); + + set1.setDrawIcons(false); + + // set the line to be drawn like this "- - - - - -" + set1.enableDashedLine(10f, 5f, 0f); + set1.enableDashedHighlightLine(10f, 5f, 0f); + set1.setColor(Color.BLACK); + set1.setCircleColor(Color.BLACK); + set1.setLineWidth(1f); + set1.setCircleRadius(3f); + set1.setDrawCircleHole(false); + set1.setValueTextSize(9f); + set1.setDrawFilled(true); + set1.setFormLineWidth(1f); + set1.setFormLineDashEffect(new DashPathEffect(new float[]{10f, 5f}, 0f)); + set1.setFormSize(15.f); + + if (Utils.getSDKInt() >= 18) { + // fill drawable only supported on api level 18 and above + Drawable drawable = ContextCompat.getDrawable(this, R.drawable.fade_red); + set1.setFillDrawable(drawable); + } + else { + set1.setFillColor(Color.BLACK); + } - for (int i = 0; i < count; i++) { - float mult = (range + 1); - float val = (float) (Math.random() * mult) + 3;// + (float) - // ((mult * - // 0.1) / 10); - yVals.add(new Entry(val, i)); - } + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); // add the datasets - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals, "DataSet 1"); - // set1.setFillAlpha(110); - // set1.setFillColor(Color.RED); - - // set the line to be drawn like this "- - - - - -" - set1.enableDashedLine(10f, 5f, 0f); - set1.setColor(Color.BLACK); - set1.setCircleColor(Color.BLACK); - set1.setLineWidth(1f); - set1.setCircleSize(4f); - set1.setFillAlpha(65); - set1.setFillColor(Color.BLACK); - // set1.setShader(new LinearGradient(0, 0, 0, mChart.getHeight(), - // Color.BLACK, Color.WHITE, Shader.TileMode.MIRROR)); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); - data.setValueTextSize(10f); - - LimitLine ll1 = new LimitLine(130f, "Upper Limit"); - ll1.setLineWidth(4f); - ll1.enableDashedLine(10f, 10f, 0f); - ll1.setLabelPosition(LimitLabelPosition.POS_RIGHT); - ll1.setTextSize(10f); + // create a data object with the datasets + LineData data = new LineData(dataSets); - LimitLine ll2 = new LimitLine(-30f, "Lower Limit"); - ll2.setLineWidth(4f); - ll2.enableDashedLine(10f, 10f, 0f); - ll2.setLabelPosition(LimitLabelPosition.POS_RIGHT); - ll2.setTextSize(10f); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.addLimitLine(ll1); - leftAxis.addLimitLine(ll2); - leftAxis.setAxisMaxValue(220f); - leftAxis.setAxisMinValue(-50f); - leftAxis.setStartAtZero(false); + // set data + mChart.setData(data); + } + } - mChart.getAxisRight().setEnabled(false); + @Override + public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + Log.i("Gesture", "START, x: " + me.getX() + ", y: " + me.getY()); + } + + @Override + public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + Log.i("Gesture", "END, lastGesture: " + lastPerformedGesture); - // set data - mChart.setData(data); + // un-highlight values after the gesture is finished and no single-tap + if(lastPerformedGesture != ChartTouchListener.ChartGesture.SINGLE_TAP) + mChart.highlightValues(null); // or highlightTouch(null) for callback to onNothingSelected(...) } @Override @@ -371,8 +441,20 @@ public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, floa } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onChartScale(MotionEvent me, float scaleX, float scaleY) { + Log.i("Scale / Zoom", "ScaleX: " + scaleX + ", ScaleY: " + scaleY); + } + + @Override + public void onChartTranslate(MotionEvent me, float dX, float dY) { + Log.i("Translate / Move", "dX: " + dX + ", dY: " + dY); + } + + @Override + public void onValueSelected(Entry e, Highlight h) { Log.i("Entry selected", e.toString()); + Log.i("LOWHIGH", "low: " + mChart.getLowestVisibleX() + ", high: " + mChart.getHighestVisibleX()); + Log.i("MIN MAX", "xmin: " + mChart.getXChartMin() + ", xmax: " + mChart.getXChartMax() + ", ymin: " + mChart.getYChartMin() + ", ymax: " + mChart.getYChartMax()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index f381c6073f..79f40c4e07 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -2,7 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -16,22 +15,20 @@ import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendForm; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.List; public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { @@ -60,21 +57,20 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); - - // no description text - mChart.setDescription(""); - mChart.setNoDataTextDescription("You need to provide data for the chart."); - // enable value highlighting - mChart.setHighlightEnabled(true); + // no description text + mChart.getDescription().setEnabled(false); // enable touch gestures mChart.setTouchEnabled(true); + mChart.setDragDecelerationFrictionCoef(0.9f); + // enable scaling and dragging mChart.setDragEnabled(true); mChart.setScaleEnabled(true); mChart.setDrawGridBackground(false); + mChart.setHighlightPerDragEnabled(true); // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(true); @@ -87,40 +83,43 @@ protected void onCreate(Bundle savedInstanceState) { mChart.animateX(2500); - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - // get the legend (only possible after setting data) Legend l = mChart.getLegend(); // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); - l.setTypeface(tf); + l.setTypeface(mTfLight); l.setTextSize(11f); l.setTextColor(Color.WHITE); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); +// l.setYOffset(11f); XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(tf); - xAxis.setTextSize(12f); + xAxis.setTypeface(mTfLight); + xAxis.setTextSize(11f); xAxis.setTextColor(Color.WHITE); xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(false); - xAxis.setSpaceBetweenLabels(1); YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); + leftAxis.setTypeface(mTfLight); leftAxis.setTextColor(ColorTemplate.getHoloBlue()); - leftAxis.setAxisMaxValue(200f); + leftAxis.setAxisMaximum(200f); + leftAxis.setAxisMinimum(0f); leftAxis.setDrawGridLines(true); - + leftAxis.setGranularityEnabled(true); + YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setTypeface(tf); + rightAxis.setTypeface(mTfLight); rightAxis.setTextColor(Color.RED); - rightAxis.setAxisMaxValue(900); - rightAxis.setStartAtZero(false); - rightAxis.setAxisMinValue(-200); + rightAxis.setAxisMaximum(900); + rightAxis.setAxisMinimum(-200); rightAxis.setDrawGridLines(false); + rightAxis.setDrawZeroLine(false); + rightAxis.setGranularityEnabled(false); } @Override @@ -134,26 +133,33 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; set.setDrawValues(!set.isDrawValuesEnabled()); + } mChart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); + if (mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } break; } case R.id.actionToggleFilled: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; if (set.isDrawFilledEnabled()) set.setDrawFilled(false); else @@ -163,10 +169,12 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleCircles: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; if (set.isDrawCirclesEnabled()) set.setDrawCircles(false); else @@ -176,21 +184,44 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleCubic: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); + } + mChart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); } mChart.invalidate(); break; } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); + case R.id.actionToggleHorizontalCubic: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.HORIZONTAL_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.HORIZONTAL_BEZIER); + } mChart.invalidate(); break; } @@ -203,8 +234,14 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); + break; + } case R.id.animateX: { mChart.animateX(3000); + //mChart.highlightValue(9.7f, 1, false); break; } case R.id.animateY: { @@ -215,30 +252,7 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.animateXY(3000, 3000); break; } - case R.id.actionToggleAdjustXLegend: { - XAxis xLabels = mChart.getXAxis(); - if (xLabels.isAdjustXLabelsEnabled()) - xLabels.setAdjustXLabels(false); - else - xLabels.setAdjustXLabels(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - // the angle of filtering is 35° - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 35); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - break; - } case R.id.actionSave: { if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", @@ -268,69 +282,107 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((i) + ""); - } - ArrayList yVals1 = new ArrayList(); for (int i = 0; i < count; i++) { float mult = range / 2f; - float val = (float) (Math.random() * mult) + 50;// + (float) - // ((mult * - // 0.1) / 10); - yVals1.add(new Entry(val, i)); + float val = (float) (Math.random() * mult) + 50; + yVals1.add(new Entry(i, val)); } - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals1, "DataSet 1"); - set1.setAxisDependency(AxisDependency.LEFT); - set1.setColor(ColorTemplate.getHoloBlue()); - set1.setCircleColor(ColorTemplate.getHoloBlue()); - set1.setLineWidth(2f); - set1.setCircleSize(4f); - set1.setFillAlpha(65); - set1.setFillColor(ColorTemplate.getHoloBlue()); - set1.setHighLightColor(Color.rgb(244, 117, 117)); - ArrayList yVals2 = new ArrayList(); + for (int i = 0; i < count-1; i++) { + float mult = range; + float val = (float) (Math.random() * mult) + 450; + yVals2.add(new Entry(i, val)); +// if(i == 10) { +// yVals2.add(new Entry(i, val + 50)); +// } + } + + ArrayList yVals3 = new ArrayList(); + for (int i = 0; i < count; i++) { float mult = range; - float val = (float) (Math.random() * mult) + 450;// + (float) - // ((mult * - // 0.1) / 10); - yVals2.add(new Entry(val, i)); + float val = (float) (Math.random() * mult) + 500; + yVals3.add(new Entry(i, val)); } - // create a dataset and give it a type - LineDataSet set2 = new LineDataSet(yVals2, "DataSet 2"); - set2.setAxisDependency(AxisDependency.RIGHT); - set2.setColor(Color.RED); - set2.setCircleColor(Color.RED); - set2.setLineWidth(2f); - set2.setCircleSize(4f); - set2.setFillAlpha(65); - set2.setFillColor(Color.RED); - set2.setHighLightColor(Color.rgb(244, 117, 117)); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - dataSets.add(set2); // add the datasets - - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); - data.setValueTextColor(Color.WHITE); - data.setValueTextSize(9f); - - // set data - mChart.setData(data); + LineDataSet set1, set2, set3; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet) mChart.getData().getDataSetByIndex(0); + set2 = (LineDataSet) mChart.getData().getDataSetByIndex(1); + set3 = (LineDataSet) mChart.getData().getDataSetByIndex(2); + set1.setValues(yVals1); + set2.setValues(yVals2); + set3.setValues(yVals3); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(yVals1, "DataSet 1"); + + set1.setAxisDependency(AxisDependency.LEFT); + set1.setColor(ColorTemplate.getHoloBlue()); + set1.setCircleColor(Color.WHITE); + set1.setLineWidth(2f); + set1.setCircleRadius(3f); + set1.setFillAlpha(65); + set1.setFillColor(ColorTemplate.getHoloBlue()); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setDrawCircleHole(false); + //set1.setFillFormatter(new MyFillFormatter(0f)); + //set1.setDrawHorizontalHighlightIndicator(false); + //set1.setVisible(false); + //set1.setCircleHoleColor(Color.WHITE); + + // create a dataset and give it a type + set2 = new LineDataSet(yVals2, "DataSet 2"); + set2.setAxisDependency(AxisDependency.RIGHT); + set2.setColor(Color.RED); + set2.setCircleColor(Color.WHITE); + set2.setLineWidth(2f); + set2.setCircleRadius(3f); + set2.setFillAlpha(65); + set2.setFillColor(Color.RED); + set2.setDrawCircleHole(false); + set2.setHighLightColor(Color.rgb(244, 117, 117)); + //set2.setFillFormatter(new MyFillFormatter(900f)); + + set3 = new LineDataSet(yVals3, "DataSet 3"); + set3.setAxisDependency(AxisDependency.RIGHT); + set3.setColor(Color.YELLOW); + set3.setCircleColor(Color.WHITE); + set3.setLineWidth(2f); + set3.setCircleRadius(3f); + set3.setFillAlpha(65); + set3.setFillColor(ColorTemplate.colorWithAlpha(Color.YELLOW, 200)); + set3.setDrawCircleHole(false); + set3.setHighLightColor(Color.rgb(244, 117, 117)); + + // create a data object with the datasets + LineData data = new LineData(set1, set2, set3); + data.setValueTextColor(Color.WHITE); + data.setValueTextSize(9f); + + // set data + mChart.setData(data); + } } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("Entry selected", e.toString()); + + mChart.centerViewToAnimated(e.getX(), e.getY(), mChart.getData().getDataSetByIndex(h.getDataSetIndex()) + .getAxisDependency(), 500); + //mChart.zoomAndCenterAnimated(2.5f, 2.5f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex) + // .getAxisDependency(), 1000); + //mChart.zoomAndCenterAnimated(1.8f, 1.8f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex) + // .getAxisDependency(), 1000); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index b21c385a07..68bba4b458 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -8,9 +8,6 @@ import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendForm; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; @@ -37,12 +34,14 @@ protected void onCreate(Bundle savedInstanceState) { mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Bold.ttf"); - LineData data = getData(36, 100); - data.setValueTypeface(mTf); + for (int i = 0; i < mCharts.length; i++) { + + LineData data = getData(36, 100); + data.setValueTypeface(mTf); - for (int i = 0; i < mCharts.length; i++) // add some transparency to the color with "& 0x90FFFFFF" setupChart(mCharts[i], data, mColors[i % mColors.length]); + } } private int[] mColors = new int[] { @@ -54,9 +53,10 @@ protected void onCreate(Bundle savedInstanceState) { private void setupChart(LineChart chart, LineData data, int color) { + ((LineDataSet) data.getDataSetByIndex(0)).setCircleColorHole(color); + // no description text - chart.setDescription(""); - chart.setNoDataTextDescription("You need to provide data for the chart."); + chart.getDescription().setEnabled(false); // mChart.setDrawHorizontalGrid(false); // @@ -84,15 +84,11 @@ private void setupChart(LineChart chart, LineData data, int color) { // get the legend (only possible after setting data) Legend l = chart.getLegend(); - - // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); - l.setForm(LegendForm.CIRCLE); - l.setFormSize(6f); - l.setTextColor(Color.WHITE); - l.setTypeface(mTf); + l.setEnabled(false); chart.getAxisLeft().setEnabled(false); + chart.getAxisLeft().setSpaceTop(40); + chart.getAxisLeft().setSpaceBottom(40); chart.getAxisRight().setEnabled(false); chart.getXAxis().setEnabled(false); @@ -103,16 +99,11 @@ private void setupChart(LineChart chart, LineData data, int color) { private LineData getData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add(mMonths[i % 12]); - } - ArrayList yVals = new ArrayList(); for (int i = 0; i < count; i++) { float val = (float) (Math.random() * range) + 3; - yVals.add(new Entry(val, i)); + yVals.add(new Entry(i, val)); } // create a dataset and give it a type @@ -121,17 +112,15 @@ private LineData getData(int count, float range) { // set1.setFillColor(Color.RED); set1.setLineWidth(1.75f); - set1.setCircleSize(3f); + set1.setCircleRadius(5f); + set1.setCircleHoleRadius(2.5f); set1.setColor(Color.WHITE); set1.setCircleColor(Color.WHITE); set1.setHighLightColor(Color.WHITE); set1.setDrawValues(false); - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); + LineData data = new LineData(set1); return data; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java new file mode 100644 index 0000000000..30e5e2a978 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -0,0 +1,316 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; +import android.widget.Toast; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.sql.Time; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class LineChartTime extends DemoBase implements OnSeekBarChangeListener { + + private LineChart mChart; + private SeekBar mSeekBarX; + private TextView tvX; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart_time); + + tvX = (TextView) findViewById(R.id.tvXMax); + mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarX.setProgress(100); + tvX.setText("100"); + + mSeekBarX.setOnSeekBarChangeListener(this); + + mChart = (LineChart) findViewById(R.id.chart1); + + // no description text + mChart.getDescription().setEnabled(false); + + // enable touch gestures + mChart.setTouchEnabled(true); + + mChart.setDragDecelerationFrictionCoef(0.9f); + + // enable scaling and dragging + mChart.setDragEnabled(true); + mChart.setScaleEnabled(true); + mChart.setDrawGridBackground(false); + mChart.setHighlightPerDragEnabled(true); + + // set an alternative background color + mChart.setBackgroundColor(Color.WHITE); + mChart.setViewPortOffsets(0f, 0f, 0f, 0f); + + // add data + setData(100, 30); + mChart.invalidate(); + + // get the legend (only possible after setting data) + Legend l = mChart.getLegend(); + l.setEnabled(false); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setPosition(XAxis.XAxisPosition.TOP_INSIDE); + xAxis.setTypeface(mTfLight); + xAxis.setTextSize(10f); + xAxis.setTextColor(Color.WHITE); + xAxis.setDrawAxisLine(false); + xAxis.setDrawGridLines(true); + xAxis.setTextColor(Color.rgb(255, 192, 56)); + xAxis.setCenterAxisLabels(true); + xAxis.setGranularity(1f); // one hour + xAxis.setValueFormatter(new IAxisValueFormatter() { + + private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm"); + + @Override + public String getFormattedValue(float value, AxisBase axis) { + + long millis = TimeUnit.HOURS.toMillis((long) value); + return mFormat.format(new Date(millis)); + } + }); + + YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); + leftAxis.setTypeface(mTfLight); + leftAxis.setTextColor(ColorTemplate.getHoloBlue()); + leftAxis.setDrawGridLines(true); + leftAxis.setGranularityEnabled(true); + leftAxis.setAxisMinimum(0f); + leftAxis.setAxisMaximum(170f); + leftAxis.setYOffset(-9f); + leftAxis.setTextColor(Color.rgb(255, 192, 56)); + + YAxis rightAxis = mChart.getAxisRight(); + rightAxis.setEnabled(false); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.line, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.actionToggleValues: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + mChart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if (mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } + case R.id.actionToggleFilled: { + + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawFilledEnabled()) + set.setDrawFilled(false); + else + set.setDrawFilled(true); + } + mChart.invalidate(); + break; + } + case R.id.actionToggleCircles: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawCirclesEnabled()) + set.setDrawCircles(false); + else + set.setDrawCircles(true); + } + mChart.invalidate(); + break; + } + case R.id.actionToggleCubic: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.getMode() == LineDataSet.Mode.CUBIC_BEZIER) + set.setMode(LineDataSet.Mode.LINEAR); + else + set.setMode(LineDataSet.Mode.CUBIC_BEZIER); + } + mChart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.getMode() == LineDataSet.Mode.STEPPED) + set.setMode(LineDataSet.Mode.LINEAR); + else + set.setMode(LineDataSet.Mode.STEPPED); + } + mChart.invalidate(); + break; + } + case R.id.actionTogglePinch: { + if (mChart.isPinchZoomEnabled()) + mChart.setPinchZoom(false); + else + mChart.setPinchZoom(true); + + mChart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); + break; + } + case R.id.animateX: { + mChart.animateX(3000); + break; + } + case R.id.animateY: { + mChart.animateY(3000); + break; + } + case R.id.animateXY: { + mChart.animateXY(3000, 3000); + break; + } + + case R.id.actionSave: { + if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { + Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", + Toast.LENGTH_SHORT).show(); + } else + Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) + .show(); + + // mChart.saveToGallery("title"+System.currentTimeMillis()) + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText("" + (mSeekBarX.getProgress())); + + setData(mSeekBarX.getProgress(), 50); + + // redraw + mChart.invalidate(); + } + + private void setData(int count, float range) { + + // now in hours + long now = TimeUnit.MILLISECONDS.toHours(System.currentTimeMillis()); + + ArrayList values = new ArrayList(); + + float from = now; + + // count = hours + float to = now + count; + + // increment by 1 hour + for (float x = from; x < to; x++) { + + float y = getRandom(range, 50); + values.add(new Entry(x, y)); // add one entry per hour + } + + // create a dataset and give it a type + LineDataSet set1 = new LineDataSet(values, "DataSet 1"); + set1.setAxisDependency(AxisDependency.LEFT); + set1.setColor(ColorTemplate.getHoloBlue()); + set1.setValueTextColor(ColorTemplate.getHoloBlue()); + set1.setLineWidth(1.5f); + set1.setDrawCircles(false); + set1.setDrawValues(false); + set1.setFillAlpha(65); + set1.setFillColor(ColorTemplate.getHoloBlue()); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setDrawCircleHole(false); + + // create a data object with the datasets + LineData data = new LineData(set1); + data.setValueTextColor(Color.WHITE); + data.setValueTextSize(9f); + + // set data + mChart.setData(data); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } +} \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index 258d509a42..7ee212ff60 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -19,6 +19,7 @@ import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -55,12 +56,8 @@ protected void onCreate(Bundle savedInstanceState) { private class ChartDataAdapter extends ArrayAdapter { - private Typeface mTf; - public ChartDataAdapter(Context context, List objects) { super(context, 0, objects); - - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); } @Override @@ -85,28 +82,29 @@ public View getView(int position, View convertView, ViewGroup parent) { } // apply styling - data.setValueTypeface(mTf); - holder.chart.setDescription(""); + data.setValueTypeface(mTfLight); + data.setValueTextColor(Color.BLACK); + holder.chart.getDescription().setEnabled(false); holder.chart.setDrawGridBackground(false); - data.setValueTextColor(Color.WHITE); XAxis xAxis = holder.chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTf); + xAxis.setTypeface(mTfLight); xAxis.setDrawGridLines(false); YAxis leftAxis = holder.chart.getAxisLeft(); - leftAxis.setTypeface(mTf); - leftAxis.setLabelCount(5); + leftAxis.setTypeface(mTfLight); + leftAxis.setLabelCount(5, false); leftAxis.setSpaceTop(15f); YAxis rightAxis = holder.chart.getAxisRight(); - rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(5); + rightAxis.setTypeface(mTfLight); + rightAxis.setLabelCount(5, false); rightAxis.setSpaceTop(15f); // set data holder.chart.setData(data); + holder.chart.setFitBars(true); // do not forget to refresh the chart // holder.chart.invalidate(); @@ -131,37 +129,18 @@ private BarData generateData(int cnt) { ArrayList entries = new ArrayList(); for (int i = 0; i < 12; i++) { - entries.add(new BarEntry((int) (Math.random() * 70) + 30, i)); + entries.add(new BarEntry(i, (float) (Math.random() * 70) + 30)); } - BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); - d.setBarSpacePercent(20f); + BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setBarShadowColor(Color.rgb(203, 203, 203)); - ArrayList sets = new ArrayList(); + ArrayList sets = new ArrayList(); sets.add(d); - BarData cd = new BarData(getMonths(), sets); + BarData cd = new BarData(sets); + cd.setBarWidth(0.9f); return cd; } - - private ArrayList getMonths() { - - ArrayList m = new ArrayList(); - m.add("Jan"); - m.add("Feb"); - m.add("Mar"); - m.add("Apr"); - m.add("May"); - m.add("Jun"); - m.add("Jul"); - m.add("Aug"); - m.add("Sep"); - m.add("Okt"); - m.add("Nov"); - m.add("Dec"); - - return m; - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java index 4a47000f90..0e273596fe 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java @@ -18,6 +18,8 @@ import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.listviewitems.BarChartItem; import com.xxmassdeveloper.mpchartexample.listviewitems.ChartItem; @@ -97,34 +99,34 @@ private LineData generateDataLine(int cnt) { ArrayList e1 = new ArrayList(); for (int i = 0; i < 12; i++) { - e1.add(new Entry((int) (Math.random() * 65) + 40, i)); + e1.add(new Entry(i, (int) (Math.random() * 65) + 40)); } LineDataSet d1 = new LineDataSet(e1, "New DataSet " + cnt + ", (1)"); d1.setLineWidth(2.5f); - d1.setCircleSize(4.5f); + d1.setCircleRadius(4.5f); d1.setHighLightColor(Color.rgb(244, 117, 117)); d1.setDrawValues(false); ArrayList e2 = new ArrayList(); for (int i = 0; i < 12; i++) { - e2.add(new Entry(e1.get(i).getVal() - 30, i)); + e2.add(new Entry(i, e1.get(i).getY() - 30)); } LineDataSet d2 = new LineDataSet(e2, "New DataSet " + cnt + ", (2)"); d2.setLineWidth(2.5f); - d2.setCircleSize(4.5f); + d2.setCircleRadius(4.5f); d2.setHighLightColor(Color.rgb(244, 117, 117)); d2.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); d2.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[0]); d2.setDrawValues(false); - ArrayList sets = new ArrayList(); + ArrayList sets = new ArrayList(); sets.add(d1); sets.add(d2); - LineData cd = new LineData(getMonths(), sets); + LineData cd = new LineData(sets); return cd; } @@ -138,15 +140,15 @@ private BarData generateDataBar(int cnt) { ArrayList entries = new ArrayList(); for (int i = 0; i < 12; i++) { - entries.add(new BarEntry((int) (Math.random() * 70) + 30, i)); + entries.add(new BarEntry(i, (int) (Math.random() * 70) + 30)); } BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); - d.setBarSpacePercent(20f); d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setHighLightAlpha(255); - BarData cd = new BarData(getMonths(), d); + BarData cd = new BarData(d); + cd.setBarWidth(0.9f); return cd; } @@ -157,10 +159,10 @@ private BarData generateDataBar(int cnt) { */ private PieData generateDataPie(int cnt) { - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList(); for (int i = 0; i < 4; i++) { - entries.add(new Entry((int) (Math.random() * 70) + 30, i)); + entries.add(new PieEntry((float) ((Math.random() * 70) + 30), "Quarter " + (i+1))); } PieDataSet d = new PieDataSet(entries, ""); @@ -169,37 +171,7 @@ private PieData generateDataPie(int cnt) { d.setSliceSpace(2f); d.setColors(ColorTemplate.VORDIPLOM_COLORS); - PieData cd = new PieData(getQuarters(), d); + PieData cd = new PieData(d); return cd; } - - private ArrayList getQuarters() { - - ArrayList q = new ArrayList(); - q.add("1st Quarter"); - q.add("2nd Quarter"); - q.add("3rd Quarter"); - q.add("4th Quarter"); - - return q; - } - - private ArrayList getMonths() { - - ArrayList m = new ArrayList(); - m.add("Jan"); - m.add("Feb"); - m.add("Mar"); - m.add("Apr"); - m.add("May"); - m.add("Jun"); - m.add("Jul"); - m.add("Aug"); - m.add("Sep"); - m.add("Okt"); - m.add("Nov"); - m.add("Dec"); - - return m; - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index 666bc45006..19f7bae938 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -13,19 +13,17 @@ import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.List; public class MultiLineChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { @@ -54,11 +52,14 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); + mChart.getDescription().setEnabled(false); + mChart.setDrawBorders(false); - // mChart.setStartAtZero(true); - - // enable value highlighting - mChart.setHighlightEnabled(true); + mChart.getAxisLeft().setEnabled(false); + mChart.getAxisRight().setDrawAxisLine(false); + mChart.getAxisRight().setDrawGridLines(false); + mChart.getXAxis().setDrawAxisLine(false); + mChart.getXAxis().setDrawGridLines(false); // enable touch gestures mChart.setTouchEnabled(true); @@ -74,7 +75,10 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setProgress(100); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); } @Override @@ -88,8 +92,14 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; set.setDrawValues(!set.isDrawValuesEnabled()); + } mChart.invalidate(); break; @@ -103,19 +113,25 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); + break; + } case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } break; } case R.id.actionToggleFilled: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; if (set.isDrawFilledEnabled()) set.setDrawFilled(false); else @@ -125,10 +141,12 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleCircles: { - ArrayList sets = (ArrayList) mChart.getData() + List sets = mChart.getData() .getDataSets(); - for (LineDataSet set : sets) { + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; if (set.isDrawCirclesEnabled()) set.setDrawCircles(false); else @@ -137,36 +155,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleFilter: { - - // the angle of filtering is 35° - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 35); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionToggleAdjustXLegend: { - XAxis xLabels = mChart.getXAxis(); - - if (xLabels.isAdjustXLabelsEnabled()) - xLabels.setAdjustXLabels(false); - else - xLabels.setAdjustXLabels(true); - - mChart.invalidate(); - break; - } case R.id.actionSave: { // mChart.saveToGallery("title"+System.currentTimeMillis()); mChart.saveToPath("title" + System.currentTimeMillis(), ""); @@ -197,16 +185,13 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + mChart.resetTracking(); tvX.setText("" + (mSeekBarX.getProgress())); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - xVals.add((i) + ""); - } - - ArrayList dataSets = new ArrayList(); + ArrayList dataSets = new ArrayList(); for (int z = 0; z < 3; z++) { @@ -214,12 +199,12 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { for (int i = 0; i < mSeekBarX.getProgress(); i++) { double val = (Math.random() * mSeekBarY.getProgress()) + 3; - values.add(new Entry((float) val, i)); + values.add(new Entry(i, (float) val)); } LineDataSet d = new LineDataSet(values, "DataSet " + (z + 1)); d.setLineWidth(2.5f); - d.setCircleSize(4f); + d.setCircleRadius(4f); int color = mColors[z % mColors.length]; d.setColor(color); @@ -228,20 +213,20 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } // make the first DataSet dashed - dataSets.get(0).enableDashedLine(10, 10, 0); - dataSets.get(0).setColors(ColorTemplate.VORDIPLOM_COLORS); - dataSets.get(0).setCircleColors(ColorTemplate.VORDIPLOM_COLORS); + ((LineDataSet) dataSets.get(0)).enableDashedLine(10, 10, 0); + ((LineDataSet) dataSets.get(0)).setColors(ColorTemplate.VORDIPLOM_COLORS); + ((LineDataSet) dataSets.get(0)).setCircleColors(ColorTemplate.VORDIPLOM_COLORS); - LineData data = new LineData(xVals, dataSets); + LineData data = new LineData(dataSets); mChart.setData(data); mChart.invalidate(); } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index 300ab09aa2..0763f7f88a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -42,11 +42,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); // no description text - mChart.setDescription(""); - mChart.setNoDataTextDescription("You need to provide data for the chart."); - - // disable value highlighting - mChart.setHighlightEnabled(false); + mChart.getDescription().setEnabled(false); // enable touch gestures mChart.setTouchEnabled(true); @@ -57,14 +53,10 @@ protected void onCreate(Bundle savedInstanceState) { // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(false); - - // enable/disable highlight indicators (the lines that indicate the - // highlighted Entry) - mChart.setHighlightIndicatorEnabled(false); mChart.getAxisLeft().setDrawGridLines(false); mChart.getAxisRight().setEnabled(false); - mChart.getXAxis().setDrawGridLines(false); + mChart.getXAxis().setDrawGridLines(true); mChart.getXAxis().setDrawAxisLine(false); // dont forget to refresh the drawing @@ -74,9 +66,12 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - mTvCount.setText("" + (mSeekBarValues.getProgress())); + int count = mSeekBarValues.getProgress() + 1000; + mTvCount.setText("" + count); + + mChart.resetTracking(); - setData(mSeekBarValues.getProgress(), 500f); + setData(count, 500f); // redraw mChart.invalidate(); @@ -96,11 +91,6 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((i) + ""); - } - ArrayList yVals = new ArrayList(); for (int i = 0; i < count; i++) { @@ -108,19 +98,21 @@ private void setData(int count, float range) { float val = (float) (Math.random() * mult) + 3;// + (float) // ((mult * // 0.1) / 10); - yVals.add(new Entry(val, i)); + yVals.add(new Entry(i * 0.001f, val)); } // create a dataset and give it a type LineDataSet set1 = new LineDataSet(yVals, "DataSet 1"); set1.setColor(Color.BLACK); - set1.setLineWidth(1f); + set1.setLineWidth(0.5f); set1.setDrawValues(false); set1.setDrawCircles(false); + set1.setMode(LineDataSet.Mode.LINEAR); + set1.setDrawFilled(false); // create a data object with the datasets - LineData data = new LineData(xVals, set1); + LineData data = new LineData(set1); // set data mChart.setData(data); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index fa8c91483c..085580a923 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -4,6 +4,10 @@ import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StyleSpan; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -12,17 +16,20 @@ import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; +import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.PercentFormatter; +import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -33,8 +40,6 @@ public class PieChartActivity extends DemoBase implements OnSeekBarChangeListene private PieChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -48,53 +53,62 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - + mSeekBarX.setProgress(4); mSeekBarY.setProgress(10); - mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY.setOnSeekBarChangeListener(this); - mChart = (PieChart) findViewById(R.id.chart1); mChart.setUsePercentValues(true); + mChart.getDescription().setEnabled(false); + mChart.setExtraOffsets(5, 10, 5, 5); - // change the color of the center-hole - // mChart.setHoleColor(Color.rgb(235, 235, 235)); - mChart.setHoleColorTransparent(true); + mChart.setDragDecelerationFrictionCoef(0.95f); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + mChart.setCenterTextTypeface(mTfLight); + mChart.setCenterText(generateCenterSpannableText()); - mChart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); + mChart.setDrawHoleEnabled(true); + mChart.setHoleColor(Color.WHITE); - mChart.setHoleRadius(60f); + mChart.setTransparentCircleColor(Color.WHITE); + mChart.setTransparentCircleAlpha(110); - mChart.setDescription(""); + mChart.setHoleRadius(58f); + mChart.setTransparentCircleRadius(61f); mChart.setDrawCenterText(true); - mChart.setDrawHoleEnabled(true); - mChart.setRotationAngle(0); // enable rotation of the chart by touch mChart.setRotationEnabled(true); + mChart.setHighlightPerTapEnabled(true); // mChart.setUnit(" €"); // mChart.setDrawUnitsInChart(true); // add a selection listener mChart.setOnChartValueSelectedListener(this); - // mChart.setTouchEnabled(false); - - mChart.setCenterText("MPAndroidChart\nLibrary"); - setData(3, 100); + setData(4, 100); - mChart.animateXY(1500, 1500); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); // mChart.spin(2000, 0, 360); + mSeekBarX.setOnSeekBarChangeListener(this); + mSeekBarY.setOnSeekBarChangeListener(this); + Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); l.setXEntrySpace(7f); - l.setYEntrySpace(5f); + l.setYEntrySpace(0f); + l.setYOffset(0f); + + // entry label styling + mChart.setEntryLabelColor(Color.WHITE); + mChart.setEntryLabelTypeface(mTfRegular); + mChart.setEntryLabelTextSize(12f); } @Override @@ -108,12 +122,19 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + for (IDataSet set : mChart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); mChart.invalidate(); break; } + case R.id.actionToggleIcons: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + mChart.invalidate(); + break; + } case R.id.actionToggleHole: { if (mChart.isDrawHoleEnabled()) mChart.setDrawHoleEnabled(false); @@ -132,7 +153,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } case R.id.actionToggleXVals: { - mChart.setDrawSliceText(!mChart.isDrawSliceTextEnabled()); + mChart.setDrawEntryLabels(!mChart.isDrawEntryLabelsEnabled()); mChart.invalidate(); break; } @@ -146,15 +167,20 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; case R.id.animateX: { - mChart.animateX(1800); + mChart.animateX(1400); break; } case R.id.animateY: { - mChart.animateY(1800); + mChart.animateY(1400); break; } case R.id.animateXY: { - mChart.animateXY(1800, 1800); + mChart.animateXY(1400, 1400); + break; + } + case R.id.actionToggleSpin: { + mChart.spin(1000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360, Easing.EasingOption + .EaseInCubic); break; } } @@ -164,7 +190,7 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); + tvX.setText("" + (mSeekBarX.getProgress())); tvY.setText("" + (mSeekBarY.getProgress())); setData(mSeekBarX.getProgress(), mSeekBarY.getProgress()); @@ -174,22 +200,23 @@ private void setData(int count, float range) { float mult = range; - ArrayList yVals1 = new ArrayList(); + ArrayList entries = new ArrayList(); - // IMPORTANT: In a PieChart, no values (Entry) should have the same - // xIndex (even if from different DataSets), since no values can be - // drawn above each other. - for (int i = 0; i < count + 1; i++) { - yVals1.add(new Entry((float) (Math.random() * mult) + mult / 5, i)); + // NOTE: The order of the entries when being added to the entries array determines their position around the center of + // the chart. + for (int i = 0; i < count ; i++) { + entries.add(new PieEntry((float) ((Math.random() * mult) + mult / 5), + mParties[i % mParties.length], + getResources().getDrawable(R.drawable.star))); } - ArrayList xVals = new ArrayList(); + PieDataSet dataSet = new PieDataSet(entries, "Election Results"); - for (int i = 0; i < count + 1; i++) - xVals.add(mParties[i % mParties.length]); + dataSet.setDrawIcons(false); - PieDataSet dataSet = new PieDataSet(yVals1, "Election Results"); dataSet.setSliceSpace(3f); + dataSet.setIconsOffset(new MPPointF(0, 40)); + dataSet.setSelectionShift(5f); // add a lot of colors @@ -213,12 +240,13 @@ private void setData(int count, float range) { colors.add(ColorTemplate.getHoloBlue()); dataSet.setColors(colors); + //dataSet.setSelectionShift(0f); - PieData data = new PieData(xVals, dataSet); + PieData data = new PieData(dataSet); data.setValueFormatter(new PercentFormatter()); data.setValueTextSize(11f); data.setValueTextColor(Color.WHITE); - data.setValueTypeface(tf); + data.setValueTypeface(mTfLight); mChart.setData(data); // undo all highlights @@ -227,14 +255,26 @@ private void setData(int count, float range) { mChart.invalidate(); } + private SpannableString generateCenterSpannableText() { + + SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda"); + s.setSpan(new RelativeSizeSpan(1.7f), 0, 14, 0); + s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0); + s.setSpan(new RelativeSizeSpan(.8f), 14, s.length() - 15, 0); + s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0); + return s; + } + @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { if (e == null) return; Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); + "Value: " + e.getY() + ", index: " + h.getX() + + ", DataSet index: " + h.getDataSetIndex()); } @Override @@ -253,46 +293,4 @@ public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } - - // private void removeLastEntry() { - // - // PieData data = mChart.getDataOriginal(); - // - // if (data != null) { - // - // PieDataSet set = data.getDataSet(); - // - // if (set != null) { - // - // Entry e = set.getEntryForXIndex(set.getEntryCount() - 1); - // - // data.removeEntry(e, 0); - // // or remove by index - // // mData.removeEntry(xIndex, dataSetIndex); - // - // mChart.notifyDataSetChanged(); - // mChart.invalidate(); - // } - // } - // } - // - // private void addEntry() { - // - // PieData data = mChart.getDataOriginal(); - // - // if (data != null) { - // - // PieDataSet set = data.getDataSet(); - // // set.addEntry(...); - // - // data.addEntry(new Entry((float) (Math.random() * 25) + 20f, - // set.getEntryCount()), 0); - // - // // let the chart know it's data has changed - // mChart.notifyDataSetChanged(); - // - // // redraw the chart - // mChart.invalidate(); - // } - // } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java new file mode 100644 index 0000000000..49fc4959e9 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -0,0 +1,283 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.graphics.Typeface; +import android.os.Bundle; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StyleSpan; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.Legend.LegendPosition; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class PiePolylineChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private PieChart mChart; + private SeekBar mSeekBarX, mSeekBarY; + private TextView tvX, tvY; + + private Typeface tf; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_piechart); + + tvX = (TextView) findViewById(R.id.tvXMax); + tvY = (TextView) findViewById(R.id.tvYMax); + + mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + + mSeekBarY.setProgress(10); + + mSeekBarX.setOnSeekBarChangeListener(this); + mSeekBarY.setOnSeekBarChangeListener(this); + + mChart = (PieChart) findViewById(R.id.chart1); + mChart.setUsePercentValues(true); + mChart.getDescription().setEnabled(false); + mChart.setExtraOffsets(5, 10, 5, 5); + + mChart.setDragDecelerationFrictionCoef(0.95f); + + tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + + mChart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); + mChart.setCenterText(generateCenterSpannableText()); + + mChart.setExtraOffsets(20.f, 0.f, 20.f, 0.f); + + mChart.setDrawHoleEnabled(true); + mChart.setHoleColor(Color.WHITE); + + mChart.setTransparentCircleColor(Color.WHITE); + mChart.setTransparentCircleAlpha(110); + + mChart.setHoleRadius(58f); + mChart.setTransparentCircleRadius(61f); + + mChart.setDrawCenterText(true); + + mChart.setRotationAngle(0); + // enable rotation of the chart by touch + mChart.setRotationEnabled(true); + mChart.setHighlightPerTapEnabled(true); + + // mChart.setUnit(" €"); + // mChart.setDrawUnitsInChart(true); + + // add a selection listener + mChart.setOnChartValueSelectedListener(this); + + setData(4, 100); + + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); + // mChart.spin(2000, 0, 360); + + Legend l = mChart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); + l.setEnabled(false); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.pie, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.actionToggleValues: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + mChart.invalidate(); + break; + } + case R.id.actionToggleHole: { + if (mChart.isDrawHoleEnabled()) + mChart.setDrawHoleEnabled(false); + else + mChart.setDrawHoleEnabled(true); + mChart.invalidate(); + break; + } + case R.id.actionDrawCenter: { + if (mChart.isDrawCenterTextEnabled()) + mChart.setDrawCenterText(false); + else + mChart.setDrawCenterText(true); + mChart.invalidate(); + break; + } + case R.id.actionToggleXVals: { + + mChart.setDrawEntryLabels(!mChart.isDrawEntryLabelsEnabled()); + mChart.invalidate(); + break; + } + case R.id.actionSave: { + // mChart.saveToGallery("title"+System.currentTimeMillis()); + mChart.saveToPath("title" + System.currentTimeMillis(), ""); + break; + } + case R.id.actionTogglePercent: + mChart.setUsePercentValues(!mChart.isUsePercentValuesEnabled()); + mChart.invalidate(); + break; + case R.id.animateX: { + mChart.animateX(1400); + break; + } + case R.id.animateY: { + mChart.animateY(1400); + break; + } + case R.id.animateXY: { + mChart.animateXY(1400, 1400); + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText("" + (mSeekBarX.getProgress())); + tvY.setText("" + (mSeekBarY.getProgress())); + + setData(mSeekBarX.getProgress(), mSeekBarY.getProgress()); + } + + private void setData(int count, float range) { + + float mult = range; + + ArrayList entries = new ArrayList(); + + // NOTE: The order of the entries when being added to the entries array determines their position around the center of + // the chart. + for (int i = 0; i < count; i++) { + entries.add(new PieEntry((float) (Math.random() * mult) + mult / 5, mParties[i % mParties.length])); + } + + PieDataSet dataSet = new PieDataSet(entries, "Election Results"); + dataSet.setSliceSpace(3f); + dataSet.setSelectionShift(5f); + + // add a lot of colors + + ArrayList colors = new ArrayList(); + + for (int c : ColorTemplate.VORDIPLOM_COLORS) + colors.add(c); + + for (int c : ColorTemplate.JOYFUL_COLORS) + colors.add(c); + + for (int c : ColorTemplate.COLORFUL_COLORS) + colors.add(c); + + for (int c : ColorTemplate.LIBERTY_COLORS) + colors.add(c); + + for (int c : ColorTemplate.PASTEL_COLORS) + colors.add(c); + + colors.add(ColorTemplate.getHoloBlue()); + + dataSet.setColors(colors); + //dataSet.setSelectionShift(0f); + + + dataSet.setValueLinePart1OffsetPercentage(80.f); + dataSet.setValueLinePart1Length(0.2f); + dataSet.setValueLinePart2Length(0.4f); + //dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); + dataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); + + PieData data = new PieData(dataSet); + data.setValueFormatter(new PercentFormatter()); + data.setValueTextSize(11f); + data.setValueTextColor(Color.BLACK); + data.setValueTypeface(tf); + mChart.setData(data); + + // undo all highlights + mChart.highlightValues(null); + + mChart.invalidate(); + } + + private SpannableString generateCenterSpannableText() { + + SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda"); + s.setSpan(new RelativeSizeSpan(1.5f), 0, 14, 0); + s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0); + s.setSpan(new RelativeSizeSpan(.65f), 14, s.length() - 15, 0); + s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0); + return s; + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + + if (e == null) + return; + Log.i("VAL SELECTED", + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); + } + + @Override + public void onNothingSelected() { + Log.i("PieChart", "nothing selected"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index b8e91db183..937973fe4e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -1,24 +1,29 @@ package com.xxmassdeveloper.mpchartexample; -import android.graphics.Typeface; +import android.graphics.Color; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; +import android.widget.TextView; import android.widget.Toast; +import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendPosition; +import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.data.RadarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; +import com.github.mikephil.charting.data.RadarEntry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; +import com.xxmassdeveloper.mpchartexample.custom.RadarMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -26,49 +31,76 @@ public class RadarChartActivitry extends DemoBase { private RadarChart mChart; - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_radarchart); + setContentView(R.layout.activity_radarchart_noseekbar); - mChart = (RadarChart) findViewById(R.id.chart1); + TextView tv = (TextView) findViewById(R.id.textView); + tv.setTypeface(mTfLight); + tv.setTextColor(Color.WHITE); + tv.setBackgroundColor(Color.rgb(60, 65, 82)); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + mChart = (RadarChart) findViewById(R.id.chart1); + mChart.setBackgroundColor(Color.rgb(60, 65, 82)); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); - mChart.setWebLineWidth(1.5f); - mChart.setWebLineWidthInner(0.75f); + mChart.setWebLineWidth(1f); + mChart.setWebColor(Color.LTGRAY); + mChart.setWebLineWidthInner(1f); + mChart.setWebColorInner(Color.LTGRAY); mChart.setWebAlpha(100); // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it - MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - - // set the marker to the chart - mChart.setMarkerView(mv); + MarkerView mv = new RadarMarkerView(this, R.layout.radar_markerview); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart setData(); + mChart.animateXY( + 1400, 1400, + Easing.EasingOption.EaseInOutQuad, + Easing.EasingOption.EaseInOutQuad); + XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(tf); + xAxis.setTypeface(mTfLight); xAxis.setTextSize(9f); + xAxis.setYOffset(0f); + xAxis.setXOffset(0f); + xAxis.setValueFormatter(new IAxisValueFormatter() { + + private String[] mActivities = new String[]{"Burger", "Steak", "Salad", "Pasta", "Pizza"}; + + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mActivities[(int) value % mActivities.length]; + } + }); + xAxis.setTextColor(Color.WHITE); YAxis yAxis = mChart.getYAxis(); - yAxis.setTypeface(tf); - yAxis.setLabelCount(5); + yAxis.setTypeface(mTfLight); + yAxis.setLabelCount(5, false); yAxis.setTextSize(9f); - yAxis.setStartAtZero(true); + yAxis.setAxisMinimum(0f); + yAxis.setAxisMaximum(80f); + yAxis.setDrawLabels(false); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); - l.setTypeface(tf); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setTypeface(mTfLight); l.setXEntrySpace(7f); l.setYEntrySpace(5f); + l.setTextColor(Color.WHITE); } @Override @@ -82,18 +114,17 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + for (IDataSet set : mChart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); mChart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); + if (mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } break; } case R.id.actionToggleRotate: { @@ -106,10 +137,10 @@ public boolean onOptionsItemSelected(MenuItem item) { } case R.id.actionToggleFilled: { - ArrayList sets = (ArrayList) mChart.getData() + ArrayList sets = (ArrayList) mChart.getData() .getDataSets(); - for (RadarDataSet set : sets) { + for (IRadarDataSet set : sets) { if (set.isDrawFilledEnabled()) set.setDrawFilled(false); else @@ -118,6 +149,17 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleHighlightCircle: { + + ArrayList sets = (ArrayList) mChart.getData() + .getDataSets(); + + for (IRadarDataSet set : sets) { + set.setDrawHighlightCircleEnabled(!set.isDrawHighlightCircleEnabled()); + } + mChart.invalidate(); + break; + } case R.id.actionSave: { if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", @@ -129,6 +171,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } case R.id.actionToggleXLabels: { mChart.getXAxis().setEnabled(!mChart.getXAxis().isEnabled()); + mChart.notifyDataSetChanged(); mChart.invalidate(); break; } @@ -138,64 +181,75 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.animateX: { + mChart.animateX(1400); + break; + } + case R.id.animateY: { + mChart.animateY(1400); + break; + } + case R.id.animateXY: { + mChart.animateXY(1400, 1400); + break; + } case R.id.actionToggleSpin: { - mChart.spin(2000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360); + mChart.spin(2000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360, Easing.EasingOption + .EaseInCubic); break; } } return true; } - private String[] mParties = new String[] { - "Party A", "Party B", "Party C", "Party D", "Party E", "Party F", "Party G", "Party H", - "Party I" - }; - public void setData() { - float mult = 150; - int cnt = 9; + float mult = 80; + float min = 20; + int cnt = 5; - ArrayList yVals1 = new ArrayList(); - ArrayList yVals2 = new ArrayList(); + ArrayList entries1 = new ArrayList(); + ArrayList entries2 = new ArrayList(); - // IMPORTANT: In a PieChart, no values (Entry) should have the same - // xIndex (even if from different DataSets), since no values can be - // drawn above each other. + // NOTE: The order of the entries when being added to the entries array determines their position around the center of + // the chart. for (int i = 0; i < cnt; i++) { - yVals1.add(new Entry((float) (Math.random() * mult) + mult / 2, i)); - } + float val1 = (float) (Math.random() * mult) + min; + entries1.add(new RadarEntry(val1)); - for (int i = 0; i < cnt; i++) { - yVals2.add(new Entry((float) (Math.random() * mult) + mult / 2, i)); + float val2 = (float) (Math.random() * mult) + min; + entries2.add(new RadarEntry(val2)); } - ArrayList xVals = new ArrayList(); - - for (int i = 0; i < cnt; i++) - xVals.add(mParties[i % mParties.length]); - - RadarDataSet set1 = new RadarDataSet(yVals1, "Set 1"); - set1.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); + RadarDataSet set1 = new RadarDataSet(entries1, "Last Week"); + set1.setColor(Color.rgb(103, 110, 129)); + set1.setFillColor(Color.rgb(103, 110, 129)); set1.setDrawFilled(true); + set1.setFillAlpha(180); set1.setLineWidth(2f); + set1.setDrawHighlightCircleEnabled(true); + set1.setDrawHighlightIndicators(false); - RadarDataSet set2 = new RadarDataSet(yVals2, "Set 2"); - set2.setColor(ColorTemplate.VORDIPLOM_COLORS[4]); + RadarDataSet set2 = new RadarDataSet(entries2, "This Week"); + set2.setColor(Color.rgb(121, 162, 175)); + set2.setFillColor(Color.rgb(121, 162, 175)); set2.setDrawFilled(true); + set2.setFillAlpha(180); set2.setLineWidth(2f); + set2.setDrawHighlightCircleEnabled(true); + set2.setDrawHighlightIndicators(false); - ArrayList sets = new ArrayList(); + ArrayList sets = new ArrayList(); sets.add(set1); sets.add(set2); - RadarData data = new RadarData(xVals, sets); - data.setValueTypeface(tf); + RadarData data = new RadarData(sets); + data.setValueTypeface(mTfLight); data.setValueTextSize(8f); data.setDrawValues(false); + data.setValueTextColor(Color.WHITE); mChart.setData(data); - mChart.invalidate(); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 6ae19d6f92..13346bf631 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -2,12 +2,12 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; +import android.widget.Toast; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Legend; @@ -18,9 +18,10 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; public class RealtimeLineChartActivity extends DemoBase implements @@ -38,12 +39,8 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); - // no description text - mChart.setDescription(""); - mChart.setNoDataTextDescription("You need to provide data for the chart."); - - // enable value highlighting - mChart.setHighlightEnabled(true); + // enable description text + mChart.getDescription().setEnabled(true); // enable touch gestures mChart.setTouchEnabled(true); @@ -61,35 +58,35 @@ protected void onCreate(Bundle savedInstanceState) { LineData data = new LineData(); data.setValueTextColor(Color.WHITE); - + // add empty data mChart.setData(data); - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - // get the legend (only possible after setting data) Legend l = mChart.getLegend(); // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); - l.setTypeface(tf); + l.setTypeface(mTfLight); l.setTextColor(Color.WHITE); XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); + xl.setTypeface(mTfLight); xl.setTextColor(Color.WHITE); xl.setDrawGridLines(false); xl.setAvoidFirstLastClipping(true); + xl.setEnabled(true); YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); + leftAxis.setTypeface(mTfLight); leftAxis.setTextColor(Color.WHITE); - leftAxis.setAxisMaxValue(120f); + leftAxis.setAxisMaximum(100f); + leftAxis.setAxisMinimum(0f); leftAxis.setDrawGridLines(true); - + YAxis rightAxis = mChart.getAxisRight(); rightAxis.setEnabled(false); + } @Override @@ -106,19 +103,26 @@ public boolean onOptionsItemSelected(MenuItem item) { addEntry(); break; } + case R.id.actionClear: { + mChart.clearValues(); + Toast.makeText(this, "Chart cleared!", Toast.LENGTH_SHORT).show(); + break; + } + case R.id.actionFeedMultiple: { + feedMultiple(); + break; + } } return true; } - private int year = 15; - private void addEntry() { LineData data = mChart.getData(); if (data != null) { - LineDataSet set = data.getDataSetByIndex(0); + ILineDataSet set = data.getDataSetByIndex(0); // set.addEntry(...); // can be called as well if (set == null) { @@ -126,25 +130,22 @@ private void addEntry() { data.addDataSet(set); } - // add a new x-value first - data.addXValue(mMonths[data.getXValCount() % 12] + " " + (year + data.getXValCount() / 12)); - data.addEntry(new Entry((float) (Math.random() * 40) + 40f, set.getEntryCount()), 0); + data.addEntry(new Entry(set.getEntryCount(), (float) (Math.random() * 40) + 30f), 0); + data.notifyDataChanged(); // let the chart know it's data has changed mChart.notifyDataSetChanged(); // limit the number of visible entries - mChart.setVisibleXRange(6); + mChart.setVisibleXRangeMaximum(120); // mChart.setVisibleYRange(30, AxisDependency.LEFT); - - // move to the latest entry - mChart.moveViewToX(data.getXValCount()-7); - - // this automatically refreshes the chart (calls invalidate()) -// mChart.moveViewTo(data.getXValCount()-7, 55f, AxisDependency.LEFT); - - // redraw the chart -// mChart.invalidate(); + + // move to the latest entry + mChart.moveViewToX(data.getEntryCount()); + + // this automatically refreshes the chart (calls invalidate()) + // mChart.moveViewTo(data.getXValCount()-7, 55f, + // AxisDependency.LEFT); } } @@ -153,19 +154,57 @@ private LineDataSet createSet() { LineDataSet set = new LineDataSet(null, "Dynamic Data"); set.setAxisDependency(AxisDependency.LEFT); set.setColor(ColorTemplate.getHoloBlue()); - set.setCircleColor(ColorTemplate.getHoloBlue()); + set.setCircleColor(Color.WHITE); set.setLineWidth(2f); - set.setCircleSize(4f); + set.setCircleRadius(4f); set.setFillAlpha(65); set.setFillColor(ColorTemplate.getHoloBlue()); set.setHighLightColor(Color.rgb(244, 117, 117)); set.setValueTextColor(Color.WHITE); - set.setValueTextSize(10f); + set.setValueTextSize(9f); + set.setDrawValues(false); return set; } + private Thread thread; + + private void feedMultiple() { + + if (thread != null) + thread.interrupt(); + + final Runnable runnable = new Runnable() { + + @Override + public void run() { + addEntry(); + } + }; + + thread = new Thread(new Runnable() { + + @Override + public void run() { + for (int i = 0; i < 1000; i++) { + + // Don't generate garbage runnables inside the loop. + runOnUiThread(runnable); + + try { + Thread.sleep(25); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + }); + + thread.start(); + } + @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("Entry selected", e.toString()); } @@ -173,4 +212,13 @@ public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { public void onNothingSelected() { Log.i("Nothing selected", "Nothing selected."); } + + @Override + protected void onPause() { + super.onPause(); + + if (thread != null) { + thread.interrupt(); + } + } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index f63c7e99ca..29904a95c9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -1,7 +1,6 @@ package com.xxmassdeveloper.mpchartexample; -import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -12,23 +11,22 @@ import android.widget.TextView; import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; +import com.xxmassdeveloper.mpchartexample.custom.CustomScatterShapeRenderer; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.List; public class ScatterChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { @@ -36,8 +34,6 @@ public class ScatterChartActivity extends DemoBase implements OnSeekBarChangeLis private ScatterChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -56,16 +52,12 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setOnSeekBarChangeListener(this); mChart = (ScatterChart) findViewById(R.id.chart1); - mChart.setDescription(""); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - + mChart.getDescription().setEnabled(false); mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); - mChart.setTouchEnabled(true); - mChart.setHighlightEnabled(true); + mChart.setMaxHighlightDistance(50f); // enable scaling and dragging mChart.setDragEnabled(true); @@ -78,16 +70,21 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setProgress(100); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); - l.setTypeface(tf); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); + l.setTypeface(mTfLight); + l.setXOffset(5f); YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(tf); + yl.setTypeface(mTfLight); + yl.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); + xl.setTypeface(mTfLight); xl.setDrawGridLines(false); } @@ -102,18 +99,23 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + List sets = mChart.getData() + .getDataSets(); + + for (IScatterDataSet iSet : sets) { + + ScatterDataSet set = (ScatterDataSet) iSet; set.setDrawValues(!set.isDrawValuesEnabled()); + } mChart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } break; } case R.id.actionTogglePinch: { @@ -125,33 +127,9 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionToggleAdjustXLegend: { - XAxis xLabels = mChart.getXAxis(); - - if (xLabels.isAdjustXLabelsEnabled()) - xLabels.setAdjustXLabels(false); - else - xLabels.setAdjustXLabels(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); break; } case R.id.actionSave: { @@ -182,63 +160,60 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress() + 1)); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - xVals.add((i) + ""); - } - ArrayList yVals1 = new ArrayList(); ArrayList yVals2 = new ArrayList(); ArrayList yVals3 = new ArrayList(); for (int i = 0; i < mSeekBarX.getProgress(); i++) { float val = (float) (Math.random() * mSeekBarY.getProgress()) + 3; - yVals1.add(new Entry(val, i)); + yVals1.add(new Entry(i, val)); } for (int i = 0; i < mSeekBarX.getProgress(); i++) { float val = (float) (Math.random() * mSeekBarY.getProgress()) + 3; - yVals2.add(new Entry(val, i)); + yVals2.add(new Entry(i+0.33f, val)); } for (int i = 0; i < mSeekBarX.getProgress(); i++) { float val = (float) (Math.random() * mSeekBarY.getProgress()) + 3; - yVals3.add(new Entry(val, i)); + yVals3.add(new Entry(i+0.66f, val)); } // create a dataset and give it a type ScatterDataSet set1 = new ScatterDataSet(yVals1, "DS 1"); - set1.setScatterShape(ScatterShape.SQUARE); + set1.setScatterShape(ScatterChart.ScatterShape.SQUARE); set1.setColor(ColorTemplate.COLORFUL_COLORS[0]); ScatterDataSet set2 = new ScatterDataSet(yVals2, "DS 2"); - set2.setScatterShape(ScatterShape.CIRCLE); + set2.setScatterShape(ScatterChart.ScatterShape.CIRCLE); + set2.setScatterShapeHoleColor(ColorTemplate.COLORFUL_COLORS[3]); + set2.setScatterShapeHoleRadius(3f); set2.setColor(ColorTemplate.COLORFUL_COLORS[1]); ScatterDataSet set3 = new ScatterDataSet(yVals3, "DS 3"); - set3.setScatterShape(ScatterShape.TRIANGLE); + set3.setShapeRenderer(new CustomScatterShapeRenderer()); set3.setColor(ColorTemplate.COLORFUL_COLORS[2]); set1.setScatterShapeSize(8f); set2.setScatterShapeSize(8f); set3.setScatterShapeSize(8f); - ArrayList dataSets = new ArrayList(); + ArrayList dataSets = new ArrayList(); dataSets.add(set1); // add the datasets dataSets.add(set2); dataSets.add(set3); // create a data object with the datasets - ScatterData data = new ScatterData(xVals, dataSets); - data.setValueTypeface(tf); + ScatterData data = new ScatterData(dataSets); + data.setValueTypeface(mTfLight); mChart.setData(data); mChart.invalidate(); } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java new file mode 100644 index 0000000000..ff098c32e0 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -0,0 +1,70 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class ScrollViewActivity extends DemoBase { + + private BarChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_scrollview); + + mChart = (BarChart) findViewById(R.id.chart1); + + mChart.getDescription().setEnabled(false); + + // scaling can now only be done on x- and y-axis separately + mChart.setPinchZoom(false); + + mChart.setDrawBarShadow(false); + mChart.setDrawGridBackground(false); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setPosition(XAxisPosition.BOTTOM); + xAxis.setDrawGridLines(false); + + mChart.getAxisLeft().setDrawGridLines(false); + + mChart.getLegend().setEnabled(false); + + setData(10); + mChart.setFitBars(true); + } + + private void setData(int count) { + + ArrayList yVals = new ArrayList(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * count) + 15; + yVals.add(new BarEntry(i, (int) val)); + } + + BarDataSet set = new BarDataSet(yVals, "Data Set"); + set.setColors(ColorTemplate.VORDIPLOM_COLORS); + set.setDrawValues(false); + + BarData data = new BarData(set); + + mChart.setData(data); + mChart.invalidate(); + mChart.animateY(800); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 2019c32c22..70afd7c4c6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -1,6 +1,6 @@ - package com.xxmassdeveloper.mpchartexample; +import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -20,20 +20,19 @@ import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; +import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.List; -public class StackedBarActivity extends DemoBase implements OnSeekBarChangeListener, - OnChartValueSelectedListener { +public class StackedBarActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { private BarChart mChart; private SeekBar mSeekBarX, mSeekBarY; @@ -42,8 +41,7 @@ public class StackedBarActivity extends DemoBase implements OnSeekBarChangeListe @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart); tvX = (TextView) findViewById(R.id.tvXMax); @@ -58,29 +56,26 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (BarChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn - mChart.setMaxVisibleValueCount(60); + mChart.setMaxVisibleValueCount(40); - // if false values are only drawn for the stack sum, else each value is - // drawn - mChart.setDrawValuesForWholeStack(true); // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); + mChart.setDrawGridBackground(false); mChart.setDrawBarShadow(false); - + mChart.setDrawValueAboveBar(false); + mChart.setHighlightFullBarEnabled(false); // change the position of the y-labels - YAxis yLabels = mChart.getAxisLeft(); - // yLabels.setPosition(YLabelPosition.BOTH_SIDED); - yLabels.setLabelCount(5); - yLabels.setValueFormatter(new MyValueFormatter()); - - mChart.getAxisRight().setValueFormatter(new MyValueFormatter()); + YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.setValueFormatter(new MyAxisValueFormatter()); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + mChart.getAxisRight().setEnabled(false); XAxis xLabels = mChart.getXAxis(); xLabels.setPosition(XAxisPosition.TOP); @@ -93,7 +88,10 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setProgress(100); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_RIGHT); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); l.setFormSize(8f); l.setFormToTextSpace(4f); l.setXEntrySpace(6f); @@ -112,20 +110,38 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + BarDataSet set = (BarDataSet) iSet; set.setDrawValues(!set.isDrawValuesEnabled()); + } mChart.invalidate(); break; } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); + case R.id.actionToggleIcons: { + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + BarDataSet set = (BarDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + mChart.invalidate(); break; } + case R.id.actionToggleHighlight: { + if (mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } case R.id.actionTogglePinch: { if (mChart.isPinchZoomEnabled()) mChart.setPinchZoom(false); @@ -135,27 +151,14 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); break; } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionToggleAdjustXLegend: { - XAxis xLabels = mChart.getXAxis(); - - if (xLabels.isAdjustXLabelsEnabled()) - xLabels.setAdjustXLabels(false); - else - xLabels.setAdjustXLabels(true); + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : mChart.getData().getDataSets()) + ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); mChart.invalidate(); break; @@ -173,25 +176,11 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.animateXY(3000, 3000); break; } - case R.id.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - break; - } case R.id.actionSave: { if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); + Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", Toast.LENGTH_SHORT).show(); } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); + Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT).show(); break; } } @@ -204,11 +193,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress() + 1)); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add(mMonths[i % mMonths.length]); - } - ArrayList yVals1 = new ArrayList(); for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { @@ -217,24 +201,37 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float val2 = (float) (Math.random() * mult) + mult / 3; float val3 = (float) (Math.random() * mult) + mult / 3; - yVals1.add(new BarEntry(new float[] { - val1, val2, val3 - }, i)); + yVals1.add(new BarEntry( + i, + new float[]{val1, val2, val3}, + getResources().getDrawable(R.drawable.star))); } - BarDataSet set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); - set1.setColors(ColorTemplate.VORDIPLOM_COLORS); - set1.setStackLabels(new String[] { - "Births", "Divorces", "Marriages" - }); + BarDataSet set1; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); + set1.setValues(yVals1); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); + set1.setDrawIcons(false); + set1.setColors(getColors()); + set1.setStackLabels(new String[]{"Births", "Divorces", "Marriages"}); - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); - BarData data = new BarData(xVals, dataSets); - data.setValueFormatter(new MyValueFormatter()); + BarData data = new BarData(dataSets); + data.setValueFormatter(new MyValueFormatter()); + data.setValueTextColor(Color.WHITE); - mChart.setData(data); + mChart.setData(data); + } + + mChart.setFitBars(true); mChart.invalidate(); } @@ -251,11 +248,14 @@ public void onStopTrackingTouch(SeekBar seekBar) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { BarEntry entry = (BarEntry) e; - Log.i("VAL SELECTED", - "Value: " + entry.getVals()[h.getStackIndex()]); + + if (entry.getYVals() != null) + Log.i("VAL SELECTED", "Value: " + entry.getYVals()[h.getStackIndex()]); + else + Log.i("VAL SELECTED", "Value: " + entry.getY()); } @Override @@ -263,4 +263,18 @@ public void onNothingSelected() { // TODO Auto-generated method stub } + + private int[] getColors() { + + int stacksize = 3; + + // have as many colors as stack-values per entry + int[] colors = new int[stacksize]; + + for (int i = 0; i < colors.length; i++) { + colors[i] = ColorTemplate.MATERIAL_COLORS[i]; + } + + return colors; + } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java new file mode 100644 index 0000000000..d5e0f8c885 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -0,0 +1,257 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.Toast; + +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.Legend.LegendPosition; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ViewPortHandler; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +public class StackedBarActivityNegative extends DemoBase implements + OnChartValueSelectedListener { + + private HorizontalBarChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_age_distribution); + + setTitle("Age Distribution Austria"); + + mChart = (HorizontalBarChart) findViewById(R.id.chart1); + mChart.setOnChartValueSelectedListener(this); + mChart.setDrawGridBackground(false); + mChart.getDescription().setEnabled(false); + + // scaling can now only be done on x- and y-axis separately + mChart.setPinchZoom(false); + + mChart.setDrawBarShadow(false); + mChart.setDrawValueAboveBar(true); + mChart.setHighlightFullBarEnabled(false); + + mChart.getAxisLeft().setEnabled(false); + mChart.getAxisRight().setAxisMaximum(25f); + mChart.getAxisRight().setAxisMinimum(-25f); + mChart.getAxisRight().setDrawGridLines(false); + mChart.getAxisRight().setDrawZeroLine(true); + mChart.getAxisRight().setLabelCount(7, false); + mChart.getAxisRight().setValueFormatter(new CustomFormatter()); + mChart.getAxisRight().setTextSize(9f); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setPosition(XAxisPosition.BOTH_SIDED); + xAxis.setDrawGridLines(false); + xAxis.setDrawAxisLine(false); + xAxis.setTextSize(9f); + xAxis.setAxisMinimum(0f); + xAxis.setAxisMaximum(110f); + xAxis.setCenterAxisLabels(true); + xAxis.setLabelCount(12); + xAxis.setGranularity(10f); + xAxis.setValueFormatter(new IAxisValueFormatter() { + + private DecimalFormat format = new DecimalFormat("###"); + + @Override + public String getFormattedValue(float value, AxisBase axis) { + return format.format(value) + "-" + format.format(value + 10); + } + }); + + Legend l = mChart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setFormSize(8f); + l.setFormToTextSpace(4f); + l.setXEntrySpace(6f); + + // IMPORTANT: When using negative values in stacked bars, always make sure the negative values are in the array first + ArrayList yValues = new ArrayList(); + yValues.add(new BarEntry(5, new float[]{ -10, 10 })); + yValues.add(new BarEntry(15, new float[]{ -12, 13 })); + yValues.add(new BarEntry(25, new float[]{ -15, 15 })); + yValues.add(new BarEntry(35, new float[]{ -17, 17 })); + yValues.add(new BarEntry(45, new float[]{ -19, 20 })); + yValues.add(new BarEntry(45, new float[]{ -19, 20 }, getResources().getDrawable(R.drawable.star))); + yValues.add(new BarEntry(55, new float[]{ -19, 19 })); + yValues.add(new BarEntry(65, new float[]{ -16, 16 })); + yValues.add(new BarEntry(75, new float[]{ -13, 14 })); + yValues.add(new BarEntry(85, new float[]{ -10, 11 })); + yValues.add(new BarEntry(95, new float[]{ -5, 6 })); + yValues.add(new BarEntry(105, new float[]{ -1, 2 })); + + BarDataSet set = new BarDataSet(yValues, "Age Distribution"); + set.setDrawIcons(false); + set.setValueFormatter(new CustomFormatter()); + set.setValueTextSize(7f); + set.setAxisDependency(YAxis.AxisDependency.RIGHT); + set.setColors(new int[] {Color.rgb(67,67,72), Color.rgb(124,181,236)}); + set.setStackLabels(new String[]{ + "Men", "Women" + }); + + String []xLabels = new String[]{"0-10", "10-20", "20-30", "30-40", "40-50", "50-60", "60-70", "70-80", "80-90", "90-100", "100+"}; + + BarData data = new BarData(set); + data.setBarWidth(8.5f); + mChart.setData(data); + mChart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.actionToggleValues: { + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + BarDataSet set = (BarDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + mChart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + BarDataSet set = (BarDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + + mChart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (mChart.isPinchZoomEnabled()) + mChart.setPinchZoom(false); + else + mChart.setPinchZoom(true); + + mChart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : mChart.getData().getDataSets()) + ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + mChart.invalidate(); + break; + } + case R.id.animateX: { + mChart.animateX(3000); + break; + } + case R.id.animateY: { + mChart.animateY(3000); + break; + } + case R.id.animateXY: { + + mChart.animateXY(3000, 3000); + break; + } + case R.id.actionSave: { + if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { + Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", + Toast.LENGTH_SHORT).show(); + } else + Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) + .show(); + break; + } + } + return true; + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + + BarEntry entry = (BarEntry) e; + Log.i("VAL SELECTED", + "Value: " + Math.abs(entry.getYVals()[h.getStackIndex()])); + } + + @Override + public void onNothingSelected() { + // TODO Auto-generated method stub + Log.i("NOTING SELECTED", ""); + } + + private class CustomFormatter implements IValueFormatter, IAxisValueFormatter + { + + private DecimalFormat mFormat; + + public CustomFormatter() { + mFormat = new DecimalFormat("###"); + } + + // data + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return mFormat.format(Math.abs(value)) + "m"; + } + + // YAxis + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mFormat.format(Math.abs(value)) + "m"; + } + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java new file mode 100644 index 0000000000..6279e395f6 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java @@ -0,0 +1,30 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Custom shape renderer that draws a single line. + * Created by philipp on 26/06/16. + */ +public class CustomScatterShapeRenderer implements IShapeRenderer +{ + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; + + c.drawLine( + posX - shapeHalf, + posY - shapeHalf, + posX + shapeHalf, + posY + shapeHalf, + renderPaint); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java new file mode 100644 index 0000000000..b8bc1a41c2 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -0,0 +1,139 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.charts.BarLineChartBase; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; + +/** + * Created by philipp on 02/06/16. + */ +public class DayAxisValueFormatter implements IAxisValueFormatter +{ + + protected String[] mMonths = new String[]{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + private BarLineChartBase chart; + + public DayAxisValueFormatter(BarLineChartBase chart) { + this.chart = chart; + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + + int days = (int) value; + + int year = determineYear(days); + + int month = determineMonth(days); + String monthName = mMonths[month % mMonths.length]; + String yearName = String.valueOf(year); + + if (chart.getVisibleXRange() > 30 * 6) { + + return monthName + " " + yearName; + } else { + + int dayOfMonth = determineDayOfMonth(days, month + 12 * (year - 2016)); + + String appendix = "th"; + + switch (dayOfMonth) { + case 1: + appendix = "st"; + break; + case 2: + appendix = "nd"; + break; + case 3: + appendix = "rd"; + break; + case 21: + appendix = "st"; + break; + case 22: + appendix = "nd"; + break; + case 23: + appendix = "rd"; + break; + case 31: + appendix = "st"; + break; + } + + return dayOfMonth == 0 ? "" : dayOfMonth + appendix + " " + monthName; + } + } + + private int getDaysForMonth(int month, int year) { + + // month is 0-based + + if (month == 1) { + boolean is29Feb = false; + + if (year < 1582) + is29Feb = (year < 1 ? year + 1 : year) % 4 == 0; + else if (year > 1582) + is29Feb = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + + return is29Feb ? 29 : 28; + } + + if (month == 3 || month == 5 || month == 8 || month == 10) + return 30; + else + return 31; + } + + private int determineMonth(int dayOfYear) { + + int month = -1; + int days = 0; + + while (days < dayOfYear) { + month = month + 1; + + if (month >= 12) + month = 0; + + int year = determineYear(days); + days += getDaysForMonth(month, year); + } + + return Math.max(month, 0); + } + + private int determineDayOfMonth(int days, int month) { + + int count = 0; + int daysForMonths = 0; + + while (count < month) { + + int year = determineYear(daysForMonths); + daysForMonths += getDaysForMonth(count % 12, year); + count++; + } + + return days - daysForMonths; + } + + private int determineYear(int days) { + + if (days <= 366) + return 2016; + else if (days <= 730) + return 2017; + else if (days <= 1094) + return 2018; + else if (days <= 1458) + return 2019; + else + return 2020; + + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java new file mode 100644 index 0000000000..e8456675a9 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java @@ -0,0 +1,21 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; + +import java.text.DecimalFormat; + +public class MyAxisValueFormatter implements IAxisValueFormatter +{ + + private DecimalFormat mFormat; + + public MyAxisValueFormatter() { + mFormat = new DecimalFormat("###,###,###,##0.0"); + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mFormat.format(value) + " $"; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java new file mode 100644 index 0000000000..c66e5d4569 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -0,0 +1,40 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; + +/** + * Created by Philipp Jahoda on 14/09/15. + */ +public class MyCustomXAxisValueFormatter implements IAxisValueFormatter +{ + + private DecimalFormat mFormat; + private ViewPortHandler mViewPortHandler; + + public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) { + mViewPortHandler = viewPortHandler; + // maybe do something here or provide parameters in constructor + mFormat = new DecimalFormat("###,###,###,##0.0"); + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + + //Log.i("TRANS", "x: " + viewPortHandler.getTransX() + ", y: " + viewPortHandler.getTransY()); + + // e.g. adjust the x-axis values depending on scale / zoom level + final float xScale = mViewPortHandler.getScaleX(); + if (xScale > 5) + return "4"; + else if (xScale > 3) + return "3"; + else if (xScale > 1) + return "2"; + else + return mFormat.format(value); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyEasingFunction.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyEasingFunction.java new file mode 100644 index 0000000000..e874a57ab6 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyEasingFunction.java @@ -0,0 +1,18 @@ + +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.animation.EasingFunction; + +/** + * Example of a custom made animation EasingFunction. + * + * @author Philipp Jahoda + */ +public class MyEasingFunction implements EasingFunction { + + @Override + public float getInterpolation(float input) { + // do awesome stuff here, this is just linear easing + return input; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java new file mode 100644 index 0000000000..e4b94c331f --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java @@ -0,0 +1,24 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.formatter.IFillFormatter; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; + +/** + * Created by Philipp Jahoda on 12/09/15. + */ +public class MyFillFormatter implements IFillFormatter +{ + + private float mFillPos = 0f; + + public MyFillFormatter(float fillpos) { + this.mFillPos = fillpos; + } + + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + // your logic could be here + return mFillPos; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java index 0f21a17df9..8d97346195 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java @@ -7,6 +7,8 @@ import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.R; @@ -28,7 +30,7 @@ public MyMarkerView(Context context, int layoutResource) { // callbacks everytime the MarkerView is redrawn, can be used to update the // content (user-interface) @Override - public void refreshContent(Entry e, int dataSetIndex) { + public void refreshContent(Entry e, Highlight highlight) { if (e instanceof CandleEntry) { @@ -37,19 +39,14 @@ public void refreshContent(Entry e, int dataSetIndex) { tvContent.setText("" + Utils.formatNumber(ce.getHigh(), 0, true)); } else { - tvContent.setText("" + Utils.formatNumber(e.getVal(), 0, true)); + tvContent.setText("" + Utils.formatNumber(e.getY(), 0, true)); } - } - @Override - public int getXOffset() { - // this will center the marker-view horizontally - return -(getWidth() / 2); + super.refreshContent(e, highlight); } @Override - public int getYOffset() { - // this will cause the marker-view to be above the selected value - return -getHeight(); + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight()); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java index c71b1ac1b8..cbf5fd56c8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java @@ -1,20 +1,22 @@ package com.xxmassdeveloper.mpchartexample.custom; -import com.github.mikephil.charting.utils.ValueFormatter; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; -public class MyValueFormatter implements ValueFormatter { +public class MyValueFormatter implements IValueFormatter +{ private DecimalFormat mFormat; public MyValueFormatter() { mFormat = new DecimalFormat("###,###,###,##0.0"); } - + @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { return mFormat.format(value) + " $"; } - } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java new file mode 100644 index 0000000000..1a350f45ac --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -0,0 +1,48 @@ + +package com.xxmassdeveloper.mpchartexample.custom; + +import android.content.Context; +import android.graphics.Typeface; +import android.widget.TextView; + +import com.github.mikephil.charting.components.MarkerView; +import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.xxmassdeveloper.mpchartexample.R; + +import java.text.DecimalFormat; + +/** + * Custom implementation of the MarkerView. + * + * @author Philipp Jahoda + */ +public class RadarMarkerView extends MarkerView { + + private TextView tvContent; + private DecimalFormat format = new DecimalFormat("##0"); + + public RadarMarkerView(Context context, int layoutResource) { + super(context, layoutResource); + + tvContent = (TextView) findViewById(R.id.tvContent); + tvContent.setTypeface(Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf")); + } + + // callbacks everytime the MarkerView is redrawn, can be used to update the + // content (user-interface) + @Override + public void refreshContent(Entry e, Highlight highlight) { + tvContent.setText(format.format(e.getY()) + " %"); + + super.refreshContent(e, highlight); + } + + @Override + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight() - 10); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java new file mode 100644 index 0000000000..7becc88c90 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java @@ -0,0 +1,180 @@ +package com.xxmassdeveloper.mpchartexample.custom; + + +import io.realm.RealmList; +import io.realm.RealmObject; + +/** + * Demo class that encapsulates data stored in realm.io database. + * This class represents data suitable for all chart-types. + */ +public class RealmDemoData extends RealmObject { + + private float yValue; + private float xValue; + + private float open, close, high, low; + + private float bubbleSize; + + private RealmList stackValues; + + private String someStringField; + + /** + * label for pie entries + */ + private String label; + + // ofc there could me more fields here... + + public RealmDemoData() { + + } + + public RealmDemoData(float yValue) { + this.yValue = yValue; + } + + public RealmDemoData(float xValue, float yValue) { + this.xValue = xValue; + this.yValue = yValue; + } + + /** + * Constructor for stacked bars. + * + * @param xValue + * @param stackValues + */ + public RealmDemoData(float xValue, float[] stackValues) { + this.xValue = xValue; + this.stackValues = new RealmList(); + + for (float val : stackValues) { + this.stackValues.add(new RealmFloat(val)); + } + } + + /** + * Constructor for candles. + * + * @param xValue + * @param high + * @param low + * @param open + * @param close + */ + public RealmDemoData(float xValue, float high, float low, float open, float close) { + this.yValue = (high + low) / 2f; + this.high = high; + this.low = low; + this.open = open; + this.close = close; + this.xValue = xValue; + } + + /** + * Constructor for bubbles. + * + * @param xValue + * @param yValue + * @param bubbleSize + */ + public RealmDemoData(float xValue, float yValue, float bubbleSize) { + this.xValue = xValue; + this.yValue = yValue; + this.bubbleSize = bubbleSize; + } + + /** + * Constructor for pie chart. + * + * @param yValue + * @param label + */ + public RealmDemoData(float yValue, String label) { + this.yValue = yValue; + this.label = label; + } + + public float getyValue() { + return yValue; + } + + public void setyValue(float yValue) { + this.yValue = yValue; + } + + public float getxValue() { + return xValue; + } + + public void setxValue(float xValue) { + this.xValue = xValue; + } + + public RealmList getStackValues() { + return stackValues; + } + + public void setStackValues(RealmList stackValues) { + this.stackValues = stackValues; + } + + public float getOpen() { + return open; + } + + public void setOpen(float open) { + this.open = open; + } + + public float getClose() { + return close; + } + + public void setClose(float close) { + this.close = close; + } + + public float getHigh() { + return high; + } + + public void setHigh(float high) { + this.high = high; + } + + public float getLow() { + return low; + } + + public void setLow(float low) { + this.low = low; + } + + public float getBubbleSize() { + return bubbleSize; + } + + public void setBubbleSize(float bubbleSize) { + this.bubbleSize = bubbleSize; + } + + public String getSomeStringField() { + return someStringField; + } + + public void setSomeStringField(String someStringField) { + this.someStringField = someStringField; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } +} \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java new file mode 100644 index 0000000000..15b027b3ff --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java @@ -0,0 +1,27 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import io.realm.RealmObject; + +/** + * Created by Philipp Jahoda on 09/11/15. + */ +public class RealmFloat extends RealmObject { + + private float floatValue; + + public RealmFloat() { + + } + + public RealmFloat(float floatValue) { + this.floatValue = floatValue; + } + + public float getFloatValue() { + return floatValue; + } + + public void setFloatValue(float value) { + this.floatValue = value; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java new file mode 100644 index 0000000000..d0781b2ee2 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java @@ -0,0 +1,58 @@ + +package com.xxmassdeveloper.mpchartexample.custom; + +import android.content.Context; +import android.widget.TextView; + +import com.github.mikephil.charting.components.MarkerView; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.xxmassdeveloper.mpchartexample.R; + +/** + * Custom implementation of the MarkerView. + * + * @author Philipp Jahoda + */ +public class StackedBarsMarkerView extends MarkerView { + + private TextView tvContent; + + public StackedBarsMarkerView(Context context, int layoutResource) { + super(context, layoutResource); + + tvContent = (TextView) findViewById(R.id.tvContent); + } + + // callbacks everytime the MarkerView is redrawn, can be used to update the + // content (user-interface) + @Override + public void refreshContent(Entry e, Highlight highlight) { + + if (e instanceof BarEntry) { + + BarEntry be = (BarEntry) e; + + if(be.getYVals() != null) { + + // draw the stack value + tvContent.setText("" + Utils.formatNumber(be.getYVals()[highlight.getStackIndex()], 0, true)); + } else { + tvContent.setText("" + Utils.formatNumber(be.getY(), 0, true)); + } + } else { + + tvContent.setText("" + Utils.formatNumber(e.getY(), 0, true)); + } + + super.refreshContent(e, highlight); + } + + @Override + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight()); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java new file mode 100644 index 0000000000..177527b3bf --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -0,0 +1,50 @@ + +package com.xxmassdeveloper.mpchartexample.custom; + +import android.content.Context; +import android.widget.TextView; + +import com.github.mikephil.charting.components.MarkerView; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.R; + +import java.text.DecimalFormat; + +/** + * Custom implementation of the MarkerView. + * + * @author Philipp Jahoda + */ +public class XYMarkerView extends MarkerView { + + private TextView tvContent; + private IAxisValueFormatter xAxisValueFormatter; + + private DecimalFormat format; + + public XYMarkerView(Context context, IAxisValueFormatter xAxisValueFormatter) { + super(context, R.layout.custom_marker_view); + + this.xAxisValueFormatter = xAxisValueFormatter; + tvContent = (TextView) findViewById(R.id.tvContent); + format = new DecimalFormat("###.0"); + } + + // callbacks everytime the MarkerView is redrawn, can be used to update the + // content (user-interface) + @Override + public void refreshContent(Entry e, Highlight highlight) { + + tvContent.setText("x: " + xAxisValueFormatter.getFormattedValue(e.getX(), null) + ", y: " + format.format(e.getY())); + + super.refreshContent(e, highlight); + } + + @Override + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight()); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java new file mode 100644 index 0000000000..34d76c25eb --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java @@ -0,0 +1,27 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; + +/** + * Created by Philipp Jahoda on 14/09/15. + */ +public class YearXAxisFormatter implements IAxisValueFormatter +{ + + protected String[] mMonths = new String[]{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" + }; + + public YearXAxisFormatter() { + // maybe do something here or provide parameters in constructor + + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + + float percent = value / axis.mAxisRange; + return mMonths[(int) (mMonths.length * percent)]; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java index 91c8316e5f..1cdba2735f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java @@ -2,7 +2,9 @@ import android.graphics.Typeface; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.util.Log; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -11,11 +13,13 @@ import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.listener.ChartTouchListener; +import com.github.mikephil.charting.listener.OnChartGestureListener; import com.xxmassdeveloper.mpchartexample.R; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; -public class BarChartFrag extends SimpleFragment { +public class BarChartFrag extends SimpleFragment implements OnChartGestureListener { public static Fragment newInstance() { return new BarChartFrag(); @@ -29,13 +33,12 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa // create a new chart object mChart = new BarChart(getActivity()); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); + mChart.setOnChartGestureListener(this); MyMarkerView mv = new MyMarkerView(getActivity(), R.layout.custom_marker_view); - - mChart.setMarkerView(mv); - - mChart.setHighlightIndicatorEnabled(false); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); mChart.setDrawGridBackground(false); mChart.setDrawBarShadow(false); @@ -49,6 +52,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); @@ -61,4 +65,46 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa return v; } + + @Override + public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + Log.i("Gesture", "START"); + } + + @Override + public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + Log.i("Gesture", "END"); + mChart.highlightValues(null); + } + + @Override + public void onChartLongPressed(MotionEvent me) { + Log.i("LongPress", "Chart longpressed."); + } + + @Override + public void onChartDoubleTapped(MotionEvent me) { + Log.i("DoubleTap", "Chart double-tapped."); + } + + @Override + public void onChartSingleTapped(MotionEvent me) { + Log.i("SingleTap", "Chart single-tapped."); + } + + @Override + public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { + Log.i("Fling", "Chart flinged. VeloX: " + velocityX + ", VeloY: " + velocityY); + } + + @Override + public void onChartScale(MotionEvent me, float scaleX, float scaleY) { + Log.i("Scale / Zoom", "ScaleX: " + scaleX + ", ScaleY: " + scaleY); + } + + @Override + public void onChartTranslate(MotionEvent me, float dX, float dY) { + Log.i("Translate / Move", "dX: " + dX + ", dY: " + dY); + } + } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java index 6968d09b1f..caf9e3e295 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java @@ -26,18 +26,14 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View v = inflater.inflate(R.layout.frag_simple_line, container, false); mChart = (LineChart) v.findViewById(R.id.lineChart1); - - mChart.setDescription(""); - - mChart.setHighlightIndicatorEnabled(false); + + mChart.getDescription().setEnabled(false); + mChart.setDrawGridBackground(false); mChart.setData(getComplexity()); mChart.animateX(3000); -// mChart.setScaleMinima(3f, 3f); -// mChart.centerViewPort(300, 0); - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); Legend l = mChart.getLegend(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java index 851351b1c4..7888aa632f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java @@ -1,7 +1,11 @@ package com.xxmassdeveloper.mpchartexample.fragments; +import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -25,25 +29,34 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View v = inflater.inflate(R.layout.frag_simple_pie, container, false); mChart = (PieChart) v.findViewById(R.id.pieChart1); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); - mChart.setCenterTextTypeface(Typeface.createFromAsset(getActivity().getAssets(), "OpenSans-Light.ttf")); - mChart.setCenterText("Quarterly\nRevenue"); - mChart.setCenterTextSize(22f); + Typeface tf = Typeface.createFromAsset(getActivity().getAssets(), "OpenSans-Light.ttf"); + + mChart.setCenterTextTypeface(tf); + mChart.setCenterText(generateCenterText()); + mChart.setCenterTextSize(10f); + mChart.setCenterTextTypeface(tf); // radius of the center hole in percent of maximum radius - mChart.setHoleRadius(45f); + mChart.setHoleRadius(45f); mChart.setTransparentCircleRadius(50f); - // enable / disable drawing of x- and y-values -// mChart.setDrawYValues(false); -// mChart.setDrawXValues(false); + Legend l = mChart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); mChart.setData(generatePieData()); - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); - return v; } + + private SpannableString generateCenterText() { + SpannableString s = new SpannableString("Revenues\nQuarters 2015"); + s.setSpan(new RelativeSizeSpan(2f), 0, 8, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 8, s.length(), 0); + return s; + } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java index 41a44d4104..90aba051d3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java @@ -1,4 +1,5 @@ package com.xxmassdeveloper.mpchartexample.fragments; + import android.graphics.Typeface; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -9,6 +10,7 @@ import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; import com.xxmassdeveloper.mpchartexample.R; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; @@ -27,21 +29,20 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View v = inflater.inflate(R.layout.frag_simple_scatter, container, false); mChart = (ScatterChart) v.findViewById(R.id.scatterChart1); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); MyMarkerView mv = new MyMarkerView(getActivity(), R.layout.custom_marker_view); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); - mChart.setMarkerView(mv); - - mChart.setHighlightIndicatorEnabled(false); -// mChart.setBorderStyles(new BorderStyle[] { BorderStyle.LEFT }); mChart.setDrawGridBackground(false); - mChart.setData(generateScatterData(3, 10000, 150)); + mChart.setData(generateScatterData(6, 10000, 200)); XAxis xAxis = mChart.getXAxis(); - xAxis.setEnabled(false); + xAxis.setEnabled(true); + xAxis.setPosition(XAxisPosition.BOTTOM); YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); @@ -51,7 +52,14 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa rightAxis.setDrawGridLines(false); Legend l = mChart.getLegend(); + l.setWordWrapEnabled(true); l.setTypeface(tf); + l.setFormSize(14f); + l.setTextSize(9f); + + // increase the space between legend & bottom and legend & content + l.setYOffset(13f); + mChart.setExtraBottomOffset(16f); return v; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java index 4f12b62770..22b35e4963 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java @@ -1,5 +1,6 @@ package com.xxmassdeveloper.mpchartexample.fragments; +import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -8,18 +9,20 @@ import android.view.ViewGroup; import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.FileUtils; @@ -41,7 +44,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa protected BarData generateBarData(int dataSets, float range, int count) { - ArrayList sets = new ArrayList(); + ArrayList sets = new ArrayList(); for(int i = 0; i < dataSets; i++) { @@ -50,7 +53,7 @@ protected BarData generateBarData(int dataSets, float range, int count) { // entries = FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "stacked_bars.txt"); for(int j = 0; j < count; j++) { - entries.add(new BarEntry((float) (Math.random() * range) + range / 4, j)); + entries.add(new BarEntry(j, (float) (Math.random() * range) + range / 4)); } BarDataSet ds = new BarDataSet(entries, getLabel(i)); @@ -58,23 +61,23 @@ protected BarData generateBarData(int dataSets, float range, int count) { sets.add(ds); } - BarData d = new BarData(ChartData.generateXVals(0, count), sets); + BarData d = new BarData(sets); d.setValueTypeface(tf); return d; } protected ScatterData generateScatterData(int dataSets, float range, int count) { - ArrayList sets = new ArrayList(); + ArrayList sets = new ArrayList(); - ScatterShape[] shapes = ScatterChart.getAllPossibleShapes(); + ScatterChart.ScatterShape[] shapes = ScatterChart.ScatterShape.getAllDefaultShapes(); for(int i = 0; i < dataSets; i++) { ArrayList entries = new ArrayList(); for(int j = 0; j < count; j++) { - entries.add(new Entry((float) (Math.random() * range) + range / 4, j)); + entries.add(new Entry(j, (float) (Math.random() * range) + range / 4)); } ScatterDataSet ds = new ScatterDataSet(entries, getLabel(i)); @@ -85,7 +88,7 @@ protected ScatterData generateScatterData(int dataSets, float range, int count) sets.add(ds); } - ScatterData d = new ScatterData(ChartData.generateXVals(0, count), sets); + ScatterData d = new ScatterData(sets); d.setValueTypeface(tf); return d; } @@ -98,37 +101,27 @@ protected PieData generatePieData() { int count = 4; - ArrayList entries1 = new ArrayList(); - ArrayList xVals = new ArrayList(); - - xVals.add("Quarter 1"); - xVals.add("Quarter 2"); - xVals.add("Quarter 3"); - xVals.add("Quarter 4"); + ArrayList entries1 = new ArrayList(); for(int i = 0; i < count; i++) { - xVals.add("entry" + (i+1)); - - entries1.add(new Entry((float) (Math.random() * 60) + 40, i)); + entries1.add(new PieEntry((float) ((Math.random() * 60) + 40), "Quarter " + (i+1))); } - PieDataSet ds1 = new PieDataSet(entries1, "Quarterly Revenues 2014"); + PieDataSet ds1 = new PieDataSet(entries1, "Quarterly Revenues 2015"); ds1.setColors(ColorTemplate.VORDIPLOM_COLORS); ds1.setSliceSpace(2f); + ds1.setValueTextColor(Color.WHITE); + ds1.setValueTextSize(12f); - PieData d = new PieData(xVals, ds1); + PieData d = new PieData(ds1); d.setValueTypeface(tf); + return d; } protected LineData generateLineData() { -// DataSet ds1 = new DataSet(n, "O(n)"); -// DataSet ds2 = new DataSet(nlogn, "O(nlogn)"); -// DataSet ds3 = new DataSet(nsquare, "O(n\u00B2)"); -// DataSet ds4 = new DataSet(nthree, "O(n\u00B3)"); - - ArrayList sets = new ArrayList(); + ArrayList sets = new ArrayList(); LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "sine.txt"), "Sine function"); LineDataSet ds2 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "cosine.txt"), "Cosine function"); @@ -146,21 +139,14 @@ protected LineData generateLineData() { sets.add(ds1); sets.add(ds2); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "n.txt")); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "nlogn.txt")); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "square.txt")); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "three.txt")); - - int max = Math.max(sets.get(0).getEntryCount(), sets.get(1).getEntryCount()); - - LineData d = new LineData(ChartData.generateXVals(0, max), sets); + LineData d = new LineData(sets); d.setValueTypeface(tf); return d; } protected LineData getComplexity() { - ArrayList sets = new ArrayList(); + ArrayList sets = new ArrayList(); LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "n.txt"), "O(n)"); LineDataSet ds2 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "nlogn.txt"), "O(nlogn)"); @@ -178,13 +164,13 @@ protected LineData getComplexity() { ds4.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[3]); ds1.setLineWidth(2.5f); - ds1.setCircleSize(3f); + ds1.setCircleRadius(3f); ds2.setLineWidth(2.5f); - ds2.setCircleSize(3f); + ds2.setCircleRadius(3f); ds3.setLineWidth(2.5f); - ds3.setCircleSize(3f); + ds3.setCircleRadius(3f); ds4.setLineWidth(2.5f); - ds4.setCircleSize(3f); + ds4.setCircleRadius(3f); // load DataSets from textfiles in assets folders @@ -193,7 +179,7 @@ protected LineData getComplexity() { sets.add(ds3); sets.add(ds4); - LineData d = new LineData(ChartData.generateXVals(0, ds1.getEntryCount()), sets); + LineData d = new LineData(sets); d.setValueTypeface(tf); return d; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java index 35f459eff4..60173c368f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java @@ -26,19 +26,14 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View v = inflater.inflate(R.layout.frag_simple_line, container, false); mChart = (LineChart) v.findViewById(R.id.lineChart1); - - mChart.setDescription(""); -// mChart.setCircleSize(5f); - - mChart.setHighlightIndicatorEnabled(false); + + mChart.getDescription().setEnabled(false); + mChart.setDrawGridBackground(false); mChart.setData(generateLineData()); mChart.animateX(3000); -// mChart.setScaleMinima(3f, 3f); -// mChart.centerViewPort(300, 0); - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); Legend l = mChart.getLegend(); @@ -46,9 +41,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); - leftAxis.setStartAtZero(false); - leftAxis.setAxisMaxValue(1.2f); - leftAxis.setAxisMinValue(-1.2f); + leftAxis.setAxisMaximum(1.2f); + leftAxis.setAxisMinimum(-1.2f); mChart.getAxisRight().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java index d886c9d485..cf9b4b553d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java @@ -48,7 +48,7 @@ public View getView(int position, View convertView, Context c) { } // apply styling - holder.chart.setDescription(""); + holder.chart.getDescription().setEnabled(false); holder.chart.setDrawGridBackground(false); holder.chart.setDrawBarShadow(false); @@ -60,18 +60,21 @@ public View getView(int position, View convertView, Context c) { YAxis leftAxis = holder.chart.getAxisLeft(); leftAxis.setTypeface(mTf); - leftAxis.setLabelCount(5); + leftAxis.setLabelCount(5, false); leftAxis.setSpaceTop(20f); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) YAxis rightAxis = holder.chart.getAxisRight(); rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(5); + rightAxis.setLabelCount(5, false); rightAxis.setSpaceTop(20f); + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChartData.setValueTypeface(mTf); // set data holder.chart.setData((BarData) mChartData); + holder.chart.setFitBars(true); // do not forget to refresh the chart // holder.chart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java index 16346251dd..f988844fce 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java @@ -50,7 +50,7 @@ public View getView(int position, View convertView, Context c) { // apply styling // holder.chart.setValueTypeface(mTf); - holder.chart.setDescription(""); + holder.chart.getDescription().setEnabled(false); holder.chart.setDrawGridBackground(false); XAxis xAxis = holder.chart.getXAxis(); @@ -61,12 +61,14 @@ public View getView(int position, View convertView, Context c) { YAxis leftAxis = holder.chart.getAxisLeft(); leftAxis.setTypeface(mTf); - leftAxis.setLabelCount(5); + leftAxis.setLabelCount(5, false); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) YAxis rightAxis = holder.chart.getAxisRight(); rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(5); + rightAxis.setLabelCount(5, false); rightAxis.setDrawGridLines(false); + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) // set data holder.chart.setData((LineData) mChartData); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java index 5578c146d4..db6ba32416 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java @@ -4,6 +4,9 @@ import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; import android.view.LayoutInflater; import android.view.View; @@ -12,17 +15,20 @@ import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.utils.PercentFormatter; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.R; public class PieChartItem extends ChartItem { private Typeface mTf; + private SpannableString mCenterText; public PieChartItem(ChartData cd, Context c) { super(cd); mTf = Typeface.createFromAsset(c.getAssets(), "OpenSans-Regular.ttf"); + mCenterText = generateCenterText(); } @Override @@ -50,13 +56,14 @@ public View getView(int position, View convertView, Context c) { } // apply styling - holder.chart.setDescription(""); + holder.chart.getDescription().setEnabled(false); holder.chart.setHoleRadius(52f); holder.chart.setTransparentCircleRadius(57f); - holder.chart.setCenterText("MPChart\nAndroid"); + holder.chart.setCenterText(mCenterText); holder.chart.setCenterTextTypeface(mTf); - holder.chart.setCenterTextSize(18f); + holder.chart.setCenterTextSize(9f); holder.chart.setUsePercentValues(true); + holder.chart.setExtraOffsets(5, 10, 50, 10); mChartData.setValueFormatter(new PercentFormatter()); mChartData.setValueTypeface(mTf); @@ -66,15 +73,31 @@ public View getView(int position, View convertView, Context c) { holder.chart.setData((PieData) mChartData); Legend l = holder.chart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); + l.setYEntrySpace(0f); + l.setYOffset(0f); // do not forget to refresh the chart // holder.chart.invalidate(); - holder.chart.animateXY(900, 900); + holder.chart.animateY(900); return convertView; } + private SpannableString generateCenterText() { + SpannableString s = new SpannableString("MPAndroidChart\ncreated by\nPhilipp Jahoda"); + s.setSpan(new RelativeSizeSpan(1.6f), 0, 14, 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.VORDIPLOM_COLORS[0]), 0, 14, 0); + s.setSpan(new RelativeSizeSpan(.9f), 14, 25, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, 25, 0); + s.setSpan(new RelativeSizeSpan(1.4f), 25, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), 25, s.length(), 0); + return s; + } + private static class ViewHolder { PieChart chart; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java new file mode 100644 index 0000000000..97bb230ec9 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java @@ -0,0 +1,16 @@ +package com.xxmassdeveloper.mpchartexample.notimportant; + +/** + * Created by Philipp Jahoda on 07/12/15. + */ +public class ContentItem { + + String name; + String desc; + boolean isNew = false; + + public ContentItem(String n, String d) { + name = n; + desc = d; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java index 774aff1f75..59b73b1ad7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java @@ -1,6 +1,10 @@ package com.xxmassdeveloper.mpchartexample.notimportant; +import android.graphics.Typeface; +import android.os.Bundle; +import android.renderscript.Type; +import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import com.xxmassdeveloper.mpchartexample.R; @@ -23,6 +27,21 @@ public abstract class DemoBase extends FragmentActivity { "Party Y", "Party Z" }; + protected Typeface mTfRegular; + protected Typeface mTfLight; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mTfRegular = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + mTfLight = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf"); + } + + protected float getRandom(float range, float startsfrom) { + return (float) (Math.random() * range) + startsfrom; + } + @Override public void onBackPressed() { super.onBackPressed(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index d41335f7ff..617e43c021 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -2,50 +2,53 @@ package com.xxmassdeveloper.mpchartexample.notimportant; import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; import android.widget.ListView; -import android.widget.TextView; import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.AnotherBarActivity; import com.xxmassdeveloper.mpchartexample.BarChartActivity; import com.xxmassdeveloper.mpchartexample.BarChartActivityMultiDataset; +import com.xxmassdeveloper.mpchartexample.BarChartActivitySinus; +import com.xxmassdeveloper.mpchartexample.BarChartPositiveNegative; +import com.xxmassdeveloper.mpchartexample.BubbleChartActivity; import com.xxmassdeveloper.mpchartexample.CandleStickChartActivity; import com.xxmassdeveloper.mpchartexample.CombinedChartActivity; import com.xxmassdeveloper.mpchartexample.CubicLineChartActivity; import com.xxmassdeveloper.mpchartexample.DynamicalAddingActivity; +import com.xxmassdeveloper.mpchartexample.FilledLineActivity; +import com.xxmassdeveloper.mpchartexample.HalfPieChartActivity; import com.xxmassdeveloper.mpchartexample.HorizontalBarChartActivity; import com.xxmassdeveloper.mpchartexample.InvertedLineChartActivity; import com.xxmassdeveloper.mpchartexample.LineChartActivity1; import com.xxmassdeveloper.mpchartexample.LineChartActivity2; import com.xxmassdeveloper.mpchartexample.LineChartActivityColored; +import com.xxmassdeveloper.mpchartexample.LineChartTime; import com.xxmassdeveloper.mpchartexample.ListViewBarChartActivity; import com.xxmassdeveloper.mpchartexample.ListViewMultiChartActivity; import com.xxmassdeveloper.mpchartexample.MultiLineChartActivity; import com.xxmassdeveloper.mpchartexample.PerformanceLineChart; import com.xxmassdeveloper.mpchartexample.PieChartActivity; +import com.xxmassdeveloper.mpchartexample.PiePolylineChartActivity; import com.xxmassdeveloper.mpchartexample.R; import com.xxmassdeveloper.mpchartexample.RadarChartActivitry; import com.xxmassdeveloper.mpchartexample.RealtimeLineChartActivity; import com.xxmassdeveloper.mpchartexample.ScatterChartActivity; +import com.xxmassdeveloper.mpchartexample.ScrollViewActivity; import com.xxmassdeveloper.mpchartexample.StackedBarActivity; +import com.xxmassdeveloper.mpchartexample.StackedBarActivityNegative; import com.xxmassdeveloper.mpchartexample.fragments.SimpleChartDemo; +import com.xxmassdeveloper.mpchartexample.realm.RealmMainActivity; import java.util.ArrayList; -import java.util.List; public class MainActivity extends Activity implements OnItemClickListener { @@ -56,8 +59,10 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_main); + setTitle("MPAndroidChart Example"); + // initialize the utilities - Utils.init(getResources()); + Utils.init(this); ArrayList objects = new ArrayList(); @@ -70,19 +75,21 @@ protected void onCreate(Bundle savedInstanceState) { objects.add(new ContentItem("Combined Chart", "Demonstrates how to create a combined chart (bar and line in this case).")); objects.add(new ContentItem("Pie Chart", "A simple demonstration of the pie chart.")); + objects.add(new ContentItem("Pie Chart with value lines", "A simple demonstration of the pie chart with polyline notes.")); objects.add(new ContentItem("Scatter Chart", "A simple demonstration of the scatter chart.")); + objects.add(new ContentItem("Bubble Chart", "A simple demonstration of the bubble chart.")); objects.add(new ContentItem("Stacked Bar Chart", "A simple demonstration of a bar chart with stacked bars.")); + objects.add(new ContentItem("Stacked Bar Chart Negative", + "A simple demonstration of stacked bars with negative and positive values.")); objects.add(new ContentItem("Another Bar Chart", "Implementation of a BarChart that only shows values at the bottom.")); objects.add(new ContentItem("Multiple Lines Chart", "A line chart with multiple DataSet objects. One color per DataSet.")); objects.add(new ContentItem("Multiple Bars Chart", "A bar chart with multiple DataSet objects. One multiple colors per DataSet.")); - objects.add(new ContentItem("Draw Chart", - "Demonstration of drawing values into the chart per touch-gesture. With callbacks.")); objects.add(new ContentItem( - "Charts in Fragments, awesome design.", + "Charts in ViewPager Fragments", "Demonstration of charts inside ViewPager Fragments. In this example the focus was on the design and look and feel of the chart.")); objects.add(new ContentItem( "BarChart inside ListView", @@ -111,9 +118,35 @@ protected void onCreate(Bundle savedInstanceState) { objects.add(new ContentItem( "Dynamical data adding", "This Activity demonstrates dynamical adding of Entries and DataSets (real time graph).")); -// objects.add(new ContentItem( -// "Performance Line Chart", -// "Renders up to 3000 objects.")); + objects.add(new ContentItem( + "Performance Line Chart", + "Renders up to 30.000 objects smoothly.")); + objects.add(new ContentItem( + "Sinus Bar Chart", + "A Bar Chart plotting the sinus function with 8.000 values.")); + objects.add(new ContentItem( + "Chart in ScrollView", + "This demonstrates how to use a chart inside a ScrollView.")); + objects.add(new ContentItem( + "BarChart positive / negative", + "This demonstrates how to create a BarChart with positive and negative values in different colors.")); + + ContentItem realm = new ContentItem( + "Realm.io Database", + "This demonstrates how to use this library with Realm.io mobile database."); + objects.add(realm); + + ContentItem time = new ContentItem( + "Time Chart", + "Simple demonstration of a time-chart. This chart draws one line entry per hour originating from the current time in milliseconds."); + time.isNew = true; + objects.add(time); + objects.add(new ContentItem( + "Filled LineChart", + "This demonstrates how to fill an area between two LineDataSets.")); + objects.add(new ContentItem( + "Half PieChart", + "This demonstrates how to create a 180 degree PieChart.")); MyAdapter adapter = new MyAdapter(this, objects); @@ -154,79 +187,110 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { startActivity(i); break; case 6: - i = new Intent(this, ScatterChartActivity.class); + i = new Intent(this, PiePolylineChartActivity.class); startActivity(i); break; case 7: - i = new Intent(this, StackedBarActivity.class); + i = new Intent(this, ScatterChartActivity.class); startActivity(i); break; case 8: - i = new Intent(this, AnotherBarActivity.class); + i = new Intent(this, BubbleChartActivity.class); startActivity(i); break; case 9: - i = new Intent(this, MultiLineChartActivity.class); + i = new Intent(this, StackedBarActivity.class); startActivity(i); break; case 10: - i = new Intent(this, BarChartActivityMultiDataset.class); + i = new Intent(this, StackedBarActivityNegative.class); startActivity(i); break; case 11: - // i = new Intent(this, DrawChartActivity.class); - // startActivity(i); - - AlertDialog.Builder b = new AlertDialog.Builder(this); - b.setTitle("Feature not available"); - b.setMessage("Due to recent changes to the data model of the library, this feature is temporarily not available."); - b.setPositiveButton("OK", null); - b.create().show(); + i = new Intent(this, AnotherBarActivity.class); + startActivity(i); break; case 12: - i = new Intent(this, SimpleChartDemo.class); + i = new Intent(this, MultiLineChartActivity.class); startActivity(i); break; case 13: - i = new Intent(this, ListViewBarChartActivity.class); + i = new Intent(this, BarChartActivityMultiDataset.class); startActivity(i); break; case 14: - i = new Intent(this, ListViewMultiChartActivity.class); + i = new Intent(this, SimpleChartDemo.class); startActivity(i); break; case 15: - i = new Intent(this, InvertedLineChartActivity.class); + i = new Intent(this, ListViewBarChartActivity.class); startActivity(i); break; case 16: - i = new Intent(this, CandleStickChartActivity.class); + i = new Intent(this, ListViewMultiChartActivity.class); startActivity(i); break; case 17: - i = new Intent(this, CubicLineChartActivity.class); + i = new Intent(this, InvertedLineChartActivity.class); startActivity(i); break; case 18: - i = new Intent(this, RadarChartActivitry.class); + i = new Intent(this, CandleStickChartActivity.class); startActivity(i); break; case 19: - i = new Intent(this, LineChartActivityColored.class); + i = new Intent(this, CubicLineChartActivity.class); startActivity(i); break; case 20: - i = new Intent(this, RealtimeLineChartActivity.class); + i = new Intent(this, RadarChartActivitry.class); startActivity(i); break; case 21: - i = new Intent(this, DynamicalAddingActivity.class); + i = new Intent(this, LineChartActivityColored.class); startActivity(i); break; case 22: + i = new Intent(this, RealtimeLineChartActivity.class); + startActivity(i); + break; + case 23: + i = new Intent(this, DynamicalAddingActivity.class); + startActivity(i); + break; + case 24: i = new Intent(this, PerformanceLineChart.class); startActivity(i); break; + case 25: + i = new Intent(this, BarChartActivitySinus.class); + startActivity(i); + break; + case 26: + i = new Intent(this, ScrollViewActivity.class); + startActivity(i); + break; + case 27: + i = new Intent(this, BarChartPositiveNegative.class); + startActivity(i); + break; + case 28: + i = new Intent(this, RealmMainActivity.class); + startActivity(i); + break; + case 29: + i = new Intent(this, LineChartTime.class); + startActivity(i); + break; + case 30: + i = new Intent(this, FilledLineActivity.class); + startActivity(i); + break; + case 31: + i = new Intent(this, HalfPieChartActivity.class); + startActivity(i); + break; + } overridePendingTransition(R.anim.move_right_in_activity, R.anim.move_left_out_activity); @@ -270,53 +334,4 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; } - - private class ContentItem { - String name; - String desc; - - public ContentItem(String n, String d) { - name = n; - desc = d; - } - } - - private class MyAdapter extends ArrayAdapter { - - public MyAdapter(Context context, List objects) { - super(context, 0, objects); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - - ContentItem c = getItem(position); - - ViewHolder holder = null; - - if (convertView == null) { - - holder = new ViewHolder(); - - convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, null); - holder.tvName = (TextView) convertView.findViewById(R.id.tvName); - holder.tvDesc = (TextView) convertView.findViewById(R.id.tvDesc); - - convertView.setTag(holder); - - } else { - holder = (ViewHolder) convertView.getTag(); - } - - holder.tvName.setText(c.name); - holder.tvDesc.setText(c.desc); - - return convertView; - } - - private class ViewHolder { - - TextView tvName, tvDesc; - } - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java new file mode 100644 index 0000000000..8395ce2720 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java @@ -0,0 +1,72 @@ +package com.xxmassdeveloper.mpchartexample.notimportant; + +import android.content.Context; +import android.graphics.Typeface; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import com.xxmassdeveloper.mpchartexample.R; + +import java.util.List; + +/** + * Created by Philipp Jahoda on 07/12/15. + */ +public class MyAdapter extends ArrayAdapter { + + private Typeface mTypeFaceLight; + private Typeface mTypeFaceRegular; + + public MyAdapter(Context context, List objects) { + super(context, 0, objects); + + mTypeFaceLight = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + mTypeFaceRegular = Typeface.createFromAsset(context.getAssets(), "OpenSans-Regular.ttf"); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + ContentItem c = getItem(position); + + ViewHolder holder = null; + + if (convertView == null) { + + holder = new ViewHolder(); + + convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, null); + holder.tvName = (TextView) convertView.findViewById(R.id.tvName); + holder.tvDesc = (TextView) convertView.findViewById(R.id.tvDesc); + holder.tvNew = (TextView) convertView.findViewById(R.id.tvNew); + + convertView.setTag(holder); + + } else { + holder = (ViewHolder) convertView.getTag(); + } + + holder.tvNew.setTypeface(mTypeFaceRegular); + holder.tvName.setTypeface(mTypeFaceLight); + holder.tvDesc.setTypeface(mTypeFaceLight); + + holder.tvName.setText(c.name); + holder.tvDesc.setText(c.desc); + + if(c.isNew) + holder.tvNew.setVisibility(View.VISIBLE); + else + holder.tvNew.setVisibility(View.GONE); + + return convertView; + } + + private class ViewHolder { + + TextView tvName, tvDesc; + TextView tvNew; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java new file mode 100644 index 0000000000..9b98f00f92 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -0,0 +1,202 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.graphics.Color; +import android.graphics.Typeface; +import android.os.Bundle; + +import com.github.mikephil.charting.charts.BarLineChartBase; +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import io.realm.Realm; +import io.realm.RealmConfiguration; + +/** + * Created by Philipp Jahoda on 05/11/15. + */ +public abstract class RealmBaseActivity extends DemoBase { + + protected Realm mRealm; + + protected Typeface mTf; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setTitle("Realm.io Examples"); + } + + protected void setup(Chart chart) { + + mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + + // no description text + chart.getDescription().setEnabled(false); + + // enable touch gestures + chart.setTouchEnabled(true); + + if (chart instanceof BarLineChartBase) { + + BarLineChartBase mChart = (BarLineChartBase) chart; + + mChart.setDrawGridBackground(false); + + // enable scaling and dragging + mChart.setDragEnabled(true); + mChart.setScaleEnabled(true); + + // if disabled, scaling can be done on x- and y-axis separately + mChart.setPinchZoom(false); + + YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines + leftAxis.setTypeface(mTf); + leftAxis.setTextSize(8f); + leftAxis.setTextColor(Color.DKGRAY); + leftAxis.setValueFormatter(new PercentFormatter()); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setTypeface(mTf); + xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); + xAxis.setTextSize(8f); + xAxis.setTextColor(Color.DKGRAY); + + mChart.getAxisRight().setEnabled(false); + } + } + + protected void styleData(ChartData data) { + data.setValueTypeface(mTf); + data.setValueTextSize(8f); + data.setValueTextColor(Color.DKGRAY); + data.setValueFormatter(new PercentFormatter()); + } + + @Override + protected void onResume() { + super.onResume(); + + // Create a RealmConfiguration that saves the Realm file in the app's "files" directory. + RealmConfiguration realmConfig = new RealmConfiguration.Builder().build(); + Realm.setDefaultConfiguration(realmConfig); + + mRealm = Realm.getDefaultInstance(); + } + + @Override + protected void onPause() { + super.onPause(); + mRealm.close(); + } + + protected void writeToDB(int objectCount) { + + mRealm.beginTransaction(); + + mRealm.delete(RealmDemoData.class); + + for (int i = 0; i < objectCount; i++) { + + float value = 40f + (float) (Math.random() * 60f); + + RealmDemoData d = new RealmDemoData(i, value); + mRealm.copyToRealm(d); + } + + mRealm.commitTransaction(); + } + + protected void writeToDBStack(int objectCount) { + + mRealm.beginTransaction(); + + mRealm.delete(RealmDemoData.class); + + for (int i = 0; i < objectCount; i++) { + + float val1 = 34f + (float) (Math.random() * 12.0f); + float val2 = 34f + (float) (Math.random() * 12.0f); + float[] stack = new float[]{val1, val2, 100 - val1 - val2}; + + RealmDemoData d = new RealmDemoData(i, stack); + mRealm.copyToRealm(d); + } + + mRealm.commitTransaction(); + } + + protected void writeToDBCandle(int objectCount) { + + mRealm.beginTransaction(); + + mRealm.delete(RealmDemoData.class); + + for (int i = 0; i < objectCount; i++) { + + float mult = 50; + float val = (float) (Math.random() * 40) + mult; + + float high = (float) (Math.random() * 9) + 8f; + float low = (float) (Math.random() * 9) + 8f; + + float open = (float) (Math.random() * 6) + 1f; + float close = (float) (Math.random() * 6) + 1f; + + boolean even = i % 2 == 0; + + RealmDemoData d = new RealmDemoData(i, val + high, val - low, even ? val + open : val - open, + even ? val - close : val + close); + + mRealm.copyToRealm(d); + } + + mRealm.commitTransaction(); + } + + protected void writeToDBBubble(int objectCount) { + + mRealm.beginTransaction(); + + mRealm.delete(RealmDemoData.class); + + for (int i = 0; i < objectCount; i++) { + + float value = 30f + (float) (Math.random() * 100.0); + float size = 15f + (float) (Math.random() * 20.0); + + RealmDemoData d = new RealmDemoData(i, value, size); + mRealm.copyToRealm(d); + } + + mRealm.commitTransaction(); + } + + protected void writeToDBPie() { + + mRealm.beginTransaction(); + + mRealm.delete(RealmDemoData.class); + + float value1 = 15f + (float) (Math.random() * 8f); + float value2 = 15f + (float) (Math.random() * 8f); + float value3 = 15f + (float) (Math.random() * 8f); + float value4 = 15f + (float) (Math.random() * 8f); + float value5 = 100f - value1 - value2 - value3 - value4; + + float[] values = new float[]{value1, value2, value3, value4, value5}; + String[] labels = new String[]{"iOS", "Android", "WP 10", "BlackBerry", "Other"}; + + for (int i = 0; i < values.length; i++) { + RealmDemoData d = new RealmDemoData(values[i], labels[i]); + mRealm.copyToRealm(d); + } + + mRealm.commitTransaction(); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java new file mode 100644 index 0000000000..c87290050d --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -0,0 +1,69 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityBar extends RealmBaseActivity { + + private BarChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart_noseekbar); + + mChart = (BarChart) findViewById(R.id.chart1); + setup(mChart); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDB(20); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries + RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "yValue"); // stacked entries + set.setColors(new int[] {ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); + set.setLabel("Realm BarDataSet"); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + BarData data = new BarData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.setFitBars(true); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java new file mode 100644 index 0000000000..d0aa25b864 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -0,0 +1,71 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.BubbleChart; +import com.github.mikephil.charting.data.BubbleData; +import com.github.mikephil.charting.data.realm.implementation.RealmBubbleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityBubble extends RealmBaseActivity { + + private BubbleChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_bubblechart_noseekbar); + + mChart = (BubbleChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getXAxis().setDrawGridLines(false); + mChart.getAxisLeft().setDrawGridLines(false); + mChart.setPinchZoom(true); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDBBubble(10); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + RealmBubbleDataSet set = new RealmBubbleDataSet(result, "xValue", "yValue", "bubbleSize"); + set.setLabel("Realm BubbleDataSet"); + set.setColors(ColorTemplate.COLORFUL_COLORS, 110); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + BubbleData data = new BubbleData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java new file mode 100644 index 0000000000..a388df3741 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java @@ -0,0 +1,77 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.graphics.Color; +import android.graphics.Paint; +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.CandleStickChart; +import com.github.mikephil.charting.data.CandleData; +import com.github.mikephil.charting.data.realm.implementation.RealmCandleDataSet; +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityCandle extends RealmBaseActivity { + + private CandleStickChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_candlechart_noseekbar); + + mChart = (CandleStickChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getAxisLeft().setDrawGridLines(false); + mChart.getXAxis().setDrawGridLines(false); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDBCandle(50); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + RealmCandleDataSet set = new RealmCandleDataSet(result, "xValue", "high", "low", "open", "close"); + set.setLabel("Realm CandleDataSet"); + set.setShadowColor(Color.DKGRAY); + set.setShadowWidth(0.7f); + set.setDecreasingColor(Color.RED); + set.setDecreasingPaintStyle(Paint.Style.FILL); + set.setIncreasingColor(Color.rgb(122, 242, 84)); + set.setIncreasingPaintStyle(Paint.Style.STROKE); + set.setNeutralColor(Color.BLUE); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + CandleData data = new CandleData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java new file mode 100644 index 0000000000..5fcfa76bff --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java @@ -0,0 +1,74 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityHorizontalBar extends RealmBaseActivity { + + private HorizontalBarChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_horizontalbarchart_noseekbar); + + mChart = (HorizontalBarChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getAxisLeft().setAxisMinimum(0f); + mChart.setDrawValueAboveBar(false); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDBStack(50); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries + RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "stackValues", "floatValue"); // stacked entries + set.setColors(new int[]{ColorTemplate.rgb("#8BC34A"), ColorTemplate.rgb("#FFC107"), ColorTemplate.rgb("#9E9E9E")}); + set.setLabel("Mobile OS distribution"); + set.setStackLabels(new String[]{"iOS", "Android", "Other"}); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + BarData data = new BarData(dataSets); + styleData(data); + data.setValueTextColor(Color.WHITE); + + // set data + mChart.setData(data); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java new file mode 100644 index 0000000000..55f7f6dd4c --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -0,0 +1,78 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityLine extends RealmBaseActivity { + + private LineChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart_noseekbar); + + mChart = (LineChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getAxisLeft().setAxisMaximum(150f); + mChart.getAxisLeft().setAxisMinimum(0f); + mChart.getAxisLeft().setDrawGridLines(false); + mChart.getXAxis().setDrawGridLines(false); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDB(40); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "yValue"); + set.setMode(LineDataSet.Mode.CUBIC_BEZIER); + set.setLabel("Realm LineDataSet"); + set.setDrawCircleHole(false); + set.setColor(ColorTemplate.rgb("#FF5722")); + set.setCircleColor(ColorTemplate.rgb("#FF5722")); + set.setLineWidth(1.8f); + set.setCircleRadius(3.6f); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + LineData data = new LineData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java new file mode 100644 index 0000000000..2936379d55 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -0,0 +1,82 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.graphics.Color; +import android.graphics.Typeface; +import android.os.Bundle; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StyleSpan; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.realm.implementation.RealmPieDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityPie extends RealmBaseActivity { + + private PieChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_piechart_noseekbar); + + mChart = (PieChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.setCenterText(generateCenterSpannableText()); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDBPie(); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "label"); + set.setColors(ColorTemplate.VORDIPLOM_COLORS); + set.setLabel("Example market share"); + set.setSliceSpace(2); + + // create a data object with the dataset list + PieData data = new PieData(set); + styleData(data); + data.setValueTextColor(Color.WHITE); + data.setValueTextSize(12f); + + // set data + mChart.setData(data); + mChart.animateY(1400); + } + + private SpannableString generateCenterSpannableText() { + + SpannableString s = new SpannableString("Realm.io\nmobile database"); + s.setSpan(new ForegroundColorSpan(Color.rgb(240, 115, 126)), 0, 8, 0); + s.setSpan(new RelativeSizeSpan(2.2f), 0, 8, 0); + s.setSpan(new StyleSpan(Typeface.ITALIC), 9, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), 9, s.length(), 0); + s.setSpan(new RelativeSizeSpan(0.85f), 9, s.length(), 0); + return s; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java new file mode 100644 index 0000000000..411f4b6ac9 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -0,0 +1,78 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.data.RadarData; +import com.github.mikephil.charting.data.realm.implementation.RealmRadarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityRadar extends RealmBaseActivity { + + private RadarChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_radarchart_noseekbar); + + mChart = (RadarChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getYAxis().setEnabled(false); + mChart.getXAxis().setEnabled(false); + mChart.setWebAlpha(180); + mChart.setWebColorInner(Color.DKGRAY); + mChart.setWebColor(Color.GRAY); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDB(7); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries + RealmRadarDataSet set = new RealmRadarDataSet(result, "yValue"); // stacked entries + set.setLabel("Realm RadarDataSet"); + set.setDrawFilled(true); + set.setColor(ColorTemplate.rgb("#009688")); + set.setFillColor(ColorTemplate.rgb("#009688")); + set.setFillAlpha(130); + set.setLineWidth(2f); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + RadarData data = new RadarData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.animateY(1400); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java new file mode 100644 index 0000000000..14175ac73a --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -0,0 +1,73 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.ScatterChart; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.data.realm.implementation.RealmScatterDataSet; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityScatter extends RealmBaseActivity { + + private ScatterChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_scatterchart_noseekbar); + + mChart = (ScatterChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getAxisLeft().setDrawGridLines(false); + mChart.getXAxis().setDrawGridLines(false); + mChart.setPinchZoom(true); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDB(45); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + RealmScatterDataSet set = new RealmScatterDataSet(result, "xValue", "yValue"); + set.setLabel("Realm ScatterDataSet"); + set.setScatterShapeSize(9f); + set.setColor(ColorTemplate.rgb("#CDDC39")); + set.setScatterShape(ScatterChart.ScatterShape.CIRCLE); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + ScatterData data = new ScatterData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java new file mode 100644 index 0000000000..07c54ad88c --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java @@ -0,0 +1,133 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.WindowManager; +import android.widget.AdapterView; +import android.widget.ListView; + +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.notimportant.ContentItem; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; +import com.xxmassdeveloper.mpchartexample.notimportant.MyAdapter; + +import java.util.ArrayList; + +import io.realm.Realm; +import io.realm.RealmConfiguration; + +/** + * Created by Philipp Jahoda on 07/12/15. + */ +public class RealmMainActivity extends DemoBase implements AdapterView.OnItemClickListener { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_main); + + setTitle("Realm.io Examples"); + + ArrayList objects = new ArrayList(); + + objects.add(new ContentItem("Line Chart", "Creating a LineChart with Realm.io database")); + objects.add(new ContentItem("Bar Chart", + "Creating a BarChart with Realm.io database")); + objects.add(new ContentItem("Horizontal Bar Chart", + "Creating a HorizontalBarChart with Realm.io database")); + objects.add(new ContentItem("Scatter Chart", + "Creating a ScatterChart with Realm.io database")); + objects.add(new ContentItem("Candle Stick Chart", "Creating a CandleStickChart with Realm.io database")); + objects.add(new ContentItem("Bubble Chart", "Creating a BubbleChart with Realm.io database")); + objects.add(new ContentItem("Pie Chart", "Creating a PieChart with Realm.io database")); + objects.add(new ContentItem("Radar Chart", "Creating a RadarChart with Realm.io database")); + objects.add(new ContentItem("Realm Wiki", "This is the code related to the wiki entry about realm.io on the MPAndroidChart github page.")); + + MyAdapter adapter = new MyAdapter(this, objects); + + ListView lv = (ListView) findViewById(R.id.listView1); + lv.setAdapter(adapter); + + lv.setOnItemClickListener(this); + + Realm.init(this); + + // Create a RealmConfiguration that saves the Realm file in the app's "files" directory. + RealmConfiguration realmConfig = new RealmConfiguration.Builder().build(); + Realm.setDefaultConfiguration(realmConfig); + + Realm realm = Realm.getDefaultInstance(); + realm.beginTransaction(); + realm.deleteAll(); + realm.commitTransaction(); + } + + @Override + public void onItemClick(AdapterView av, View v, int pos, long arg3) { + + Intent i; + + switch (pos) { + case 0: + i = new Intent(this, RealmDatabaseActivityLine.class); + startActivity(i); + break; + case 1: + i = new Intent(this, RealmDatabaseActivityBar.class); + startActivity(i); + break; + case 2: + i = new Intent(this, RealmDatabaseActivityHorizontalBar.class); + startActivity(i); + break; + case 3: + i = new Intent(this, RealmDatabaseActivityScatter.class); + startActivity(i); + break; + case 4: + i = new Intent(this, RealmDatabaseActivityCandle.class); + startActivity(i); + break; + case 5: + i = new Intent(this, RealmDatabaseActivityBubble.class); + startActivity(i); + break; + case 6: + i = new Intent(this, RealmDatabaseActivityPie.class); + startActivity(i); + break; + case 7: + i = new Intent(this, RealmDatabaseActivityRadar.class); + startActivity(i); + break; + case 8: + i = new Intent(this, RealmWikiExample.class); + startActivity(i); + break; + } + + overridePendingTransition(R.anim.move_right_in_activity, R.anim.move_left_out_activity); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.realm, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://realm.io")); + startActivity(i); + + return super.onOptionsItemSelected(item); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java new file mode 100644 index 0000000000..6c1d7cde03 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -0,0 +1,133 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; +import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 18/12/15. + */ +public class RealmWikiExample extends RealmBaseActivity { + + private LineChart lineChart; + private BarChart barChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_realm_wiki); + + lineChart = (LineChart) findViewById(R.id.lineChart); + barChart = (BarChart) findViewById(R.id.barChart); + setup(lineChart); + setup(barChart); + + lineChart.setExtraBottomOffset(5f); + barChart.setExtraBottomOffset(5f); + + lineChart.getAxisLeft().setDrawGridLines(false); + lineChart.getXAxis().setDrawGridLines(false); + lineChart.getXAxis().setLabelCount(5); + lineChart.getXAxis().setGranularity(1f); + barChart.getAxisLeft().setDrawGridLines(false); + barChart.getXAxis().setDrawGridLines(false); + barChart.getXAxis().setLabelCount(5); + barChart.getXAxis().setGranularity(1f); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + mRealm.beginTransaction(); + + // write some demo-data into the realm.io database + Score score1 = new Score(100f, 0f, "Peter"); + mRealm.copyToRealm(score1); + Score score2 = new Score(110f, 1f, "Lisa"); + mRealm.copyToRealm(score2); + Score score3 = new Score(130f, 2f, "Dennis"); + mRealm.copyToRealm(score3); + Score score4 = new Score(70f, 3f, "Luke"); + mRealm.copyToRealm(score4); + Score score5 = new Score(80f, 4f, "Sarah"); + mRealm.copyToRealm(score5); + + mRealm.commitTransaction(); + + // add data to the chart + setData(); + } + + private void setData() { + + // LINE-CHART + final RealmResults results = mRealm.where(Score.class).findAll(); + + + IAxisValueFormatter formatter = new IAxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return results.get((int) value).getPlayerName(); + } + }; + + lineChart.getXAxis().setValueFormatter(formatter); + barChart.getXAxis().setValueFormatter(formatter); + + RealmLineDataSet lineDataSet = new RealmLineDataSet(results, "scoreNr", "totalScore"); + lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER); + lineDataSet.setLabel("Result Scores"); + lineDataSet.setDrawCircleHole(false); + lineDataSet.setColor(ColorTemplate.rgb("#FF5722")); + lineDataSet.setCircleColor(ColorTemplate.rgb("#FF5722")); + lineDataSet.setLineWidth(1.8f); + lineDataSet.setCircleRadius(3.6f); + + ArrayList dataSets = new ArrayList(); + dataSets.add(lineDataSet); + + LineData lineData = new LineData(dataSets); + styleData(lineData); + + // set data + lineChart.setData(lineData); + lineChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + + + // BAR-CHART + RealmBarDataSet barDataSet = new RealmBarDataSet(results, "scoreNr", "totalScore"); + barDataSet.setColors(new int[]{ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); + barDataSet.setLabel("Realm BarDataSet"); + + ArrayList barDataSets = new ArrayList(); + barDataSets.add(barDataSet); + + BarData barData = new BarData(barDataSets); + styleData(barData); + + barChart.setData(barData); + barChart.setFitBars(true); + barChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java new file mode 100644 index 0000000000..870e371491 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java @@ -0,0 +1,51 @@ +package com.xxmassdeveloper.mpchartexample.realm; + + +import io.realm.RealmObject; + +/** + * our data object + */ +public class Score extends RealmObject { + + private float totalScore; + + private float scoreNr; + + private String playerName; + + public Score() { + } + + public Score(float totalScore, float scoreNr, String playerName) { + this.scoreNr = scoreNr; + this.playerName = playerName; + this.totalScore = totalScore; + } + + // all getters and setters... + + public float getTotalScore() { + return totalScore; + } + + public void setTotalScore(float totalScore) { + this.totalScore = totalScore; + } + + public float getScoreNr() { + return scoreNr; + } + + public void setScoreNr(float scoreNr) { + this.scoreNr = scoreNr; + } + + public String getPlayerName() { + return playerName; + } + + public void setPlayerName(String playerName) { + this.playerName = playerName; + } +} \ No newline at end of file diff --git a/MPChartLib/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs b/MPChartLib/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs new file mode 100644 index 0000000000..77dc3a2d85 --- /dev/null +++ b/MPChartLib/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs @@ -0,0 +1,4 @@ +#org.springsource.ide.eclipse.gradle.core.preferences.GradleProjectPreferences +#Mon Jan 18 23:02:46 CET 2016 +org.springsource.ide.eclipse.gradle.linkedresources= +org.springsource.ide.eclipse.gradle.rootprojectloc=.. diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 2b10d4db7c..429fc1df45 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -1,24 +1,17 @@ apply plugin: 'com.android.library' -apply plugin: 'android-maven' +apply plugin: 'maven' +//apply plugin: 'com.github.dcendents.android-maven' +//apply plugin: 'realm-android' android { - compileSdkVersion 21 - buildToolsVersion '21.1.1' + compileSdkVersion 24 + buildToolsVersion '23.0.3' // resourcePrefix 'mpcht' defaultConfig { - minSdkVersion 8 - targetSdkVersion 21 - versionCode 1 - versionName '1.0' - - sourceSets { - main { - java.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - manifest.srcFile 'AndroidManifest.xml' - } - } + minSdkVersion 9 + targetSdkVersion 24 + versionCode 3 + versionName '3.0.1' } buildTypes { release { @@ -29,12 +22,23 @@ android { lintOptions { abortOnError false } + testOptions { + unitTests.returnDefaultValues = true // this prevents "not mocked" error + } +} + +repositories { + maven { + url 'http://oss.jfrog.org/artifactory/oss-snapshot-local' + } } dependencies { //compile fileTree(dir: 'libs', include: ['*.jar']) //compile 'com.android.support:support-v4:19.+' - //compile 'com.nineoldandroids:library:2.4.+' + //provided 'io.realm:realm-android:0.87.5' // "optional" dependency to realm-database API + testCompile 'junit:junit:4.12' + testCompile "org.mockito:mockito-core:1.9.5" } android.libraryVariants.all { variant -> @@ -50,6 +54,19 @@ task sourcesJar(type: Jar) { classifier = 'sources' } +task javadoc(type: Javadoc) { + options.charSet = 'UTF-8' + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + artifacts { archives sourcesJar + archives javadocJar } diff --git a/MPChartLib/project.properties b/MPChartLib/project.properties index 7e2ca64fff..b2ef7dccc5 100644 --- a/MPChartLib/project.properties +++ b/MPChartLib/project.properties @@ -11,5 +11,5 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-20 +target=android-23 android.library=true diff --git a/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java b/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java deleted file mode 100644 index 6f018f11de..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java +++ /dev/null @@ -1,148 +0,0 @@ - -package com.github.mikephil.charting.animation; - -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.SuppressLint; - -/** - * Object responsible for all animations in the Chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.x) AND HIGHER. - * - * @author Philipp Jahoda - */ -@SuppressLint("NewApi") -public class ChartAnimator { - - /** object that is updated upon animation update */ - private AnimatorUpdateListener mListener; - - public ChartAnimator() { - - } - - public ChartAnimator(AnimatorUpdateListener listener) { - mListener = listener; - } - - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW THIS RELATED TO ANIMATION */ - - /** the phase that is animated and influences the drawn values on the y-axis */ - protected float mPhaseY = 1f; - - /** the phase that is animated and influences the drawn values on the x-axis */ - protected float mPhaseX = 1f; - - /** objectanimator used for animating values on y-axis */ - private ObjectAnimator mAnimatorY; - - /** objectanimator used for animating values on x-axis */ - private ObjectAnimator mAnimatorX; - - /** - * Animates the drawing / rendering of the chart on both x- and y-axis with - * the specified animation time. If animate(...) is called, no further - * calling of invalidate() is necessary to refresh the chart. - * - * @param durationMillisX - * @param durationMillisY - */ - public void animateXY(int durationMillisX, int durationMillisY) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - mAnimatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - mAnimatorY.setDuration( - durationMillisY); - mAnimatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - mAnimatorX.setDuration( - durationMillisX); - - // make sure only one animator produces update-callbacks (which then - // call invalidate()) - if (durationMillisX > durationMillisY) { - mAnimatorX.addUpdateListener(mListener); - } else { - mAnimatorY.addUpdateListener(mListener); - } - - mAnimatorX.start(); - mAnimatorY.start(); - } - - /** - * Animates the rendering of the chart on the x-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. - * - * @param durationMillis - */ - public void animateX(int durationMillis) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - mAnimatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - mAnimatorX.setDuration(durationMillis); - mAnimatorX.addUpdateListener(mListener); - mAnimatorX.start(); - } - - /** - * Animates the rendering of the chart on the y-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. - * - * @param durationMillis - */ - public void animateY(int durationMillis) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - mAnimatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - mAnimatorY.setDuration(durationMillis); - mAnimatorY.addUpdateListener(mListener); - mAnimatorY.start(); - } - - /** - * This gets the y-phase that is used to animate the values. - * - * @return - */ - public float getPhaseY() { - return mPhaseY; - } - - /** - * This modifys the y-phase that is used to animate the values. - * - * @param phase - */ - public void setPhaseY(float phase) { - mPhaseY = phase; - } - - /** - * This gets the x-phase that is used to animate the values. - * - * @return - */ - public float getPhaseX() { - return mPhaseX; - } - - /** - * This modifys the x-phase that is used to animate the values. - * - * @param phase - */ - public void setPhaseX(float phase) { - mPhaseX = phase; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java deleted file mode 100644 index f00ea9c26f..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java +++ /dev/null @@ -1,328 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.Log; - -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.BarDataProvider; -import com.github.mikephil.charting.renderer.BarChartRenderer; -import com.github.mikephil.charting.renderer.XAxisRendererBarChart; -import com.github.mikephil.charting.utils.Highlight; - -/** - * Chart that draws bars. - * - * @author Philipp Jahoda - */ -public class BarChart extends BarLineChartBase implements BarDataProvider { - - /** flag that enables or disables the highlighting arrow */ - private boolean mDrawHighlightArrow = false; - - /** - * if set to true, all values are drawn above their bars, instead of below - * their top - */ - private boolean mDrawValueAboveBar = true; - - /** - * if set to true, all values of a stack are drawn individually, and not - * just their sum - */ - private boolean mDrawValuesForWholeStack = true; - - /** - * if set to true, a grey area is darawn behind each bar that indicates the - * maximum value - */ - protected boolean mDrawBarShadow = true; - - public BarChart(Context context) { - super(context); - } - - public BarChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BarChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new BarChartRenderer(this, mAnimator, mViewPortHandler); - mXAxisRenderer = new XAxisRendererBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, - this); - - mXChartMin = -0.5f; - } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - // increase deltax by 1 because the bars have a width of 1 - mDeltaX += 0.5f; - - // extend xDelta to make space for multiple datasets (if ther are one) - mDeltaX *= mData.getDataSetCount(); - - int maxEntry = 0; - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - DataSet set = mData.getDataSetByIndex(i); - - if (maxEntry < set.getEntryCount()) - maxEntry = set.getEntryCount(); - } - - float groupSpace = mData.getGroupSpace(); - mDeltaX += maxEntry * groupSpace; - mXChartMax = mDeltaX - mXChartMin; - } - - /** - * Returns the Highlight object (contains x-index and DataSet index) of the - * selected value at the given touch point inside the BarChart. - * - * @param x - * @param y - * @return - */ - @Override - public Highlight getHighlightByTouchPoint(float x, float y) { - - if (mDataNotSet || mData == null) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); - return null; - } - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; - - mLeftAxisTransformer.pixelsToValue(pts); - - if (pts[0] < mXChartMin || pts[0] > mXChartMax) - return null; - - return getHighlight(pts[0], pts[1]); - } - - /** - * Returns the correct Highlight object (including xIndex and dataSet-index) - * for the specified touch position. - * - * @param xPosition - * @return - */ - protected Highlight getHighlight(double xPosition, double yPosition) { - - int setCount = mData.getDataSetCount(); - int valCount = mData.getXValCount(); - int dataSetIndex = 0; - int xIndex = 0; - - // only one dataset exists - if (!mData.isGrouped()) { - - xIndex = (int) Math.round(xPosition); - - // check bounds - if (xIndex < 0) { - xIndex = 0; - } else if (xIndex >= valCount) { - xIndex = valCount - 1; - } - - // if this bardata is grouped into more datasets - } else { - - // calculate how often the group-space appears - int steps = (int) ((float) xPosition / ((float) setCount + mData.getGroupSpace())); - - float groupSpaceSum = mData.getGroupSpace() * (float) steps; - - float baseNoSpace = (float) xPosition - groupSpaceSum; - - if (mLogEnabled) - Log.i(LOG_TAG, "base: " + xPosition + ", steps: " + steps + ", groupSpaceSum: " - + groupSpaceSum - + ", baseNoSpace: " + baseNoSpace); - - dataSetIndex = (int) baseNoSpace % setCount; - xIndex = (int) baseNoSpace / setCount; - - if (mLogEnabled) - Log.i(LOG_TAG, "xIndex: " + xIndex + ", dataSet: " + dataSetIndex); - - // check bounds - if (xIndex < 0) { - xIndex = 0; - dataSetIndex = 0; - } else if (xIndex >= valCount) { - xIndex = valCount - 1; - dataSetIndex = setCount - 1; - } - - // check bounds - if (dataSetIndex < 0) - dataSetIndex = 0; - else if (dataSetIndex >= setCount) - dataSetIndex = setCount - 1; - } - - if (!mData.getDataSetByIndex(dataSetIndex).isStacked()) - return new Highlight(xIndex, dataSetIndex); - else - return getStackedHighlight(xIndex, dataSetIndex, yPosition); - } - - /** - * This method creates the Highlight object that also indicates which value - * of a stacked BarEntry has been selected. - * - * @param xIndex - * @param dataSet - * @param yValue - * @return - */ - protected Highlight getStackedHighlight(int xIndex, int dataSet, double yValue) { - - BarEntry entry = mData.getDataSetByIndex(dataSet).getEntryForXIndex(xIndex); - - if (entry != null) { - int stackIndex = entry.getClosestIndexAbove((float) yValue); - Highlight h = new Highlight(xIndex, dataSet, stackIndex); - return h; - } else - return null; - } - - /** - * Returns the bounding box of the specified Entry in the specified DataSet. - * Returns null if the Entry could not be found in the charts data. - * - * @param e - * @param dataSetIndex - * @return - */ - public RectF getBarBounds(BarEntry e) { - - BarDataSet set = mData.getDataSetForEntry(e); - - if (set == null) - return null; - - float barspace = set.getBarSpace(); - float y = e.getVal(); - float x = e.getXIndex(); - - float barWidth = 0.5f; - - float spaceHalf = barspace / 2f; - float left = x - barWidth + spaceHalf; - float right = x + barWidth - spaceHalf; - float top = y >= 0 ? y : 0; - float bottom = y <= 0 ? y : 0; - - RectF bounds = new RectF(left, top, right, bottom); - - getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); - - return bounds; - } - - /** - * set this to true to draw the highlightning arrow - * - * @param enabled - */ - public void setDrawHighlightArrow(boolean enabled) { - mDrawHighlightArrow = enabled; - } - - /** - * returns true if drawing the highlighting arrow is enabled, false if not - * - * @return - */ - public boolean isDrawHighlightArrowEnabled() { - return mDrawHighlightArrow; - } - - /** - * If set to true, all values are drawn above their bars, instead of below - * their top. - * - * @param enabled - */ - public void setDrawValueAboveBar(boolean enabled) { - mDrawValueAboveBar = enabled; - } - - /** - * returns true if drawing values above bars is enabled, false if not - * - * @return - */ - public boolean isDrawValueAboveBarEnabled() { - return mDrawValueAboveBar; - } - - /** - * if set to true, all values of a stack are drawn individually, and not - * just their sum - * - * @param enabled - */ - public void setDrawValuesForWholeStack(boolean enabled) { - mDrawValuesForWholeStack = enabled; - } - - /** - * returns true if all values of a stack are drawn, and not just their sum - * - * @return - */ - public boolean isDrawValuesForWholeStackEnabled() { - return mDrawValuesForWholeStack; - } - - /** - * If set to true, a grey area is drawn behind each bar that indicates the - * maximum value. Enabling his will reduce performance by about 50%. - * - * @param enabled - */ - public void setDrawBarShadow(boolean enabled) { - mDrawBarShadow = enabled; - } - - /** - * returns true if drawing shadows (maxvalue) for each bar is enabled, false - * if not - * - * @return - */ - public boolean isDrawBarShadowEnabled() { - return mDrawBarShadow; - } - - @Override - public BarData getBarData() { - return mData; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java deleted file mode 100644 index 4112d449b9..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ /dev/null @@ -1,1261 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.PointF; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; - -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarLineScatterCandleData; -import com.github.mikephil.charting.data.BarLineScatterCandleDataSet; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.listener.BarLineChartTouchListener; -import com.github.mikephil.charting.listener.OnDrawListener; -import com.github.mikephil.charting.renderer.XAxisRenderer; -import com.github.mikephil.charting.renderer.YAxisRenderer; -import com.github.mikephil.charting.utils.FillFormatter; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.PointD; -import com.github.mikephil.charting.utils.SelInfo; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -/** - * Base-class of LineChart, BarChart, ScatterChart and CandleStickChart. - * - * @author Philipp Jahoda - */ -@SuppressLint("RtlHardcoded") -public abstract class BarLineChartBase>> - extends Chart { - - /** the maximum number of entried to which values will be drawn */ - protected int mMaxVisibleCount = 100; - - /** - * flag that indicates if pinch-zoom is enabled. if true, both x and y axis - * can be scaled with 2 fingers, if false, x and y axis can be scaled - * separately - */ - protected boolean mPinchZoomEnabled = false; - - /** flat that indicates if double tap zoom is enabled or not */ - protected boolean mDoubleTapToZoomEnabled = true; - - /** if true, dragging is enabled for the chart */ - private boolean mDragEnabled = true; - - /** if true, scaling is enabled for the chart */ - private boolean mScaleEnabled = true; - - /** if true, data filtering is enabled */ - protected boolean mFilterData = false; - - /** paint object for the (by default) lightgrey background of the grid */ - protected Paint mGridBackgroundPaint; - - /** - * if set to true, the highlight indicator (lines for linechart, dark bar - * for barchart) will be drawn upon selecting values. - */ - protected boolean mHighLightIndicatorEnabled = true; - - /** flag indicating if the grid background should be drawn or not */ - protected boolean mDrawGridBackground = true; - - /** the listener for user drawing on the chart */ - protected OnDrawListener mDrawListener; - - /** - * the object representing the labels on the y-axis, this object is prepared - * in the pepareYLabels() method - */ - protected YAxis mAxisLeft; - protected YAxis mAxisRight; - - /** the object representing the labels on the x-axis */ - protected XAxis mXAxis; - - protected YAxisRenderer mAxisRendererLeft; - protected YAxisRenderer mAxisRendererRight; - - protected Transformer mLeftAxisTransformer; - protected Transformer mRightAxisTransformer; - - protected XAxisRenderer mXAxisRenderer; - - // /** the approximator object used for data filtering */ - // private Approximator mApproximator; - - public BarLineChartBase(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public BarLineChartBase(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BarLineChartBase(Context context) { - super(context); - } - - @Override - protected void init() { - super.init(); - - mAxisLeft = new YAxis(AxisDependency.LEFT); - mAxisRight = new YAxis(AxisDependency.RIGHT); - - mXAxis = new XAxis(); - - mLeftAxisTransformer = new Transformer(mViewPortHandler); - mRightAxisTransformer = new Transformer(mViewPortHandler); - - mAxisRendererLeft = new YAxisRenderer(mViewPortHandler, mAxisLeft, mLeftAxisTransformer); - mAxisRendererRight = new YAxisRenderer(mViewPortHandler, mAxisRight, mRightAxisTransformer); - - mXAxisRenderer = new XAxisRenderer(mViewPortHandler, mXAxis, mLeftAxisTransformer); - - mListener = new BarLineChartTouchListener(this, mViewPortHandler.getMatrixTouch()); - - mGridBackgroundPaint = new Paint(); - mGridBackgroundPaint.setStyle(Style.FILL); - // mGridBackgroundPaint.setColor(Color.WHITE); - mGridBackgroundPaint.setColor(Color.rgb(240, 240, 240)); // light - // grey - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (mDataNotSet) - return; - - long starttime = System.currentTimeMillis(); - - // if data filtering is enabled - if (mFilterData) { - mData = getFilteredData(); - - Log.i(LOG_TAG, "FilterTime: " + (System.currentTimeMillis() - - starttime) + " ms"); - starttime = System.currentTimeMillis(); - } else { - mData = getData(); - // Log.i(LOG_TAG, "Filtering disabled."); - } - - if (mXAxis.isAdjustXLabelsEnabled()) - calcModulus(); - - // execute all drawing commands - drawGridBackground(); - - mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum); - mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum); - - // make sure the graph values and grid cannot be drawn outside the - // content-rect - int clipRestoreCount = mDrawCanvas.save(); - mDrawCanvas.clipRect(mViewPortHandler.getContentRect()); - - mXAxisRenderer.renderGridLines(mDrawCanvas); - mAxisRendererLeft.renderGridLines(mDrawCanvas); - mAxisRendererRight.renderGridLines(mDrawCanvas); - - mAxisRendererLeft.renderLimitLines(mDrawCanvas); - mAxisRendererRight.renderLimitLines(mDrawCanvas); - - mRenderer.drawData(mDrawCanvas); - - // if highlighting is enabled - if (mHighlightEnabled && mHighLightIndicatorEnabled && valuesToHighlight()) - mRenderer.drawHighlighted(mDrawCanvas, mIndicesToHightlight); - - // Removes clipping rectangle - mDrawCanvas.restoreToCount(clipRestoreCount); - - mRenderer.drawExtras(mDrawCanvas); - - mXAxisRenderer.renderAxisLabels(mDrawCanvas); - mXAxisRenderer.renderAxisLine(mDrawCanvas); - - mAxisRendererLeft.renderAxisLabels(mDrawCanvas); - mAxisRendererLeft.renderAxisLine(mDrawCanvas); - - mAxisRendererRight.renderAxisLabels(mDrawCanvas); - mAxisRendererRight.renderAxisLine(mDrawCanvas); - - mRenderer.drawValues(mDrawCanvas); - - mLegendRenderer.renderLegend(mDrawCanvas, mLegend); - // drawLegend(); - - drawMarkers(); - - drawDescription(); - - canvas.drawBitmap(mDrawBitmap, 0, 0, mDrawPaint); - - if (mLogEnabled) { - long drawtime = (System.currentTimeMillis() - starttime); - Log.i(LOG_TAG, "DrawTime: " + drawtime + " ms"); - } - } - - protected void prepareValuePxMatrix() { - - if (mLogEnabled) - Log.i(LOG_TAG, "Preparing Value-Px Matrix, xmin: " + mXChartMin + ", xmax: " - + mXChartMax + ", xdelta: " + mDeltaX); - - mRightAxisTransformer.prepareMatrixValuePx(mXChartMin, mDeltaX, mAxisRight.mAxisRange, - mAxisRight.mAxisMinimum); - mLeftAxisTransformer.prepareMatrixValuePx(mXChartMin, mDeltaX, mAxisLeft.mAxisRange, - mAxisLeft.mAxisMinimum); - } - - protected void prepareOffsetMatrix() { - - mRightAxisTransformer.prepareMatrixOffset(mAxisRight.isInverted()); - mLeftAxisTransformer.prepareMatrixOffset(mAxisLeft.isInverted()); - } - - @Override - public void notifyDataSetChanged() { - - if (mDataNotSet) { - if (mLogEnabled) - Log.i(LOG_TAG, "Preparing... DATA NOT SET."); - return; - } else { - if (mLogEnabled) - Log.i(LOG_TAG, "Preparing..."); - } - - calcMinMax(); - - if (mAxisLeft.needsDefaultFormatter()) - mAxisLeft.setValueFormatter(mDefaultFormatter); - if (mAxisRight.needsDefaultFormatter()) - mAxisRight.setValueFormatter(mDefaultFormatter); - - mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum); - mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum); - - mXAxisRenderer.computeAxis(mData.getXValAverageLength(), mData.getXVals()); - - mLegend = mLegendRenderer.computeLegend(mData, mLegend); - - calculateOffsets(); - } - - @Override - protected void calcMinMax() { - - float minLeft = mData.getYMin(AxisDependency.LEFT); - float maxLeft = mData.getYMax(AxisDependency.LEFT); - float minRight = mData.getYMin(AxisDependency.RIGHT); - float maxRight = mData.getYMax(AxisDependency.RIGHT); - - float leftRange = Math.abs(maxLeft - (mAxisLeft.isStartAtZeroEnabled() ? 0 : minLeft)); - float rightRange = Math.abs(maxRight - (mAxisRight.isStartAtZeroEnabled() ? 0 : minRight)); - - float topSpaceLeft = leftRange / 100f * mAxisLeft.getSpaceTop(); - float topSpaceRight = rightRange / 100f * mAxisRight.getSpaceTop(); - float bottomSpaceLeft = leftRange / 100f * mAxisLeft.getSpaceBottom(); - float bottomSpaceRight = rightRange / 100f * mAxisRight.getSpaceBottom(); - - mXChartMax = mData.getXVals().size() - 1; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - - mAxisLeft.mAxisMaximum = !Float.isNaN(mAxisLeft.getAxisMaxValue()) ? mAxisLeft - .getAxisMaxValue() : maxLeft + topSpaceLeft; - mAxisRight.mAxisMaximum = !Float.isNaN(mAxisRight.getAxisMaxValue()) ? mAxisRight - .getAxisMaxValue() : maxRight + topSpaceRight; - mAxisLeft.mAxisMinimum = !Float.isNaN(mAxisLeft.getAxisMinValue()) ? mAxisLeft - .getAxisMinValue() : minLeft - bottomSpaceLeft; - mAxisRight.mAxisMinimum = !Float.isNaN(mAxisRight.getAxisMinValue()) ? mAxisRight - .getAxisMinValue() : minRight - bottomSpaceRight; - - // consider starting at zero (0) - if (mAxisLeft.isStartAtZeroEnabled()) - mAxisLeft.mAxisMinimum = 0f; - - if (mAxisRight.isStartAtZeroEnabled()) - mAxisRight.mAxisMinimum = 0f; - - mAxisLeft.mAxisRange = Math.abs(mAxisLeft.mAxisMaximum - mAxisLeft.mAxisMinimum); - mAxisRight.mAxisRange = Math.abs(mAxisRight.mAxisMaximum - mAxisRight.mAxisMinimum); - } - - @Override - protected void calculateOffsets() { - - if (!mCustomViewPortEnabled) { - - float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; - - // setup offsets for legend - if (mLegend != null && mLegend.isEnabled()) { - - if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART - || mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART_CENTER) { - - offsetRight += mLegend.mTextWidthMax + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { - - offsetBottom += mLegend.mTextHeightMax * 3f; - } - } - - // offsets for y-labels - if (mAxisLeft.isEnabled()) { - offsetLeft += mAxisLeft.getRequiredWidthSpace(mAxisRendererLeft.getAxisPaint()); - } - - if (mAxisRight.isEnabled()) { - offsetRight += mAxisRight.getRequiredWidthSpace(mAxisRendererRight.getAxisPaint()); - } - - float xlabelheight = mXAxis.mLabelHeight * 2f; - - if (mXAxis.isEnabled()) { - - // offsets for x-labels - if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - offsetBottom += xlabelheight; - - } else if (mXAxis.getPosition() == XAxisPosition.TOP) { - - offsetTop += xlabelheight; - - } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - - offsetBottom += xlabelheight; - offsetTop += xlabelheight; - } - } - - float min = Utils.convertDpToPixel(10f); - - mViewPortHandler.restrainViewPort(Math.max(min, offsetLeft), Math.max(min, offsetTop), - Math.max(min, offsetRight), Math.max(min, offsetBottom)); - - if (mLogEnabled) { - Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop - + ", offsetRight: " + offsetRight + ", offsetBottom: " + offsetBottom); - Log.i(LOG_TAG, "Content: " + mViewPortHandler.getContentRect().toString()); - } - } - - prepareOffsetMatrix(); - prepareValuePxMatrix(); - } - - /** - * calculates the modulus for x-labels and grid - */ - protected void calcModulus() { - - if (mXAxis == null) - return; - - float[] values = new float[9]; - mViewPortHandler.getMatrixTouch().getValues(values); - - mXAxis.mAxisLabelModulus = (int) Math - .ceil((mData.getXValCount() * mXAxis.mLabelWidth) - / (mViewPortHandler.contentWidth() * values[Matrix.MSCALE_X])); - - if (mLogEnabled) - Log.i(LOG_TAG, "X-Axis modulus: " + mXAxis.mAxisLabelModulus + ", x-axis label width: " - + mXAxis.mLabelWidth + ", content width: " + mViewPortHandler.contentWidth()); - } - - @Override - protected float[] getMarkerPosition(Entry e, int dataSetIndex) { - - float xPos = e.getXIndex(); - - if (this instanceof BarChart) { - - BarData bd = (BarData) mData; - float space = bd.getGroupSpace(); - float j = mData.getDataSetByIndex(dataSetIndex) - .getEntryPosition(e); - - float x = (j * (mData.getDataSetCount() - 1)) + dataSetIndex + space * j + space - / 2f; - - xPos += x; - } - - // position of the marker depends on selected value index and value - float[] pts = new float[] { - xPos, e.getVal() * mAnimator.getPhaseY() - }; - - getTransformer(mData.getDataSetByIndex(dataSetIndex).getAxisDependency()) - .pointValuesToPixel(pts); - - return pts; - } - - /** - * draws the grid background - */ - protected void drawGridBackground() { - - if (!mDrawGridBackground) - return; - - // draw the grid background - mDrawCanvas.drawRect(mViewPortHandler.getContentRect(), mGridBackgroundPaint); - } - - /** - * Returns the Transformer class that contains all matrices and is - * responsible for transforming values into pixels on the screen and - * backwards. - * - * @return - */ - public Transformer getTransformer(AxisDependency which) { - if (which == AxisDependency.LEFT) - return mLeftAxisTransformer; - else - return mRightAxisTransformer; - } - - /** touchlistener that handles touches and gestures on the chart */ - protected OnTouchListener mListener; - - @Override - public boolean onTouchEvent(MotionEvent event) { - super.onTouchEvent(event); - - if (mListener == null || mDataNotSet) - return false; - - // check if touch gestures are enabled - if (!mTouchEnabled) - return false; - else - return mListener.onTouch(this, event); - } - - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW THIS RELATED TO SCALING AND GESTURES */ - - /** - * Zooms in by 1.4f, into the charts center. center. - */ - public void zoomIn() { - Matrix save = mViewPortHandler.zoomIn(getWidth() / 2f, -(getHeight() / 2f)); - mViewPortHandler.refresh(save, this, true); - } - - /** - * Zooms out by 0.7f, from the charts center. center. - */ - public void zoomOut() { - Matrix save = mViewPortHandler.zoomOut(getWidth() / 2f, -(getHeight() / 2f)); - mViewPortHandler.refresh(save, this, true); - } - - /** - * Zooms in or out by the given scale factor. x and y are the coordinates - * (in pixels) of the zoom center. - * - * @param scaleX if < 1f --> zoom out, if > 1f --> zoom in - * @param scaleY if < 1f --> zoom out, if > 1f --> zoom in - * @param x - * @param y - */ - public void zoom(float scaleX, float scaleY, float x, float y) { - Matrix save = mViewPortHandler.zoom(scaleX, scaleY, x, -y); - mViewPortHandler.refresh(save, this, true); - } - - /** - * Resets all zooming and dragging and makes the chart fit exactly it's - * bounds. - */ - public void fitScreen() { - Matrix save = mViewPortHandler.fitScreen(); - mViewPortHandler.refresh(save, this, true); - } - - /** - * Sets the size of the area (range on the x-axis) that should be maximum - * visible at once. If this is e.g. set to 10, no more than 10 values on the - * x-axis can be viewed at once without scrolling. - * - * @param xRange - */ - public void setVisibleXRange(float xRange) { - float xScale = mDeltaX / (xRange + 0.01f); - mViewPortHandler.setMinimumScaleX(xScale); - } - - /** - * Sets the size of the area (range on the y-axis) that should be maximum - * visible at once. - * - * @param yRange - * @param axis - the axis for which this limit should apply - */ - public void setVisibleYRange(float yRange, AxisDependency axis) { - float yScale = getDeltaY(axis) / yRange; - mViewPortHandler.setMinimumScaleY(yScale); - } - - /** - * Moves the left side of the current viewport to the specified x-index. - * - * @param xIndex - */ - public void moveViewToX(int xIndex) { - - float[] pts = new float[] { - xIndex, 0f - }; - - getTransformer(AxisDependency.LEFT).pointValuesToPixel(pts); - mViewPortHandler.centerViewPort(pts, this); - } - - /** - * Centers the viewport to the specified y-value on the y-axis. - * - * @param yValue - * @param axis - which axis should be used as a reference for the y-axis - */ - public void moveViewToY(float yValue, AxisDependency axis) { - - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - - float[] pts = new float[] { - 0f, yValue + valsInView / 2f - }; - - getTransformer(axis).pointValuesToPixel(pts); - mViewPortHandler.centerViewPort(pts, this); - } - - /** - * This will move the left side of the current viewport to the specified - * x-index on the x-axis, and center the viewport to the specified y-value - * on the y-axis. - * - * @param xIndex - * @param yValue - * @param axis - which axis should be used as a reference for the y-axis - */ - public void moveViewTo(int xIndex, float yValue, AxisDependency axis) { - - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - - float[] pts = new float[] { - xIndex, yValue + valsInView / 2f - }; - - getTransformer(axis).pointValuesToPixel(pts); - mViewPortHandler.centerViewPort(pts, this); - } - - /** flag that indicates if a custom viewport offset has been set */ - private boolean mCustomViewPortEnabled = false; - - /** - * Sets custom offsets for the current ViewPort (the offsets on the sides of - * the actual chart window). Setting this will prevent the chart from - * automatically calculating it's offsets. Use resetViewPortOffsets() to - * undo this. - * - * @param left - * @param top - * @param right - * @param bottom - */ - public void setViewPortOffsets(final float left, final float top, - final float right, final float bottom) { - - mCustomViewPortEnabled = true; - - post(new Runnable() { - - @Override - public void run() { - mViewPortHandler.restrainViewPort(left, top, right, bottom); - } - }); - } - - /** - * Resets all custom offsets set via setViewPortOffsets(...) method. Allows - * the chart to again calculate all offsets automatically. - */ - public void resetViewPortOffsets() { - mCustomViewPortEnabled = false; - calculateOffsets(); - } - - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW IS GETTERS AND SETTERS */ - - /** - * Returns the delta-y value (y-value range) of the specified axis. - * - * @param axis - * @return - */ - public float getDeltaY(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mAxisLeft.mAxisRange; - else - return mAxisRight.mAxisRange; - } - - /** - * set a new (e.g. custom) charttouchlistener NOTE: make sure to - * setTouchEnabled(true); if you need touch gestures on the chart - * - * @param l - */ - public void setOnTouchListener(OnTouchListener l) { - this.mListener = l; - } - - /** - * Sets the OnDrawListener - * - * @param drawListener - */ - public void setOnDrawListener(OnDrawListener drawListener) { - this.mDrawListener = drawListener; - } - - /** - * Gets the OnDrawListener. May be null. - * - * @return - */ - public OnDrawListener getDrawListener() { - return mDrawListener; - } - - /** - * Returns the position (in pixels) the provided Entry has inside the chart - * view or null, if the provided Entry is null. - * - * @param e - * @return - */ - public PointF getPosition(Entry e, AxisDependency axis) { - - if (e == null) - return null; - - float[] vals = new float[] { - e.getXIndex(), e.getVal() - }; - - getTransformer(axis).pointValuesToPixel(vals); - - return new PointF(vals[0], vals[1]); - } - - /** - * sets the number of maximum visible drawn values on the chart only active - * when setDrawValues() is enabled - * - * @param count - */ - public void setMaxVisibleValueCount(int count) { - this.mMaxVisibleCount = count; - } - - public int getMaxVisibleCount() { - return mMaxVisibleCount; - } - - /** - * If set to true, the highlight indicators (cross of two lines for - * LineChart and ScatterChart, dark bar overlay for BarChart) that give - * visual indication that an Entry has been selected will be drawn upon - * selecting values. This does not depend on the MarkerView. Default: true - * - * @param enabled - */ - public void setHighlightIndicatorEnabled(boolean enabled) { - mHighLightIndicatorEnabled = enabled; - } - - /** - * Sets the color for the background of the chart-drawing area (everything - * behind the grid lines). - * - * @param color - */ - public void setGridBackgroundColor(int color) { - mGridBackgroundPaint.setColor(color); - } - - /** - * Set this to true to enable dragging (moving the chart with the finger) - * for the chart (this does not effect scaling). - * - * @param enabled - */ - public void setDragEnabled(boolean enabled) { - this.mDragEnabled = enabled; - } - - /** - * Returns true if dragging is enabled for the chart, false if not. - * - * @return - */ - public boolean isDragEnabled() { - return mDragEnabled; - } - - /** - * Set this to true to enable scaling (zooming in and out by gesture) for - * the chart (this does not effect dragging). - * - * @param enabled - */ - public void setScaleEnabled(boolean enabled) { - this.mScaleEnabled = enabled; - } - - /** - * Returns true if scaling (zooming in and out by gesture) is enabled for - * the chart, false if not. - * - * @return - */ - public boolean isScaleEnabled() { - return mScaleEnabled; - } - - /** - * Set this to true to enable zooming in by double-tap on the chart. - * Default: enabled - * - * @param enabled - */ - public void setDoubleTapToZoomEnabled(boolean enabled) { - mDoubleTapToZoomEnabled = enabled; - } - - /** - * Returns true if zooming via double-tap is enabled false if not. - * - * @return - */ - public boolean isDoubleTapToZoomEnabled() { - return mDoubleTapToZoomEnabled; - } - - /** - * set this to true to draw the grid background, false if not - * - * @param enabled - */ - public void setDrawGridBackground(boolean enabled) { - mDrawGridBackground = enabled; - } - - /** - * Returns the Highlight object (contains x-index and DataSet index) of the - * selected value at the given touch point inside the Line-, Scatter-, or - * CandleStick-Chart. - * - * @param x - * @param y - * @return - */ - public Highlight getHighlightByTouchPoint(float x, float y) { - - if (mDataNotSet || mData == null) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); - return null; - } - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - - // take any transformer to determine the x-axis value - mLeftAxisTransformer.pixelsToValue(pts); - - double xTouchVal = pts[0]; - double base = Math.floor(xTouchVal); - - double touchOffset = mDeltaX * 0.025; - - // touch out of chart - if (xTouchVal < -touchOffset || xTouchVal > mDeltaX + touchOffset) - return null; - - if (base < 0) - base = 0; - if (base >= mDeltaX) - base = mDeltaX - 1; - - int xIndex = (int) base; - - // check if we are more than half of a x-value or not - if (xTouchVal - base > 0.5) { - xIndex = (int) base + 1; - } - - ArrayList valsAtIndex = getYValsAtIndex(xIndex); - - float leftdist = Utils.getMinimumDistance(valsAtIndex, y, AxisDependency.LEFT); - float rightdist = Utils.getMinimumDistance(valsAtIndex, y, AxisDependency.RIGHT); - - if (mData.getFirstRight() == null) - rightdist = Float.MAX_VALUE; - if (mData.getFirstLeft() == null) - leftdist = Float.MAX_VALUE; - - AxisDependency axis = leftdist < rightdist ? AxisDependency.LEFT : AxisDependency.RIGHT; - - int dataSetIndex = Utils.getClosestDataSetIndex(valsAtIndex, y, axis); - - if (dataSetIndex == -1) - return null; - - return new Highlight(xIndex, dataSetIndex); - } - - /** - * Returns an array of SelInfo objects for the given x-index. The SelInfo - * objects give information about the value at the selected index and the - * DataSet it belongs to. INFORMATION: This method does calculations at - * runtime. Do not over-use in performance critical situations. - * - * @return - */ - public ArrayList getYValsAtIndex(int xIndex) { - - ArrayList vals = new ArrayList(); - - float[] pts = new float[2]; - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - DataSet dataSet = mData.getDataSetByIndex(i); - - // extract all y-values from all DataSets at the given x-index - float yVal = dataSet.getYValForXIndex(xIndex); - pts[1] = yVal; - - getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); - - if (!Float.isNaN(pts[1])) { - vals.add(new SelInfo(pts[1], i, dataSet)); - } - } - - return vals; - } - - /** - * Returns the x and y values in the chart at the given touch point - * (encapsulated in a PointD). This method transforms pixel coordinates to - * coordinates / values in the chart. This is the opposite method to - * getPixelsForValues(...). - * - * @param x - * @param y - * @return - */ - public PointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; - - getTransformer(axis).pixelsToValue(pts); - - double xTouchVal = pts[0]; - double yTouchVal = pts[1]; - - return new PointD(xTouchVal, yTouchVal); - } - - /** - * Transforms the given chart values into pixels. This is the opposite - * method to getValuesByTouchPoint(...). - * - * @param x - * @param y - * @return - */ - public PointD getPixelsForValues(float x, float y, AxisDependency axis) { - - float[] pts = new float[] { - x, y - }; - - getTransformer(axis).pointValuesToPixel(pts); - - return new PointD(pts[0], pts[1]); - } - - /** - * returns the y-value at the given touch position (must not necessarily be - * a value contained in one of the datasets) - * - * @param x - * @param y - * @return - */ - public float getYValueByTouchPoint(float x, float y, AxisDependency axis) { - return (float) getValuesByTouchPoint(x, y, axis).y; - } - - /** - * returns the Entry object displayed at the touched position of the chart - * - * @param x - * @param y - * @return - */ - public Entry getEntryByTouchPoint(float x, float y) { - Highlight h = getHighlightByTouchPoint(x, y); - if (h != null) { - return mData.getEntryForHighlight(h); - } - return null; - } - - /** - * returns the DataSet object displayed at the touched position of the chart - * - * @param x - * @param y - * @return - */ - public BarLineScatterCandleDataSet getDataSetByTouchPoint(float x, float y) { - Highlight h = getHighlightByTouchPoint(x, y); - if (h != null) { - return mData.getDataSetByIndex(h.getDataSetIndex()); - } - return null; - } - - /** - * returns the current x-scale factor - */ - public float getScaleX() { - return mViewPortHandler.getScaleX(); - } - - /** - * returns the current y-scale factor - */ - public float getScaleY() { - return mViewPortHandler.getScaleY(); - } - - /** - * if the chart is fully zoomed out, return true - * - * @return - */ - public boolean isFullyZoomedOut() { - return mViewPortHandler.isFullyZoomedOut(); - } - - /** - * Returns the left y-axis object. In the horizontal bar-chart, this is the - * top axis. - * - * @return - */ - public YAxis getAxisLeft() { - return mAxisLeft; - } - - /** - * Returns the right y-axis object. In the horizontal bar-chart, this is the - * bottom axis. - * - * @return - */ - public YAxis getAxisRight() { - return mAxisRight; - } - - /** - * Returns the y-axis object to the corresponding AxisDependency. In the - * horizontal bar-chart, LEFT == top, RIGHT == BOTTOM - * - * @param axis - * @return - */ - public YAxis getAxis(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mAxisLeft; - else - return mAxisRight; - } - - /** - * Returns the object representing all x-labels, this method can be used to - * acquire the XAxis object and modify it (e.g. change the position of the - * labels) - * - * @return - */ - public XAxis getXAxis() { - return mXAxis; - } - - /** - * Enables data filtering for the chart data, filtering will use the user - * customized Approximator handed over to this method. - * - * @param a - */ - public void enableFiltering(Approximator a) { - mFilterData = true; - // mApproximator = a; - } - - /** - * Disables data filtering for the chart. - */ - public void disableFiltering() { - mFilterData = false; - } - - /** - * returns true if data filtering is enabled, false if not - * - * @return - */ - public boolean isFilteringEnabled() { - return mFilterData; - } - - /** - * if set to true, both x and y axis can be scaled with 2 fingers, if false, - * x and y axis can be scaled separately. default: false - * - * @param enabled - */ - public void setPinchZoom(boolean enabled) { - mPinchZoomEnabled = enabled; - } - - /** - * returns true if pinch-zoom is enabled, false if not - * - * @return - */ - public boolean isPinchZoomEnabled() { - return mPinchZoomEnabled; - } - - /** - * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the x-axis. - * - * @param offset - */ - public void setDragOffsetX(float offset) { - mViewPortHandler.setDragOffsetX(offset); - } - - /** - * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the y-axis. - * - * @param offset - */ - public void setDragOffsetY(float offset) { - mViewPortHandler.setDragOffsetY(offset); - } - - /** - * Returns true if both drag offsets (x and y) are zero or smaller. - * - * @return - */ - public boolean hasNoDragOffset() { - return mViewPortHandler.hasNoDragOffset(); - } - - public XAxisRenderer getRendererXAxis() { - return mXAxisRenderer; - } - - public YAxisRenderer getRendererLeftYAxis() { - return mAxisRendererLeft; - } - - public YAxisRenderer getRendererRightYAxis() { - return mAxisRendererRight; - } - - public float getYChartMax() { - return Math.max(mAxisLeft.mAxisMaximum, mAxisRight.mAxisMaximum); - } - - public float getYChartMin() { - return Math.min(mAxisLeft.mAxisMinimum, mAxisRight.mAxisMinimum); - } - - /** - * Returns true if either the left or the right or both axes are inverted. - * - * @return - */ - public boolean isAnyAxisInverted() { - if (mAxisLeft.isInverted()) - return true; - if (mAxisRight.isInverted()) - return true; - return false; - } - - /** - * returns the filtered ChartData object depending on approximator settings, - * current scale level and x- and y-axis ratio - * - * @return - */ - private T getFilteredData() { - // - // float deltaRatio = mDeltaY / mDeltaX; - // float scaleRatio = mScaleY / mScaleX; - // - // // set the determined ratios - // mApproximator.setRatios(deltaRatio, scaleRatio); - // - // // Log.i("Approximator", "DeltaRatio: " + deltaRatio + - // ", ScaleRatio: " - // // + scaleRatio); - // - // ArrayList dataSets = new ArrayList(); - // - // for (int j = 0; j < mOriginalData.getDataSetCount(); j++) { - // - // DataSet old = mOriginalData.getDataSetByIndex(j); - // - // // do the filtering - // ArrayList approximated = mApproximator.filter(old.getYVals()); - // - // DataSet set = new DataSet(approximated, old.getLabel()); - // dataSets.add(set); - // } - // - // ChartData d = new ChartData(mOriginalData.getXVals(), dataSets); - // return d; - - return null; - } - - @Override - public void setPaint(Paint p, int which) { - super.setPaint(p, which); - - switch (which) { - case PAINT_GRID_BACKGROUND: - mGridBackgroundPaint = p; - break; - } - } - - @Override - public Paint getPaint(int which) { - Paint p = super.getPaint(which); - if (p != null) - return p; - - switch (which) { - case PAINT_GRID_BACKGROUND: - return mGridBackgroundPaint; - } - - return null; - } - - /** - * Default formatter that calculates the position of the filled line. - * - * @author Philipp Jahoda - */ - protected class DefaultFillFormatter implements FillFormatter { - - @Override - public float getFillLinePosition(LineDataSet dataSet, LineData data, - float chartMaxY, float chartMinY) { - - float fillMin = 0f; - - if (dataSet.getYMax() > 0 && dataSet.getYMin() < 0) { - fillMin = 0f; - } else { - - if (!getAxis(dataSet.getAxisDependency()).isStartAtZeroEnabled()) { - - float max, min; - - if (data.getYMax() > 0) - max = 0f; - else - max = chartMaxY; - if (data.getYMin() < 0) - min = 0f; - else - min = chartMinY; - - fillMin = dataSet.getYMin() >= 0 ? min : max; - } else { - fillMin = 0f; - } - - } - - return fillMin; - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java deleted file mode 100644 index eff8b1371f..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ /dev/null @@ -1,1373 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.SuppressLint; -import android.content.ContentValues; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Bitmap.CompressFormat; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Paint.Style; -import android.graphics.PointF; -import android.graphics.RectF; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.os.Environment; -import android.provider.MediaStore.Images; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.MarkerView; -import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.ChartInterface; -import com.github.mikephil.charting.listener.OnChartGestureListener; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.renderer.DataRenderer; -import com.github.mikephil.charting.renderer.LegendRenderer; -import com.github.mikephil.charting.renderer.ViewPortHandler; -import com.github.mikephil.charting.utils.DefaultValueFormatter; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ValueFormatter; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; - -/** - * Baseclass of all Chart-Views. - * - * @author Philipp Jahoda - */ -@SuppressLint("NewApi") -public abstract class Chart>> extends - ViewGroup - implements ChartInterface { - - public static final String LOG_TAG = "MPAndroidChart"; - - /** flag that indicates if logging is enabled or not */ - protected boolean mLogEnabled = false; - - /** - * object that holds all data that was originally set for the chart, before - * it was modified or any filtering algorithms had been applied - */ - protected T mData = null; - - /** default value-formatter, number of digits depends on provided chart-data */ - protected ValueFormatter mDefaultFormatter; - - /** the canvas that is used for drawing on the bitmap */ - protected Canvas mDrawCanvas; - - /** - * paint object used for drawing the description text in the bottom right - * corner of the chart - */ - protected Paint mDescPaint; - - /** - * paint object for drawing the information text when there are no values in - * the chart - */ - protected Paint mInfoPaint; - - /** this is the paint object used for drawing the data onto the chart */ - protected Paint mRenderPaint; - - /** description text that appears in the bottom right corner of the chart */ - protected String mDescription = "Description"; - - /** flag that indicates if the chart has been fed with data yet */ - protected boolean mDataNotSet = true; - - /** if true, units are drawn next to the values in the chart */ - protected boolean mDrawUnitInChart = false; - - /** the number of x-values the chart displays */ - protected float mDeltaX = 1f; - - protected float mXChartMin = 0f; - protected float mXChartMax = 0f; - - /** if true, touch gestures are enabled on the chart */ - protected boolean mTouchEnabled = true; - - /** if true, value highlightning is enabled */ - protected boolean mHighlightEnabled = true; - - /** the legend object containing all data associated with the legend */ - protected Legend mLegend; - - /** listener that is called when a value on the chart is selected */ - protected OnChartValueSelectedListener mSelectionListener; - - /** text that is displayed when the chart is empty */ - private String mNoDataText = "No chart data available."; - - /** - * Gesture listener for custom callbacks when making gestures on the chart. - */ - private OnChartGestureListener mGestureListener; - - /** - * text that is displayed when the chart is empty that describes why the - * chart is empty - */ - private String mNoDataTextDescription; - - protected LegendRenderer mLegendRenderer; - - /** object responsible for rendering the data */ - protected DataRenderer mRenderer; - - /** object that manages the bounds and drawing constraints of the chart */ - protected ViewPortHandler mViewPortHandler; - - /** object responsible for animations */ - protected ChartAnimator mAnimator; - - /** default constructor for initialization in code */ - public Chart(Context context) { - super(context); - init(); - } - - /** constructor for initialization in xml */ - public Chart(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** even more awesome constructor */ - public Chart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * initialize all paints and stuff - */ - protected void init() { - - setWillNotDraw(false); - // setLayerType(View.LAYER_TYPE_SOFTWARE, null); - - if (android.os.Build.VERSION.SDK_INT < 11) - mAnimator = new ChartAnimator(); - else - mAnimator = new ChartAnimator(new AnimatorUpdateListener() { - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - // ViewCompat.postInvalidateOnAnimation(Chart.this); - postInvalidate(); - } - }); - - // initialize the utils - Utils.init(getContext().getResources()); - - mDefaultFormatter = new DefaultValueFormatter(1); - - mViewPortHandler = new ViewPortHandler(); - - mLegendRenderer = new LegendRenderer(mViewPortHandler); - - mRenderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mRenderPaint.setStyle(Style.FILL); - - mDescPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mDescPaint.setColor(Color.BLACK); - mDescPaint.setTextAlign(Align.RIGHT); - mDescPaint.setTextSize(Utils.convertDpToPixel(9f)); - - mInfoPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mInfoPaint.setColor(Color.rgb(247, 189, 51)); // orange - mInfoPaint.setTextAlign(Align.CENTER); - mInfoPaint.setTextSize(Utils.convertDpToPixel(12f)); - - mDrawPaint = new Paint(Paint.DITHER_FLAG); - - if (mLogEnabled) - Log.i("", "Chart.init()"); - } - - // public void initWithDummyData() { - // ColorTemplate template = new ColorTemplate(); - // template.addColorsForDataSets(ColorTemplate.COLORFUL_COLORS, - // getContext()); - // - // setColorTemplate(template); - // setDrawYValues(false); - // - // ArrayList xVals = new ArrayList(); - // Calendar calendar = Calendar.getInstance(); - // for (int i = 0; i < 12; i++) { - // xVals.add(calendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, - // Locale.getDefault())); - // } - // - // ArrayList dataSets = new ArrayList(); - // for (int i = 0; i < 3; i++) { - // - // ArrayList yVals = new ArrayList(); - // - // for (int j = 0; j < 12; j++) { - // float val = (float) (Math.random() * 100); - // yVals.add(new Entry(val, j)); - // } - // - // DataSet set = new DataSet(yVals, "DataSet " + i); - // dataSets.add(set); // add the datasets - // } - // // create a data object with the datasets - // ChartData data = new ChartData(xVals, dataSets); - // setData(data); - // invalidate(); - // } - - /** - * Sets a new data object for the chart. The data object contains all values - * and information needed for displaying. - * - * @param data - */ - public void setData(T data) { - - // if (data == null || !data.isValid()) { - // Log.e(LOG_TAG, - // "Cannot set data for chart. Provided chart values are null or contain less than 1 entry."); - // mDataNotSet = true; - // return; - // } - - if (data == null) { - Log.e(LOG_TAG, - "Cannot set data for chart. Provided data object is null."); - return; - } - - // Log.i(LOG_TAG, "xvalcount: " + data.getXValCount()); - // Log.i(LOG_TAG, "entrycount: " + data.getYValCount()); - - // LET THE CHART KNOW THERE IS DATA - mDataNotSet = false; - mOffsetsCalculated = false; - mData = data; - - // calculate how many digits are needed - calculateFormatter(data.getYMin(), data.getYMax()); - - for (DataSet set : mData.getDataSets()) { - if (set.needsDefaultFormatter()) - set.setValueFormatter(mDefaultFormatter); - } - - // let the chart know there is new data - notifyDataSetChanged(); - - if (mLogEnabled) - Log.i(LOG_TAG, "Data is set."); - } - - /** - * Clears the chart from all data and refreshes it (by calling - * invalidate()). - */ - public void clear() { - mData = null; - mDataNotSet = true; - invalidate(); - } - - /** - * Returns true if the chart is empty (meaning it's data object is either - * null or contains no entries). - * - * @return - */ - public boolean isEmpty() { - - if (mData == null) - return true; - else { - - if (mData.getYValCount() <= 0) - return true; - else - return false; - } - } - - /** - * Lets the chart know its underlying data has changed and performs all - * necessary recalculations. - */ - public abstract void notifyDataSetChanged(); - - /** - * calculates the offsets of the chart to the border depending on the - * position of an eventual legend or depending on the length of the y-axis - * and x-axis labels and their position - */ - protected abstract void calculateOffsets(); - - /** - * calcualtes the y-min and y-max value and the y-delta and x-delta value - */ - protected abstract void calcMinMax(); - - /** - * calculates the required number of digits for the values that might be - * drawn in the chart (if enabled), and creates the default-value-formatter - */ - protected void calculateFormatter(float min, float max) { - - float reference = 0f; - - if (mData == null || mData.getXValCount() < 2) { - - reference = Math.max(Math.abs(min), Math.abs(max)); - } else { - reference = Math.abs(max - min); - } - - int digits = Utils.getDecimals(reference); - mDefaultFormatter = new DefaultValueFormatter(digits); - } - - /** flag that indicates if offsets calculation has already been done or not */ - private boolean mOffsetsCalculated = false; - - /** - * Bitmap object used for drawing. This is necessary because hardware - * acceleration uses OpenGL which only allows a specific texture size to be - * drawn on the canvas directly. - **/ - protected Bitmap mDrawBitmap; - - /** paint object used for drawing the bitmap */ - protected Paint mDrawPaint; - - @Override - protected void onDraw(Canvas canvas) { - // super.onDraw(canvas); - - if (mDataNotSet) { // check if there is data - - // if no data, inform the user - canvas.drawText(mNoDataText, getWidth() / 2, getHeight() / 2, mInfoPaint); - - if (!TextUtils.isEmpty(mNoDataTextDescription)) { - float textOffset = -mInfoPaint.ascent() + mInfoPaint.descent(); - canvas.drawText(mNoDataTextDescription, getWidth() / 2, (getHeight() / 2) - + textOffset, mInfoPaint); - } - return; - } - - if (!mOffsetsCalculated) { - - calculateOffsets(); - mOffsetsCalculated = true; - } - - if (mDrawCanvas == null) { - mDrawCanvas = new Canvas(mDrawBitmap); - } - - // clear everything - mDrawBitmap.eraseColor(Color.TRANSPARENT); - - // mDrawCanvas.drawColor(Color.WHITE); - // canvas.drawColor(Color.TRANSPARENT, - // android.graphics.PorterDuff.Mode.XOR); // clear all - } - - /** - * draws the description text in the bottom right corner of the chart - */ - protected void drawDescription() { - - mDrawCanvas - .drawText(mDescription, getWidth() - mViewPortHandler.offsetRight() - 10, - getHeight() - mViewPortHandler.offsetBottom() - - 10, mDescPaint); - } - - /** - * ################ ################ ################ ################ - */ - /** BELOW THIS CODE FOR HIGHLIGHTING */ - - /** - * array of Highlight objects that reference the highlighted slices in the - * chart - */ - protected Highlight[] mIndicesToHightlight = new Highlight[0]; - - /** - * Returns true if there are values to highlight, false if there are no - * values to highlight. Checks if the highlight array is null, has a length - * of zero or if the first object is null. - * - * @return - */ - public boolean valuesToHighlight() { - return mIndicesToHightlight == null || mIndicesToHightlight.length <= 0 - || mIndicesToHightlight[0] == null ? false - : true; - } - - /** - * Highlights the values at the given indices in the given DataSets. Provide - * null or an empty array to undo all highlighting. This should be used to - * programmatically highlight values. This DOES NOT generate a callback to - * the OnChartValueSelectedListener. - * - * @param highs - */ - public void highlightValues(Highlight[] highs) { - - // set the indices to highlight - mIndicesToHightlight = highs; - - // redraw the chart - invalidate(); - } - - /** - * Highlights the value at the given x-index in the given DataSet. Provide - * -1 as the x-index to undo all highlighting. - * - * @param xIndex - * @param dataSetIndex - */ - public void highlightValue(int xIndex, int dataSetIndex) { - - if (xIndex < 0 || dataSetIndex < 0 || xIndex >= mData.getXValCount() - || dataSetIndex >= mData.getDataSetCount()) { - - highlightValues(null); - } else { - highlightValues(new Highlight[] { - new Highlight(xIndex, dataSetIndex) - }); - } - } - - /** - * Highlights the value selected by touch gesture. Unlike - * highlightValues(...), this generates a callback to the - * OnChartValueSelectedListener. - * - * @param highs - */ - public void highlightTouch(Highlight high) { - - if (high == null) - mIndicesToHightlight = null; - else { - - if (mLogEnabled) - Log.i(LOG_TAG, "Highlighted: " + high.toString()); - - // set the indices to highlight - mIndicesToHightlight = new Highlight[] { - high - }; - } - - // redraw the chart - invalidate(); - - if (mSelectionListener != null) { - - if (!valuesToHighlight()) - mSelectionListener.onNothingSelected(); - else { - - Entry e = mData.getEntryForHighlight(high); - - // notify the listener - mSelectionListener.onValueSelected(e, high.getDataSetIndex(), high); - } - } - } - - /** - * ################ ################ ################ ################ - */ - /** BELOW CODE IS FOR THE MARKER VIEW */ - - /** if set to true, the marker view is drawn when a value is clicked */ - protected boolean mDrawMarkerViews = true; - - /** the view that represents the marker */ - protected MarkerView mMarkerView; - - /** - * draws all MarkerViews on the highlighted positions - */ - protected void drawMarkers() { - - // if there is no marker view or drawing marker is disabled - if (mMarkerView == null || !mDrawMarkerViews || !valuesToHighlight()) - return; - - for (int i = 0; i < mIndicesToHightlight.length; i++) { - - int xIndex = mIndicesToHightlight[i].getXIndex(); - int dataSetIndex = mIndicesToHightlight[i].getDataSetIndex(); - - if (xIndex <= mDeltaX && xIndex <= mDeltaX * mAnimator.getPhaseX()) { - - Entry e = mData.getEntryForHighlight(mIndicesToHightlight[i]); - - // make sure entry not null - if (e == null) - continue; - - float[] pos = getMarkerPosition(e, dataSetIndex); - - // check bounds - if (!mViewPortHandler.isInBounds(pos[0], pos[1])) - continue; - - // callbacks to update the content - mMarkerView.refreshContent(e, dataSetIndex); - - // mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, - // MeasureSpec.UNSPECIFIED), - // MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - // mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), - // mMarkerView.getMeasuredHeight()); - // mMarkerView.draw(mDrawCanvas, pos[0], pos[1]); - - mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), - mMarkerView.getMeasuredHeight()); - - if (pos[1] - mMarkerView.getHeight() <= 0) { - float y = mMarkerView.getHeight() - pos[1]; - mMarkerView.draw(mDrawCanvas, pos[0], pos[1] + y); - } else { - mMarkerView.draw(mDrawCanvas, pos[0], pos[1]); - } - } - } - } - - /** - * Returns the actual position in pixels of the MarkerView for the given - * Entry in the given DataSet. - * - * @param xIndex - * @param dataSetIndex - * @return - */ - protected abstract float[] getMarkerPosition(Entry e, int dataSetIndex); - - /** - * ################ ################ ################ ################ - * ANIMATIONS ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. - */ - /** CODE BELOW THIS RELATED TO ANIMATION */ - - /** - * Returns the animator responsible for animating chart values. - * - * @return - */ - public ChartAnimator getAnimator() { - return mAnimator; - } - - /** - * Animates the drawing / rendering of the chart on both x- and y-axis with - * the specified animation time. If animate(...) is called, no further - * calling of invalidate() is necessary to refresh the chart. ANIMATIONS - * ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. - * - * @param durationMillisX - * @param durationMillisY - */ - public void animateXY(int durationMillisX, int durationMillisY) { - mAnimator.animateXY(durationMillisX, durationMillisY); - } - - /** - * Animates the rendering of the chart on the x-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.x) AND HIGHER. - * - * @param durationMillis - */ - public void animateX(int durationMillis) { - mAnimator.animateX(durationMillis); - } - - /** - * Animates the rendering of the chart on the y-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.x) AND HIGHER. - * - * @param durationMillis - */ - public void animateY(int durationMillis) { - mAnimator.animateY(durationMillis); - } - - /** - * ################ ################ ################ ################ - */ - /** BELOW THIS FOR DYNAMICALLY ADDING ENTRIES AND DATASETS */ - - // public void addEntry(Entry e, int dataSetIndex) { - // mOriginalData.getDataSetByIndex(dataSetIndex).addEntry(e); - // - // prepare(); - // calcMinMax(false); - // prepareMatrix(); - // calculateOffsets(); - // } - // - // public void addEntry(Entry e, String label) { - // mOriginalData.getDataSetByLabel(label, false).addEntry(e); - // - // prepare(); - // calcMinMax(false); - // prepareMatrix(); - // calculateOffsets(); - // } - // - // public void addDataSet(DataSet d) { - // mOriginalData.addDataSet(d); - // - // prepare(); - // calcMinMax(false); - // prepareMatrix(); - // calculateOffsets(); - // } - - /** - * ################ ################ ################ ################ - */ - /** BELOW THIS ONLY GETTERS AND SETTERS */ - - /** - * Returns the canvas object the chart uses for drawing. - * - * @return - */ - public Canvas getCanvas() { - return mDrawCanvas; - } - - /** - * Returns the default ValueFormatter that has been determined by the chart - * considering the provided minimum and maximum values. - * - * @return - */ - public ValueFormatter getDefaultValueFormatter() { - return mDefaultFormatter; - } - - /** - * set a selection listener for the chart - * - * @param l - */ - public void setOnChartValueSelectedListener(OnChartValueSelectedListener l) { - this.mSelectionListener = l; - } - - /** - * Sets a gesture-listener for the chart for custom callbacks when executing - * gestures on the chart surface. - * - * @param l - */ - public void setOnChartGestureListener(OnChartGestureListener l) { - this.mGestureListener = l; - } - - /** - * Returns the custom gesture listener. - * - * @return - */ - public OnChartGestureListener getOnChartGestureListener() { - return mGestureListener; - } - - /** - * If set to true, value highlighting is enabled which means that values can - * be highlighted programmatically or by touch gesture. - * - * @param enabled - */ - public void setHighlightEnabled(boolean enabled) { - mHighlightEnabled = enabled; - } - - /** - * returns true if highlighting of values is enabled, false if not - * - * @return - */ - public boolean isHighlightEnabled() { - return mHighlightEnabled; - } - - /** - * returns the total value (sum) of all y-values across all DataSets - * - * @return - */ - public float getYValueSum() { - return mData.getYValueSum(); - } - - /** - * returns the current y-max value across all DataSets - * - * @return - */ - public float getYMax() { - return mData.getYMax(); - } - - /** - * returns the current y-min value across all DataSets - * - * @return - */ - public float getYMin() { - return mData.getYMin(); - } - - // /** - // * Get the total number of X-values. - // * - // * @return - // */ - // @Override - // public float getDeltaX() { - // return mDeltaX; - // } - - @Override - public float getXChartMax() { - return mXChartMax; - } - - @Override - public float getXChartMin() { - return mXChartMin; - } - - /** - * returns the average value of all values the chart holds - * - * @return - */ - public float getAverage() { - return getYValueSum() / mData.getYValCount(); - } - - /** - * returns the average value for a specific DataSet (with a specific label) - * in the chart - * - * @param dataSetLabel - * @return - */ - public float getAverage(String dataSetLabel) { - - DataSet ds = mData.getDataSetByLabel(dataSetLabel, true); - - return ds.getYValueSum() - / ds.getEntryCount(); - } - - /** - * returns the total number of values the chart holds (across all DataSets) - * - * @return - */ - public int getValueCount() { - return mData.getYValCount(); - } - - /** - * Returns the center point of the chart (the whole View) in pixels. - * - * @return - */ - public PointF getCenter() { - return new PointF(getWidth() / 2f, getHeight() / 2f); - } - - /** - * Returns the center of the chart taking offsets under consideration. - * (returns the center of the content rectangle) - * - * @return - */ - @Override - public PointF getCenterOffsets() { - return mViewPortHandler.getContentCenter(); - } - - /** - * sets the size of the description text in pixels, min 6f, max 16f - * - * @param size - */ - public void setDescriptionTextSize(float size) { - - if (size > 16f) - size = 16f; - if (size < 6f) - size = 6f; - - mDescPaint.setTextSize(Utils.convertDpToPixel(size)); - } - - /** - * Set this to true to enable logcat outputs for the chart. Default: - * disabled - * - * @param enabled - */ - public void setLogEnabled(boolean enabled) { - mLogEnabled = enabled; - } - - /** - * set a description text that appears in the bottom right corner of the - * chart, size = Y-legend text size - * - * @param desc - */ - public void setDescription(String desc) { - if (desc == null) - desc = ""; - this.mDescription = desc; - } - - /** - * Sets the text that informs the user that there is no data available with - * which to draw the chart. - * - * @param text - */ - public void setNoDataText(String text) { - mNoDataText = text; - } - - /** - * Sets descriptive text to explain to the user why there is no chart - * available Defaults to empty if not set - * - * @param text - */ - public void setNoDataTextDescription(String text) { - mNoDataTextDescription = text; - } - - // /** - // * Sets the offsets from the border of the view to the actual chart in - // every - // * direction manually. This method needs to be recalled everytime a new - // data - // * object is set for the chart. Provide density pixels -> they are then - // * rendered to pixels inside the chart. - // * - // * @param left - // * @param right - // * @param top - // * @param bottom - // */ - // public void setOffsets(float left, float top, float right, float bottom) - // { - // - // mOffsetBottom = Utils.convertDpToPixel(bottom); - // mOffsetLeft = Utils.convertDpToPixel(left); - // mOffsetRight = Utils.convertDpToPixel(right); - // mOffsetTop = Utils.convertDpToPixel(top); - // - // mTrans.prepareMatrixValuePx(this); - // mTrans.prepareMatrixOffset(this); - // prepareContentRect(); - // } - - /** - * Set this to false to disable all gestures and touches on the chart, - * default: true - * - * @param enabled - */ - public void setTouchEnabled(boolean enabled) { - this.mTouchEnabled = enabled; - } - - /** - * sets the view that is displayed when a value is clicked on the chart - * - * @param v - */ - public void setMarkerView(MarkerView v) { - mMarkerView = v; - } - - /** - * returns the view that is set as a marker view for the chart - * - * @return - */ - public MarkerView getMarkerView() { - return mMarkerView; - } - - /** - * Returns the Legend object of the chart. This method can be used to - * customize the automatically generated Legend. IMPORTANT: Since the Legend - * is generated from data provided by the user (via setData(...) method), - * this will return NULL if no data has been set for the chart. You need to - * set data for the chart before calling this method. - * - * @return - */ - public Legend getLegend() { - return mLegend; - } - - /** - * Returns the renderer object responsible for rendering / drawing the - * Legend. - * - * @return - */ - public LegendRenderer getLegendRenderer() { - return mLegendRenderer; - } - - /** - * Returns the rectangle that defines the borders of the chart-value surface - * (into which the actual values are drawn). - * - * @return - */ - @Override - public RectF getContentRect() { - return mViewPortHandler.getContentRect(); - } - - /** - * disables intercept touchevents - */ - public void disableScroll() { - ViewParent parent = getParent(); - if (parent != null) - parent.requestDisallowInterceptTouchEvent(true); - } - - /** - * enables intercept touchevents - */ - public void enableScroll() { - ViewParent parent = getParent(); - if (parent != null) - parent.requestDisallowInterceptTouchEvent(false); - } - - /** paint for the grid background (only line and barchart) */ - public static final int PAINT_GRID_BACKGROUND = 4; - - /** - * paint for the info text that is displayed when there are no values in the - * chart - */ - public static final int PAINT_INFO = 7; - - /** paint for the description text in the bottom right corner */ - public static final int PAINT_DESCRIPTION = 11; - - /** paint for the hole in the middle of the pie chart */ - public static final int PAINT_HOLE = 13; - - /** paint for the text in the middle of the pie chart */ - public static final int PAINT_CENTER_TEXT = 14; - - /** paint used for all rendering processes */ - public static final int PAINT_RENDER = 17; - - /** paint used for the legend */ - public static final int PAINT_LEGEND_LABEL = 18; - - /** - * set a new paint object for the specified parameter in the chart e.g. - * Chart.PAINT_VALUES - * - * @param p the new paint object - * @param which Chart.PAINT_VALUES, Chart.PAINT_GRID, Chart.PAINT_VALUES, - * ... - */ - public void setPaint(Paint p, int which) { - - switch (which) { - case PAINT_INFO: - mInfoPaint = p; - break; - case PAINT_DESCRIPTION: - mDescPaint = p; - break; - case PAINT_RENDER: - mRenderPaint = p; - break; - } - } - - /** - * Returns the paint object associated with the provided constant. - * - * @param which e.g. Chart.PAINT_LEGEND_LABEL - * @return - */ - public Paint getPaint(int which) { - switch (which) { - case PAINT_INFO: - return mInfoPaint; - case PAINT_DESCRIPTION: - return mDescPaint; - case PAINT_RENDER: - return mRenderPaint; - } - - return null; - } - - /** - * returns true if drawing the marker-view is enabled when tapping on values - * (use the setMarkerView(View v) method to specify a marker view) - * - * @return - */ - public boolean isDrawMarkerViewEnabled() { - return mDrawMarkerViews; - } - - /** - * Set this to true to draw a user specified marker-view when tapping on - * chart values (use the setMarkerView(MarkerView mv) method to specify a - * marker view). Default: true - * - * @param enabled - */ - public void setDrawMarkerViews(boolean enabled) { - mDrawMarkerViews = enabled; - } - - // /** - // * sets the draw color for the value paint object - // * - // * @param color - // */ - // public void setValueTextColor(int color) { - // mRenderer.getPaintValues().setColor(color); - // } - // - // /** - // * Sets the font size of the values that are drawn inside the chart. - // * - // * @param size - // */ - // public void setValueTextSize(float size) { - // mRenderer.getPaintValues().setTextSize(Utils.convertDpToPixel(size)); - // } - // - // /** - // * sets a typeface for the value-paint - // * - // * @param t - // */ - // public void setValueTypeface(Typeface t) { - // mRenderer.getPaintValues().setTypeface(t); - // } - - /** - * returns the x-value at the given index - * - * @param index - * @return - */ - public String getXValue(int index) { - if (mData == null || mData.getXValCount() <= index) - return null; - else - return mData.getXVals().get(index); - } - - /** - * Get all Entry objects at the given index across all DataSets. - * INFORMATION: This method does calculations at runtime. Do not over-use in - * performance critical situations. - * - * @param xIndex - * @return - */ - public ArrayList getEntriesAtIndex(int xIndex) { - - ArrayList vals = new ArrayList(); - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - DataSet set = mData.getDataSetByIndex(i); - - Entry e = set.getEntryForXIndex(xIndex); - - if (e != null) { - vals.add(e); - } - } - - return vals; - } - - /** - * Returns the ChartData object that has been set for the chart. - * - * @return - */ - public T getData() { - return mData; - } - - /** - * returns the percentage the given value has of the total y-value sum - * - * @param val - * @return - */ - public float getPercentOfTotal(float val) { - return val / mData.getYValueSum() * 100f; - } - - /** - * sets the typeface for the description paint - * - * @param t - */ - public void setDescriptionTypeface(Typeface t) { - mDescPaint.setTypeface(t); - } - - /** - * Returns the ViewPortHandler of the chart that is responsible for the - * content area of the chart and its offsets and dimensions. - * - * @return - */ - public ViewPortHandler getViewPortHandler() { - return mViewPortHandler; - } - - /** - * Returns the Renderer object the chart uses for drawing data. - * - * @return - */ - public DataRenderer getRenderer() { - return mRenderer; - } - - /** - * Returns the bitmap that represents the chart. - * - * @return - */ - public Bitmap getChartBitmap() { - // Define a bitmap with the same size as the view - Bitmap returnedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565); - // Bind a canvas to it - Canvas canvas = new Canvas(returnedBitmap); - // Get the view's background - Drawable bgDrawable = getBackground(); - if (bgDrawable != null) - // has background drawable, then draw it on the canvas - bgDrawable.draw(canvas); - else - // does not have background drawable, then draw white background on - // the canvas - canvas.drawColor(Color.WHITE); - // draw the view on the canvas - draw(canvas); - // return the bitmap - return returnedBitmap; - } - - /** - * Saves the current chart state with the given name to the given path on - * the sdcard leaving the path empty "" will put the saved file directly on - * the SD card chart is saved as a PNG image, example: - * saveToPath("myfilename", "foldername1/foldername2"); - * - * @param title - * @param pathOnSD e.g. "folder1/folder2/folder3" - * @return returns true on success, false on error - */ - public boolean saveToPath(String title, String pathOnSD) { - - Bitmap b = getChartBitmap(); - - OutputStream stream = null; - try { - stream = new FileOutputStream(Environment.getExternalStorageDirectory().getPath() - + pathOnSD + "/" + title - + ".png"); - - /* - * Write bitmap to file using JPEG or PNG and 40% quality hint for - * JPEG. - */ - b.compress(CompressFormat.PNG, 40, stream); - - stream.close(); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - - return true; - } - - /** - * Saves the current state of the chart to the gallery as a JPEG image. The - * filename and compression can be set. 0 == maximum compression, 100 = low - * compression (high quality). NOTE: Needs permission WRITE_EXTERNAL_STORAGE - * - * @param fileName e.g. "my_image" - * @param quality e.g. 50, min = 0, max = 100 - * @return returns true if saving was successfull, false if not - */ - public boolean saveToGallery(String fileName, int quality) { - - // restrain quality - if (quality < 0 || quality > 100) - quality = 50; - - long currentTime = System.currentTimeMillis(); - - File extBaseDir = Environment.getExternalStorageDirectory(); - File file = new File(extBaseDir.getAbsolutePath() + "/DCIM"); - if (!file.exists()) { - if (!file.mkdirs()) { - return false; - } - } - - String filePath = file.getAbsolutePath() + "/" + fileName; - FileOutputStream out = null; - try { - out = new FileOutputStream(filePath); - - Bitmap b = getChartBitmap(); - - b.compress(Bitmap.CompressFormat.JPEG, quality, out); // control - // the jpeg - // quality - - out.flush(); - out.close(); - - } catch (IOException e) { - e.printStackTrace(); - - return false; - } - - long size = new File(filePath).length(); - - ContentValues values = new ContentValues(8); - - // store the details - values.put(Images.Media.TITLE, fileName); - values.put(Images.Media.DISPLAY_NAME, fileName); - values.put(Images.Media.DATE_ADDED, currentTime); - values.put(Images.Media.MIME_TYPE, "image/jpeg"); - values.put(Images.Media.DESCRIPTION, "MPAndroidChart-Library Save"); - values.put(Images.Media.ORIENTATION, 0); - values.put(Images.Media.DATA, filePath); - values.put(Images.Media.SIZE, size); - - return getContext().getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values) == null - ? false : true; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - - // prepareContentRect(); - for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).layout(left, top, right, bottom); - } - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - if (mLogEnabled) - Log.i(LOG_TAG, "OnSizeChanged()"); - - if (w > 0 && h > 0 && w < 10000 && h < 10000) { - // create a new bitmap with the new dimensions - mDrawBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444); - mDrawCanvas = new Canvas(mDrawBitmap); - mViewPortHandler.setChartDimens(w, h); - - if (mLogEnabled) - Log.i(LOG_TAG, "Setting chart dimens, width: " + w + ", height: " + h); - } - - notifyDataSetChanged(); - - super.onSizeChanged(w, h, oldw, oldh); - } - - @Override - public View getChartView() { - return this; - } - - @Override - public PointF getCenterOfView() { - return getCenter(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/CombinedChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/CombinedChart.java deleted file mode 100644 index d617da3a25..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/CombinedChart.java +++ /dev/null @@ -1,132 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.util.AttributeSet; - -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.data.CombinedData; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.interfaces.BarDataProvider; -import com.github.mikephil.charting.interfaces.CandleDataProvider; -import com.github.mikephil.charting.interfaces.LineDataProvider; -import com.github.mikephil.charting.interfaces.ScatterDataProvider; -import com.github.mikephil.charting.renderer.CombinedChartRenderer; -import com.github.mikephil.charting.utils.FillFormatter; - -/** - * This chart class allows the combination of lines, bars, scatter and candle - * data all displayed in one chart area. - * - * @author Philipp Jahoda - */ -public class CombinedChart extends BarLineChartBase implements LineDataProvider, - BarDataProvider, ScatterDataProvider, CandleDataProvider { - - private FillFormatter mFillFormatter; - - public CombinedChart(Context context) { - super(context); - } - - public CombinedChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public CombinedChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mFillFormatter = new DefaultFillFormatter(); - // mRenderer = new CombinedChartRenderer(this, mAnimator, - // mViewPortHandler); - } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (getBarData() != null) { - mXChartMin = -0.5f; - mXChartMax = mData.getXVals().size() - 0.5f; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - } - } - - @Override - public void setData(CombinedData data) { - super.setData(data); - mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler); - } - - public void setFillFormatter(FillFormatter formatter) { - - if (formatter == null) - formatter = new DefaultFillFormatter(); - else - mFillFormatter = formatter; - } - - @Override - public FillFormatter getFillFormatter() { - return mFillFormatter; - } - - @Override - public LineData getLineData() { - if (mData == null) - return null; - return mData.getLineData(); - } - - @Override - public BarData getBarData() { - if (mData == null) - return null; - return mData.getBarData(); - } - - @Override - public ScatterData getScatterData() { - if (mData == null) - return null; - return mData.getScatterData(); - } - - @Override - public CandleData getCandleData() { - if (mData == null) - return null; - return mData.getCandleData(); - } - - @Override - public boolean isDrawBarShadowEnabled() { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean isDrawValueAboveBarEnabled() { - // TODO Auto-generated method stub - return true; - } - - @Override - public boolean isDrawHighlightArrowEnabled() { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean isDrawValuesForWholeStackEnabled() { - // TODO Auto-generated method stub - return false; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java deleted file mode 100644 index ce4c1904ce..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java +++ /dev/null @@ -1,211 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.graphics.Matrix; -import android.graphics.PointF; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.Log; - -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer; -import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart; -import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Utils; - -/** - * BarChart with horizontal bar orientation. In this implementation, x- and - * y-axis are switched. - * - * @author Philipp Jahoda - */ -public class HorizontalBarChart extends BarChart { - - public HorizontalBarChart(Context context) { - super(context); - } - - public HorizontalBarChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public HorizontalBarChart(Context context, AttributeSet attrs, - int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new HorizontalBarChartRenderer(this, mAnimator, mViewPortHandler); - mAxisRendererLeft = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, - mLeftAxisTransformer); - mAxisRendererRight = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, - mRightAxisTransformer); - mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, - mLeftAxisTransformer, this); - } - - @Override - protected void calculateOffsets() { - - float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; - - // setup offsets for legend - if (mLegend != null && mLegend.isEnabled()) { - - if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART - || mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART_CENTER) { - - offsetRight += mLegend.mTextWidthMax + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { - - offsetBottom += mLegend.mTextHeightMax * 3f; - } - } - - // offsets for y-labels - if (mAxisLeft.isEnabled()) { - offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.getAxisPaint()); - } - - if (mAxisRight.isEnabled()) { - offsetBottom += mAxisRight.getRequiredHeightSpace(mAxisRendererRight.getAxisPaint()); - } - - float xlabelwidth = mXAxis.mLabelWidth; - - if (mXAxis.isEnabled()) { - - // offsets for x-labels - if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - offsetLeft += xlabelwidth; - - } else if (mXAxis.getPosition() == XAxisPosition.TOP) { - - offsetRight += xlabelwidth; - - } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - - offsetLeft += xlabelwidth; - offsetRight += xlabelwidth; - } - } - - float min = Utils.convertDpToPixel(10f); - - mViewPortHandler.restrainViewPort(Math.max(min, offsetLeft), Math.max(min, offsetTop), - Math.max(min, offsetRight), Math.max(min, offsetBottom)); - - if (mLogEnabled) { - Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop - + ", offsetRight: " + offsetRight + ", offsetBottom: " + offsetBottom); - Log.i(LOG_TAG, "Content: " + mViewPortHandler.getContentRect().toString()); - } - - prepareOffsetMatrix(); - prepareValuePxMatrix(); - } - - @Override - protected void prepareValuePxMatrix() { - mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, - mDeltaX, - mXChartMin); - mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, - mDeltaX, - mXChartMin); - } - - @Override - protected void calcModulus() { - float[] values = new float[9]; - mViewPortHandler.getMatrixTouch().getValues(values); - - mXAxis.mAxisLabelModulus = (int) Math - .ceil((mData.getXValCount() * mXAxis.mLabelHeight) - / (mViewPortHandler.contentHeight() * values[Matrix.MSCALE_Y])); - } - - @Override - public RectF getBarBounds(BarEntry e) { - - BarDataSet set = mData.getDataSetForEntry(e); - - if (set == null) - return null; - - float barspace = set.getBarSpace(); - float y = e.getVal(); - float x = e.getXIndex(); - - float spaceHalf = barspace / 2f; - - float top = x - 0.5f + spaceHalf; - float bottom = x + 0.5f - spaceHalf; - float left = y >= 0 ? y : 0; - float right = y <= 0 ? y : 0; - - RectF bounds = new RectF(left, top, right, bottom); - - getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); - - return bounds; - } - - @Override - public PointF getPosition(Entry e, AxisDependency axis) { - - if (e == null) - return null; - - float[] vals = new float[] { - e.getVal(), e.getXIndex() - }; - - getTransformer(axis).pointValuesToPixel(vals); - - return new PointF(vals[0], vals[1]); - } - - /** - * Returns the Highlight object (contains x-index and DataSet index) of the - * selected value at the given touch point inside the BarChart. - * - * @param x - * @param y - * @return - */ - @Override - public Highlight getHighlightByTouchPoint(float x, float y) { - - if (mDataNotSet || mData == null) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); - return null; - } - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; - - mLeftAxisTransformer.pixelsToValue(pts); - - if (pts[1] < mXChartMin || pts[1] > mXChartMax) - return null; - - return getHighlight(pts[1], pts[0]); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java deleted file mode 100644 index ee6b58aeca..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java +++ /dev/null @@ -1,95 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.util.AttributeSet; - -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.interfaces.LineDataProvider; -import com.github.mikephil.charting.renderer.LineChartRenderer; -import com.github.mikephil.charting.utils.FillFormatter; - -/** - * Chart that draws lines, surfaces, circles, ... - * - * @author Philipp Jahoda - */ -public class LineChart extends BarLineChartBase implements LineDataProvider { - - /** the width of the highlighning line */ - protected float mHighlightWidth = 3f; - - private FillFormatter mFillFormatter; - - public LineChart(Context context) { - super(context); - } - - public LineChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public LineChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler); - - mFillFormatter = new DefaultFillFormatter(); - } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - // // if there is only one value in the chart - // if (mOriginalData.getYValCount() == 1 - // || mOriginalData.getYValCount() <= mOriginalData.getDataSetCount()) { - // mDeltaX = 1; - // } - - if (mDeltaX == 0 && mData.getYValCount() > 0) - mDeltaX = 1; - } - - /** - * set the width of the highlightning lines, default 3f - * - * @param width - */ - public void setHighlightLineWidth(float width) { - mHighlightWidth = width; - } - - /** - * returns the width of the highlightning line, default 3f - * - * @return - */ - public float getHighlightLineWidth() { - return mHighlightWidth; - } - - @Override - public void setFillFormatter(FillFormatter formatter) { - - if (formatter == null) - formatter = new DefaultFillFormatter(); - else - mFillFormatter = formatter; - } - - @Override - public FillFormatter getFillFormatter() { - return mFillFormatter; - } - - @Override - public LineData getLineData() { - return mData; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java deleted file mode 100644 index 858ac2d984..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java +++ /dev/null @@ -1,494 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.PointF; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.RectF; -import android.graphics.Typeface; -import android.util.AttributeSet; - -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.data.PieDataSet; -import com.github.mikephil.charting.renderer.PieChartRenderer; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -/** - * View that represents a pie chart. Draws cake like slices. - * - * @author Philipp Jahoda - */ -public class PieChart extends PieRadarChartBase { - - /** - * rect object that represents the bounds of the piechart, needed for - * drawing the circle - */ - private RectF mCircleBox = new RectF(); - - /** flag indicating if the x-labels should be drawn or not */ - protected boolean mDrawXLabels = true; - - /** array that holds the width of each pie-slice in degrees */ - private float[] mDrawAngles; - - /** array that holds the absolute angle in degrees of each slice */ - private float[] mAbsoluteAngles; - - /** if true, the white hole inside the chart will be drawn */ - private boolean mDrawHole = true; - - /** if true, the values inside the piechart are drawn as percent values */ - private boolean mUsePercentValues = false; - - /** - * variable for the text that is drawn in the center of the pie-chart. If - * this value is null, the default is "Total Value\n + getYValueSum()" - */ - private String mCenterText = ""; - - /** - * indicates the size of the hole in the center of the piechart, default: - * radius / 2 - */ - private float mHoleRadiusPercent = 50f; - - /** - * the radius of the transparent circle next to the chart-hole in the center - */ - private float mTransparentCircleRadiusPercent = 55f; - - /** if enabled, centertext is drawn */ - private boolean mDrawCenterText = true; - - public PieChart(Context context) { - super(context); - } - - public PieChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public PieChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (mDataNotSet) - return; - - if (mHighlightEnabled && valuesToHighlight()) - mRenderer.drawHighlighted(mDrawCanvas, mIndicesToHightlight); - - mRenderer.drawData(mDrawCanvas); - - mRenderer.drawExtras(mDrawCanvas); - - mRenderer.drawValues(mDrawCanvas); - - mLegendRenderer.renderLegend(mDrawCanvas, mLegend); - - drawDescription(); - - canvas.drawBitmap(mDrawBitmap, 0, 0, mDrawPaint); - } - - @Override - protected void calculateOffsets() { - super.calculateOffsets(); - - // prevent nullpointer when no data set - if (mDataNotSet) - return; - - float diameter = getDiameter(); - float boxSize = diameter / 2f; - - PointF c = getCenterOffsets(); - - // create the circle box that will contain the pie-chart (the bounds of - // the pie-chart) - mCircleBox.set(c.x - boxSize, c.y - boxSize, - c.x + boxSize, c.y + boxSize); - } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - calcAngles(); - } - - /** PieChart does not support MarkerView */ - @Override - protected float[] getMarkerPosition(Entry e, int dataSetIndex) { - return new float[0]; - } - - /** - * calculates the needed angles for the chart slices - */ - private void calcAngles() { - - mDrawAngles = new float[mData.getYValCount()]; - mAbsoluteAngles = new float[mData.getYValCount()]; - - ArrayList dataSets = mData.getDataSets(); - - int cnt = 0; - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - PieDataSet set = dataSets.get(i); - ArrayList entries = set.getYVals(); - - for (int j = 0; j < entries.size(); j++) { - - mDrawAngles[cnt] = calcAngle(Math.abs(entries.get(j).getVal())); - - if (cnt == 0) { - mAbsoluteAngles[cnt] = mDrawAngles[cnt]; - } else { - mAbsoluteAngles[cnt] = mAbsoluteAngles[cnt - 1] + mDrawAngles[cnt]; - } - - cnt++; - } - } - - } - - /** - * checks if the given index in the given DataSet is set for highlighting or - * not - * - * @param xIndex - * @param dataSetIndex - * @return - */ - public boolean needsHighlight(int xIndex, int dataSetIndex) { - - // no highlight - if (!valuesToHighlight() || dataSetIndex < 0) - return false; - - for (int i = 0; i < mIndicesToHightlight.length; i++) - - // check if the xvalue for the given dataset needs highlight - if (mIndicesToHightlight[i].getXIndex() == xIndex - && mIndicesToHightlight[i].getDataSetIndex() == dataSetIndex) - return true; - - return false; - } - - /** - * calculates the needed angle for a given value - * - * @param value - * @return - */ - private float calcAngle(float value) { - return value / mData.getYValueSum() * 360f; - } - - @Override - public int getIndexForAngle(float angle) { - - // take the current angle of the chart into consideration - float a = (angle - mRotationAngle + 360) % 360f; - - for (int i = 0; i < mAbsoluteAngles.length; i++) { - if (mAbsoluteAngles[i] > a) - return i; - } - - return -1; // return -1 if no index found - } - - /** - * Returns the index of the DataSet this x-index belongs to. - * - * @param xIndex - * @return - */ - public int getDataSetIndexForIndex(int xIndex) { - - ArrayList> dataSets = mData.getDataSets(); - - for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i).getEntryForXIndex(xIndex) != null) - return i; - } - - return -1; - } - - /** - * returns an integer array of all the different angles the chart slices - * have the angles in the returned array determine how much space (of 360°) - * each slice takes - * - * @return - */ - public float[] getDrawAngles() { - return mDrawAngles; - } - - /** - * returns the absolute angles of the different chart slices (where the - * slices end) - * - * @return - */ - public float[] getAbsoluteAngles() { - return mAbsoluteAngles; - } - - /** - * Sets the color for the hole that is drawn in the center of the PieChart - * (if enabled). NOTE: Use setHoleColorTransparent(boolean enabled) to make - * the hole transparent. - * - * @param color - */ - public void setHoleColor(int color) { - ((PieChartRenderer) mRenderer).getPaintHole().setXfermode(null); - ((PieChartRenderer) mRenderer).getPaintHole().setColor(color); - } - - /** - * Set the hole in the center of the PieChart transparent. Thank you, code - * provided by: - * - * @link https://github.com/tbarthel-fr - * @param enable - */ - public void setHoleColorTransparent(boolean enable) { - if (enable) { - ((PieChartRenderer) mRenderer).getPaintHole().setXfermode( - new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); - } else { - ((PieChartRenderer) mRenderer).getPaintHole().setXfermode(null); - } - } - - /** - * Returns true if the hole in the center of the PieChart is transparent, - * false if not. - * - * @return true if hole is transparent. - */ - public boolean isHoleTransparent() { - return ((PieChartRenderer) mRenderer).getPaintHole().getXfermode() != null; - } - - /** - * set this to true to draw the pie center empty - * - * @param enabled - */ - public void setDrawHoleEnabled(boolean enabled) { - this.mDrawHole = enabled; - } - - /** - * returns true if the hole in the center of the pie-chart is set to be - * visible, false if not - * - * @return - */ - public boolean isDrawHoleEnabled() { - return mDrawHole; - } - - /** - * sets the text that is displayed in the center of the pie-chart. By - * default, the text is "Total Value + sumofallvalues" - * - * @param text - */ - public void setCenterText(String text) { - mCenterText = text; - } - - /** - * returns the text that is drawn in the center of the pie-chart - * - * @return - */ - public String getCenterText() { - return mCenterText; - } - - /** - * set this to true to draw the text that is displayed in the center of the - * pie chart - * - * @param enabled - */ - public void setDrawCenterText(boolean enabled) { - this.mDrawCenterText = enabled; - } - - /** - * returns true if drawing the center text is enabled - * - * @return - */ - public boolean isDrawCenterTextEnabled() { - return mDrawCenterText; - } - - @Override - protected float getRequiredBottomOffset() { - return mLegendRenderer.getLabelPaint().getTextSize() * 4f; - } - - @Override - protected float getRequiredBaseOffset() { - return 0; - } - - @Override - public float getRadius() { - if (mCircleBox == null) - return 0; - else - return Math.min(mCircleBox.width() / 2f, mCircleBox.height() / 2f); - } - - /** - * returns the circlebox, the boundingbox of the pie-chart slices - * - * @return - */ - public RectF getCircleBox() { - return mCircleBox; - } - - /** - * returns the center of the circlebox - * - * @return - */ - public PointF getCenterCircleBox() { - return new PointF(mCircleBox.centerX(), mCircleBox.centerY()); - } - - /** - * sets the typeface for the center-text paint - * - * @param t - */ - public void setCenterTextTypeface(Typeface t) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setTypeface(t); - } - - /** - * Sets the size of the center text of the PieChart in dp. - * - * @param size - */ - public void setCenterTextSize(float sizeDp) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize( - Utils.convertDpToPixel(sizeDp)); - } - - /** - * Sets the size of the center text of the PieChart in pixels. - * - * @param size - */ - public void setCenterTextSizePixels(float sizePixels) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(sizePixels); - } - - /** - * Sets the color of the center text of the PieChart. - * - * @param color - */ - public void setCenterTextColor(int color) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setColor(color); - } - - /** - * sets the radius of the hole in the center of the piechart in percent of - * the maximum radius (max = the radius of the whole chart), default 50% - * - * @param size - */ - public void setHoleRadius(final float percent) { - mHoleRadiusPercent = percent; - } - - public float getHoleRadius() { - return mHoleRadiusPercent; - } - - /** - * sets the radius of the transparent circle that is drawn next to the hole - * in the piechart in percent of the maximum radius (max = the radius of the - * whole chart), default 55% -> means 5% larger than the center-hole by - * default - * - * @param percent - */ - public void setTransparentCircleRadius(final float percent) { - mTransparentCircleRadiusPercent = percent; - } - - public float getTransparentCircleRadius() { - return mTransparentCircleRadiusPercent; - } - - /** - * set this to true to draw the x-value text into the pie slices - * - * @param enabled - */ - public void setDrawSliceText(boolean enabled) { - mDrawXLabels = enabled; - } - - /** - * returns true if drawing x-values is enabled, false if not - * - * @return - */ - public boolean isDrawSliceTextEnabled() { - return mDrawXLabels; - } - - /** - * If this is enabled, values inside the PieChart are drawn in percent and - * not with their original value. Values provided for the ValueFormatter to - * format are then provided in percent. - * - * @param enabled - */ - public void setUsePercentValues(boolean enabled) { - mUsePercentValues = enabled; - } - - public boolean isUsePercentValuesEnabled() { - return mUsePercentValues; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java deleted file mode 100644 index 0cc0284810..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ /dev/null @@ -1,450 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.PointF; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; - -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.listener.PieRadarChartTouchListener; -import com.github.mikephil.charting.utils.SelInfo; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -/** - * Baseclass of PieChart and RadarChart. - * - * @author Philipp Jahoda - */ -public abstract class PieRadarChartBase>> - extends Chart { - - /** holds the current rotation angle of the chart */ - protected float mRotationAngle = 270f; - - /** flag that indicates if rotation is enabled or not */ - protected boolean mRotateEnabled = true; - - /** the pie- and radarchart touchlistener */ - private OnTouchListener mListener; - - public PieRadarChartBase(Context context) { - super(context); - } - - public PieRadarChartBase(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public PieRadarChartBase(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mListener = new PieRadarChartTouchListener(this); - } - - @Override - protected void calcMinMax() { - mDeltaX = mData.getXVals().size() - 1; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - // use the pie- and radarchart listener own listener - if (mTouchEnabled && mListener != null) - return mListener.onTouch(this, event); - else - return super.onTouchEvent(event); - } - - @Override - public void notifyDataSetChanged() { - if (mDataNotSet) - return; - - calcMinMax(); - - mLegend = mLegendRenderer.computeLegend(mData, mLegend); - - calculateOffsets(); - } - - @Override - protected void calculateOffsets() { - - float legendRight = 0f, legendBottom = 0f, legendTop = 0f; - - if (mLegend != null && mLegend.isEnabled()) { - - if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART_CENTER) { - - // this is the space between the legend and the chart - float spacing = Utils.convertDpToPixel(13f); - - legendRight = getFullLegendWidth() + spacing; - - } else if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART) { - - // this is the space between the legend and the chart - float spacing = Utils.convertDpToPixel(8f); - - float legendWidth = getFullLegendWidth() + spacing; - - float legendHeight = mLegend.mNeededHeight + mLegend.mTextHeightMax; - - PointF c = getCenter(); - - PointF bottomRight = new PointF(getWidth() - legendWidth + 15, legendHeight + 15); - float distLegend = distanceToCenter(bottomRight.x, bottomRight.y); - - PointF reference = getPosition(c, getRadius(), - getAngleForPoint(bottomRight.x, bottomRight.y)); - - float distReference = distanceToCenter(reference.x, reference.y); - float min = Utils.convertDpToPixel(5f); - - if (distLegend < distReference) { - - float diff = distReference - distLegend; - legendRight = min + diff; - } - - if (bottomRight.y >= c.y && getHeight() - legendWidth > getWidth()) { - legendRight = legendWidth; - } - - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { - - legendBottom = getRequiredBottomOffset(); - } - - legendRight += getRequiredBaseOffset(); - legendTop += getRequiredBaseOffset(); - } - - float min = Utils.convertDpToPixel(10f); - - float offsetLeft = Math.max(min, getRequiredBaseOffset()); - float offsetTop = Math.max(min, legendTop); - float offsetRight = Math.max(min, legendRight); - float offsetBottom = Math.max(min, Math.max(getRequiredBaseOffset(), legendBottom)); - - mViewPortHandler.restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom); - - if (mLogEnabled) - Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop - + ", offsetRight: " + offsetRight + ", offsetBottom: " + offsetBottom); - } - - /** the angle where the dragging started */ - private float mStartAngle = 0f; - - /** - * sets the starting angle of the rotation, this is only used by the touch - * listener, x and y is the touch position - * - * @param x - * @param y - */ - public void setStartAngle(float x, float y) { - - mStartAngle = getAngleForPoint(x, y); - - // take the current angle into consideration when starting a new drag - mStartAngle -= mRotationAngle; - } - - /** - * updates the view rotation depending on the given touch position, also - * takes the starting angle into consideration - * - * @param x - * @param y - */ - public void updateRotation(float x, float y) { - - mRotationAngle = getAngleForPoint(x, y); - - // take the offset into consideration - mRotationAngle -= mStartAngle; - - // keep the angle >= 0 and <= 360 - mRotationAngle = (mRotationAngle + 360f) % 360f; - } - - /** - * returns the angle relative to the chart center for the given point on the - * chart in degrees. The angle is always between 0 and 360°, 0° is NORTH, - * 90° is EAST, ... - * - * @param x - * @param y - * @return - */ - public float getAngleForPoint(float x, float y) { - - PointF c = getCenterOffsets(); - - double tx = x - c.x, ty = y - c.y; - double length = Math.sqrt(tx * tx + ty * ty); - double r = Math.acos(ty / length); - - float angle = (float) Math.toDegrees(r); - - if (x > c.x) - angle = 360f - angle; - - // add 90° because chart starts EAST - angle = angle + 90f; - - // neutralize overflow - if (angle > 360f) - angle = angle - 360f; - - return angle; - } - - /** - * Calculates the position around a center point, depending on the distance - * from the center, and the angle of the position around the center. - * - * @param center - * @param dist - * @param angle in degrees, converted to radians internally - * @return - */ - protected PointF getPosition(PointF center, float dist, float angle) { - - PointF p = new PointF((float) (center.x + dist * Math.cos(Math.toRadians(angle))), - (float) (center.y + dist * Math.sin(Math.toRadians(angle)))); - return p; - } - - /** - * Returns the distance of a certain point on the chart to the center of the - * chart. - * - * @param c the center - * @param x - * @param y - * @return - */ - public float distanceToCenter(float x, float y) { - - PointF c = getCenterOffsets(); - - float dist = 0f; - - float xDist = 0f; - float yDist = 0f; - - if (x > c.x) { - xDist = x - c.x; - } else { - xDist = c.x - x; - } - - if (y > c.y) { - yDist = y - c.y; - } else { - yDist = c.y - y; - } - - // pythagoras - dist = (float) Math.sqrt(Math.pow(xDist, 2.0) + Math.pow(yDist, 2.0)); - - return dist; - } - - /** - * Returns the xIndex for the given angle around the center of the chart. - * Returns -1 if not found / outofbounds. - * - * @param angle - * @return - */ - public abstract int getIndexForAngle(float angle); - - /** - * Set an offset for the rotation of the RadarChart in degrees. Default 270f - * --> top (NORTH) - * - * @param angle - */ - public void setRotationAngle(float angle) { - - angle = (int) Math.abs(angle % 360); - mRotationAngle = angle; - } - - /** - * gets the current rotation angle of the pie chart - * - * @return - */ - public float getRotationAngle() { - return mRotationAngle; - } - - /** - * Set this to true to enable the rotation / spinning of the chart by touch. - * Set it to false to disable it. Default: true - * - * @param enabled - */ - public void setRotationEnabled(boolean enabled) { - mRotateEnabled = enabled; - } - - /** - * Returns true if rotation of the chart by touch is enabled, false if not. - * - * @return - */ - public boolean isRotationEnabled() { - return mRotateEnabled; - } - - /** - * returns the diameter of the pie- or radar-chart - * - * @return - */ - public float getDiameter() { - RectF content = mViewPortHandler.getContentRect(); - return Math.min(content.width(), content.height()); - } - - /** - * Returns the radius of the chart in pixels. - * - * @return - */ - public abstract float getRadius(); - - /** - * Returns the required bottom offset for the chart. - * - * @return - */ - protected abstract float getRequiredBottomOffset(); - - /** - * Returns the base offset needed for the chart without calculating the - * legend size. - * - * @return - */ - protected abstract float getRequiredBaseOffset(); - - /** - * Returns the required right offset for the chart. - * - * @return - */ - private float getFullLegendWidth() { - return mLegend.mTextWidthMax + mLegend.getFormSize() + mLegend.getFormToTextSpace(); - } - - /** - * set a new (e.g. custom) charttouchlistener NOTE: make sure to - * setTouchEnabled(true); if you need touch gestures on the chart - * - * @param l - */ - public void setOnTouchListener(OnTouchListener l) { - this.mListener = l; - } - - @Override - public float getYChartMax() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public float getYChartMin() { - // TODO Auto-generated method stub - return 0; - } - - /** - * Returns an array of SelInfo objects for the given x-index. The SelInfo - * objects give information about the value at the selected index and the - * DataSet it belongs to. INFORMATION: This method does calculations at - * runtime. Do not over-use in performance critical situations. - * - * @return - */ - public ArrayList getYValsAtIndex(int xIndex) { - - ArrayList vals = new ArrayList(); - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - DataSet dataSet = mData.getDataSetByIndex(i); - - // extract all y-values from all DataSets at the given x-index - float yVal = dataSet.getYValForXIndex(xIndex); - - if (!Float.isNaN(yVal)) { - vals.add(new SelInfo(yVal, i, dataSet)); - } - } - - return vals; - } - - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW THIS RELATED TO ANIMATION */ - - /** objectanimator used for animating values on y-axis */ - private ObjectAnimator mSpinAnimator; - - /** - * Applys a spin animation to the Chart. - * - * @param durationmillis - * @param fromangle - * @param toangle - */ - @SuppressLint("NewApi") - public void spin(int durationmillis, float fromangle, float toangle) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - mRotationAngle = fromangle; - - mSpinAnimator = ObjectAnimator.ofFloat(this, "rotationAngle", fromangle, toangle); - mSpinAnimator.setDuration(durationmillis); - - mSpinAnimator.addUpdateListener(new AnimatorUpdateListener() { - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - postInvalidate(); - } - }); - mSpinAnimator.start(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java deleted file mode 100644 index 0f5140cf9a..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java +++ /dev/null @@ -1,81 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.util.AttributeSet; - -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.interfaces.ScatterDataProvider; -import com.github.mikephil.charting.renderer.ScatterChartRenderer; - -/** - * The ScatterChart. Draws dots, triangles, squares and custom shapes into the - * chartview. - * - * @author Philipp Jahoda - */ -public class ScatterChart extends BarLineChartBase implements ScatterDataProvider { - - /** enum that defines the shape that is drawn where the values are */ - public enum ScatterShape { - CROSS, TRIANGLE, CIRCLE, SQUARE, CUSTOM - } - - public ScatterChart(Context context) { - super(context); - } - - public ScatterChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ScatterChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); - mXChartMin = -0.5f; - } - -// @Override -// protected void calculateOffsets() { -// super.calculateOffsets(); -// -// float offset = mData.getGreatestShapeSize() / 2f; -// mViewPortHandler.restrainViewPort(mViewPortHandler.offsetLeft() - offset, -// mViewPortHandler.offsetTop(), mViewPortHandler.offsetRight() - offset, -// mViewPortHandler.offsetBottom()); -// -// prepareOffsetMatrix(); -// } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (mDeltaX == 0 && mData.getYValCount() > 0) - mDeltaX = 1; - - mXChartMax += 0.5f; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - } - - /** - * Returns all possible predefined ScatterShapes (excluding CUSTOM). - * - * @return - */ - public static ScatterShape[] getAllPossibleShapes() { - return new ScatterShape[] { - ScatterShape.SQUARE, ScatterShape.CIRCLE, ScatterShape.TRIANGLE, ScatterShape.CROSS - }; - } - - public ScatterData getScatterData() { - return mData; - }; -} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java deleted file mode 100644 index f8027b645e..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java +++ /dev/null @@ -1,177 +0,0 @@ - -package com.github.mikephil.charting.components; - -import android.graphics.Color; - -import com.github.mikephil.charting.utils.Utils; - -/** - * Baseclass of all labels. - * - * @author Philipp Jahoda - */ -public abstract class AxisBase extends ComponentBase { - - private int mGridColor = Color.GRAY; - - private float mGridLineWidth = 1f; - - private int mAxisLineColor = Color.GRAY; - - private float mAxisLineWidth = 1f; - - /** flag indicating if the grid lines for this axis should be drawn */ - protected boolean mDrawGridLines = true; - - /** flag that indicates if the line alongside the axis is drawn or not */ - protected boolean mDrawAxisLine = true; - - /** flag that indicates of the labels of this axis should be drawn or not */ - protected boolean mDrawLabels = true; - - /** default constructor */ - public AxisBase() { - this.mTextSize = Utils.convertDpToPixel(10f); - this.mXOffset = Utils.convertDpToPixel(5f); - this.mYOffset = Utils.convertDpToPixel(5f); - } - - /** - * Set this to true to enable drawing the grid lines for this axis. - * - * @param enabled - */ - public void setDrawGridLines(boolean enabled) { - mDrawGridLines = enabled; - } - - /** - * Returns true if drawing grid lines is enabled for this axis. - * - * @return - */ - public boolean isDrawGridLinesEnabled() { - return mDrawGridLines; - } - - /** - * Set this to true if the line alongside the axis should be drawn or not. - * - * @param enabled - */ - public void setDrawAxisLine(boolean enabled) { - mDrawAxisLine = enabled; - } - - /** - * Returns true if the line alongside the axis should be drawn. - * - * @return - */ - public boolean isDrawAxisLineEnabled() { - return mDrawAxisLine; - } - - /** - * Sets the color of the grid lines for this axis (the horizontal lines - * coming from each label). - * - * @param color - */ - public void setGridColor(int color) { - mGridColor = color; - } - - /** - * Returns the color of the grid lines for this axis (the horizontal lines - * coming from each label). - * - * @return - */ - public int getGridColor() { - return mGridColor; - } - - /** - * Sets the width of the border surrounding the chart in dp. - * - * @param width - */ - public void setAxisLineWidth(float width) { - mAxisLineWidth = Utils.convertDpToPixel(width); - } - - /** - * Returns the width of the axis line (line alongside the axis). - * - * @return - */ - public float getAxisLineWidth() { - return mAxisLineWidth; - } - - /** - * Sets the width of the grid lines that are drawn away from each axis - * label. - * - * @param width - */ - public void setGridLineWidth(float width) { - mGridLineWidth = Utils.convertDpToPixel(width); - } - - /** - * Returns the width of the grid lines that are drawn away from each axis - * label. - * - * @return - */ - public float getGridLineWidth() { - return mGridLineWidth; - } - - /** - * Sets the color of the border surrounding the chart. - * - * @param color - */ - public void setAxisLineColor(int color) { - mAxisLineColor = color; - } - - /** - * Returns the color of the axis line (line alongside the axis). - * - * @return - */ - public int getAxisLineColor() { - return mAxisLineColor; - } - - /** - * Set this to true to enable drawing the labels of this axis (this will not - * affect drawing the grid lines or axis lines). - * - * @param enabled - */ - public void setDrawLabels(boolean enabled) { - mDrawLabels = enabled; - } - - /** - * Returns true if drawing the labels is enabled for this axis. - * - * @return - */ - public boolean isDrawLabelsEnabled() { - return mDrawLabels; - } - - /** - * Returns the longest formatted label (in terms of characters), this axis - * contains. - * - * @return - */ - public abstract String getLongestLabel(); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/com/github/mikephil/charting/components/Legend.java deleted file mode 100644 index 07d426cb47..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/Legend.java +++ /dev/null @@ -1,452 +0,0 @@ - -package com.github.mikephil.charting.components; - -import android.graphics.Paint; - -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -/** - * Class representing the legend of the chart. The legend will contain one entry - * per color and DataSet. Multiple colors in one DataSet are grouped together. - * The legend object is NOT available before setting data to the chart. - * - * @author Philipp Jahoda - */ -public class Legend extends ComponentBase { - - public enum LegendPosition { - RIGHT_OF_CHART, RIGHT_OF_CHART_CENTER, RIGHT_OF_CHART_INSIDE, BELOW_CHART_LEFT, BELOW_CHART_RIGHT, BELOW_CHART_CENTER, PIECHART_CENTER - } - - public enum LegendForm { - SQUARE, CIRCLE, LINE - } - - /** the legend colors */ - private int[] mColors; - - /** the legend labels */ - private String[] mLabels; - - /** the position relative to the chart the legend is drawn on */ - private LegendPosition mPosition = LegendPosition.BELOW_CHART_LEFT; - - /** the shape/form the legend colors are drawn in */ - private LegendForm mShape = LegendForm.SQUARE; - - /** the size of the legend forms/shapes */ - private float mFormSize = 8f; - - /** - * the space between the legend entries on a horizontal axis, default 6f - */ - private float mXEntrySpace = 6f; - - /** - * the space between the legend entries on a vertical axis, default 5f - */ - private float mYEntrySpace = 5f; - - /** - * the space between the legend entries on a vertical axis, default 2f - * private float mYEntrySpace = 2f; /** the space between the form and the - * actual label/text - */ - private float mFormToTextSpace = 5f; - - /** the space that should be left between stacked forms */ - private float mStackSpace = 3f; - - /** default constructor */ - public Legend() { - - mFormSize = Utils.convertDpToPixel(8f); - mXEntrySpace = Utils.convertDpToPixel(6f); - mYEntrySpace = Utils.convertDpToPixel(5f); - mFormToTextSpace = Utils.convertDpToPixel(5f); - mTextSize = Utils.convertDpToPixel(10f); - mStackSpace = Utils.convertDpToPixel(3f); - this.mXOffset = Utils.convertDpToPixel(5f); - this.mYOffset = Utils.convertDpToPixel(6f); - } - - /** - * Constructor. Provide colors and labels for the legend. - * - * @param colors - * @param labels - */ - public Legend(int[] colors, String[] labels) { - this(); - - if (colors == null || labels == null) { - throw new IllegalArgumentException("colors array or labels array is NULL"); - } - - if (colors.length != labels.length) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); - } - - this.mColors = colors; - this.mLabels = labels; - } - - /** - * Constructor. Provide colors and labels for the legend. - * - * @param colors - * @param labels - */ - public Legend(ArrayList colors, ArrayList labels) { - this(); - - if (colors == null || labels == null) { - throw new IllegalArgumentException("colors array or labels array is NULL"); - } - - if (colors.size() != labels.size()) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); - } - - this.mColors = Utils.convertIntegers(colors); - this.mLabels = Utils.convertStrings(labels); - } - - /** - * returns the maximum length in pixels across all legend labels + formsize - * + formtotextspace - * - * @param p the paint object used for rendering the text - * @return - */ - public float getMaximumEntryWidth(Paint p) { - - float max = 0f; - - for (int i = 0; i < mLabels.length; i++) { - - if (mLabels[i] != null) { - - float length = (float) Utils.calcTextWidth(p, mLabels[i]); - - if (length > max) - max = length; - } - } - - return max + mFormSize + mFormToTextSpace; - } - - /** - * returns the maximum height in pixels across all legend labels - * - * @param p the paint object used for rendering the text - * @return - */ - public float getMaximumEntryHeight(Paint p) { - - float max = 0f; - - for (int i = 0; i < mLabels.length; i++) { - - if (mLabels[i] != null) { - - float length = (float) Utils.calcTextHeight(p, mLabels[i]); - - if (length > max) - max = length; - } - } - - return max; - } - - /** - * returns all the colors the legend uses - * - * @return - */ - public int[] getColors() { - return mColors; - } - - /** - * returns all the labels the legend uses - * - * @return - */ - public String[] getLegendLabels() { - return mLabels; - } - - /** - * Sets a custom array of labels for the legend. Make sure the labels array - * has the same length as the colors array. - * - * @param labels - */ - public void setLegendLabels(String[] labels) { - - if (mColors.length != labels.length) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); - } - - this.mLabels = labels; - } - - /** - * Returns the legend-label at the given index. - * - * @param index - * @return - */ - public String getLabel(int index) { - return mLabels[index]; - } - - /** - * returns the position of the legend relative to the chart - * - * @return - */ - public LegendPosition getPosition() { - return mPosition; - } - - /** - * sets the position of the legend relative to the whole chart - * - * @param pos - */ - public void setPosition(LegendPosition pos) { - mPosition = pos; - } - - /** - * returns the current form/shape that is set for the legend - * - * @return - */ - public LegendForm getForm() { - return mShape; - } - - /** - * sets the form/shape of the legend forms - * - * @param shape - */ - public void setForm(LegendForm shape) { - mShape = shape; - } - - /** - * sets the size in pixels of the legend forms, this is internally converted - * in dp, default 8f - * - * @param size - */ - public void setFormSize(float size) { - mFormSize = Utils.convertDpToPixel(size); - } - - /** - * returns the size in dp of the legend forms - * - * @return - */ - public float getFormSize() { - return mFormSize; - } - - /** - * returns the space between the legend entries on a horizontal axis in - * pixels - * - * @return - */ - public float getXEntrySpace() { - return mXEntrySpace; - } - - /** - * sets the space between the legend entries on a horizontal axis in pixels, - * converts to dp internally - * - * @param space - */ - public void setXEntrySpace(float space) { - mXEntrySpace = Utils.convertDpToPixel(space); - } - - /** - * returns the space between the legend entries on a vertical axis in pixels - * - * @return - */ - public float getYEntrySpace() { - return mYEntrySpace; - } - - /** - * sets the space between the legend entries on a vertical axis in pixels, - * converts to dp internally - * - * @param space - */ - public void setYEntrySpace(float space) { - mYEntrySpace = Utils.convertDpToPixel(space); - } - - /** - * returns the space between the form and the actual label/text - * - * @return - */ - public float getFormToTextSpace() { - return mFormToTextSpace; - } - - /** - * sets the space between the form and the actual label/text, converts to dp - * internally - * - * @param mFormToTextSpace - */ - public void setFormToTextSpace(float space) { - this.mFormToTextSpace = Utils.convertDpToPixel(space); - } - - /** - * applies the state from the legend in the parameter to this legend (except - * colors, labels and offsets) - * - * @param l - */ - public void apply(Legend l) { - - mPosition = l.mPosition; - mShape = l.mShape; - mTypeface = l.mTypeface; - mFormSize = l.mFormSize; - mXEntrySpace = l.mXEntrySpace; - mYEntrySpace = l.mYEntrySpace; - mFormToTextSpace = l.mFormToTextSpace; - mTextSize = l.mTextSize; - mStackSpace = l.mStackSpace; - mTextColor = l.mTextColor; - mEnabled = l.mEnabled; - mXOffset = l.mXOffset; - mYOffset = l.mYOffset; - } - - /** - * returns the space that is left out between stacked forms (with no label) - * - * @return - */ - public float getStackSpace() { - return mStackSpace; - } - - /** - * sets the space that is left out between stacked forms (with no label) - * - * @param space - */ - public void setStackSpace(float space) { - mStackSpace = space; - } - - /** - * calculates the full width the fully drawn legend will use in pixels - * - * @return - */ - public float getFullWidth(Paint labelpaint) { - - float width = 0f; - - for (int i = 0; i < mLabels.length; i++) { - - // grouped forms have null labels - if (mLabels[i] != null) { - - // make a step to the left - if (mColors[i] != -2) - width += mFormSize + mFormToTextSpace; - - width += Utils.calcTextWidth(labelpaint, mLabels[i]) - + mXEntrySpace; - } else { - width += mFormSize + mStackSpace; - } - } - - return width; - } - - /** - * Calculates the full height of the drawn legend. - * - * @param mLegendLabelPaint - * @return - */ - public float getFullHeight(Paint labelpaint) { - - float height = 0f; - - for (int i = 0; i < mLabels.length; i++) { - - // grouped forms have null labels - if (mLabels[i] != null) { - - height += Utils.calcTextHeight(labelpaint, mLabels[i]) - + mYEntrySpace; - } - } - - return height; - } - - /** the total width of the legend (needed width space) */ - public float mNeededWidth = 0f; - - /** the total height of the legend (needed height space) */ - public float mNeededHeight = 0f; - - public float mTextHeightMax = 0f; - - public float mTextWidthMax = 0f; - - /** - * Calculates the dimensions of the Legend. This includes the maximum width - * and height of a single entry, as well as the total width and height of - * the Legend. - * - * @param labelpaint - */ - public void calculateDimensions(Paint labelpaint) { - - if (mPosition == LegendPosition.RIGHT_OF_CHART - || mPosition == LegendPosition.RIGHT_OF_CHART_CENTER - || mPosition == LegendPosition.PIECHART_CENTER) { - mNeededWidth = getMaximumEntryWidth(labelpaint); - mNeededHeight = getFullHeight(labelpaint); - mTextWidthMax = mNeededWidth; - mTextHeightMax = getMaximumEntryHeight(labelpaint); - - } else { - - mNeededWidth = getFullWidth(labelpaint); - mNeededHeight = getMaximumEntryHeight(labelpaint); - mTextWidthMax = getMaximumEntryWidth(labelpaint); - mTextHeightMax = mNeededHeight; - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/MarkerView.java b/MPChartLib/src/com/github/mikephil/charting/components/MarkerView.java deleted file mode 100644 index 14a0aed6c4..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/MarkerView.java +++ /dev/null @@ -1,97 +0,0 @@ - -package com.github.mikephil.charting.components; - -import android.content.Context; -import android.graphics.Canvas; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.RelativeLayout; - -import com.github.mikephil.charting.data.Entry; - -/** - * View that can be displayed when selecting values in the chart. Extend this - * class to provide custom layouts for your markers. - * - * @author Philipp Jahoda - */ -public abstract class MarkerView extends RelativeLayout { - - /** - * Constructor. Sets up the MarkerView with a custom layout resource. - * - * @param context - * @param layoutResource the layout resource to use for the MarkerView - */ - public MarkerView(Context context, int layoutResource) { - super(context); - setupLayoutResource(layoutResource); - } - - /** - * Sets the layout resource for a custom MarkerView. - * - * @param layoutResource - */ - private void setupLayoutResource(int layoutResource) { - - View inflated = LayoutInflater.from(getContext()).inflate(layoutResource, this); - - inflated.setLayoutParams(new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, - RelativeLayout.LayoutParams.WRAP_CONTENT)); - inflated.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - - // measure(getWidth(), getHeight()); - inflated.layout(0, 0, inflated.getMeasuredWidth(), inflated.getMeasuredHeight()); - } - - /** - * Draws the MarkerView on the given position on the screen with the given - * Canvas object. - * - * @param canvas - * @param posx - * @param posy - */ - public void draw(Canvas canvas, float posx, float posy) { - - // take offsets into consideration - posx += getXOffset(); - posy += getYOffset(); - - // translate to the correct position and draw - canvas.translate(posx, posy); - draw(canvas); - canvas.translate(-posx, -posy); - } - - /** - * This method enables a specified custom MarkerView to update it's content - * everytime the MarkerView is redrawn. - * - * @param e The Entry the MarkerView belongs to. This can also be any - * subclass of Entry, like BarEntry or CandleEntry, simply cast - * it at runtime. - * @param dataSetIndex the index of the DataSet the selected value is in - */ - public abstract void refreshContent(Entry e, int dataSetIndex); - - /** - * Use this to return the desired offset you wish the MarkerView to have on - * the x-axis. By returning -(getWidth() / 2) you will center the MarkerView - * horizontally. - * - * @return - */ - public abstract int getXOffset(); - - /** - * Use this to return the desired position offset you wish the MarkerView to - * have on the y-axis. By returning -getHeight() you will cause the - * MarkerView to be above the selected value. - * - * @return - */ - public abstract int getYOffset(); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java deleted file mode 100644 index 55b3426d8c..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java +++ /dev/null @@ -1,181 +0,0 @@ - -package com.github.mikephil.charting.components; - -import java.util.ArrayList; - -/** - * Class representing the x-axis labels settings. Only use the setter methods to - * modify it. Do not access public variables directly. Be aware that not all - * features the XLabels class provides are suitable for the RadarChart. - * - * @author Philipp Jahoda - */ -public class XAxis extends AxisBase { - - /** the arraylist containing all the x-axis labels */ - protected ArrayList mValues = new ArrayList(); - - /** - * width of the x-axis labels in pixels - this is calculated by the - * calcTextWidth() method of the utils - */ - public int mLabelWidth = 1; - - /** - * height of the x-axis labels in pixels - this is calculated by the - * calcTextHeight() method of the utils - */ - public int mLabelHeight = 1; - - /** - * the space that should be left out (in characters) between the x-axis - * labels - */ - private int mSpaceBetweenLabels = 4; - - /** - * the modulus that indicates if a value at a specified index in an - * array(list) for the x-axis-labels is drawn or not. If index % modulus == - * 0 DRAW, else dont draw. - */ - public int mAxisLabelModulus = 1; - - /** - * the modulus that indicates if a value at a specified index in an - * array(list) for the y-axis-labels is drawn or not. If index % modulus == - * 0 DRAW, else dont draw. THIS IS ONLY FOR HORIZONTAL BARCHART. - */ - public int mYAxisLabelModulus = 1; - - /** - * if set to true, the chart will avoid that the first and last label entry - * in the chart "clip" off the edge of the chart - */ - private boolean mAvoidFirstLastClipping = false; - - /** - * if set to true, the x-axis label entries will adjust themselves when - * scaling the graph - */ - protected boolean mAdjustXAxisLabels = true; - - /** the position of the x-labels relative to the chart */ - private XAxisPosition mPosition = XAxisPosition.TOP; - - /** enum for the position of the x-labels relative to the chart */ - public enum XAxisPosition { - TOP, BOTTOM, BOTH_SIDED, TOP_INSIDE, BOTTOM_INSIDE - } - - public XAxis() { - super(); - } - - /** - * if set to true, the x-label entries will adjust themselves when scaling - * the graph default: true - * - * @param enabled - */ - public void setAdjustXLabels(boolean enabled) { - mAdjustXAxisLabels = enabled; - } - - /** - * returns true if the x-labels adjust themselves when scaling the graph, - * false if not - * - * @return - */ - public boolean isAdjustXLabelsEnabled() { - return mAdjustXAxisLabels; - } - - /** - * returns the position of the x-labels - */ - public XAxisPosition getPosition() { - return mPosition; - } - - /** - * sets the position of the x-labels - * - * @param pos - */ - public void setPosition(XAxisPosition pos) { - mPosition = pos; - } - - /** - * Sets the space (in characters) that should be left out between the x-axis - * labels, default 4 - * - * @param space - */ - public void setSpaceBetweenLabels(int space) { - mSpaceBetweenLabels = space; - } - - /** - * Returns the space (in characters) that should be left out between the - * x-axis labels - * - * @param space - */ - public int getSpaceBetweenLabels() { - return mSpaceBetweenLabels; - } - - /** - * if set to true, the chart will avoid that the first and last label entry - * in the chart "clip" off the edge of the chart or the screen - * - * @param enabled - */ - public void setAvoidFirstLastClipping(boolean enabled) { - mAvoidFirstLastClipping = enabled; - } - - /** - * returns true if avoid-first-lastclipping is enabled, false if not - * - * @return - */ - public boolean isAvoidFirstLastClippingEnabled() { - return mAvoidFirstLastClipping; - } - - /** - * Sets the labels for this axis. - * - * @param values - */ - public void setValues(ArrayList values) { - mValues = values; - } - - /** - * Returns the labels for this axis. - * - * @return - */ - public ArrayList getValues() { - return mValues; - } - - @Override - public String getLongestLabel() { - - String longest = ""; - - for (int i = 0; i < mValues.size(); i++) { - String text = mValues.get(i); - - if (longest.length() < text.length()) - longest = text; - } - - return longest; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java deleted file mode 100644 index 5732672cb3..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ /dev/null @@ -1,429 +0,0 @@ - -package com.github.mikephil.charting.components; - -import android.graphics.Paint; - -import com.github.mikephil.charting.utils.DefaultValueFormatter; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ValueFormatter; - -import java.util.ArrayList; - -/** - * Class representing the y-axis labels settings and its entries. Only use the - * setter methods to modify it. Do not access public variables directly. Be - * aware that not all features the YLabels class provides are suitable for the - * RadarChart. - * - * @author Philipp Jahoda - */ -public class YAxis extends AxisBase { - - /** custom formatter that is used instead of the auto-formatter if set */ - protected ValueFormatter mValueFormatter; - - /** the actual array of entries */ - public float[] mEntries = new float[] {}; - - /** the number of entries the legend contains */ - public int mEntryCount; - - /** the number of decimal digits to use */ - public int mDecimals; - - /** the number of y-label entries the y-labels should have, default 6 */ - private int mLabelCount = 6; - - /** indicates if the top y-label entry is drawn or not */ - private boolean mDrawTopYLabelEntry = true; - - /** if true, the y-labels show only the minimum and maximum value */ - protected boolean mShowOnlyMinMax = false; - - /** flag that indicates if the axis is inverted or not */ - protected boolean mInverted = false; - - /** if true, the y-label entries will always start at zero */ - protected boolean mStartAtZero = true; - - /** array of limitlines that can be set for the axis */ - private ArrayList mLimitLines; - - /** custom minimum value this axis represents */ - protected float mCustomAxisMin = Float.NaN; - - /** custom maximum value this axis represents */ - protected float mCustomAxisMax = Float.NaN; - - /** - * axis space from the largest value to the top in percent of the total axis - * range - */ - protected float mSpacePercentTop = 10f; - - /** - * axis space from the smallest value to the bottom in percent of the total - * axis range - */ - protected float mSpacePercentBottom = 10f; - - public float mAxisMaximum = 0f; - public float mAxisMinimum = 0f; - - /** the total range of values this axis covers */ - public float mAxisRange = 0f; - - /** the position of the y-labels relative to the chart */ - private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART; - - /** enum for the position of the y-labels relative to the chart */ - public enum YAxisLabelPosition { - OUTSIDE_CHART, INSIDE_CHART - } - - /** the side this axis object represents */ - private AxisDependency mAxisDependency; - - /** - * Enum that specifies the axis a DataSet should be plotted against, either - * LEFT or RIGHT. - * - * @author Philipp Jahoda - */ - public enum AxisDependency { - LEFT, RIGHT - } - - public YAxis(AxisDependency position) { - super(); - this.mAxisDependency = position; - this.mLimitLines = new ArrayList(); - } - - public AxisDependency getAxisDependency() { - return mAxisDependency; - } - - /** - * returns the position of the y-labels - */ - public YAxisLabelPosition getLabelPosition() { - return mPosition; - } - - /** - * sets the position of the y-labels - * - * @param pos - */ - public void setPosition(YAxisLabelPosition pos) { - mPosition = pos; - } - - /** - * returns true if drawing the top y-axis label entry is enabled - * - * @return - */ - public boolean isDrawTopYLabelEntryEnabled() { - return mDrawTopYLabelEntry; - } - - /** - * set this to true to enable drawing the top y-label entry. Disabling this - * can be helpful when the top y-label and left x-label interfere with each - * other. default: true - * - * @param enabled - */ - public void setDrawTopYLabelEntry(boolean enabled) { - mDrawTopYLabelEntry = enabled; - } - - /** - * sets the number of label entries for the y-axis max = 15, min = 2, - * default: 6, be aware that this number is not fixed and can only be - * approximated - * - * @param yCount - */ - public void setLabelCount(int yCount) { - - if (yCount > 15) - yCount = 15; - if (yCount < 2) - yCount = 2; - - mLabelCount = yCount; - } - - /** - * Returns the number of label entries the y-axis should have - * - * @return - */ - public int getLabelCount() { - return mLabelCount; - } - - /** - * If enabled, the YLabels will only show the minimum and maximum value of - * the chart. This will ignore/override the set label count. - * - * @param enabled - */ - public void setShowOnlyMinMax(boolean enabled) { - mShowOnlyMinMax = enabled; - } - - /** - * Returns true if showing only min and max value is enabled. - * - * @return - */ - public boolean isShowOnlyMinMaxEnabled() { - return mShowOnlyMinMax; - } - - /** - * If this is set to true, the y-axis is inverted which means that low - * values are on top of the chart, high values on bottom. - * - * @param enabled - */ - public void setInverted(boolean enabled) { - mInverted = enabled; - } - - /** - * If this returns true, the y-axis is inverted. - * - * @return - */ - public boolean isInverted() { - return mInverted; - } - - /** - * enable this to force the y-axis labels to always start at zero - * - * @param enabled - */ - public void setStartAtZero(boolean enabled) { - this.mStartAtZero = enabled; - } - - /** - * returns true if the chart is set to start at zero, false otherwise - * - * @return - */ - public boolean isStartAtZeroEnabled() { - return mStartAtZero; - } - - /** - * Adds a new LimitLine to this axis. - * - * @param l - */ - public void addLimitLine(LimitLine l) { - mLimitLines.add(l); - } - - /** - * Removes the specified LimitLine from the axis. - * - * @param l - */ - public void removeLimitLine(LimitLine l) { - mLimitLines.remove(l); - } - - /** - * Removes all LimitLines from the axis. - */ - public void removeAllLimitLines() { - mLimitLines = new ArrayList(); - } - - /** - * Returns the LimitLines of this axis. - * - * @return - */ - public ArrayList getLimitLines() { - return mLimitLines; - } - - public float getAxisMinValue() { - return mCustomAxisMin; - } - - /** - * Set a custom minimum value for this axis. If set, this value will not be - * calculated automatically depending on the provided data. Use - * resetAxisMinValue() to undo this. Do not forget to call - * setStartAtZero(false) if you use this method. Otherwise, the axis-minimum - * value will still be forced to 0. - * - * @param min - */ - public void setAxisMinValue(float min) { - mCustomAxisMin = min; - } - - /** - * By calling this method, any custom minimum value that has been previously - * set is reseted, and the calculation is done automatically. - */ - public void resetAxisMinValue() { - mCustomAxisMin = Float.NaN; - } - - public float getAxisMaxValue() { - return mCustomAxisMax; - } - - /** - * Set a custom maximum value for this axis. If set, this value will not be - * calculated automatically depending on the provided data. Use - * resetAxisMaxValue() to undo this. - * - * @param max - */ - public void setAxisMaxValue(float max) { - mCustomAxisMax = max; - } - - /** - * By calling this method, any custom maximum value that has been previously - * set is reseted, and the calculation is done automatically. - */ - public void resetAxisMaxValue() { - mCustomAxisMax = Float.NaN; - } - - /** - * Sets the top axis space in percent of the full range. Default 10f - * - * @param percent - */ - public void setSpaceTop(float percent) { - mSpacePercentTop = percent; - } - - /** - * Returns the top axis space in percent of the full range. Default 10f - * - * @return - */ - public float getSpaceTop() { - return mSpacePercentTop; - } - - /** - * Sets the bottom axis space in percent of the full range. Default 10f - * - * @param percent - */ - public void setSpaceBottom(float percent) { - mSpacePercentBottom = percent; - } - - /** - * Returns the bottom axis space in percent of the full range. Default 10f - * - * @return - */ - public float getSpaceBottom() { - return mSpacePercentBottom; - } - - public float getRequiredWidthSpace(Paint p) { - - p.setTextSize(mTextSize); - - String label = getLongestLabel(); - return (float) Utils.calcTextWidth(p, label) + getXOffset() * 2f; - } - - public float getRequiredHeightSpace(Paint p) { - - p.setTextSize(mTextSize); - - String label = getLongestLabel(); - return (float) Utils.calcTextHeight(p, label) + getYOffset() * 2f; - } - - @Override - public String getLongestLabel() { - - String longest = ""; - - for (int i = 0; i < mEntries.length; i++) { - String text = getFormattedLabel(i); - - if (longest.length() < text.length()) - longest = text; - } - - return longest; - } - - /** - * Returns the formatted y-label at the specified index. This will either - * use the auto-formatter or the custom formatter (if one is set). - * - * @param index - * @return - */ - public String getFormattedLabel(int index) { - - if (index < 0 || index >= mEntries.length) - return ""; - else - return getValueFormatter().getFormattedValue(mEntries[index]); - } - - /** - * Sets the formatter to be used for drawing the values inside the chart. If - * no formatter is set, the chart will automatically determine a reasonable - * formatting (concerning decimals) for all the values that are drawn inside - * the chart. Use chart.getDefaultValueFormatter() to use the formatter - * calculated by the chart. - * - * @param f - */ - public void setValueFormatter(ValueFormatter f) { - - if (f == null) - return; - else - mValueFormatter = f; - } - - /** - * Returns the formatter used for drawing the values inside the chart. - * - * @return - */ - public ValueFormatter getValueFormatter() { - return mValueFormatter; - } - - /** - * If this component has no ValueFormatter or is only equipped with the - * default one (no custom set), return true. - * - * @return - */ - public boolean needsDefaultFormatter() { - if (mValueFormatter == null) - return true; - if (mValueFormatter instanceof DefaultValueFormatter) - return true; - - return false; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarData.java b/MPChartLib/src/com/github/mikephil/charting/data/BarData.java deleted file mode 100644 index d10d6f006d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarData.java +++ /dev/null @@ -1,111 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; - -/** - * Data object that represents all data for the BarChart. - * - * @author Philipp Jahoda - */ -public class BarData extends BarLineScatterCandleData { - - /** the space that is left between groups of bars */ - private float mGroupSpace = 0.8f; - - // /** - // * The maximum space (in pixels on the screen) a single bar can consume. - // */ - // private float mMaximumBarWidth = 100f; - - public BarData() { - super(); - } - - public BarData(ArrayList xVals) { - super(xVals); - } - - public BarData(String[] xVals) { - super(xVals); - } - - public BarData(ArrayList xVals, ArrayList dataSets) { - super(xVals, dataSets); - } - - public BarData(String[] xVals, ArrayList dataSets) { - super(xVals, dataSets); - } - - public BarData(ArrayList xVals, BarDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - public BarData(String[] xVals, BarDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - private static ArrayList toArrayList(BarDataSet dataSet) { - ArrayList sets = new ArrayList(); - sets.add(dataSet); - return sets; - } - - /** - * Returns the space that is left out between groups of bars. Always returns - * 0 if the BarData object only contains one DataSet (because for one - * DataSet, there is no group-space needed). - * - * @return - */ - public float getGroupSpace() { - - if (mDataSets.size() <= 1) - return 0f; - else - return mGroupSpace; - } - - /** - * Sets the space between groups of bars of different datasets in percent of - * the total width of one bar. 100 = space is exactly one bar width, - * default: 80 - * - * @param percent - */ - public void setGroupSpace(float percent) { - mGroupSpace = percent / 100f; - } - - /** - * Returns true if this BarData object contains grouped DataSets (more than - * 1 DataSet). - * - * @return - */ - public boolean isGrouped() { - return mDataSets.size() > 1 ? true : false; - } - - // - // /** - // * Sets the maximum width (in density pixels) a single bar in the barchart - // * should consume. - // * - // * @param max - // */ - // public void setBarWidthMaximum(float max) { - // mMaximumBarWidth = Utils.convertDpToPixel(max); - // } - // - // /** - // * Returns the maximum width (in density pixels) a single bar in the - // * barchart should consume. - // * - // * @return - // */ - // public float getBarWidthMaximum() { - // return mMaximumBarWidth; - // } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java b/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java deleted file mode 100644 index b472e41ee5..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java +++ /dev/null @@ -1,144 +0,0 @@ - -package com.github.mikephil.charting.data; - -/** - * Entry class for the BarChart. (especially stacked bars) - * - * @author Philipp Jahoda - */ -public class BarEntry extends Entry { - - /** the values the stacked barchart holds */ - private float[] mVals; - - /** - * Constructor for stacked bar entries. - * - * @param vals - * @param xIndex - */ - public BarEntry(float[] vals, int xIndex) { - super(calcSum(vals), xIndex); - - this.mVals = vals; - } - - /** - * Constructor for normal bars (not stacked). - * - * @param val - * @param xIndex - */ - public BarEntry(float val, int xIndex) { - super(val, xIndex); - } - - /** - * Constructor for stacked bar entries. - * - * @param vals - * @param xIndex - * @param label Additional description label. - */ - public BarEntry(float[] vals, int xIndex, String label) { - super(calcSum(vals), xIndex, label); - - this.mVals = vals; - } - - /** - * Constructor for normal bars (not stacked). - * - * @param val - * @param xIndex - * @param data Spot for additional data this Entry represents. - */ - public BarEntry(float val, int xIndex, Object data) { - super(val, xIndex, data); - } - - /** - * Returns an exact copy of the BarEntry. - */ - public BarEntry copy() { - - BarEntry copied = new BarEntry(getVal(), getXIndex(), getData()); - copied.mVals = mVals; - return copied; - } - - /** - * Returns the stacked values this BarEntry represents, or null, if only a - * single value is represented (then, use getVal()). - * - * @return - */ - public float[] getVals() { - return mVals; - } - - /** - * Set the array of values this BarEntry should represent. - * - * @param vals - */ - public void setVals(float[] vals) { - mVals = vals; - } - - /** - * Returns the closest value inside the values array (for stacked barchart) - * to the value given as a parameter. The closest value must be higher - * (above) the provided value. - * - * @param val - * @return - */ - public int getClosestIndexAbove(float val) { - - if (mVals == null) - return 0; - - int index = mVals.length - 1; - float remainder = 0f; - - while(index > 0 && val > mVals[index] + remainder) { - remainder += mVals[index]; - index--; - } - - return index; - } - - public float getBelowSum(int stackIndex) { - - if (mVals == null) - return 0; - - float remainder = 0f; - int index = mVals.length - 1; - - while(index > stackIndex && index >= 0) { - remainder += mVals[index]; - index--; - } - - return remainder; - } - - /** - * Calculates the sum across all values. - * - * @param vals - * @return - */ - private static float calcSum(float[] vals) { - - float sum = 0f; - - for (float f : vals) - sum += f; - - return sum; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleData.java b/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleData.java deleted file mode 100644 index 2b5e13bf5d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleData.java +++ /dev/null @@ -1,33 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; - -/** - * Baseclass for all Line, Bar and ScatterData. - * - * @author Philipp Jahoda - */ -public abstract class BarLineScatterCandleData> - extends ChartData { - - public BarLineScatterCandleData() { - super(); - } - - public BarLineScatterCandleData(ArrayList xVals) { - super(xVals); - } - - public BarLineScatterCandleData(String[] xVals) { - super(xVals); - } - - public BarLineScatterCandleData(ArrayList xVals, ArrayList sets) { - super(xVals, sets); - } - - public BarLineScatterCandleData(String[] xVals, ArrayList sets) { - super(xVals, sets); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java b/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java deleted file mode 100644 index 96d8c6cff1..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.mikephil.charting.data; - -import java.util.ArrayList; - -public class CandleData extends BarLineScatterCandleData { - - public CandleData() { - super(); - } - - public CandleData(ArrayList xVals) { - super(xVals); - } - - public CandleData(String[] xVals) { - super(xVals); - } - - public CandleData(ArrayList xVals, ArrayList dataSets) { - super(xVals, dataSets); - } - - public CandleData(String[] xVals, ArrayList dataSets) { - super(xVals, dataSets); - } - - public CandleData(ArrayList xVals, CandleDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - public CandleData(String[] xVals, CandleDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - private static ArrayList toArrayList(CandleDataSet dataSet) { - ArrayList sets = new ArrayList(); - sets.add(dataSet); - return sets; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java deleted file mode 100644 index 9acd447cee..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java +++ /dev/null @@ -1,111 +0,0 @@ - -package com.github.mikephil.charting.data; - -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -/** - * DataSet for the CandleStickChart. - * - * @author Philipp Jahoda - */ -public class CandleDataSet extends BarLineScatterCandleDataSet { - - /** the width of the shadow of the candle */ - private float mShadowWidth = 3f; - - /** the space between the candle entries, default 0.1f (10%) */ - private float mBodySpace = 0.1f; - - public CandleDataSet(ArrayList yVals, String label) { - super(yVals, label); - } - - @Override - public DataSet copy() { - - ArrayList yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(((CandleEntry) mYVals.get(i)).copy()); - } - - CandleDataSet copied = new CandleDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mShadowWidth = mShadowWidth; - copied.mBodySpace = mBodySpace; - copied.mHighLightColor = mHighLightColor; - - return copied; - } - - @Override - protected void calcMinMax() { -// super.calcMinMax(); - - if (mYVals.size() == 0) { - return; - } - - ArrayList entries = (ArrayList) mYVals; - - mYMin = entries.get(0).getLow(); - mYMax = entries.get(0).getHigh(); - - for (int i = 0; i < entries.size(); i++) { - - CandleEntry e = entries.get(i); - - if (e.getLow() < mYMin) - mYMin = e.getLow(); - - if (e.getHigh() > mYMax) - mYMax = e.getHigh(); - } - } - - /** - * Sets the space that is left out on the left and right side of each - * candle, default 0.1f (10%), max 0.45f, min 0f - * - * @param space - */ - public void setBodySpace(float space) { - - if (space < 0f) - space = 0f; - if (space > 0.45f) - space = 0.45f; - - mBodySpace = space; - } - - /** - * Returns the space that is left out on the left and right side of each - * candle. - * - * @return - */ - public float getBodySpace() { - return mBodySpace; - } - - /** - * Sets the width of the candle-shadow-line in pixels. Default 3f. - * - * @param width - */ - public void setShadowWidth(float width) { - mShadowWidth = Utils.convertDpToPixel(width); - } - - /** - * Returns the width of the candle-shadow-line in pixels. - * - * @return - */ - public float getShadowWidth() { - return mShadowWidth; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java deleted file mode 100644 index 9de93161ac..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ /dev/null @@ -1,882 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.graphics.Typeface; -import android.util.Log; - -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.ValueFormatter; - -import java.util.ArrayList; - -/** - * Class that holds all relevant data that represents the chart. That involves - * at least one (or more) DataSets, and an array of x-values. - * - * @author Philipp Jahoda - */ -public abstract class ChartData> { - - /** maximum y-value in the y-value array across all axes */ - protected float mYMax = 0.0f; - - /** the minimum y-value in the y-value array across all axes */ - protected float mYMin = 0.0f; - - protected float mLeftAxisMax = 0.0f; - - protected float mLeftAxisMin = 0.0f; - - protected float mRightAxisMax = 0.0f; - - protected float mRightAxisMin = 0.0f; - - /** the total sum of all y-values */ - private float mYValueSum = 0f; - - /** total number of y-values across all DataSet objects */ - private int mYValCount = 0; - - /** - * contains the average length (in characters) an entry in the x-vals array - * has - */ - private float mXValAverageLength = 0; - - /** holds all x-values the chart represents */ - protected ArrayList mXVals; - - /** array that holds all DataSets the ChartData object represents */ - protected ArrayList mDataSets; - - public ChartData() { - mXVals = new ArrayList(); - mDataSets = new ArrayList(); - } - - /** - * Constructor for only x-values. This constructor can be used for setting - * up an empty chart without data. - * - * @param xVals - */ - public ChartData(ArrayList xVals) { - this.mXVals = xVals; - this.mDataSets = new ArrayList(); - init(mDataSets); - } - - /** - * Constructor for only x-values. This constructor can be used for setting - * up an empty chart without data. - * - * @param xVals - */ - public ChartData(String[] xVals) { - this.mXVals = arrayToArrayList(xVals); - this.mDataSets = new ArrayList(); - init(mDataSets); - } - - /** - * constructor for chart data - * - * @param xVals The values describing the x-axis. Must be at least as long - * as the highest xIndex in the Entry objects across all - * DataSets. - * @param sets the dataset array - */ - public ChartData(ArrayList xVals, ArrayList sets) { - this.mXVals = xVals; - this.mDataSets = sets; - - init(mDataSets); - } - - /** - * constructor that takes string array instead of arraylist string - * - * @param xVals The values describing the x-axis. Must be at least as long - * as the highest xIndex in the Entry objects across all - * DataSets. - * @param sets the dataset array - */ - public ChartData(String[] xVals, ArrayList sets) { - this.mXVals = arrayToArrayList(xVals); - this.mDataSets = sets; - - init(mDataSets); - } - - /** - * Turns an array of strings into an arraylist of strings. - * - * @param array - * @return - */ - private ArrayList arrayToArrayList(String[] array) { - - ArrayList arraylist = new ArrayList(); - for (int i = 0; i < array.length; i++) { - arraylist.add(array[i]); - } - - return arraylist; - } - - /** - * performs all kinds of initialization calculations, such as min-max and - * value count and sum - */ - protected void init(ArrayList> dataSets) { - - isLegal(dataSets); - - calcMinMax(dataSets); - calcYValueSum(dataSets); - calcYValueCount(dataSets); - - calcXValAverageLength(); - } - - /** - * calculates the average length (in characters) across all x-value strings - */ - private void calcXValAverageLength() { - - if (mXVals.size() == 0) { - mXValAverageLength = 1; - return; - } - - float sum = 1f; - - for (int i = 0; i < mXVals.size(); i++) { - sum += mXVals.get(i).length(); - } - - mXValAverageLength = sum / (float) mXVals.size(); - } - - /** - * Checks if the combination of x-values array and DataSet array is legal or - * not. - * - * @param dataSets - */ - private void isLegal(ArrayList> dataSets) { - - if (dataSets == null) - return; - - for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i) - .getYVals() - .size() > mXVals.size()) { - throw new IllegalArgumentException( - "One or more of the DataSet Entry arrays are longer than the x-values array of this ChartData object."); - } - } - } - - /** - * Call this method to let the CartData know that the underlying data has - * changed. - */ - public void notifyDataChanged() { - init(mDataSets); - } - - /** - * calc minimum and maximum y value over all datasets - */ - protected void calcMinMax(ArrayList> dataSets) { - - if (dataSets == null || dataSets.size() < 1) { - - mYMax = 0f; - mYMin = 0f; - } else { - - // calculate absolute min and max - mYMin = dataSets.get(0).getYMin(); - mYMax = dataSets.get(0).getYMax(); - - for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i).getYMin() < mYMin) - mYMin = dataSets.get(i).getYMin(); - - if (dataSets.get(i).getYMax() > mYMax) - mYMax = dataSets.get(i).getYMax(); - } - - // left axis - T firstLeft = getFirstLeft(); - - if (firstLeft != null) { - - mLeftAxisMax = firstLeft.getYMax(); - mLeftAxisMin = firstLeft.getYMin(); - - for (DataSet dataSet : dataSets) { - if (dataSet.getAxisDependency() == AxisDependency.LEFT) { - if (dataSet.getYMin() < mLeftAxisMin) - mLeftAxisMin = dataSet.getYMin(); - - if (dataSet.getYMax() > mLeftAxisMax) - mLeftAxisMax = dataSet.getYMax(); - } - } - } - - // right axis - T firstRight = getFirstRight(); - - if (firstRight != null) { - - mRightAxisMax = firstRight.getYMax(); - mRightAxisMin = firstRight.getYMin(); - - for (DataSet dataSet : dataSets) { - if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { - if (dataSet.getYMin() < mRightAxisMin) - mRightAxisMin = dataSet.getYMin(); - - if (dataSet.getYMax() > mRightAxisMax) - mRightAxisMax = dataSet.getYMax(); - } - } - } - - // in case there is only one axis, adjust the second axis - handleEmptyAxis(firstLeft, firstRight); - } - } - - /** - * calculates the sum of all y-values in all datasets - */ - protected void calcYValueSum(ArrayList> dataSets) { - - mYValueSum = 0; - - if (dataSets == null) - return; - - for (int i = 0; i < dataSets.size(); i++) { - mYValueSum += Math.abs(dataSets.get(i).getYValueSum()); - } - } - - /** - * Calculates the total number of y-values across all DataSets the ChartData - * represents. - * - * @return - */ - protected void calcYValueCount(ArrayList> dataSets) { - - mYValCount = 0; - - if (dataSets == null) - return; - - int count = 0; - - for (int i = 0; i < dataSets.size(); i++) { - count += dataSets.get(i).getEntryCount(); - } - - mYValCount = count; - } - - /** ONLY GETTERS AND SETTERS BELOW THIS */ - - /** - * returns the number of LineDataSets this object contains - * - * @return - */ - public int getDataSetCount() { - if (mDataSets == null) - return 0; - return mDataSets.size(); - } - - /** - * Returns the smallest y-value the data object contains. - * - * @return - */ - public float getYMin() { - return mYMin; - } - - /** - * Returns the minimum y-value for the specified axis. - * - * @param axis - * @return - */ - public float getYMin(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mLeftAxisMin; - else - return mRightAxisMin; - } - - /** - * Returns the greatest y-value the data object contains. - * - * @return - */ - public float getYMax() { - return mYMax; - } - - /** - * Returns the maximum y-value for the specified axis. - * - * @param axis - * @return - */ - public float getYMax(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mLeftAxisMax; - else - return mRightAxisMax; - } - - /** - * returns the average length (in characters) across all values in the - * x-vals array - * - * @return - */ - public float getXValAverageLength() { - return mXValAverageLength; - } - - /** - * Returns the total y-value sum across all DataSet objects the this object - * represents. - * - * @return - */ - public float getYValueSum() { - return mYValueSum; - } - - /** - * Returns the total number of y-values across all DataSet objects the this - * object represents. - * - * @return - */ - public int getYValCount() { - return mYValCount; - } - - /** - * returns the x-values the chart represents - * - * @return - */ - public ArrayList getXVals() { - return mXVals; - } - - /** - * Adds a new x-value to the chart data. - * - * @param xVal - */ - public void addXValue(String xVal) { - mXVals.add(xVal); - } - - /** - * Removes the x-value at the specified index. - * - * @param index - */ - public void removeXValue(int index) { - mXVals.remove(index); - } - - /** - * Returns an the array of DataSets this object holds. - * - * @return - */ - public ArrayList getDataSets() { - return mDataSets; - } - - /** - * Retrieve the index of a DataSet with a specific label from the ChartData. - * Search can be case sensitive or not. IMPORTANT: This method does - * calculations at runtime, do not over-use in performance critical - * situations. - * - * @param dataSets the DataSet array to search - * @param type - * @param ignorecase if true, the search is not case-sensitive - * @return - */ - protected int getDataSetIndexByLabel(ArrayList dataSets, String label, - boolean ignorecase) { - - if (ignorecase) { - for (int i = 0; i < dataSets.size(); i++) - if (label.equalsIgnoreCase(dataSets.get(i).getLabel())) - return i; - } else { - for (int i = 0; i < dataSets.size(); i++) - if (label.equals(dataSets.get(i).getLabel())) - return i; - } - - return -1; - } - - /** - * returns the total number of x-values this ChartData object represents - * (the size of the x-values array) - * - * @return - */ - public int getXValCount() { - return mXVals.size(); - } - - /** - * Returns the labels of all DataSets as a string array. - * - * @return - */ - protected String[] getDataSetLabels() { - - String[] types = new String[mDataSets.size()]; - - for (int i = 0; i < mDataSets.size(); i++) { - types[i] = mDataSets.get(i).getLabel(); - } - - return types; - } - - /** - * Get the Entry for a corresponding highlight object - * - * @param highlight - * @return the entry that is highlighted - */ - public Entry getEntryForHighlight(Highlight highlight) { - return mDataSets.get(highlight.getDataSetIndex()).getEntryForXIndex( - highlight.getXIndex()); - } - - /** - * Returns the DataSet object with the given label. Search can be case - * sensitive or not. IMPORTANT: This method does calculations at runtime. - * Use with care in performance critical situations. - * - * @param label - * @param ignorecase - * @return - */ - public T getDataSetByLabel(String label, boolean ignorecase) { - - int index = getDataSetIndexByLabel(mDataSets, label, ignorecase); - - if (index < 0 || index >= mDataSets.size()) - return null; - else - return mDataSets.get(index); - } - - /** - * Returns the DataSet object at the given index. - * - * @param index - * @return - */ - public T getDataSetByIndex(int index) { - - if (mDataSets == null || index < 0 || index >= mDataSets.size()) - return null; - - return mDataSets.get(index); - } - - /** - * Adds a DataSet dynamically. - * - * @param d - */ - public void addDataSet(T d) { - if (mDataSets == null) - mDataSets = new ArrayList(); - mDataSets.add(d); - - mYValCount += d.getEntryCount(); - mYValueSum += d.getYValueSum(); - - if (mYMax < d.getYMax()) - mYMax = d.getYMax(); - if (mYMin > d.getYMin()) - mYMin = d.getYMin(); - - if (d.getAxisDependency() == AxisDependency.LEFT) { - - if (mLeftAxisMax < d.getYMax()) - mLeftAxisMax = d.getYMax(); - if (mLeftAxisMin > d.getYMin()) - mLeftAxisMin = d.getYMin(); - } else { - if (mRightAxisMax < d.getYMax()) - mRightAxisMax = d.getYMax(); - if (mRightAxisMin > d.getYMin()) - mRightAxisMin = d.getYMin(); - } - - handleEmptyAxis(getFirstLeft(), getFirstRight()); - } - - /** - * This adjusts the other axis if one axis is empty and the other is not. - * - * @param firstLeft - * @param firstRight - */ - private void handleEmptyAxis(T firstLeft, T firstRight) { - - // in case there is only one axis, adjust the second axis - if (firstLeft == null) { - mLeftAxisMax = mRightAxisMax; - mLeftAxisMin = mRightAxisMin; - } else if (firstRight == null) { - mRightAxisMax = mLeftAxisMax; - mRightAxisMin = mLeftAxisMin; - } - } - - /** - * Removes the given DataSet from this data object. Also recalculates all - * minimum and maximum values. Returns true if a DataSet was removed, false - * if no DataSet could be removed. - * - * @param d - */ - public boolean removeDataSet(T d) { - - if (mDataSets == null || d == null) - return false; - - boolean removed = mDataSets.remove(d); - - // if a DataSet was removed - if (removed) { - - mYValCount -= d.getEntryCount(); - mYValueSum -= d.getYValueSum(); - - calcMinMax(mDataSets); - } - - return removed; - } - - /** - * Removes the DataSet at the given index in the DataSet array from the data - * object. Also recalculates all minimum and maximum values. Returns true if - * a DataSet was removed, false if no DataSet could be removed. - * - * @param index - */ - public boolean removeDataSet(int index) { - - if (mDataSets == null || index >= mDataSets.size() || index < 0) - return false; - - T set = mDataSets.get(index); - return removeDataSet(set); - } - - /** - * Adds an Entry to the DataSet at the specified index. Entries are added to - * the end of the list. - * - * @param e - * @param dataSetIndex - */ - public void addEntry(Entry e, int dataSetIndex) { - - if (mDataSets == null) - mDataSets = new ArrayList(); - - if (mDataSets.size() > dataSetIndex && dataSetIndex >= 0) { - - float val = e.getVal(); - - mYValCount += 1; - mYValueSum += val; - - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - - if (mDataSets == null) - mDataSets = new ArrayList(); - - T set = mDataSets.get(dataSetIndex); - - if (set != null) { - - if (set.getAxisDependency() == AxisDependency.LEFT) { - - if (mLeftAxisMax < e.getVal()) - mLeftAxisMax = e.getVal(); - if (mLeftAxisMin > e.getVal()) - mLeftAxisMin = e.getVal(); - } else { - if (mRightAxisMax < e.getVal()) - mRightAxisMax = e.getVal(); - if (mRightAxisMin > e.getVal()) - mRightAxisMin = e.getVal(); - } - - handleEmptyAxis(getFirstLeft(), getFirstRight()); - - // add the entry to the dataset - set.addEntry(e); - } - } else { - Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low."); - } - } - - /** - * Removes the given Entry object from the DataSet at the specified index. - * - * @param e - * @param dataSetIndex - */ - public boolean removeEntry(Entry e, int dataSetIndex) { - - // entry null, outofbounds - if (e == null || dataSetIndex >= mDataSets.size()) - return false; - - // remove the entry from the dataset - boolean removed = mDataSets.get(dataSetIndex).removeEntry(e.getXIndex()); - - if (removed) { - - float val = e.getVal(); - - mYValCount -= 1; - mYValueSum -= val; - - calcMinMax(mDataSets); - } - - return removed; - } - - /** - * Removes the Entry object at the given xIndex from the DataSet at the - * specified index. Returns true if an Entry was removed, false if no Entry - * was found that meets the specified requirements. - * - * @param xIndex - * @param dataSetIndex - * @return - */ - public boolean removeEntry(int xIndex, int dataSetIndex) { - - if (dataSetIndex >= mDataSets.size()) - return false; - - T dataSet = mDataSets.get(dataSetIndex); - Entry e = dataSet.getEntryForXIndex(xIndex); - - return removeEntry(e, dataSetIndex); - } - - /** - * Returns the DataSet that contains the provided Entry, or null, if no - * DataSet contains this Entry. - * - * @param e - * @return - */ - public T getDataSetForEntry(Entry e) { - - if (e == null) - return null; - - for (int i = 0; i < mDataSets.size(); i++) { - - T set = mDataSets.get(i); - - for (int j = 0; j < set.getEntryCount(); j++) { - if (e.equalTo(set.getEntryForXIndex(e.getXIndex()))) - return set; - } - } - - return null; - } - - /** - * Returns all colors used across all DataSet objects this object - * represents. - * - * @return - */ - public int[] getColors() { - - if (mDataSets == null) - return null; - - int clrcnt = 0; - - for (int i = 0; i < mDataSets.size(); i++) { - clrcnt += mDataSets.get(i).getColors().size(); - } - - int[] colors = new int[clrcnt]; - int cnt = 0; - - for (int i = 0; i < mDataSets.size(); i++) { - - ArrayList clrs = mDataSets.get(i).getColors(); - - for (Integer clr : clrs) { - colors[cnt] = clr; - cnt++; - } - } - - return colors; - } - - /** - * Returns the index of the provided DataSet inside the DataSets array of - * this data object. Returns -1 if the DataSet was not found. - * - * @param dataSet - * @return - */ - public int getIndexOfDataSet(T dataSet) { - - for (int i = 0; i < mDataSets.size(); i++) { - if (mDataSets.get(i) == dataSet) - return i; - } - - return -1; - } - - public T getFirstLeft() { - for (T dataSet : mDataSets) { - if (dataSet.getAxisDependency() == AxisDependency.LEFT) - return dataSet; - } - - return null; - } - - public T getFirstRight() { - for (T dataSet : mDataSets) { - if (dataSet.getAxisDependency() == AxisDependency.RIGHT) - return dataSet; - } - - return null; - } - - /** - * Generates an x-values array filled with numbers in range specified by the - * parameters. Can be used for convenience. - * - * @return - */ - public static ArrayList generateXVals(int from, int to) { - - ArrayList xvals = new ArrayList(); - - for (int i = from; i < to; i++) { - xvals.add("" + i); - } - - return xvals; - } - - /** - * Sets a custom ValueFormatter for all DataSets this data object contains. - * - * @param f - */ - public void setValueFormatter(ValueFormatter f) { - if (f == null) - return; - else { - for (DataSet set : mDataSets) { - set.setValueFormatter(f); - } - } - } - - /** - * Sets the color of the value-text (color in which the value-labels are - * drawn) for all DataSets this data object contains. - * - * @param color - */ - public void setValueTextColor(int color) { - for (DataSet set : mDataSets) { - set.setValueTextColor(color); - } - } - - /** - * Sets the Typeface for all value-labels for all DataSets this data object - * contains. - * - * @param color - */ - public void setValueTypeface(Typeface tf) { - for (DataSet set : mDataSets) { - set.setValueTypeface(tf); - } - } - - /** - * Sets the size (in dp) of the value-text for all DataSets this data object - * contains. - * - * @param color - */ - public void setValueTextSize(float size) { - for (DataSet set : mDataSets) { - set.setValueTextSize(size); - } - } - - /** - * Enables / disables drawing values (value-text) for all DataSets this data - * object contains. - * - * @param enabled - */ - public void setDrawValues(boolean enabled) { - for (DataSet set : mDataSets) { - set.setDrawValues(enabled); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java deleted file mode 100644 index 369988d861..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java +++ /dev/null @@ -1,78 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; - -/** - * Data object that allows the combination of Line-, Bar-, Scatter- and - * CandleData. Used in the CombinedChart class. - * - * @author Philipp Jahoda - */ -public class CombinedData extends BarLineScatterCandleData> { - - private LineData mLineData; - private BarData mBarData; - private ScatterData mScatterData; - private CandleData mCandleData; - - public CombinedData() { - super(); - } - - public CombinedData(ArrayList xVals) { - super(xVals); - } - - public CombinedData(String[] xVals) { - super(xVals); - } - - public void setData(LineData data) { - mLineData = data; - mDataSets.addAll(data.getDataSets()); - init(data.getDataSets()); - } - - public void setData(BarData data) { - mBarData = data; - mDataSets.addAll(data.getDataSets()); - init(data.getDataSets()); - } - - public void setData(ScatterData data) { - mScatterData = data; - mDataSets.addAll(data.getDataSets()); - init(data.getDataSets()); - } - - public void setData(CandleData data) { - mCandleData = data; - mDataSets.addAll(data.getDataSets()); - init(data.getDataSets()); - } - - public LineData getLineData() { - return mLineData; - } - - public BarData getBarData() { - return mBarData; - } - - public ScatterData getScatterData() { - return mScatterData; - } - - public CandleData getCandleData() { - return mCandleData; - } - - @Override - public void notifyDataChanged() { - mLineData.notifyDataChanged(); - mBarData.notifyDataChanged(); - mCandleData.notifyDataChanged(); - mScatterData.notifyDataChanged(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java deleted file mode 100644 index 1e64a497e0..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java +++ /dev/null @@ -1,671 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.Typeface; - -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.DefaultValueFormatter; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ValueFormatter; - -import java.util.ArrayList; - -/** - * The DataSet class represents one group or type of entries (Entry) in the - * Chart that belong together. It is designed to logically separate different - * groups of values inside the Chart (e.g. the values for a specific line in the - * LineChart, or the values of a specific group of bars in the BarChart). - * - * @author Philipp Jahoda - */ -public abstract class DataSet { - - /** arraylist representing all colors that are used for this DataSet */ - protected ArrayList mColors = null; - - /** the entries that this dataset represents / holds together */ - protected ArrayList mYVals = null; - - /** maximum y-value in the y-value array */ - protected float mYMax = 0.0f; - - /** the minimum y-value in the y-value array */ - protected float mYMin = 0.0f; - - /** the total sum of all y-values */ - private float mYValueSum = 0f; - - /** label that describes the DataSet or the data the DataSet represents */ - private String mLabel = "DataSet"; - - /** flag that indicates if the DataSet is visible or not */ - private boolean mVisible = true; - - /** if true, y-values are drawn on the chart */ - protected boolean mDrawValues = true; - - /** the color used for the value-text */ - private int mValueColor = Color.BLACK; - - /** the size of the value-text labels */ - private float mValueTextSize = 17f; - - /** the typeface used for the value text */ - private Typeface mValueTypeface; - - /** custom formatter that is used instead of the auto-formatter if set */ - protected ValueFormatter mValueFormatter; - - /** this specifies which axis this DataSet should be plotted against */ - protected AxisDependency mAxisDependency = AxisDependency.LEFT; - - /** - * Creates a new DataSet object with the given values it represents. Also, a - * label that describes the DataSet can be specified. The label can also be - * used to retrieve the DataSet from a ChartData object. - * - * @param yVals - * @param label - */ - public DataSet(ArrayList yVals, String label) { - - this.mLabel = label; - this.mYVals = yVals; - - if (mYVals == null) - mYVals = new ArrayList(); - - // if (yVals.size() <= 0) { - // return; - // } - - mColors = new ArrayList(); - - // default colors - // mColors.add(Color.rgb(192, 255, 140)); - // mColors.add(Color.rgb(255, 247, 140)); - mColors.add(Color.rgb(140, 234, 255)); - - calcMinMax(); - calcYValueSum(); - } - - /** - * Use this method to tell the data set that the underlying data has changed - */ - public void notifyDataSetChanged() { - calcMinMax(); - calcYValueSum(); - } - - /** - * calc minimum and maximum y value - */ - protected void calcMinMax() { - if (mYVals.size() == 0) { - return; - } - - mYMin = mYVals.get(0).getVal(); - mYMax = mYVals.get(0).getVal(); - - for (int i = 0; i < mYVals.size(); i++) { - - Entry e = mYVals.get(i); - - if (e != null) { - - if (e.getVal() < mYMin) - mYMin = e.getVal(); - - if (e.getVal() > mYMax) - mYMax = e.getVal(); - } - } - } - - /** - * calculates the sum of all y-values - */ - private void calcYValueSum() { - - mYValueSum = 0; - - for (int i = 0; i < mYVals.size(); i++) { - Entry e = mYVals.get(i); - if (e != null) - mYValueSum += Math.abs(e.getVal()); - } - } - - /** - * returns the number of y-values this DataSet represents - * - * @return - */ - public int getEntryCount() { - return mYVals.size(); - } - - /** - * Returns the value of the Entry object at the given xIndex. Returns - * Float.NaN if no value is at the given x-index. INFORMATION: This method - * does calculations at runtime. Do not over-use in performance critical - * situations. - * - * @param xIndex - * @return - */ - public float getYValForXIndex(int xIndex) { - - Entry e = getEntryForXIndex(xIndex); - - if (e != null) - return e.getVal(); - else - return Float.NaN; - } - - /** - * Returns the first Entry object found at the given xIndex with binary - * search. Returns null if no Entry object at that index. INFORMATION: This - * method does calculations at runtime. Do not over-use in performance - * critical situations. - * - * @param xIndex - * @return - */ - public T getEntryForXIndex(int x) { - - int low = 0; - int high = mYVals.size() - 1; - - while (low <= high) { - int m = (high + low) / 2; - - if (x == mYVals.get(m).getXIndex()) { - return mYVals.get(m); - } - - if (x > mYVals.get(m).getXIndex()) - low = m + 1; - else - high = m - 1; - } - - return null; - } - - /** - * Returns all Entry objects at the given xIndex. INFORMATION: This method - * does calculations at runtime. Do not over-use in performance critical - * situations. - * - * @param xIndex - * @return - */ - public ArrayList getEntriesForXIndex(int x) { - - ArrayList entries = new ArrayList(); - - int low = 0; - int high = mYVals.size(); - - while (low <= high) { - int m = (high + low) / 2; - - if (x == mYVals.get(m).getXIndex()) { - entries.add(mYVals.get(m)); - } - - if (x > mYVals.get(m).getXIndex()) - low = m + 1; - else - high = m - 1; - } - - return entries; - } - - /** - * returns the DataSets Entry array - * - * @return - */ - public ArrayList getYVals() { - return mYVals; - } - - /** - * gets the sum of all y-values - * - * @return - */ - public float getYValueSum() { - return mYValueSum; - } - - /** - * returns the minimum y-value this DataSet holds - * - * @return - */ - public float getYMin() { - return mYMin; - } - - /** - * returns the maximum y-value this DataSet holds - * - * @return - */ - public float getYMax() { - return mYMax; - } - - /** - * returns the type of the DataSet, specified via constructor - * - * @return - */ - // public int getType() { - // return mType; - // } - - /** - * The xIndex of an Entry object is provided. This method returns the actual - * index in the Entry array of the DataSet. IMPORTANT: This method does - * calculations at runtime, do not over-use in performance critical - * situations. - * - * @param xIndex - * @return - */ - public int getIndexInEntries(int xIndex) { - - for (int i = 0; i < mYVals.size(); i++) { - if (xIndex == mYVals.get(i).getXIndex()) - return i; - } - - return -1; - } - - /** - * Provides an exact copy of the DataSet this method is used on. - * - * @return - */ - public abstract DataSet copy(); - - @Override - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append(toSimpleString()); - for (int i = 0; i < mYVals.size(); i++) { - buffer.append(mYVals.get(i).toString() + " "); - } - return buffer.toString(); - } - - /** - * Returns a simple string representation of the DataSet with the type and - * the number of Entries. - * - * @return - */ - public String toSimpleString() { - StringBuffer buffer = new StringBuffer(); - buffer.append("DataSet, label: " + mLabel + ", entries: " + mYVals.size() + "\n"); - return buffer.toString(); - } - - /** - * Returns the label string that describes the DataSet. - * - * @return - */ - public String getLabel() { - return mLabel; - } - - /** - * Set the visibility of this DataSet. If not visible, the DataSet will not - * be drawn to the chart upon refreshing it. - * - * @param visible - */ - public void setVisible(boolean visible) { - mVisible = visible; - } - - /** - * Returns true if this DataSet is visible inside the chart, or false if it - * is currently hidden. - * - * @return - */ - public boolean isVisible() { - return mVisible; - } - - /** - * Returns the axis this DataSet should be plotted against. - * - * @return - */ - public AxisDependency getAxisDependency() { - return mAxisDependency; - } - - /** - * Set the y-axis this DataSet should be plotted against (either LEFT or - * RIGHT). Default: LEFT - * - * @param dependency - */ - public void setAxisDependency(AxisDependency dependency) { - mAxisDependency = dependency; - } - - /** - * set this to true to draw y-values on the chart NOTE (for bar and - * linechart): if "maxvisiblecount" is reached, no values will be drawn even - * if this is enabled - * - * @param enabled - */ - public void setDrawValues(boolean enabled) { - this.mDrawValues = enabled; - } - - /** - * returns true if y-value drawing is enabled, false if not - * - * @return - */ - public boolean isDrawValuesEnabled() { - return mDrawValues; - } - - /** - * Adds an Entry to the DataSet dynamically. This will also recalculate the - * current minimum and maximum values of the DataSet and the value-sum. - * - * @param d - */ - public void addEntry(Entry e) { - - if (e == null) - return; - - float val = e.getVal(); - - if (mYVals == null || mYVals.size() <= 0) { - - mYVals = new ArrayList(); - mYMax = val; - mYMin = val; - } else { - - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - } - - mYValueSum += val; - - // add the entry - mYVals.add((T) e); - } - - /** - * Removes an Entry from the DataSets entries array. This will also - * recalculate the current minimum and maximum values of the DataSet and the - * value-sum. Returns true if an Entry was removed, false if no Entry could - * be removed. - * - * @param e - */ - public boolean removeEntry(T e) { - - if (e == null) - return false; - - // remove the entry - boolean removed = mYVals.remove(e); - - if (removed) { - - float val = e.getVal(); - mYValueSum -= val; - - calcMinMax(); - } - - return removed; - } - - /** - * Removes the Entry object that has the given xIndex from the DataSet. - * Returns true if an Entry was removed, false if no Entry could be removed. - * - * @param xIndex - */ - public boolean removeEntry(int xIndex) { - - T e = getEntryForXIndex(xIndex); - return removeEntry(e); - } - - /** BELOW THIS COLOR HANDLING */ - - /** - * Sets the colors that should be used fore this DataSet. Colors are reused - * as soon as the number of Entries the DataSet represents is higher than - * the size of the colors array. If you are using colors from the resources, - * make sure that the colors are already prepared (by calling - * getResources().getColor(...)) before adding them to the DataSet. - * - * @param colors - */ - public void setColors(ArrayList colors) { - this.mColors = colors; - } - - /** - * Sets the colors that should be used fore this DataSet. Colors are reused - * as soon as the number of Entries the DataSet represents is higher than - * the size of the colors array. If you are using colors from the resources, - * make sure that the colors are already prepared (by calling - * getResources().getColor(...)) before adding them to the DataSet. - * - * @param colors - */ - public void setColors(int[] colors) { - this.mColors = ColorTemplate.createColors(colors); - } - - /** - * Sets the colors that should be used fore this DataSet. Colors are reused - * as soon as the number of Entries the DataSet represents is higher than - * the size of the colors array. You can use - * "new int[] { R.color.red, R.color.green, ... }" to provide colors for - * this method. Internally, the colors are resolved using - * getResources().getColor(...) - * - * @param colors - */ - public void setColors(int[] colors, Context c) { - - ArrayList clrs = new ArrayList(); - - for (int color : colors) { - clrs.add(c.getResources().getColor(color)); - } - - mColors = clrs; - } - - /** - * Adds a new color to the colors array of the DataSet. - * - * @param color - */ - public void addColor(int color) { - if (mColors == null) - mColors = new ArrayList(); - mColors.add(color); - } - - /** - * Sets the one and ONLY color that should be used for this DataSet. - * Internally, this recreates the colors array and adds the specified color. - * - * @param color - */ - public void setColor(int color) { - resetColors(); - mColors.add(color); - } - - /** - * returns all the colors that are set for this DataSet - * - * @return - */ - public ArrayList getColors() { - return mColors; - } - - /** - * Returns the color at the given index of the DataSet's color array. - * Performs a IndexOutOfBounds check by modulus. - * - * @param index - * @return - */ - public int getColor(int index) { - return mColors.get(index % mColors.size()); - } - - /** - * Returns the first color (index 0) of the colors-array this DataSet - * contains. - * - * @return - */ - public int getColor() { - return mColors.get(0); - } - - /** - * Resets all colors of this DataSet and recreates the colors array. - */ - public void resetColors() { - mColors = new ArrayList(); - } - - /** - * Returns the position of the provided entry in the DataSets Entry array. - * Returns -1 if doesnt exist. - * - * @param e - * @return - */ - public int getEntryPosition(Entry e) { - - for (int i = 0; i < mYVals.size(); i++) { - if (e.equalTo(mYVals.get(i))) - return i; - } - - return -1; - } - - /** - * Sets the formatter to be used for drawing the values inside the chart. If - * no formatter is set, the chart will automatically determine a reasonable - * formatting (concerning decimals) for all the values that are drawn inside - * the chart. Use chart.getDefaultValueFormatter() to use the formatter - * calculated by the chart. - * - * @param f - */ - public void setValueFormatter(ValueFormatter f) { - - if (f == null) - return; - else - mValueFormatter = f; - } - - /** - * Returns the formatter used for drawing the values inside the chart. - * - * @return - */ - public ValueFormatter getValueFormatter() { - if (mValueFormatter == null) - return new DefaultValueFormatter(1); - return mValueFormatter; - } - - /** - * If this component has no ValueFormatter or is only equipped with the - * default one (no custom set), return true. - * - * @return - */ - public boolean needsDefaultFormatter() { - if (mValueFormatter == null) - return true; - if (mValueFormatter instanceof DefaultValueFormatter) - return true; - - return false; - } - - /** - * Sets the color the value-labels of this DataSet should have. - * - * @param color - */ - public void setValueTextColor(int color) { - mValueColor = color; - } - - public int getValueTextColor() { - return mValueColor; - } - - /** - * Sets a Typeface for the value-labels of this DataSet. - * - * @param tf - */ - public void setValueTypeface(Typeface tf) { - mValueTypeface = tf; - } - - public Typeface getValueTypeface() { - return mValueTypeface; - } - - /** - * Sets the text-size of the value-labels of this DataSet in dp. - * - * @param size - */ - public void setValueTextSize(float size) { - mValueTextSize = Utils.convertDpToPixel(size); - } - - public float getValueTextSize() { - return mValueTextSize; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/Entry.java b/MPChartLib/src/com/github/mikephil/charting/data/Entry.java deleted file mode 100644 index 59e6fd80e3..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/Entry.java +++ /dev/null @@ -1,165 +0,0 @@ - -package com.github.mikephil.charting.data; - -/** - * Class representing one entry in the chart. Might contain multiple values. - * Might only contain a single value depending on the used constructor. - * - * @author Philipp Jahoda - */ -public class Entry { - - /** the actual value */ - private float mVal = 0f; - - /** the index on the x-axis */ - private int mXIndex = 0; - - /** optional spot for additional data this Entry represents */ - private Object mData = null; - - /** - * A Entry represents one single entry in the chart. - * - * @param val the y value (the actual value of the entry) - * @param xIndex the corresponding index in the x value array (index on the - * x-axis of the chart, must NOT be higher than the length of the - * x-values String array) - */ - public Entry(float val, int xIndex) { - mVal = val; - mXIndex = xIndex; - } - - /** - * A Entry represents one single entry in the chart. - * - * @param val the y value (the actual value of the entry) - * @param xIndex the corresponding index in the x value array (index on the - * x-axis of the chart, must NOT be higher than the length of the - * x-values String array) - * @param data Spot for additional data this Entry represents. - */ - public Entry(float val, int xIndex, Object data) { - this(val, xIndex); - - this.mData = data; - } - - /** - * returns the x-index the value of this object is mapped to - * - * @return - */ - public int getXIndex() { - return mXIndex; - } - - /** - * sets the x-index for the entry - * - * @param x - */ - public void setXIndex(int x) { - this.mXIndex = x; - } - - /** - * Returns the total value the entry represents. - * - * @return - */ - public float getVal() { - return mVal; - } - - /** - * Sets the value for the entry. - * - * @param val - */ - public void setVal(float val) { - this.mVal = val; - } - - /** - * Returns the data, additional information that this Entry represents, or - * null, if no data has been specified. - * - * @return - */ - public Object getData() { - return mData; - } - - /** - * Sets additional data this Entry should represents. - * - * @param data - */ - public void setData(Object data) { - this.mData = data; - } - - // /** - // * If this Enry represents mulitple values (e.g. Stacked BarChart), it - // will - // * return the sum of them, otherwise just the one value it represents. - // * - // * @return - // */ - // public float getSum() { - // if (mVals == null) - // return mVal; - // else { - // - // float sum = 0f; - // - // for (int i = 0; i < mVals.length; i++) - // sum += mVals[i]; - // - // return sum; - // } - // } - - /** - * returns an exact copy of the entry - * - * @return - */ - public Entry copy() { - Entry e = new Entry(mVal, mXIndex, mData); - return e; - } - - /** - * Compares value, xIndex and data of the entries. Returns true if entries - * are equal, false if not. - * - * @param e - * @return - */ - public boolean equalTo(Entry e) { - - if (e == null) - return false; - - if (e.mData != this.mData) - return false; - if (e.mXIndex != this.mXIndex) - return false; - - if (Math.abs(e.mVal - this.mVal) > 0.00001f) - return false; - - return true; - } - - /** - * returns a string representation of the entry containing x-index and value - */ - @Override - public String toString() { - return "Entry, xIndex: " + mXIndex + " val (sum): " + getVal(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineData.java b/MPChartLib/src/com/github/mikephil/charting/data/LineData.java deleted file mode 100644 index 329b0c1cc1..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineData.java +++ /dev/null @@ -1,46 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; - -/** - * Data object that encapsulates all data associated with a LineChart. - * - * @author Philipp Jahoda - */ -public class LineData extends BarLineScatterCandleData { - - public LineData() { - super(); - } - - public LineData(ArrayList xVals) { - super(xVals); - } - - public LineData(String[] xVals) { - super(xVals); - } - - public LineData(ArrayList xVals, ArrayList dataSets) { - super(xVals, dataSets); - } - - public LineData(String[] xVals, ArrayList dataSets) { - super(xVals, dataSets); - } - - public LineData(ArrayList xVals, LineDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - public LineData(String[] xVals, LineDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - private static ArrayList toArrayList(LineDataSet dataSet) { - ArrayList sets = new ArrayList(); - sets.add(dataSet); - return sets; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java deleted file mode 100644 index 60220a3ac5..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java +++ /dev/null @@ -1,272 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.DashPathEffect; - -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class LineDataSet extends LineRadarDataSet { - - /** arraylist representing all colors that are used for the circles */ - private ArrayList mCircleColors = null; - - /** the radius of the circle-shaped value indicators */ - private float mCircleSize = 8f; - - /** sets the intensity of the cubic lines */ - private float mCubicIntensity = 0.2f; - - /** the path effect of this DataSet that makes dashed lines possible */ - private DashPathEffect mDashPathEffect = null; - - /** if true, drawing circles is enabled */ - private boolean mDrawCircles = true; - - /** if true, cubic lines are drawn instead of linear */ - private boolean mDrawCubic = false; - - public LineDataSet(ArrayList yVals, String label) { - super(yVals, label); - - // mCircleSize = Utils.convertDpToPixel(4f); - // mLineWidth = Utils.convertDpToPixel(1f); - - mCircleColors = new ArrayList(); - - // default colors - // mColors.add(Color.rgb(192, 255, 140)); - // mColors.add(Color.rgb(255, 247, 140)); - mCircleColors.add(Color.rgb(140, 234, 255)); - } - - @Override - public DataSet copy() { - - ArrayList yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); - } - - LineDataSet copied = new LineDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mCircleSize = mCircleSize; - copied.mCircleColors = mCircleColors; - copied.mDashPathEffect = mDashPathEffect; - copied.mDrawCircles = mDrawCircles; - copied.mDrawCubic = mDrawCubic; - copied.mHighLightColor = mHighLightColor; - - return copied; - } - - /** - * Sets the intensity for cubic lines (if enabled). Max = 1f = very cubic, - * Min = 0.05f = low cubic effect, Default: 0.2f - * - * @param intensity - */ - public void setCubicIntensity(float intensity) { - - if (intensity > 1f) - intensity = 1f; - if (intensity < 0.05f) - intensity = 0.05f; - - mCubicIntensity = intensity; - } - - /** - * Returns the intensity of the cubic lines (the effect intensity). - * - * @return - */ - public float getCubicIntensity() { - return mCubicIntensity; - } - - /** - * sets the size (radius) of the circle shpaed value indicators, default - * size = 4f - * - * @param size - */ - public void setCircleSize(float size) { - mCircleSize = Utils.convertDpToPixel(size); - } - - /** - * returns the circlesize - */ - public float getCircleSize() { - return mCircleSize; - } - - /** - * Enables the line to be drawn in dashed mode, e.g. like this "- - - - - -" - * - * @param lineLength the length of the line pieces - * @param spaceLength the length of space inbetween the pieces - * @param phase offset, in degrees (normally, use 0) - */ - public void enableDashedLine(float lineLength, float spaceLength, float phase) { - mDashPathEffect = new DashPathEffect(new float[] { - lineLength, spaceLength - }, phase); - } - - /** - * Disables the line to be drawn in dashed mode. - */ - public void disableDashedLine() { - mDashPathEffect = null; - } - - /** - * Returns true if the dashed-line effect is enabled, false if not. - * - * @return - */ - public boolean isDashedLineEnabled() { - return mDashPathEffect == null ? false : true; - } - - /** - * returns the DashPathEffect that is set for this DataSet - * - * @return - */ - public DashPathEffect getDashPathEffect() { - return mDashPathEffect; - } - - /** - * set this to true to enable the drawing of circle indicators for this - * DataSet, default true - * - * @param enabled - */ - public void setDrawCircles(boolean enabled) { - this.mDrawCircles = enabled; - } - - /** - * returns true if drawing circles for this DataSet is enabled, false if not - * - * @return - */ - public boolean isDrawCirclesEnabled() { - return mDrawCircles; - } - - /** - * If set to true, the linechart lines are drawn in cubic-style instead of - * linear. Default: false - * - * @param enabled - */ - public void setDrawCubic(boolean enabled) { - mDrawCubic = enabled; - } - - /** - * returns true if drawing cubic lines is enabled, false if not. - * - * @return - */ - public boolean isDrawCubicEnabled() { - return mDrawCubic; - } - - /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */ - - /** - * returns all colors specified for the circles - * - * @return - */ - public ArrayList getCircleColors() { - return mCircleColors; - } - - /** - * Returns the color at the given index of the DataSet's circle-color array. - * Performs a IndexOutOfBounds check by modulus. - * - * @param index - * @return - */ - public int getCircleColor(int index) { - return mCircleColors.get(index % mCircleColors.size()); - } - - /** - * Sets the colors that should be used for the circles of this DataSet. - * Colors are reused as soon as the number of Entries the DataSet represents - * is higher than the size of the colors array. Make sure that the colors - * are already prepared (by calling getResources().getColor(...)) before - * adding them to the DataSet. - * - * @param colors - */ - public void setCircleColors(ArrayList colors) { - mCircleColors = colors; - } - - /** - * Sets the colors that should be used for the circles of this DataSet. - * Colors are reused as soon as the number of Entries the DataSet represents - * is higher than the size of the colors array. Make sure that the colors - * are already prepared (by calling getResources().getColor(...)) before - * adding them to the DataSet. - * - * @param colors - */ - public void setCircleColors(int[] colors) { - this.mCircleColors = ColorTemplate.createColors(colors); - } - - /** - * ets the colors that should be used for the circles of this DataSet. - * Colors are reused as soon as the number of Entries the DataSet represents - * is higher than the size of the colors array. You can use - * "new String[] { R.color.red, R.color.green, ... }" to provide colors for - * this method. Internally, the colors are resolved using - * getResources().getColor(...) - * - * @param colors - */ - public void setCircleColors(int[] colors, Context c) { - - ArrayList clrs = new ArrayList(); - - for (int color : colors) { - clrs.add(c.getResources().getColor(color)); - } - - mCircleColors = clrs; - } - - /** - * Sets the one and ONLY color that should be used for this DataSet. - * Internally, this recreates the colors array and adds the specified color. - * - * @param color - */ - public void setCircleColor(int color) { - resetCircleColors(); - mCircleColors.add(color); - } - - /** - * resets the circle-colors array and creates a new one - */ - public void resetCircleColors() { - mCircleColors = new ArrayList(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieData.java b/MPChartLib/src/com/github/mikephil/charting/data/PieData.java deleted file mode 100644 index feb6348adb..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/PieData.java +++ /dev/null @@ -1,73 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; - -/** - * A PieData object can only represent one DataSet. Unlike all other charts, the - * legend labels of the PieChart are created from the x-values array, and not - * from the DataSet labels. Each PieData object can only represent one - * PieDataSet (multiple PieDataSets inside a single PieChart are not possible). - * - * @author Philipp Jahoda - */ -public class PieData extends ChartData { - - public PieData() { - super(); - } - - public PieData(ArrayList xVals) { - super(xVals); - } - - public PieData(String[] xVals) { - super(xVals); - } - - public PieData(ArrayList xVals, PieDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - public PieData(String[] xVals, PieDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - private static ArrayList toArrayList(PieDataSet dataSet) { - ArrayList sets = new ArrayList(); - sets.add(dataSet); - return sets; - } - - /** - * Sets the PieDataSet this data object should represent. - * - * @param dataSet - */ - public void setDataSet(PieDataSet dataSet) { - mDataSets.clear(); - mDataSets.add(dataSet); - init(mDataSets); - } - - /** - * Returns the DataSet this PieData object represents. A PieData object can - * only contain one DataSet. - * - * @return - */ - public PieDataSet getDataSet() { - return mDataSets.get(0); - } - - @Override - public PieDataSet getDataSetByIndex(int index) { - return index == 0 ? getDataSet() : null; - } - - @Override - public PieDataSet getDataSetByLabel(String label, boolean ignorecase) { - return ignorecase ? label.equalsIgnoreCase(mDataSets.get(0).getLabel()) ? mDataSets.get(0) - : null : label.equals(mDataSets.get(0).getLabel()) ? mDataSets.get(0) : null; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java deleted file mode 100644 index cc866b2a93..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java +++ /dev/null @@ -1,82 +0,0 @@ - -package com.github.mikephil.charting.data; - -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class PieDataSet extends DataSet { - - /** the space in degrees between the chart-slices, default 0f */ - private float mSliceSpace = 0f; - - /** indicates the selection distance of a pie slice */ - private float mShift = 18f; - - public PieDataSet(ArrayList yVals, String label) { - super(yVals, label); -// mShift = Utils.convertDpToPixel(12f); - } - - @Override - public DataSet copy() { - - ArrayList yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); - } - - PieDataSet copied = new PieDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mSliceSpace = mSliceSpace; - copied.mShift = mShift; - return copied; - } - - /** - * sets the space that is left out between the piechart-slices, default: 0° - * --> no space, maximum 45, minimum 0 (no space) - * - * @param degrees - */ - public void setSliceSpace(float degrees) { - - if (degrees > 45) - degrees = 45f; - if (degrees < 0) - degrees = 0f; - - mSliceSpace = degrees; - } - - /** - * returns the space that is set to be between the piechart-slices of this - * DataSet, in degrees - * - * @return - */ - public float getSliceSpace() { - return mSliceSpace; - } - - /** - * sets the distance the highlighted piechart-slice of this DataSet is - * "shifted" away from the center of the chart, default 12f - * - * @param shift - */ - public void setSelectionShift(float shift) { - mShift = Utils.convertDpToPixel(shift); - } - - /** - * returns the distance a highlighted piechart slice is "shifted" away from - * the chart-center - * - * @return - */ - public float getSelectionShift() { - return mShift; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java b/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java deleted file mode 100644 index aca96e00a2..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java +++ /dev/null @@ -1,46 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; - -/** - * Data container for the RadarChart. - * - * @author Philipp Jahoda - */ -public class RadarData extends ChartData { - - public RadarData() { - super(); - } - - public RadarData(ArrayList xVals) { - super(xVals); - } - - public RadarData(String[] xVals) { - super(xVals); - } - - public RadarData(ArrayList xVals, ArrayList dataSets) { - super(xVals, dataSets); - } - - public RadarData(String[] xVals, ArrayList dataSets) { - super(xVals, dataSets); - } - - public RadarData(ArrayList xVals, RadarDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - public RadarData(String[] xVals, RadarDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - private static ArrayList toArrayList(RadarDataSet dataSet) { - ArrayList sets = new ArrayList(); - sets.add(dataSet); - return sets; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java deleted file mode 100644 index 9015797e4d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java +++ /dev/null @@ -1,27 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; - -public class RadarDataSet extends LineRadarDataSet { - - public RadarDataSet(ArrayList yVals, String label) { - super(yVals, label); - } - - @Override - public DataSet copy() { - - ArrayList yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); - } - - RadarDataSet copied = new RadarDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mHighLightColor = mHighLightColor; - - return copied; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java b/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java deleted file mode 100644 index 815b7249a5..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java +++ /dev/null @@ -1,60 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; - -public class ScatterData extends BarLineScatterCandleData { - - public ScatterData() { - super(); - } - - public ScatterData(ArrayList xVals) { - super(xVals); - } - - public ScatterData(String[] xVals) { - super(xVals); - } - - public ScatterData(ArrayList xVals, ArrayList dataSets) { - super(xVals, dataSets); - } - - public ScatterData(String[] xVals, ArrayList dataSets) { - super(xVals, dataSets); - } - - public ScatterData(ArrayList xVals, ScatterDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - public ScatterData(String[] xVals, ScatterDataSet dataSet) { - super(xVals, toArrayList(dataSet)); - } - - private static ArrayList toArrayList(ScatterDataSet dataSet) { - ArrayList sets = new ArrayList(); - sets.add(dataSet); - return sets; - } - - /** - * Returns the maximum shape-size across all DataSets. - * - * @return - */ - public float getGreatestShapeSize() { - - float max = 0f; - - for (ScatterDataSet set : mDataSets) { - float size = set.getScatterShapeSize(); - - if (size > max) - max = size; - } - - return max; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java deleted file mode 100644 index 13adf227ae..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java +++ /dev/null @@ -1,112 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.graphics.Path; - -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class ScatterDataSet extends BarLineScatterCandleDataSet { - - /** the size the scattershape will have, in screen pixels */ - private float mShapeSize = 15f; - - /** - * the type of shape that is set to be drawn where the values are at, - * default ScatterShape.SQUARE - */ - private ScatterShape mScatterShape = ScatterShape.SQUARE; - - /** - * Custom path object the user can provide that is drawn where the values - * are at. This is used when ScatterShape.CUSTOM is set for a DataSet. - */ - private Path mCustomScatterPath = null; - - public ScatterDataSet(ArrayList yVals, String label) { - super(yVals, label); - - // mShapeSize = Utils.convertDpToPixel(8f); - } - - @Override - public DataSet copy() { - - ArrayList yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); - } - - ScatterDataSet copied = new ScatterDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mShapeSize = mShapeSize; - copied.mScatterShape = mScatterShape; - copied.mCustomScatterPath = mCustomScatterPath; - copied.mHighLightColor = mHighLightColor; - - return copied; - } - - /** - * Sets the size in density pixels the drawn scattershape will have. This - * only applies for non custom shapes. - * - * @param size - */ - public void setScatterShapeSize(float size) { - mShapeSize = Utils.convertDpToPixel(size); - } - - /** - * returns the currently set scatter shape size - * - * @return - */ - public float getScatterShapeSize() { - return mShapeSize; - } - - /** - * Sets the shape that is drawn on the position where the values are at. If - * "CUSTOM" is chosen, you need to call setCustomScatterShape(...) and - * provide a path object that is drawn as the custom scattershape. - * - * @param shape - */ - public void setScatterShape(ScatterShape shape) { - mScatterShape = shape; - } - - /** - * returns all the different scattershapes the chart uses - * - * @return - */ - public ScatterShape getScatterShape() { - return mScatterShape; - } - - /** - * Sets a path object as the shape to be drawn where the values are at. Do - * not forget to call setScatterShape(...) and set the shape to - * ScatterShape.CUSTOM. - * - * @param shape - */ - public void setCustomScatterShape(Path shape) { - mCustomScatterPath = shape; - } - - /** - * returns the custom path / shape that is specified to be drawn where the - * values are at - * - * @return - */ - public Path getCustomScatterShape() { - return mCustomScatterPath; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/com/github/mikephil/charting/data/filter/Approximator.java deleted file mode 100644 index a4e963e2ba..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/filter/Approximator.java +++ /dev/null @@ -1,281 +0,0 @@ - -package com.github.mikephil.charting.data.filter; - -import com.github.mikephil.charting.data.Entry; - -import java.util.ArrayList; - -/** - * Implemented according to Wiki-Pseudocode {@link} - * http://en.wikipedia.org/wiki/Ramer�Douglas�Peucker_algorithm - * - * @author Philipp Baldauf & Phliipp Jahoda - */ -public class Approximator { - - /** the type of filtering algorithm to use */ - private ApproximatorType mType = ApproximatorType.DOUGLAS_PEUCKER; - - /** the tolerance to be filtered with */ - private double mTolerance = 0; - - private float mScaleRatio = 1f; - private float mDeltaRatio = 1f; - - /** - * array that contains "true" on all indices that will be kept after - * filtering - */ - private boolean[] keep; - - /** enums for the different types of filtering algorithms */ - public enum ApproximatorType { - NONE, DOUGLAS_PEUCKER - } - - /** - * Initializes the approximator with type NONE - */ - public Approximator() { - this.mType = ApproximatorType.NONE; - } - - /** - * Initializes the approximator with the given type and tolerance. If - * toleranec <= 0, no filtering will be done. - * - * @param type - */ - public Approximator(ApproximatorType type, double tolerance) { - setup(type, tolerance); - } - - /** - * sets type and tolerance, if tolerance <= 0, no filtering will be done - * - * @param type - * @param tolerance - */ - public void setup(ApproximatorType type, double tolerance) { - mType = type; - mTolerance = tolerance; - } - - /** - * sets the tolerance for the Approximator. When using the - * Douglas-Peucker-Algorithm, the tolerance is an angle in degrees, that - * will trigger the filtering. - */ - public void setTolerance(double tolerance) { - mTolerance = tolerance; - } - - /** - * Sets the filtering algorithm that should be used. - * - * @param type - */ - public void setType(ApproximatorType type) { - this.mType = type; - } - - /** - * Sets the ratios for x- and y-axis, as well as the ratio of the scale - * levels - * - * @param deltaRatio - * @param scaleRatio - */ - public void setRatios(float deltaRatio, float scaleRatio) { - mDeltaRatio = deltaRatio; - mScaleRatio = scaleRatio; - } - - /** - * Filters according to type. Uses the pre set set tolerance - * - * @param points the points to filter - * @return - */ - public ArrayList filter(ArrayList points) { - return filter(points, mTolerance); - } - - /** - * Filters according to type. - * - * @param points the points to filter - * @param tolerance the angle in degrees that will trigger the filtering - * @return - */ - public ArrayList filter(ArrayList points, double tolerance) { - - if (tolerance <= 0) - return points; - - keep = new boolean[points.size()]; - - switch (mType) { - case DOUGLAS_PEUCKER: - return reduceWithDouglasPeuker(points, tolerance); - case NONE: - return points; - default: - return points; - } - } - - /** - * uses the douglas peuker algorithm to reduce the given arraylist of - * entries - * - * @param entries - * @param epsilon - * @return - */ - private ArrayList reduceWithDouglasPeuker(ArrayList entries, double epsilon) { - // if a shape has 2 or less points it cannot be reduced - if (epsilon <= 0 || entries.size() < 3) { - return entries; - } - - // first and last always stay - keep[0] = true; - keep[entries.size() - 1] = true; - - // first and last entry are entry point to recursion - algorithmDouglasPeucker(entries, epsilon, 0, entries.size() - 1); - - // create a new array with series, only take the kept ones - ArrayList reducedEntries = new ArrayList(); - for (int i = 0; i < entries.size(); i++) { - if (keep[i]) { - Entry curEntry = entries.get(i); - reducedEntries.add(new Entry(curEntry.getVal(), curEntry.getXIndex())); - } - } - return reducedEntries; - } - - /** - * apply the Douglas-Peucker-Reduction to an ArrayList of Entry with a given - * epsilon (tolerance) - * - * @param entries - * @param epsilon as y-value - * @param start - * @param end - */ - private void algorithmDouglasPeucker(ArrayList entries, double epsilon, int start, - int end) { - if (end <= start + 1) { - // recursion finished - return; - } - - // find the greatest distance between start and endpoint - int maxDistIndex = 0; - double distMax = 0; - - Entry firstEntry = entries.get(start); - Entry lastEntry = entries.get(end); - - for (int i = start + 1; i < end; i++) { - double dist = calcAngleBetweenLines(firstEntry, lastEntry, firstEntry, entries.get(i)); - - // keep the point with the greatest distance - if (dist > distMax) { - distMax = dist; - maxDistIndex = i; - } - } - - // Log.i("maxangle", "" + distMax); - - if (distMax > epsilon) { - // keep max dist point - keep[maxDistIndex] = true; - - // recursive call - algorithmDouglasPeucker(entries, epsilon, start, maxDistIndex); - algorithmDouglasPeucker(entries, epsilon, maxDistIndex, end); - } // else don't keep the point... - } - - /** - * calculate the distance between a line between two entries and an entry - * (point) - * - * @param startEntry line startpoint - * @param endEntry line endpoint - * @param entryPoint the point to which the distance is measured from the - * line - * @return - */ - public double calcPointToLineDistance(Entry startEntry, Entry endEntry, Entry entryPoint) { - - float xDiffEndStart = (float) endEntry.getXIndex() - (float) startEntry.getXIndex(); - float xDiffEntryStart = (float) entryPoint.getXIndex() - (float) startEntry.getXIndex(); - - double normalLength = Math.sqrt((xDiffEndStart) - * (xDiffEndStart) - + (endEntry.getVal() - startEntry.getVal()) - * (endEntry.getVal() - startEntry.getVal())); - return Math.abs((xDiffEntryStart) - * (endEntry.getVal() - startEntry.getVal()) - - (entryPoint.getVal() - startEntry.getVal()) - * (xDiffEndStart)) - / normalLength; - } - - /** - * Calculates the angle between two given lines. The provided Entry objects - * mark the starting and end points of the lines. - * - * @param start1 - * @param end1 - * @param start2 - * @param end2 - * @return - */ - public double calcAngleBetweenLines(Entry start1, Entry end1, Entry start2, Entry end2) { - - double angle1 = calcAngleWithRatios(start1, end1); - double angle2 = calcAngleWithRatios(start2, end2); - - return Math.abs(angle1 - angle2); - } - - /** - * calculates the angle between two Entries (points) in the chart taking - * ratios into consideration - * - * @param p1 - * @param p2 - * @return - */ - public double calcAngleWithRatios(Entry p1, Entry p2) { - - float dx = p2.getXIndex() * mDeltaRatio - p1.getXIndex() * mDeltaRatio; - float dy = p2.getVal() * mScaleRatio - p1.getVal() * mScaleRatio; - double angle = Math.atan2(dy, dx) * 180.0 / Math.PI; - - return angle; - } - - /** - * calculates the angle between two Entries (points) in the chart - * - * @param p1 - * @param p2 - * @return - */ - public double calcAngle(Entry p1, Entry p2) { - - float dx = p2.getXIndex() - p1.getXIndex(); - float dy = p2.getVal() - p1.getVal(); - double angle = Math.atan2(dy, dx) * 180.0 / Math.PI; - - return angle; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/BarDataProvider.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/BarDataProvider.java deleted file mode 100644 index 6b9b19da8d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/BarDataProvider.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.mikephil.charting.interfaces; - -import com.github.mikephil.charting.data.BarData; - -public interface BarDataProvider extends BarLineScatterCandleDataProvider { - - public BarData getBarData(); - public boolean isDrawBarShadowEnabled(); - public boolean isDrawValueAboveBarEnabled(); - public boolean isDrawHighlightArrowEnabled(); - public boolean isDrawValuesForWholeStackEnabled(); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/BarLineScatterCandleDataProvider.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/BarLineScatterCandleDataProvider.java deleted file mode 100644 index 584ac129d7..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/BarLineScatterCandleDataProvider.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.mikephil.charting.interfaces; - -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.utils.Transformer; - -public interface BarLineScatterCandleDataProvider extends ChartInterface { - - public Transformer getTransformer(AxisDependency axis); - public int getMaxVisibleCount(); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/ChartInterface.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/ChartInterface.java deleted file mode 100644 index 93649d9c5b..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/ChartInterface.java +++ /dev/null @@ -1,40 +0,0 @@ - -package com.github.mikephil.charting.interfaces; - -import android.graphics.PointF; -import android.graphics.RectF; -import android.view.View; - -import com.github.mikephil.charting.utils.ValueFormatter; - -/** - * Interface that provides everything there is to know about the dimensions, - * bounds, and range of the chart. - * - * @author Philipp Jahoda - */ -public interface ChartInterface { - - public float getXChartMin(); - - public float getXChartMax(); - - public float getYChartMin(); - - public float getYChartMax(); - - public int getWidth(); - - public int getHeight(); - - public PointF getCenterOfView(); - - public PointF getCenterOffsets(); - - public RectF getContentRect(); - - public View getChartView(); - - public ValueFormatter getDefaultValueFormatter(); - -} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/LineDataProvider.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/LineDataProvider.java deleted file mode 100644 index 479e0d86bc..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/LineDataProvider.java +++ /dev/null @@ -1,25 +0,0 @@ - -package com.github.mikephil.charting.interfaces; - -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.utils.FillFormatter; - -public interface LineDataProvider extends BarLineScatterCandleDataProvider { - - public LineData getLineData(); - - /** - * Sets a custom FillFormatter to the chart that handles the position of the - * filled-line for each DataSet. Set this to null to use the default logic. - * - * @param formatter - */ - public void setFillFormatter(FillFormatter formatter); - - /** - * Returns the FillFormatter that handles the position of the filled-line. - * - * @return - */ - public FillFormatter getFillFormatter(); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java b/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java deleted file mode 100644 index f199619c9b..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java +++ /dev/null @@ -1,470 +0,0 @@ - -package com.github.mikephil.charting.listener; - -import android.annotation.SuppressLint; -import android.graphics.Matrix; -import android.graphics.PointF; -import android.util.Log; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; - -import com.github.mikephil.charting.charts.BarLineChartBase; -import com.github.mikephil.charting.data.BarLineScatterCandleData; -import com.github.mikephil.charting.data.BarLineScatterCandleDataSet; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.renderer.ViewPortHandler; -import com.github.mikephil.charting.utils.Highlight; - -/** - * TouchListener for Bar-, Line-, Scatter- and CandleStickChart with handles all - * touch interaction. Longpress == Zoom out. Double-Tap == Zoom in. - * - * @author Philipp Jahoda - */ -public class BarLineChartTouchListener>>> - extends SimpleOnGestureListener implements OnTouchListener { - - /** the original touch-matrix from the chart */ - private Matrix mMatrix = new Matrix(); - - /** matrix for saving the original matrix state */ - private Matrix mSavedMatrix = new Matrix(); - - /** point where the touch action started */ - private PointF mTouchStartPoint = new PointF(); - - /** center between two pointers (fingers on the display) */ - private PointF mTouchPointCenter = new PointF(); - - // states - private static final int NONE = 0; - private static final int DRAG = 1; - - private static final int X_ZOOM = 2; - private static final int Y_ZOOM = 3; - private static final int PINCH_ZOOM = 4; - private static final int POST_ZOOM = 5; - - /** integer field that holds the current touch-state */ - private int mTouchMode = NONE; - - private float mSavedXDist = 1f; - private float mSavedYDist = 1f; - private float mSavedDist = 1f; - - /** the last highlighted object */ - private Highlight mLastHighlighted; - - private DataSet mClosestDataSetToTouch; - - /** the chart the listener represents */ - private T mChart; - - /** the gesturedetector used for detecting taps and longpresses, ... */ - private GestureDetector mGestureDetector; - - public BarLineChartTouchListener(T chart, Matrix touchMatrix) { - this.mChart = chart; - this.mMatrix = touchMatrix; - - mGestureDetector = new GestureDetector(chart.getContext(), this); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouch(View v, MotionEvent event) { - - if (mTouchMode == NONE) { - mGestureDetector.onTouchEvent(event); - } - - if (!mChart.isDragEnabled() && !mChart.isScaleEnabled()) - return true; - - // Handle touch events here... - switch (event.getAction() & MotionEvent.ACTION_MASK) { - - case MotionEvent.ACTION_DOWN: - - saveTouchStart(event); - break; - case MotionEvent.ACTION_POINTER_DOWN: - - if (event.getPointerCount() >= 2) { - - mChart.disableScroll(); - - saveTouchStart(event); - - // get the distance between the pointers on the x-axis - mSavedXDist = getXDist(event); - - // get the distance between the pointers on the y-axis - mSavedYDist = getYDist(event); - - // get the total distance between the pointers - mSavedDist = spacing(event); - - if (mSavedDist > 10f) { - - if (mChart.isPinchZoomEnabled()) { - mTouchMode = PINCH_ZOOM; - } else { - if (mSavedXDist > mSavedYDist) - mTouchMode = X_ZOOM; - else - mTouchMode = Y_ZOOM; - } - } - - // determine the touch-pointer center - midPoint(mTouchPointCenter, event); - } - break; - case MotionEvent.ACTION_MOVE: - - if (mTouchMode == DRAG) { - - mChart.disableScroll(); - - if (mChart.isDragEnabled()) - performDrag(event); - - } else if (mTouchMode == X_ZOOM || mTouchMode == Y_ZOOM || mTouchMode == PINCH_ZOOM) { - - mChart.disableScroll(); - - if (mChart.isScaleEnabled()) - performZoom(event); - - } else if (mTouchMode == NONE - && Math.abs(distance(event.getX(), mTouchStartPoint.x, event.getY(), - mTouchStartPoint.y)) > 5f) { - - if (mChart.hasNoDragOffset()) { - - if (!mChart.isFullyZoomedOut()) - mTouchMode = DRAG; - - } else { - mTouchMode = DRAG; - } - } - break; - - case MotionEvent.ACTION_UP: - mTouchMode = NONE; - mChart.enableScroll(); - break; - case MotionEvent.ACTION_POINTER_UP: - mTouchMode = POST_ZOOM; - break; - } - - // Perform the transformation, update the chart - mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, true); - - return true; // indicate event was handled - } - - /** - * ################ ################ ################ ################ - */ - /** BELOW CODE PERFORMS THE ACTUAL TOUCH ACTIONS */ - - /** - * Saves the current Matrix state and the touch-start point. - * - * @param event - */ - private void saveTouchStart(MotionEvent event) { - - mSavedMatrix.set(mMatrix); - mTouchStartPoint.set(event.getX(), event.getY()); - - mClosestDataSetToTouch = mChart.getDataSetByTouchPoint(event.getX(), event.getY()); - } - - /** - * Performs all necessary operations needed for dragging. - * - * @param event - */ - private void performDrag(MotionEvent event) { - - mMatrix.set(mSavedMatrix); - PointF dragPoint = new PointF(event.getX(), event.getY()); - - // check if axis is inverted - if (mChart.isAnyAxisInverted() && mClosestDataSetToTouch != null - && mChart.getAxis(mClosestDataSetToTouch.getAxisDependency()).isInverted()) { - - mMatrix.postTranslate(dragPoint.x - mTouchStartPoint.x, -(dragPoint.y - - mTouchStartPoint.y)); - } - else { - mMatrix.postTranslate(dragPoint.x - mTouchStartPoint.x, dragPoint.y - - mTouchStartPoint.y); - } - } - - /** - * Performs the all operations necessary for pinch and axis zoom. - * - * @param event - */ - private void performZoom(MotionEvent event) { - - if (event.getPointerCount() >= 2) { - - // get the distance between the pointers of the touch - // event - float totalDist = spacing(event); - - if (totalDist > 10f) { - - // get the translation - PointF t = getTrans(mTouchPointCenter.x, mTouchPointCenter.y); - - // take actions depending on the activated touch - // mode - if (mTouchMode == PINCH_ZOOM) { - - float scale = totalDist / mSavedDist; // total - // scale - - mMatrix.set(mSavedMatrix); - mMatrix.postScale(scale, scale, t.x, t.y); - - } else if (mTouchMode == X_ZOOM) { - - float xDist = getXDist(event); - float scaleX = xDist / mSavedXDist; // x-axis - // scale - - mMatrix.set(mSavedMatrix); - mMatrix.postScale(scaleX, 1f, t.x, t.y); - - } else if (mTouchMode == Y_ZOOM) { - - float yDist = getYDist(event); - float scaleY = yDist / mSavedYDist; // y-axis - // scale - - mMatrix.set(mSavedMatrix); - - // y-axis comes from top to bottom, revert y - mMatrix.postScale(1f, scaleY, t.x, t.y); - - } - } - } - } - - /** - * ################ ################ ################ ################ - */ - /** DOING THE MATH BELOW ;-) */ - - /** - * returns the distance between two points - * - * @param eventX - * @param startX - * @param eventY - * @param startY - * @return - */ - private static float distance(float eventX, float startX, float eventY, float startY) { - float dx = eventX - startX; - float dy = eventY - startY; - return (float) Math.sqrt(dx * dx + dy * dy); - } - - /** - * Determines the center point between two pointer touch points. - * - * @param point - * @param event - */ - private static void midPoint(PointF point, MotionEvent event) { - float x = event.getX(0) + event.getX(1); - float y = event.getY(0) + event.getY(1); - point.set(x / 2f, y / 2f); - } - - /** - * returns the distance between two pointer touch points - * - * @param event - * @return - */ - private static float spacing(MotionEvent event) { - float x = event.getX(0) - event.getX(1); - float y = event.getY(0) - event.getY(1); - return (float) Math.sqrt(x * x + y * y); - } - - /** - * calculates the distance on the x-axis between two pointers (fingers on - * the display) - * - * @param e - * @return - */ - private static float getXDist(MotionEvent e) { - float x = Math.abs(e.getX(0) - e.getX(1)); - return x; - } - - /** - * calculates the distance on the y-axis between two pointers (fingers on - * the display) - * - * @param e - * @return - */ - private static float getYDist(MotionEvent e) { - float y = Math.abs(e.getY(0) - e.getY(1)); - return y; - } - - /** - * returns the correct translation depending on the provided x and y touch - * points - * - * @param e - * @return - */ - public PointF getTrans(float x, float y) { - - ViewPortHandler vph = mChart.getViewPortHandler(); - - float xTrans = x - vph.offsetLeft(); - float yTrans = 0f; - - // check if axis is inverted - if (mChart.isAnyAxisInverted() && mClosestDataSetToTouch != null - && mChart.getAxis(mClosestDataSetToTouch.getAxisDependency()).isInverted()) { - yTrans = -(y - vph.offsetTop()); - } else { - yTrans = -(mChart.getMeasuredHeight() - y - vph.offsetBottom()); - } - - return new PointF(xTrans, yTrans); - } - - /** - * ################ ################ ################ ################ - */ - /** GETTERS AND GESTURE RECOGNITION BELOW */ - - /** - * returns the matrix object the listener holds - * - * @return - */ - public Matrix getMatrix() { - return mMatrix; - } - - /** - * returns the touch mode the listener is currently in - * - * @return - */ - public int getTouchMode() { - return mTouchMode; - } - - @Override - public boolean onDoubleTap(MotionEvent e) { - - OnChartGestureListener l = mChart.getOnChartGestureListener(); - - if (l != null) { - l.onChartDoubleTapped(e); - return super.onDoubleTap(e); - } - - // check if double-tap zooming is enabled - if (mChart.isDoubleTapToZoomEnabled()) { - - PointF trans = getTrans(e.getX(), e.getY()); - - mChart.zoom(1.4f, 1.4f, trans.x, trans.y); - - Log.i("BarlineChartTouch", "Double-Tap, Zooming In, x: " + trans.x + ", y: " + trans.y); - } - - return super.onDoubleTap(e); - } - - @Override - public void onLongPress(MotionEvent e) { - - OnChartGestureListener l = mChart.getOnChartGestureListener(); - - if (l != null) { - - l.onChartLongPressed(e); - } - // else if (mTouchMode == NONE) { - // - // mChart.fitScreen(); - // - // Log.i("BarlineChartTouch", - // "Longpress, resetting zoom and drag, adjusting chart bounds to screen."); - // - // // PointF trans = getTrans(e.getX(), e.getY()); - // // - // // mChart.zoomOut(trans.x, trans.y); - // // - // // Log.i("BarlineChartTouch", "Longpress, Zooming Out, x: " + - // // trans.x + - // // ", y: " + trans.y); - // } - } - - @Override - public boolean onSingleTapUp(MotionEvent e) { - - OnChartGestureListener l = mChart.getOnChartGestureListener(); - - if (l != null) { - l.onChartSingleTapped(e); - } - - Highlight h = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); - - if (h == null || h.equalTo(mLastHighlighted)) { - mChart.highlightTouch(null); - mLastHighlighted = null; - } else { - mLastHighlighted = h; - mChart.highlightTouch(h); - } - - return super.onSingleTapUp(e); - } - - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { - return super.onSingleTapConfirmed(e); - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - - OnChartGestureListener l = mChart.getOnChartGestureListener(); - - if (l != null) - l.onChartFling(e1, e2, velocityX, velocityY); - - return super.onFling(e1, e2, velocityX, velocityY); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java b/MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java deleted file mode 100644 index 6eac55f8fd..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java +++ /dev/null @@ -1,43 +0,0 @@ - -package com.github.mikephil.charting.listener; - -import android.view.MotionEvent; - -/** - * Listener for callbacks when doing gestures on the chart. - * - * @author Philipp Jahoda - */ -public interface OnChartGestureListener { - - /** - * Callbacks when the chart is longpressed. - * - * @param me - */ - public void onChartLongPressed(MotionEvent me); - - /** - * Callbacks when the chart is double-tapped. - * - * @param me - */ - public void onChartDoubleTapped(MotionEvent me); - - /** - * Callbacks when the chart is single-tapped. - * - * @param me - */ - public void onChartSingleTapped(MotionEvent me); - - /** - * Callbacks then a fling gesture is made on the chart. - * - * @param me1 - * @param me2 - * @param velocityX - * @param velocityY - */ - public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java b/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java deleted file mode 100644 index 0c4fb4871c..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java +++ /dev/null @@ -1,186 +0,0 @@ - -package com.github.mikephil.charting.listener; - -import android.graphics.PointF; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; - -import com.github.mikephil.charting.charts.PieRadarChartBase; -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.SelInfo; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -/** - * Touchlistener for the PieChart. - * - * @author Philipp Jahoda - */ -public class PieRadarChartTouchListener extends SimpleOnGestureListener implements OnTouchListener { - - private static final int NONE = 0; - private static final int ROTATE = 1; - - private PointF mTouchStartPoint = new PointF(); - - private PieRadarChartBase mChart; - - private int mTouchMode = NONE; - - private GestureDetector mGestureDetector; - - public PieRadarChartTouchListener(PieRadarChartBase ctx) { - this.mChart = ctx; - - mGestureDetector = new GestureDetector(ctx.getContext(), this); - } - - @Override - public boolean onTouch(View v, MotionEvent e) { - - if (mGestureDetector.onTouchEvent(e)) - return true; - - // if rotation by touch is enabled - if (mChart.isRotationEnabled()) { - - float x = e.getX(); - float y = e.getY(); - - switch (e.getAction()) { - - case MotionEvent.ACTION_DOWN: - mChart.setStartAngle(x, y); - mTouchStartPoint.x = x; - mTouchStartPoint.y = y; - break; - case MotionEvent.ACTION_MOVE: - - if (mTouchMode == NONE && distance(x, mTouchStartPoint.x, y, mTouchStartPoint.y) - > Utils.convertDpToPixel(8f)) { - mTouchMode = ROTATE; - mChart.disableScroll(); - } else if (mTouchMode == ROTATE) { - mChart.updateRotation(x, y); - mChart.invalidate(); - } - - break; - case MotionEvent.ACTION_UP: - mChart.enableScroll(); - mTouchMode = NONE; - break; - } - } - - return true; - } - - @Override - public void onLongPress(MotionEvent me) { - OnChartGestureListener l = mChart.getOnChartGestureListener(); - - if (l != null) { - l.onChartLongPressed(me); - } - } - - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { - return true; - } - - /** reference to the last highlighted object */ - private Highlight mLastHighlight = null; - - @Override - public boolean onSingleTapUp(MotionEvent e) { - - OnChartGestureListener l = mChart.getOnChartGestureListener(); - - if (l != null) { - l.onChartSingleTapped(e); - } - - float distance = mChart.distanceToCenter(e.getX(), e.getY()); - - // check if a slice was touched - if (distance > mChart.getRadius()) { - - // if no slice was touched, highlight nothing - mChart.highlightValues(null); - mLastHighlight = null; - - } else { - - float angle = mChart.getAngleForPoint(e.getX(), e.getY()); - int index = mChart.getIndexForAngle(angle); - - // check if the index could be found - if (index < 0) { - - mChart.highlightValues(null); - mLastHighlight = null; - - } else { - - ArrayList valsAtIndex = mChart.getYValsAtIndex(index); - - int dataSetIndex = 0; - - // get the dataset that is closest to the selection (PieChart - // only - // has one DataSet) - if (mChart instanceof RadarChart) { - - dataSetIndex = Utils.getClosestDataSetIndex(valsAtIndex, distance - / ((RadarChart) mChart).getFactor(), null); - } - - Highlight h = new Highlight(index, dataSetIndex); - - if (h.equalTo(mLastHighlight)) { - - mChart.highlightTouch(null); - mLastHighlight = null; - } else { - - mChart.highlightTouch(h); - mLastHighlight = h; - } - } - } - - return true; - } - - @Override - public boolean onDoubleTap(MotionEvent e) { - OnChartGestureListener l = mChart.getOnChartGestureListener(); - - if (l != null) { - l.onChartDoubleTapped(e); - } - return super.onDoubleTap(e); - } - - /** - * returns the distance between two points - * - * @param eventX - * @param startX - * @param eventY - * @param startY - * @return - */ - private static float distance(float eventX, float startX, float eventY, float startY) { - float dx = eventX - startX; - float dy = eventY - startY; - return (float) Math.sqrt(dx * dx + dy * dy); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java deleted file mode 100644 index cc1d7e4763..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java +++ /dev/null @@ -1,81 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Style; - -import com.github.mikephil.charting.utils.Transformer; - -public abstract class AxisRenderer extends Renderer { - - protected Transformer mTrans; - - /** paint object for the grid lines */ - protected Paint mGridPaint; - - /** paint for the x-label values */ - protected Paint mAxisPaint; - - /** paint for the line surrounding the chart */ - protected Paint mAxisLinePaint; - - public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans) { - super(viewPortHandler); - - this.mTrans = trans; - - mAxisPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - mGridPaint = new Paint(); - mGridPaint.setColor(Color.GRAY); - mGridPaint.setStrokeWidth(1f); - mGridPaint.setStyle(Style.STROKE); - mGridPaint.setAlpha(90); - - mAxisLinePaint = new Paint(); - mAxisLinePaint.setColor(Color.BLACK); - mAxisLinePaint.setStrokeWidth(1f); - mAxisLinePaint.setStyle(Style.STROKE); - } - - /** - * Returns the Paint object used for drawing the axis. - * - * @return - */ - public Paint getAxisPaint() { - return mAxisPaint; - } - - /** - * Returns the Transformer object used for transforming the axis values. - * - * @return - */ - public Transformer getTransformer() { - return mTrans; - } - - /** - * Draws the axis labels to the screen. - * - * @param c - */ - public abstract void renderAxisLabels(Canvas c); - - /** - * Draws the grid lines belonging to the axis. - * - * @param c - */ - public abstract void renderGridLines(Canvas c); - - /** - * Draws the line that goes alongside the axis. - * - * @param c - */ - public abstract void renderAxisLine(Canvas c); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java deleted file mode 100644 index 8db89a2389..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ /dev/null @@ -1,421 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.RectF; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.interfaces.BarDataProvider; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ValueFormatter; - -import java.util.ArrayList; - -public class BarChartRenderer extends DataRenderer { - - protected BarDataProvider mChart; - - /** the rect object that is used for drawing the bar shadow */ - protected RectF mBarShadow = new RectF(); - - /** the rect object that is used for drawing the bars */ - protected RectF mBarRect = new RectF(); - - public BarChartRenderer(BarDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - this.mChart = chart; - - mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHighlightPaint.setStyle(Paint.Style.FILL); - mHighlightPaint.setColor(Color.rgb(0, 0, 0)); - // set alpha after color - mHighlightPaint.setAlpha(120); - } - - @Override - public void drawData(Canvas c) { - - BarData barData = mChart.getBarData(); - - for (int i = 0; i < barData.getDataSetCount(); i++) { - - BarDataSet set = barData.getDataSetByIndex(i); - - if (set.isVisible()) { - drawDataSet(c, set, i); - } - } - } - - protected void drawDataSet(Canvas c, BarDataSet dataSet, int index) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - // the space between bar-groups - float space = mChart.getBarData().getGroupSpace(); - - boolean noStacks = dataSet.getStackSize() == 1 ? true : false; - - ArrayList entries = dataSet.getYVals(); - - // do the drawing - for (int j = 0; j < dataSet.getEntryCount() * mAnimator.getPhaseX(); j++) { - - BarEntry e = entries.get(j); - - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + j * (mChart.getBarData().getDataSetCount() - 1) + index - + space * j + space / 2f; - float y = e.getVal(); - - // no stacks - if (noStacks) { - - prepareBar(x, y, dataSet.getBarSpace(), trans); - - // avoid drawing outofbounds values - if (!mViewPortHandler.isInBoundsRight(mBarRect.left)) - break; - - if (!mViewPortHandler.isInBoundsLeft(mBarRect.right)) - continue; - - // if drawing the bar shadow is enabled - if (mChart.isDrawBarShadowEnabled()) { - mRenderPaint.setColor(dataSet.getBarShadowColor()); - c.drawRect(mBarShadow, mRenderPaint); - } - - // Set the color for the currently drawn value. If the index - // is - // out of bounds, reuse colors. - mRenderPaint.setColor(dataSet.getColor(j)); - c.drawRect(mBarRect, mRenderPaint); - - } else { // stacked bars - - float[] vals = e.getVals(); - - // we still draw stacked bars, but there could be one - // non-stacked - // in between - if (vals == null) { - - prepareBar(x, y, dataSet.getBarSpace(), trans); - - // if drawing the bar shadow is enabled - if (mChart.isDrawBarShadowEnabled()) { - mRenderPaint.setColor(dataSet.getBarShadowColor()); - c.drawRect(mBarShadow, mRenderPaint); - } - - mRenderPaint.setColor(dataSet.getColor(0)); - c.drawRect(mBarRect, mRenderPaint); - - } else { - - float all = e.getVal(); - - // if drawing the bar shadow is enabled - if (mChart.isDrawBarShadowEnabled()) { - - prepareBar(x, y, dataSet.getBarSpace(), trans); - mRenderPaint.setColor(dataSet.getBarShadowColor()); - c.drawRect(mBarShadow, mRenderPaint); - } - - // draw the stack - for (int k = 0; k < vals.length; k++) { - - all -= vals[k]; - - prepareBar(x, vals[k] + all, dataSet.getBarSpace(), trans); - - mRenderPaint.setColor(dataSet.getColor(k)); - c.drawRect(mBarRect, mRenderPaint); - } - } - - // avoid drawing outofbounds values - if (!mViewPortHandler.isInBoundsRight(mBarRect.left)) - break; - } - } - } - - /** - * Prepares a bar for drawing on the specified x-index and y-position. Also - * prepares the shadow-bar if enabled. - * - * @param x the x-position - * @param y the y-position - * @param barspace the space between bars - */ - protected void prepareBar(float x, float y, float barspace, Transformer trans) { - - float barWidth = 0.5f; - - float spaceHalf = barspace / 2f; - float left = x - barWidth + spaceHalf; - float right = x + barWidth - spaceHalf; - float top = y >= 0 ? y : 0; - float bottom = y <= 0 ? y : 0; - - mBarRect.set(left, top, right, bottom); - - trans.rectValueToPixel(mBarRect, mAnimator.getPhaseY()); - - // if a shadow is drawn, prepare it too - if (mChart.isDrawBarShadowEnabled()) { - mBarShadow.set(mBarRect.left, mViewPortHandler.offsetTop(), mBarRect.right, - mViewPortHandler.contentBottom()); - } - } - - /** - * Prepares a bar for being highlighted. - * - * @param x the x-position - * @param y the y-position - * @param barspace the space between bars - * @param from - * @param trans - */ - protected void prepareBarHighlight(float x, float y, float barspace, float from, Transformer trans) { - - float barWidth = 0.5f; - - float spaceHalf = barspace / 2f; - float left = x - barWidth + spaceHalf; - float right = x + barWidth - spaceHalf; - float top = y >= from ? y : from; - float bottom = y <= from ? y : from; - - mBarRect.set(left, top, right, bottom); - - trans.rectValueToPixel(mBarRect, mAnimator.getPhaseY()); - } - - @Override - public void drawValues(Canvas c) { - // if values are drawn - if (passesCheck()) { - - ArrayList dataSets = mChart.getBarData().getDataSets(); - - float posOffset = 0f; - float negOffset = 0f; - boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); - - // calculate the correct offset depending on the draw position of - // the value - posOffset = (drawValueAboveBar ? -Utils.convertDpToPixel(5) : Utils.calcTextHeight( - mValuePaint, - "8") * 1.5f); - negOffset = (drawValueAboveBar ? Utils.calcTextHeight(mValuePaint, "8") * 1.5f : -Utils - .convertDpToPixel(5)); - - for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { - - BarDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - ValueFormatter formatter = dataSet.getValueFormatter(); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - ArrayList entries = dataSet.getYVals(); - - float[] valuePoints = getTransformedValues(trans, entries, i); - - // if only single values are drawn (sum) - if (!mChart.isDrawValuesForWholeStackEnabled()) { - - for (int j = 0; j < valuePoints.length * mAnimator.getPhaseX(); j += 2) { - - if (!mViewPortHandler.isInBoundsRight(valuePoints[j])) - break; - - if (!mViewPortHandler.isInBoundsY(valuePoints[j + 1]) - || !mViewPortHandler.isInBoundsLeft(valuePoints[j])) - continue; - - float val = entries.get(j / 2).getVal(); - - drawValue(c, val, valuePoints[j], - valuePoints[j + 1] + (val >= 0 ? posOffset : negOffset), formatter); - } - - // if each value of a potential stack should be drawn - } else { - - for (int j = 0; j < (valuePoints.length - 1) * mAnimator.getPhaseX(); j += 2) { - - BarEntry e = entries.get(j / 2); - - float[] vals = e.getVals(); - - // we still draw stacked bars, but there is one - // non-stacked - // in between - if (vals == null) { - - if (!mViewPortHandler.isInBoundsRight(valuePoints[j])) - break; - - if (!mViewPortHandler.isInBoundsY(valuePoints[j + 1]) - || !mViewPortHandler.isInBoundsLeft(valuePoints[j])) - continue; - - drawValue(c, e.getVal(), valuePoints[j], - valuePoints[j + 1] + (e.getVal() >= 0 ? posOffset : negOffset), - formatter); - - } else { - - float[] transformed = new float[vals.length * 2]; - int cnt = 0; - float add = e.getVal(); - - for (int k = 0; k < transformed.length; k += 2) { - - add -= vals[cnt]; - transformed[k + 1] = (vals[cnt] + add) * mAnimator.getPhaseY(); - cnt++; - } - - trans.pointValuesToPixel(transformed); - - for (int k = 0; k < transformed.length; k += 2) { - - float x = valuePoints[j]; - float y = transformed[k + 1] - + (vals[k / 2] >= 0 ? posOffset : negOffset); - - if (!mViewPortHandler.isInBoundsRight(x)) - break; - - if (!mViewPortHandler.isInBoundsY(y) - || !mViewPortHandler.isInBoundsLeft(x)) - continue; - - drawValue(c, vals[k / 2], x, y, formatter); - } - } - } - } - } - } - } - - /** - * Draws a value at the specified x and y position. - * - * @param value - * @param xPos - * @param yPos - * @formatter - */ - protected void drawValue(Canvas c, float val, float xPos, float yPos, ValueFormatter formatter) { - - String value = formatter.getFormattedValue(val); - c.drawText(value, xPos, yPos, - mValuePaint); - } - - @Override - public void drawExtras(Canvas c) { - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - int setCount = mChart.getBarData().getDataSetCount(); - - for (int i = 0; i < indices.length; i++) { - - Highlight h = indices[i]; - int index = h.getXIndex(); - - int dataSetIndex = h.getDataSetIndex(); - BarDataSet set = mChart.getBarData().getDataSetByIndex(dataSetIndex); - - if (set == null) - continue; - - Transformer trans = mChart.getTransformer(set.getAxisDependency()); - - mHighlightPaint.setColor(set.getHighLightColor()); - mHighlightPaint.setAlpha(set.getHighLightAlpha()); - - // check outofbounds - if (index < mChart.getBarData().getYValCount() && index >= 0 - && index < (mChart.getXChartMax() * mAnimator.getPhaseX()) / setCount) { - - BarEntry e = mChart.getBarData().getDataSetByIndex(dataSetIndex) - .getEntryForXIndex(index); - - if (e == null) - continue; - - float groupspace = mChart.getBarData().getGroupSpace(); - boolean isStack = h.getStackIndex() < 0 ? false : true; - - // calculate the correct x-position - float x = index * setCount + dataSetIndex + groupspace / 2f - + groupspace * index; - float y = isStack ? e.getVals()[h.getStackIndex()] - + e.getBelowSum(h.getStackIndex()) : e.getVal(); - - // this is where the bar starts - float from = isStack ? e.getBelowSum(h.getStackIndex()) : 0f; - - prepareBarHighlight(x, y, set.getBarSpace(), from, trans); - - c.drawRect(mBarRect, mHighlightPaint); - - if (mChart.isDrawHighlightArrowEnabled()) { - - mHighlightPaint.setAlpha(255); - - // distance between highlight arrow and bar - float offsetY = mAnimator.getPhaseY() * 0.07f; - - Path arrow = new Path(); - arrow.moveTo(x + 0.5f, y + offsetY * 0.3f); - arrow.lineTo(x + 0.2f, y + offsetY); - arrow.lineTo(x + 0.8f, y + offsetY); - - trans.pathValueToPixel(arrow); - c.drawPath(arrow, mHighlightPaint); - } - } - } - } - - public float[] getTransformedValues(Transformer trans, ArrayList entries, - int dataSetIndex) { - return trans.generateTransformedValuesBarChart(entries, dataSetIndex, - mChart.getBarData(), - mAnimator.getPhaseY()); - } - - protected boolean passesCheck() { - return mChart.getBarData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java deleted file mode 100644 index 03b367e3f9..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ /dev/null @@ -1,241 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.data.CandleDataSet; -import com.github.mikephil.charting.data.CandleEntry; -import com.github.mikephil.charting.interfaces.CandleDataProvider; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class CandleStickChartRenderer extends DataRenderer { - - protected CandleDataProvider mChart; - - public CandleStickChartRenderer(CandleDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - } - - @Override - public void drawData(Canvas c) { - - CandleData candleData = mChart.getCandleData(); - - for (CandleDataSet set : candleData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - } - - protected void drawDataSet(Canvas c, CandleDataSet dataSet) { - - // pre allocate - float[] shadowPoints = new float[4]; - float[] bodyPoints = new float[4]; - - ArrayList entries = dataSet.getYVals(); - - mRenderPaint.setStrokeWidth(dataSet.getShadowWidth()); - - for (int j = 0; j < entries.size() * mAnimator.getPhaseX(); j++) { - - // get the color that is specified for this position from - // the DataSet, this will reuse colors, if the index is out - // of bounds - mRenderPaint.setColor(dataSet.getColor(j)); - - // get the entry - CandleEntry e = entries.get(j); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - // transform the entries values for shadow and body - transformShadow(shadowPoints, e, trans); - transformBody(bodyPoints, e, dataSet.getBodySpace(), trans); - - float xShadow = shadowPoints[0]; - float leftBody = bodyPoints[0]; - float rightBody = bodyPoints[2]; - - float high = shadowPoints[1]; - float low = shadowPoints[3]; - - float open = bodyPoints[1]; - float close = bodyPoints[3]; - - if (!mViewPortHandler.isInBoundsRight(leftBody)) - break; - - // make sure the lines don't do shitty things outside - // bounds - if (j != 0 && !mViewPortHandler.isInBoundsLeft(rightBody) - && !mViewPortHandler.isInBoundsTop(low) - && !mViewPortHandler.isInBoundsBottom(high)) - continue; - - // draw the shadow - c.drawLine(xShadow, low, xShadow, high, mRenderPaint); - - // decide weather the body is hollow or filled - if (open > close) { - - mRenderPaint.setStyle(Paint.Style.FILL); - // draw the body - c.drawRect(leftBody, close, rightBody, open, mRenderPaint); - - } else { - - mRenderPaint.setStyle(Paint.Style.STROKE); - // draw the body - c.drawRect(leftBody, open, rightBody, close, mRenderPaint); - } - } - } - - /** - * Transforms the values of an entry in order to draw the candle-body. - * - * @param bodyPoints - * @param e - * @param bodySpace - */ - private void transformBody(float[] bodyPoints, CandleEntry e, float bodySpace, Transformer trans) { - - float phase = mAnimator.getPhaseY(); - - bodyPoints[0] = e.getXIndex() - 0.5f + bodySpace; - bodyPoints[1] = e.getClose() * phase; - bodyPoints[2] = e.getXIndex() + 0.5f - bodySpace; - bodyPoints[3] = e.getOpen() * phase; - - trans.pointValuesToPixel(bodyPoints); - } - - /** - * Transforms the values of an entry in order to draw the candle-shadow. - * - * @param shadowPoints - * @param e - */ - private void transformShadow(float[] shadowPoints, CandleEntry e, Transformer trans) { - - float phase = mAnimator.getPhaseY(); - - shadowPoints[0] = e.getXIndex(); - shadowPoints[1] = e.getHigh() * phase; - shadowPoints[2] = e.getXIndex(); - shadowPoints[3] = e.getLow() * phase; - - trans.pointValuesToPixel(shadowPoints); - } - - @Override - public void drawValues(Canvas c) { - - // if values are drawn - if (mChart.getCandleData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX()) { - - ArrayList dataSets = mChart.getCandleData().getDataSets(); - - for (int i = 0; i < dataSets.size(); i++) { - - CandleDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - ArrayList entries = dataSet.getYVals(); - - float[] positions = trans.generateTransformedValuesCandle( - entries, mAnimator.getPhaseY()); - - float yOffset = Utils.convertDpToPixel(5f); - - for (int j = 0; j < positions.length * mAnimator.getPhaseX(); j += 2) { - - float x = positions[j]; - float y = positions[j + 1]; - - if (!mViewPortHandler.isInBoundsRight(x)) - break; - - if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) - continue; - - float val = entries.get(j / 2).getHigh(); - - c.drawText(dataSet.getValueFormatter().getFormattedValue(val), x, y - yOffset, - mValuePaint); - } - } - } - } - - @Override - public void drawExtras(Canvas c) { - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - for (int i = 0; i < indices.length; i++) { - - int xIndex = indices[i].getXIndex(); // get the - // x-position - - CandleDataSet set = mChart.getCandleData().getDataSetByIndex( - indices[i].getDataSetIndex()); - - if (set == null) - continue; - - mHighlightPaint.setColor(set.getHighLightColor()); - - CandleEntry e = set.getEntryForXIndex(xIndex); - - if (e == null) - continue; - - float low = e.getLow() * mAnimator.getPhaseY(); - float high = e.getHigh() * mAnimator.getPhaseY(); - - float min = mChart.getYChartMin(); - float max = mChart.getYChartMax(); - - float[] vertPts = new float[] { - xIndex - 0.5f, max, xIndex - 0.5f, min, xIndex + 0.5f, max, xIndex + 0.5f, - min - }; - - float[] horPts = new float[] { - 0, low, mChart.getXChartMax(), low, 0, high, mChart.getXChartMax(), high - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(vertPts); - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(horPts); - - // draw the vertical highlight lines - c.drawLines(vertPts, mHighlightPaint); - - // draw the horizontal highlight lines - c.drawLines(horPts, mHighlightPaint); - } - } - -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java deleted file mode 100644 index 95cec1f090..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java +++ /dev/null @@ -1,90 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.charts.CombinedChart; -import com.github.mikephil.charting.utils.Highlight; - -public class CombinedChartRenderer extends DataRenderer { - - private LineChartRenderer mLineRenderer; - private BarChartRenderer mBarRenderer; - private CandleStickChartRenderer mCandleRenderer; - private ScatterChartRenderer mScatterRenderer; - - public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - - if (chart.getLineData() != null) - mLineRenderer = new LineChartRenderer(chart, animator, viewPortHandler); - - if (chart.getBarData() != null) - mBarRenderer = new BarChartRenderer(chart, animator, viewPortHandler); - - if (chart.getScatterData() != null) - mScatterRenderer = new ScatterChartRenderer(chart, animator, viewPortHandler); - - if (chart.getCandleData() != null) - mCandleRenderer = new CandleStickChartRenderer(chart, animator, viewPortHandler); - } - - @Override - public void drawData(Canvas c) { - - if (mBarRenderer != null) - mBarRenderer.drawData(c); - - if (mCandleRenderer != null) - mCandleRenderer.drawData(c); - - if (mLineRenderer != null) - mLineRenderer.drawData(c); - - if (mScatterRenderer != null) - mScatterRenderer.drawData(c); - } - - @Override - public void drawValues(Canvas c) { - - if (mBarRenderer != null) - mBarRenderer.drawValues(c); - - if (mCandleRenderer != null) - mCandleRenderer.drawValues(c); - - if (mLineRenderer != null) - mLineRenderer.drawValues(c); - - if (mScatterRenderer != null) - mScatterRenderer.drawValues(c); - } - - @Override - public void drawExtras(Canvas c) { - - if (mBarRenderer != null) - mBarRenderer.drawExtras(c); - - if (mCandleRenderer != null) - mCandleRenderer.drawExtras(c); - - if (mLineRenderer != null) - mLineRenderer.drawExtras(c); - - if (mScatterRenderer != null) - mScatterRenderer.drawExtras(c); - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - // mBarRenderer.drawHighlighted(c, indices); - // mCandleRenderer.drawHighlighted(c, indices); - // mLineRenderer.drawHighlighted(c, indices); - // mScatterRenderer.drawHighlighted(c, indices); - } - -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/DataRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/DataRenderer.java deleted file mode 100644 index 5cbf402976..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/DataRenderer.java +++ /dev/null @@ -1,89 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Paint.Style; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Utils; - -public abstract class DataRenderer extends Renderer { - - protected ChartAnimator mAnimator; - - /** main paint object used for rendering */ - protected Paint mRenderPaint; - - /** paint used for highlighting values */ - protected Paint mHighlightPaint; - - /** - * paint object for drawing values (text representing values of chart - * entries) - */ - protected Paint mValuePaint; - - public DataRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { - super(viewPortHandler); - this.mAnimator = animator; - - mRenderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mRenderPaint.setStyle(Style.FILL); - - mValuePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mValuePaint.setColor(Color.rgb(63, 63, 63)); - mValuePaint.setTextAlign(Align.CENTER); - mValuePaint.setTextSize(Utils.convertDpToPixel(9f)); - - mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHighlightPaint.setStyle(Paint.Style.STROKE); - mHighlightPaint.setStrokeWidth(2f); - mHighlightPaint.setColor(Color.rgb(255, 187, 115)); - } - - /** - * Returns the Paint object this renderer uses for drawing the values - * (value-text). - * - * @return - */ - public Paint getPaintValues() { - return mValuePaint; - } - - /** - * Returns the Paint object this renderer uses for drawing highlight - * indicators. - * - * @return - */ - public Paint getPaintHighlight() { - return mHighlightPaint; - } - - /** - * Applies the required styling (provided by the DataSet) to the value-paint - * object. - * - * @param set - */ - protected void applyValueTextStyle(DataSet set) { - - mValuePaint.setColor(set.getValueTextColor()); - mValuePaint.setTypeface(set.getValueTypeface()); - mValuePaint.setTextSize(set.getValueTextSize()); - } - - public abstract void drawData(Canvas c); - - public abstract void drawValues(Canvas c); - - public abstract void drawExtras(Canvas c); - - public abstract void drawHighlighted(Canvas c, Highlight[] indices); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java deleted file mode 100644 index 7dffac0787..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ /dev/null @@ -1,311 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint.Align; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.interfaces.BarDataProvider; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ValueFormatter; - -import java.util.ArrayList; - -/** - * Renderer for the HorizontalBarChart. - * - * @author Philipp Jahoda - */ -public class HorizontalBarChartRenderer extends BarChartRenderer { - - private float mYOffset = 0f; - - public HorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(chart, animator, viewPortHandler); - - mValuePaint.setTextAlign(Align.LEFT); - } - - protected void drawDataSet(Canvas c, BarDataSet dataSet, int index) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - // the space between bar-groups - float space = mChart.getBarData().getGroupSpace(); - - boolean noStacks = dataSet.getStackSize() == 1 ? true : false; - - ArrayList entries = dataSet.getYVals(); - - // do the drawing - for (int j = 0; j < dataSet.getEntryCount() * mAnimator.getPhaseX(); j++) { - - BarEntry e = entries.get(j); - - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + j * (mChart.getBarData().getDataSetCount() - 1) + index - + space * j + space / 2f; - float y = e.getVal(); - - // no stacks - if (noStacks) { - - prepareBar(x, y, dataSet.getBarSpace(), trans); - - // avoid drawing outofbounds values - if (!mViewPortHandler.isInBoundsTop(mBarRect.bottom)) - break; - - if (!mViewPortHandler.isInBoundsBottom(mBarRect.top)) - continue; - - // if drawing the bar shadow is enabled - if (mChart.isDrawBarShadowEnabled()) { - mRenderPaint.setColor(dataSet.getBarShadowColor()); - c.drawRect(mBarShadow, mRenderPaint); - } - - // Set the color for the currently drawn value. If the index - // is - // out of bounds, reuse colors. - mRenderPaint.setColor(dataSet.getColor(j)); - c.drawRect(mBarRect, mRenderPaint); - - } else { // stacked bars - - float[] vals = e.getVals(); - - // we still draw stacked bars, but there could be one - // non-stacked - // in between - if (vals == null) { - - prepareBar(x, y, dataSet.getBarSpace(), trans); - - // if drawing the bar shadow is enabled - if (mChart.isDrawBarShadowEnabled()) { - mRenderPaint.setColor(dataSet.getBarShadowColor()); - c.drawRect(mBarShadow, mRenderPaint); - } - - mRenderPaint.setColor(dataSet.getColor(0)); - c.drawRect(mBarRect, mRenderPaint); - - } else { - - float all = e.getVal(); - - // if drawing the bar shadow is enabled - if (mChart.isDrawBarShadowEnabled()) { - - prepareBar(x, y, dataSet.getBarSpace(), trans); - mRenderPaint.setColor(dataSet.getBarShadowColor()); - c.drawRect(mBarShadow, mRenderPaint); - } - - // draw the stack - for (int k = 0; k < vals.length; k++) { - - all -= vals[k]; - - prepareBar(x, vals[k] + all, dataSet.getBarSpace(), trans); - - mRenderPaint.setColor(dataSet.getColor(k)); - c.drawRect(mBarRect, mRenderPaint); - } - } - - // avoid drawing outofbounds values - if (!mViewPortHandler.isInBoundsTop(mBarRect.bottom)) - break; - } - } - } - - @Override - public void drawValues(Canvas c) { - // if values are drawn - if (passesCheck()) { - - ArrayList dataSets = mChart.getBarData().getDataSets(); - - float posOffset = 0f; - float negOffset = 0f; - boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); - - // calculate the correct offset depending on the draw position of - // the value - negOffset = drawValueAboveBar ? -Utils.convertDpToPixel(5) : Utils.convertDpToPixel(5); - posOffset = drawValueAboveBar ? Utils.convertDpToPixel(5) : -Utils.convertDpToPixel(5); - - if (drawValueAboveBar) - mValuePaint.setTextAlign(Align.LEFT); - else - mValuePaint.setTextAlign(Align.RIGHT); - - for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { - - BarDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - mYOffset = Utils.calcTextHeight(mValuePaint, "10") / 2f; - - ValueFormatter formatter = dataSet.getValueFormatter(); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - ArrayList entries = dataSet.getYVals(); - - float[] valuePoints = getTransformedValues(trans, entries, i); - - // if only single values are drawn (sum) - if (!mChart.isDrawValuesForWholeStackEnabled()) { - - for (int j = 0; j < valuePoints.length * mAnimator.getPhaseX(); j += 2) { - - if (!mViewPortHandler.isInBoundsX(valuePoints[j])) - continue; - - if (!mViewPortHandler.isInBoundsTop(valuePoints[j + 1])) - break; - - if (!mViewPortHandler.isInBoundsBottom(valuePoints[j + 1])) - continue; - - float val = entries.get(j / 2).getVal(); - - drawValue(c, val, valuePoints[j] + (val >= 0 ? posOffset : negOffset), - valuePoints[j + 1], formatter); - } - - // if each value of a potential stack should be drawn - } else { - - for (int j = 0; j < (valuePoints.length - 1) * mAnimator.getPhaseX(); j += 2) { - - BarEntry e = entries.get(j / 2); - - float[] vals = e.getVals(); - - // we still draw stacked bars, but there is one - // non-stacked - // in between - if (vals == null) { - - if (!mViewPortHandler.isInBoundsX(valuePoints[j])) - continue; - - if (!mViewPortHandler.isInBoundsTop(valuePoints[j + 1])) - break; - - if (!mViewPortHandler.isInBoundsBottom(valuePoints[j + 1])) - continue; - - drawValue(c, e.getVal(), valuePoints[j] - + (e.getVal() >= 0 ? posOffset : negOffset), - valuePoints[j + 1], - formatter); - - } else { - - float[] transformed = new float[vals.length * 2]; - int cnt = 0; - float add = e.getVal(); - - for (int k = 0; k < transformed.length; k += 2) { - - add -= vals[cnt]; - transformed[k] = (vals[cnt] + add) * mAnimator.getPhaseY(); - cnt++; - } - - trans.pointValuesToPixel(transformed); - - for (int k = 0; k < transformed.length; k += 2) { - - float x = transformed[k] - + (vals[k / 2] >= 0 ? posOffset : negOffset); - float y = valuePoints[j + 1]; - - if (!mViewPortHandler.isInBoundsX(x)) - continue; - - if (!mViewPortHandler.isInBoundsTop(y)) - break; - - if (!mViewPortHandler.isInBoundsBottom(y)) - continue; - - drawValue(c, vals[k / 2], x, y, formatter); - } - } - } - } - } - } - } - - @Override - protected void prepareBar(float x, float y, float barspace, Transformer trans) { - - float spaceHalf = barspace / 2f; - - float top = x - 0.5f + spaceHalf; - float bottom = x + 0.5f - spaceHalf; - float left = y >= 0 ? y : 0; - float right = y <= 0 ? y : 0; - - mBarRect.set(left, top, right, bottom); - - trans.rectValueToPixelHorizontal(mBarRect, mAnimator.getPhaseY()); - - // if a shadow is drawn, prepare it too - if (mChart.isDrawBarShadowEnabled()) { - mBarShadow.set(mViewPortHandler.contentLeft(), mBarRect.top, - mViewPortHandler.contentRight(), - mBarRect.bottom); - } - } - - @Override - protected void prepareBarHighlight(float x, float y, float barspace, float from, Transformer trans) { - - float spaceHalf = barspace / 2f; - - float top = x - 0.5f + spaceHalf; - float bottom = x + 0.5f - spaceHalf; - float left = y >= from ? y : from; - float right = y <= from ? y : from; - - mBarRect.set(left, top, right, bottom); - - trans.rectValueToPixel(mBarRect, mAnimator.getPhaseY()); - } - - @Override - public float[] getTransformedValues(Transformer trans, ArrayList entries, - int dataSetIndex) { - return trans.generateTransformedValuesHorizontalBarChart(entries, dataSetIndex, - mChart.getBarData(), mAnimator.getPhaseY()); - } - - @Override - protected void drawValue(Canvas c, float val, float xPos, float yPos, ValueFormatter formatter) { - super.drawValue(c, val, xPos, yPos + mYOffset, formatter); - } - - @Override - protected boolean passesCheck() { - return mChart.getBarData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleY(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java deleted file mode 100644 index 2889c9b0ca..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ /dev/null @@ -1,443 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Typeface; -import android.graphics.Paint.Align; - -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.PieDataSet; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class LegendRenderer extends Renderer { - - /** paint for the legend labels */ - protected Paint mLegendLabelPaint; - - /** paint used for the legend forms */ - protected Paint mLegendFormPaint; - - public LegendRenderer(ViewPortHandler viewPortHandler) { - super(viewPortHandler); - - mLegendLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mLegendLabelPaint.setTextSize(Utils.convertDpToPixel(9f)); - mLegendLabelPaint.setTextAlign(Align.LEFT); - - mLegendFormPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mLegendFormPaint.setStyle(Paint.Style.FILL); - mLegendFormPaint.setStrokeWidth(3f); - } - - /** - * Returns the Paint object used for drawing the Legend labels. - * - * @return - */ - public Paint getLabelPaint() { - return mLegendLabelPaint; - } - - /** - * Returns the Paint object used for drawing the Legend forms. - * - * @return - */ - public Paint getFormPaint() { - return mLegendFormPaint; - } - - /** - * Prepares the legend and calculates all needed forms and colors. - * - * @param data - */ - public Legend computeLegend(ChartData data, Legend legend) { - - ArrayList labels = new ArrayList(); - ArrayList colors = new ArrayList(); - - // loop for building up the colors and labels used in the legend - for (int i = 0; i < data.getDataSetCount(); i++) { - - DataSet dataSet = data.getDataSetByIndex(i); - - ArrayList clrs = dataSet.getColors(); - int entryCount = dataSet.getEntryCount(); - - // if we have a barchart with stacked bars - if (dataSet instanceof BarDataSet && ((BarDataSet) dataSet).getStackSize() > 1) { - - BarDataSet bds = (BarDataSet) dataSet; - String[] sLabels = bds.getStackLabels(); - - for (int j = 0; j < clrs.size() && j < bds.getStackSize(); j++) { - - labels.add(sLabels[j % sLabels.length]); - colors.add(clrs.get(j)); - } - - // add the legend description label - colors.add(-2); - labels.add(bds.getLabel()); - - } else if (dataSet instanceof PieDataSet) { - - ArrayList xVals = data.getXVals(); - PieDataSet pds = (PieDataSet) dataSet; - - for (int j = 0; j < clrs.size() && j < entryCount && j < xVals.size(); j++) { - - labels.add(xVals.get(j)); - colors.add(clrs.get(j)); - } - - // add the legend description label - colors.add(-2); - labels.add(pds.getLabel()); - - } else { // all others - - for (int j = 0; j < clrs.size() && j < entryCount; j++) { - - // if multiple colors are set for a DataSet, group them - if (j < clrs.size() - 1 && j < entryCount - 1) { - - labels.add(null); - } else { // add label to the last entry - - String label = data.getDataSetByIndex(i).getLabel(); - labels.add(label); - } - - colors.add(clrs.get(j)); - } - } - } - - Legend l = new Legend(colors, labels); - - if (legend != null) { - // apply the old legend settings to a potential new legend - l.apply(legend); - } - - Typeface tf = l.getTypeface(); - - if (tf != null) - mLegendLabelPaint.setTypeface(tf); - - mLegendLabelPaint.setTextSize(l.getTextSize()); - mLegendLabelPaint.setColor(l.getTextColor()); - - // calculate all dimensions of the legend - l.calculateDimensions(mLegendLabelPaint); - - return l; - } - - public void renderLegend(Canvas c, Legend legend) { - - if (legend == null || !legend.isEnabled()) - return; - - Typeface tf = legend.getTypeface(); - - if (tf != null) - mLegendLabelPaint.setTypeface(tf); - - mLegendLabelPaint.setTextSize(legend.getTextSize()); - mLegendLabelPaint.setColor(legend.getTextColor()); - - String[] labels = legend.getLegendLabels(); - - float formSize = legend.getFormSize(); - - // space between text and shape/form of entry - float formTextSpaceAndForm = legend.getFormToTextSpace() + formSize; - - // space between the entries - float stackSpace = legend.getStackSpace(); - - // the amount of pixels the text needs to be set down to be on the same - // height as the form - float textDrop = (Utils.calcTextHeight(mLegendLabelPaint, "AQJ") + formSize) / 2f; - - float posX, posY; - - // contains the stacked legend size in pixels - float stack = 0f; - - boolean wasStacked = false; - - float yoffset = legend.getYOffset(); - float xoffset = legend.getXOffset(); - - switch (legend.getPosition()) { - case BELOW_CHART_LEFT: - - posX = mViewPortHandler.contentLeft() + xoffset; - posY = mViewPortHandler.getChartHeight() - yoffset; - - for (int i = 0; i < labels.length; i++) { - - drawForm(c, posX, posY - legend.mTextHeightMax / 2f, i, legend); - - // grouped forms have null labels - if (labels[i] != null) { - - // make a step to the left - if (legend.getColors()[i] != -2) - posX += formTextSpaceAndForm; - - drawLabel(c, posX, posY, legend.getLabel(i)); - posX += Utils.calcTextWidth(mLegendLabelPaint, labels[i]) - + legend.getXEntrySpace(); - } else { - posX += formSize + stackSpace; - } - } - - break; - case BELOW_CHART_RIGHT: - - posX = mViewPortHandler.contentRight() - xoffset;; - posY = mViewPortHandler.getChartHeight() - yoffset; - - for (int i = labels.length - 1; i >= 0; i--) { - - if (labels[i] != null) { - - posX -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]) - + legend.getXEntrySpace(); - drawLabel(c, posX, posY, legend.getLabel(i)); - if (legend.getColors()[i] != -2) - posX -= formTextSpaceAndForm; - } else { - posX -= stackSpace + formSize; - } - - drawForm(c, posX, posY - legend.mTextHeightMax / 2f, i, legend); - } - - break; - case RIGHT_OF_CHART: - - posX = mViewPortHandler.getChartWidth() - legend.mTextWidthMax - xoffset; - posY = mViewPortHandler.contentTop() + yoffset; - - for (int i = 0; i < labels.length; i++) { - - drawForm(c, posX + stack, posY, i, legend); - - if (labels[i] != null) { - - if (!wasStacked) { - - float x = posX; - - if (legend.getColors()[i] != -2) - x += formTextSpaceAndForm; - - drawLabel(c, x, posY + legend.mTextHeightMax / 2f, legend.getLabel(i)); - - posY += textDrop; - } else { - posY += legend.mTextHeightMax * 3f; - drawLabel(c, posX, posY - legend.mTextHeightMax, legend.getLabel(i)); - } - - // make a step down - posY += legend.getYEntrySpace(); - stack = 0f; - } else { - stack += formSize + stackSpace; - wasStacked = true; - } - } - break; - case RIGHT_OF_CHART_CENTER: - posX = mViewPortHandler.getChartWidth() - legend.mTextWidthMax - xoffset; - posY = mViewPortHandler.getChartHeight() / 2f - legend.mNeededHeight / 2f; - - for (int i = 0; i < labels.length; i++) { - - drawForm(c, posX + stack, posY, i, legend); - - if (labels[i] != null) { - - if (!wasStacked) { - - float x = posX; - - if (legend.getColors()[i] != -2) - x += formTextSpaceAndForm; - - drawLabel(c, x, posY + legend.mTextHeightMax / 2f, legend.getLabel(i)); - - posY += textDrop; - } else { - posY += legend.mTextHeightMax * 3f; - drawLabel(c, posX, posY - legend.mTextHeightMax, legend.getLabel(i)); - } - - // make a step down - posY += legend.getYEntrySpace(); - stack = 0f; - } else { - stack += formSize + stackSpace; - wasStacked = true; - } - } - - break; - case BELOW_CHART_CENTER: - - posX = mViewPortHandler.getChartWidth() / 2f - legend.mNeededWidth / 2f; - posY = mViewPortHandler.getChartHeight() - yoffset; - - for (int i = 0; i < labels.length; i++) { - - drawForm(c, posX, posY - legend.mTextHeightMax / 2f, i, legend); - - // grouped forms have null labels - if (labels[i] != null) { - - // make a step to the left - if (legend.getColors()[i] != -2) - posX += formTextSpaceAndForm; - - drawLabel(c, posX, posY, legend.getLabel(i)); - posX += Utils.calcTextWidth(mLegendLabelPaint, labels[i]) - + legend.getXEntrySpace(); - } else { - posX += formSize + stackSpace; - } - } - - break; - case PIECHART_CENTER: - - posX = mViewPortHandler.getChartWidth() / 2f - legend.mTextWidthMax / 2f; - posY = mViewPortHandler.getChartHeight() / 2f - legend.mNeededHeight / 2f; - - for (int i = 0; i < labels.length; i++) { - - drawForm(c, posX + stack, posY, i, legend); - - if (labels[i] != null) { - - if (!wasStacked) { - - float x = posX; - - if (legend.getColors()[i] != -2) - x += formTextSpaceAndForm; - - drawLabel(c, x, posY + legend.mTextHeightMax / 2f, legend.getLabel(i)); - - posY += textDrop; - } else { - posY += legend.mTextHeightMax * 3f; - drawLabel(c, posX, posY - legend.mTextHeightMax, legend.getLabel(i)); - } - - // make a step down - posY += legend.getYEntrySpace(); - stack = 0f; - } else { - stack += formSize + stackSpace; - wasStacked = true; - } - } - - break; - case RIGHT_OF_CHART_INSIDE: - - posX = mViewPortHandler.getChartWidth() - legend.mTextWidthMax - xoffset; - posY = mViewPortHandler.contentTop() + yoffset; - - for (int i = 0; i < labels.length; i++) { - - drawForm(c, posX + stack, posY, i, legend); - - if (labels[i] != null) { - - if (!wasStacked) { - - float x = posX; - - if (legend.getColors()[i] != -2) - x += formTextSpaceAndForm; - - drawLabel(c, x, posY + legend.mTextHeightMax / 2f, legend.getLabel(i)); - - posY += textDrop; - } else { - posY += legend.mTextHeightMax * 3f; - drawLabel(c, posX, posY - legend.mTextHeightMax, legend.getLabel(i)); - } - - // make a step down - posY += legend.getYEntrySpace(); - stack = 0f; - } else { - stack += formSize + stackSpace; - wasStacked = true; - } - } - break; - } - } - - /** - * Draws the Legend-form at the given position with the color at the given - * index. - * - * @param c canvas to draw with - * @param x - * @param y - * @param index the index of the color to use (in the colors array) - */ - protected void drawForm(Canvas c, float x, float y, int index, Legend legend) { - - if (legend.getColors()[index] == -2) - return; - - mLegendFormPaint.setColor(legend.getColors()[index]); - - float formsize = legend.getFormSize(); - float half = formsize / 2f; - - switch (legend.getForm()) { - case CIRCLE: - c.drawCircle(x + half, y, half, mLegendFormPaint); - break; - case SQUARE: - c.drawRect(x, y - half, x + formsize, y + half, mLegendFormPaint); - break; - case LINE: - c.drawLine(x, y, x + formsize, y, mLegendFormPaint); - break; - } - } - - /** - * Draws the provided label at the given position. - * - * @param c canvas to draw with - * @param x - * @param y - * @param label the label to draw - */ - protected void drawLabel(Canvas c, float x, float y, String label) { - c.drawText(label, x, y, mLegendLabelPaint); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java deleted file mode 100644 index ab5fb328c7..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ /dev/null @@ -1,446 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.interfaces.LineDataProvider; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Transformer; - -import java.util.ArrayList; - -public class LineChartRenderer extends DataRenderer { - - protected LineDataProvider mChart; - - /** paint for the inner circle of the value indicators */ - protected Paint mCirclePaintInner; - - public LineChartRenderer(LineDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - - mCirclePaintInner = new Paint(Paint.ANTI_ALIAS_FLAG); - mCirclePaintInner.setStyle(Paint.Style.FILL); - mCirclePaintInner.setColor(Color.WHITE); - } - - @Override - public void drawData(Canvas c) { - - LineData lineData = mChart.getLineData(); - - for (LineDataSet set : lineData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - } - - /** - * Class needed for saving the points when drawing cubic-lines. - * - * @author Philipp Jahoda - */ - protected class CPoint { - - public float x = 0f; - public float y = 0f; - - /** x-axis distance */ - public float dx = 0f; - - /** y-axis distance */ - public float dy = 0f; - - public CPoint(float x, float y) { - this.x = x; - this.y = y; - } - } - - protected void drawDataSet(Canvas c, LineDataSet dataSet) { - - ArrayList entries = dataSet.getYVals(); - - if (entries.size() < 1) - return; - - mRenderPaint.setStrokeWidth(dataSet.getLineWidth()); - mRenderPaint.setPathEffect(dataSet.getDashPathEffect()); - - // if drawing cubic lines is enabled - if (dataSet.isDrawCubicEnabled()) { - drawCubic(c, dataSet, entries); - - // draw normal (straight) lines - } else { - drawLinear(c, dataSet, entries); - } - - mRenderPaint.setPathEffect(null); - } - - protected void drawCubic(Canvas c, LineDataSet dataSet, ArrayList entries) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - float intensity = dataSet.getCubicIntensity(); - - // the path for the cubic-spline - Path spline = new Path(); - - ArrayList points = new ArrayList(); - for (Entry e : entries) { - if (e != null) - points.add(new CPoint(e.getXIndex(), e.getVal())); - } - - if (points.size() > 1) { - for (int j = 0; j < points.size() * phaseX; j++) { - - CPoint point = points.get(j); - - if (j == 0) { - CPoint next = points.get(j + 1); - point.dx = ((next.x - point.x) * intensity); - point.dy = ((next.y - point.y) * intensity); - } - else if (j == points.size() - 1) { - CPoint prev = points.get(j - 1); - point.dx = ((point.x - prev.x) * intensity); - point.dy = ((point.y - prev.y) * intensity); - } - else { - CPoint next = points.get(j + 1); - CPoint prev = points.get(j - 1); - point.dx = ((next.x - prev.x) * intensity); - point.dy = ((next.y - prev.y) * intensity); - } - - // create the cubic-spline path - if (j == 0) { - spline.moveTo(point.x, point.y * phaseY); - } - else { - CPoint prev = points.get(j - 1); - spline.cubicTo(prev.x + prev.dx, (prev.y + prev.dy) * phaseY, point.x - - point.dx, - (point.y - point.dy) * phaseY, point.x, point.y * phaseY); - } - } - } - - // if filled is enabled, close the path - if (dataSet.isDrawFilledEnabled()) { - - // create a new path, this is bad for performance - drawCubicFill(c, dataSet, new Path(spline), trans); - } - - mRenderPaint.setColor(dataSet.getColor()); - - mRenderPaint.setStyle(Paint.Style.STROKE); - - trans.pathValueToPixel(spline); - - c.drawPath(spline, mRenderPaint); - - mRenderPaint.setPathEffect(null); - } - - protected void drawCubicFill(Canvas c, LineDataSet dataSet, Path spline, Transformer trans) { - - float fillMin = mChart.getFillFormatter() - .getFillLinePosition(dataSet, mChart.getLineData(), mChart.getYChartMax(), - mChart.getYChartMin()); - - spline.lineTo(dataSet.getYVals().get((int) ((dataSet.getYVals().size() - 1) * mAnimator.getPhaseX())).getXIndex(), fillMin); - spline.lineTo(mChart.getXChartMin(), fillMin); - spline.close(); - - mRenderPaint.setStyle(Paint.Style.FILL); - - mRenderPaint.setColor(dataSet.getFillColor()); - // filled is drawn with less alpha - mRenderPaint.setAlpha(dataSet.getFillAlpha()); - - trans.pathValueToPixel(spline); - c.drawPath(spline, mRenderPaint); - - mRenderPaint.setAlpha(255); - } - - protected void drawLinear(Canvas c, LineDataSet dataSet, ArrayList entries) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - mRenderPaint.setStyle(Paint.Style.STROKE); - - // more than 1 color - if (dataSet.getColors() == null || dataSet.getColors().size() > 1) { - - float[] positions = trans.generateTransformedValuesLine( - entries, phaseY); - - for (int j = 0; j < (positions.length - 2) * phaseX; j += 2) { - - if (!mViewPortHandler.isInBoundsRight(positions[j])) - break; - - // make sure the lines don't do shitty things outside - // bounds - if (j != 0 && !mViewPortHandler.isInBoundsLeft(positions[j - 1]) - && !mViewPortHandler.isInBoundsY(positions[j + 1])) - continue; - - // get the color that is set for this line-segment - mRenderPaint.setColor(dataSet.getColor(j / 2)); - - c.drawLine(positions[j], positions[j + 1], - positions[j + 2], positions[j + 3], mRenderPaint); - } - - } else { // only one color per dataset - - mRenderPaint.setColor(dataSet.getColor()); - - Path line = generateLinePath(entries); - trans.pathValueToPixel(line); - - c.drawPath(line, mRenderPaint); - } - - mRenderPaint.setPathEffect(null); - - // if drawing filled is enabled - if (dataSet.isDrawFilledEnabled() && entries.size() > 0) { - drawLinearFill(c, dataSet, entries, trans); - } - } - - protected void drawLinearFill(Canvas c, LineDataSet dataSet, ArrayList entries, - Transformer trans) { - - mRenderPaint.setStyle(Paint.Style.FILL); - - mRenderPaint.setColor(dataSet.getFillColor()); - // filled is drawn with less alpha - mRenderPaint.setAlpha(dataSet.getFillAlpha()); - - // mRenderPaint.setShader(dataSet.getShader()); - - Path filled = generateFilledPath( - entries, - mChart.getFillFormatter().getFillLinePosition(dataSet, mChart.getLineData(), - mChart.getYChartMax(), mChart.getYChartMin())); - - trans.pathValueToPixel(filled); - - c.drawPath(filled, mRenderPaint); - - // restore alpha - mRenderPaint.setAlpha(255); - // mRenderPaint.setShader(null); - } - - /** - * Generates the path that is used for filled drawing. - * - * @param entries - * @return - */ - private Path generateFilledPath(ArrayList entries, float fillMin) { - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - Path filled = new Path(); - filled.moveTo(entries.get(0).getXIndex(), entries.get(0).getVal() * phaseY); - - // create a new path - for (int x = 1; x < entries.size() * phaseX; x++) { - - Entry e = entries.get(x); - filled.lineTo(e.getXIndex(), e.getVal() * phaseY); - } - - // close up - filled.lineTo(entries.get((int) ((entries.size() - 1) * phaseX)).getXIndex(), fillMin); - filled.lineTo(entries.get(0).getXIndex(), fillMin); - filled.close(); - - return filled; - } - - /** - * Generates the path that is used for drawing a single line. - * - * @param entries - * @return - */ - private Path generateLinePath(ArrayList entries) { - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - Path line = new Path(); - line.moveTo(entries.get(0).getXIndex(), entries.get(0).getVal() * phaseY); - - // create a new path - for (int x = 1; x < entries.size() * phaseX; x++) { - - Entry e = entries.get(x); - line.lineTo(e.getXIndex(), e.getVal() * phaseY); - } - - return line; - } - - @Override - public void drawValues(Canvas c) { - - if (mChart.getLineData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX()) { - - ArrayList dataSets = mChart.getLineData().getDataSets(); - - for (int i = 0; i < dataSets.size(); i++) { - - LineDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - // make sure the values do not interfear with the circles - int valOffset = (int) (dataSet.getCircleSize() * 1.75f); - - if (!dataSet.isDrawCirclesEnabled()) - valOffset = valOffset / 2; - - ArrayList entries = dataSet.getYVals(); - - float[] positions = trans.generateTransformedValuesLine( - entries, mAnimator.getPhaseY()); - - for (int j = 0; j < positions.length * mAnimator.getPhaseX(); j += 2) { - - float x = positions[j]; - float y = positions[j + 1]; - - if (!mViewPortHandler.isInBoundsRight(x)) - break; - - if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) - continue; - - float val = entries.get(j / 2).getVal(); - - c.drawText(dataSet.getValueFormatter().getFormattedValue(val), x, y - valOffset, - mValuePaint); - } - } - } - } - - @Override - public void drawExtras(Canvas c) { - drawCircles(c); - } - - protected void drawCircles(Canvas c) { - mRenderPaint.setStyle(Paint.Style.FILL); - - ArrayList dataSets = mChart.getLineData().getDataSets(); - - for (int i = 0; i < mChart.getLineData().getDataSetCount(); i++) { - - LineDataSet dataSet = dataSets.get(i); - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - // if drawing circles is enabled for this dataset - if (dataSet.isDrawCirclesEnabled()) { - - ArrayList entries = dataSet.getYVals(); - - float[] positions = trans.generateTransformedValuesLine( - entries, mAnimator.getPhaseY()); - - for (int j = 0; j < positions.length * mAnimator.getPhaseX(); j += 2) { - - mRenderPaint.setColor(dataSet.getCircleColor(j / 2)); - - float x = positions[j]; - float y = positions[j + 1]; - - if (!mViewPortHandler.isInBoundsRight(x)) - break; - - // make sure the circles don't do shitty things outside - // bounds - if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) - continue; - - c.drawCircle(x, y, dataSet.getCircleSize(), - mRenderPaint); - c.drawCircle(x, y, - dataSet.getCircleSize() / 2f, - mCirclePaintInner); - } - } // else do nothing - } - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - for (int i = 0; i < indices.length; i++) { - - LineDataSet set = mChart.getLineData().getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null) - continue; - - mHighlightPaint.setColor(set.getHighLightColor()); - - int xIndex = indices[i].getXIndex(); // get the - // x-position - - if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) - continue; - - float y = set.getYValForXIndex(xIndex) * mAnimator.getPhaseY(); // get - // the - // y-position - - float[] pts = new float[] { - xIndex, mChart.getYChartMax(), xIndex, mChart.getYChartMin(), 0, y, - mChart.getXChartMax(), y - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); - // draw the highlight lines - c.drawLines(pts, mHighlightPaint); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java deleted file mode 100644 index 27a934d57d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ /dev/null @@ -1,332 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.PointF; -import android.graphics.RectF; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.data.PieDataSet; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class PieChartRenderer extends DataRenderer { - - protected PieChart mChart; - - /** - * paint for the hole in the center of the pie chart and the transparent - * circle - */ - private Paint mHolePaint; - - /** - * paint object for the text that can be displayed in the center of the - * chart - */ - private Paint mCenterTextPaint; - - public PieChartRenderer(PieChart chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - - mHolePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHolePaint.setColor(Color.WHITE); - - mCenterTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mCenterTextPaint.setColor(Color.BLACK); - mCenterTextPaint.setTextSize(Utils.convertDpToPixel(12f)); - mCenterTextPaint.setTextAlign(Align.CENTER); - - mValuePaint.setTextSize(Utils.convertDpToPixel(13f)); - mValuePaint.setColor(Color.WHITE); - mValuePaint.setTextAlign(Align.CENTER); - } - - public Paint getPaintHole() { - return mHolePaint; - } - - public Paint getPaintCenterText() { - return mCenterTextPaint; - } - - @Override - public void drawData(Canvas c) { - - PieData pieData = mChart.getData(); - - for (PieDataSet set : pieData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - } - - protected void drawDataSet(Canvas c, PieDataSet dataSet) { - - float angle = mChart.getRotationAngle(); - - int cnt = 0; - - ArrayList entries = dataSet.getYVals(); - float[] drawAngles = mChart.getDrawAngles(); - - for (int j = 0; j < entries.size(); j++) { - - float newangle = drawAngles[cnt]; - float sliceSpace = dataSet.getSliceSpace(); - - Entry e = entries.get(j); - - // draw only if the value is greater than zero - if ((Math.abs(e.getVal()) > 0.000001)) { - - if (!mChart.needsHighlight(e.getXIndex(), - mChart.getData().getIndexOfDataSet(dataSet))) { - - mRenderPaint.setColor(dataSet.getColor(j)); - c.drawArc(mChart.getCircleBox(), angle + sliceSpace / 2f, - newangle * mAnimator.getPhaseY() - - sliceSpace / 2f, true, mRenderPaint); - } - - // if(sliceSpace > 0f) { - // - // PointF outer = getPosition(c, radius, angle); - // PointF inner = getPosition(c, radius * mHoleRadiusPercent - // / 100f, angle); - // } - } - - angle += newangle * mAnimator.getPhaseX(); - cnt++; - } - } - - @Override - public void drawValues(Canvas c) { - - PointF center = mChart.getCenterCircleBox(); - - // get whole the radius - float r = mChart.getRadius(); - float rotationAngle = mChart.getRotationAngle(); - float[] drawAngles = mChart.getDrawAngles(); - float[] absoluteAngles = mChart.getAbsoluteAngles(); - - float off = r / 2f; - - if (mChart.isDrawHoleEnabled()) { - off = (r - (r / 100f * mChart.getHoleRadius())) / 2f; - } - - r -= off; // offset to keep things inside the chart - - PieData data = mChart.getData(); - ArrayList dataSets = data.getDataSets(); - - int cnt = 0; - - for (int i = 0; i < dataSets.size(); i++) { - - PieDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - ArrayList entries = dataSet.getYVals(); - - for (int j = 0; j < entries.size() * mAnimator.getPhaseX(); j++) { - - // offset needed to center the drawn text in the slice - float offset = drawAngles[cnt] / 2; - - // calculate the text position - float x = (float) (r - * Math.cos(Math.toRadians((rotationAngle + absoluteAngles[cnt] - offset) - * mAnimator.getPhaseY())) + center.x); - float y = (float) (r - * Math.sin(Math.toRadians((rotationAngle + absoluteAngles[cnt] - offset) - * mAnimator.getPhaseY())) + center.y); - - float value = mChart.isUsePercentValuesEnabled() ? entries.get(j).getVal() - / mChart.getYValueSum() * 100f : entries.get(j).getVal(); - - String val = dataSet.getValueFormatter().getFormattedValue(value); - - boolean drawXVals = mChart.isDrawSliceTextEnabled(); - boolean drawYVals = dataSet.isDrawValuesEnabled(); - - // draw everything, depending on settings - if (drawXVals && drawYVals) { - - // use ascent and descent to calculate the new line - // position, - // 1.6f is the line spacing - float lineHeight = (mValuePaint.ascent() + mValuePaint.descent()) * 1.6f; - y -= lineHeight / 2; - - c.drawText(val, x, y, mValuePaint); - if (j < data.getXValCount()) - c.drawText(data.getXVals().get(j), x, y + lineHeight, - mValuePaint); - - } else if (drawXVals && !drawYVals) { - if (j < data.getXValCount()) - c.drawText(data.getXVals().get(j), x, y, mValuePaint); - } else if (!drawXVals && drawYVals) { - - c.drawText(val, x, y, mValuePaint); - } - - cnt++; - } - } - } - - @Override - public void drawExtras(Canvas c) { - drawHole(c); - drawCenterText(c); - } - - /** - * draws the hole in the center of the chart and the transparent circle / - * hole - */ - protected void drawHole(Canvas c) { - - if (mChart.isDrawHoleEnabled()) { - - float transparentCircleRadius = mChart.getTransparentCircleRadius(); - float holeRadius = mChart.getHoleRadius(); - float radius = mChart.getRadius(); - - PointF center = mChart.getCenterCircleBox(); - - int color = mHolePaint.getColor(); - - // draw the hole-circle - c.drawCircle(center.x, center.y, - radius / 100 * holeRadius, mHolePaint); - - if (transparentCircleRadius > holeRadius) { - - // make transparent - mHolePaint.setColor(color & 0x60FFFFFF); - - // draw the transparent-circle - c.drawCircle(center.x, center.y, - radius / 100 * transparentCircleRadius, mHolePaint); - - mHolePaint.setColor(color); - } - } - } - - /** - * draws the description text in the center of the pie chart makes most - * sense when center-hole is enabled - */ - protected void drawCenterText(Canvas c) { - - String centerText = mChart.getCenterText(); - - if (mChart.isDrawCenterTextEnabled() && centerText != null) { - - PointF center = mChart.getCenterCircleBox(); - - // get all lines from the text - String[] lines = centerText.split("\n"); - - // calculate the height for each line - float lineHeight = Utils.calcTextHeight(mCenterTextPaint, lines[0]); - float linespacing = lineHeight * 0.2f; - - float totalheight = lineHeight * lines.length - linespacing * (lines.length - 1); - - int cnt = lines.length; - - float y = center.y; - - for (int i = 0; i < lines.length; i++) { - - String line = lines[lines.length - i - 1]; - - c.drawText(line, center.x, y - + lineHeight * cnt - totalheight / 2f, - mCenterTextPaint); - cnt--; - y -= linespacing; - } - } - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - float rotationAngle = mChart.getRotationAngle(); - float angle = 0f; - - float[] drawAngles = mChart.getDrawAngles(); - float[] absoluteAngles = mChart.getAbsoluteAngles(); - - for (int i = 0; i < indices.length; i++) { - - // get the index to highlight - int xIndex = indices[i].getXIndex(); - if (xIndex >= drawAngles.length) - continue; - - if (xIndex == 0) - angle = rotationAngle; - else - angle = rotationAngle + absoluteAngles[xIndex - 1]; - - angle *= mAnimator.getPhaseY(); - - float sliceDegrees = drawAngles[xIndex]; - - PieDataSet set = mChart.getData() - .getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null) - continue; - - float shift = set.getSelectionShift(); - RectF circleBox = mChart.getCircleBox(); - - /** - * Make the box containing current arc larger equally in every - * dimension, to preserve shape of arc. Code provided by: - * - * @link https://github.com/wogg - */ - RectF highlighted = new RectF(circleBox.left - shift, - circleBox.top - shift, - circleBox.right + shift, - circleBox.bottom + shift); - - mRenderPaint.setColor(set.getColor(xIndex)); - - // redefine the rect that contains the arc so that the - // highlighted pie is not cut off - c.drawArc(highlighted, angle + set.getSliceSpace() / 2f, sliceDegrees - - set.getSliceSpace() / 2f, true, mRenderPaint); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java deleted file mode 100644 index 281f553d71..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ /dev/null @@ -1,230 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PointF; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.RadarData; -import com.github.mikephil.charting.data.RadarDataSet; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class RadarChartRenderer extends DataRenderer { - - protected RadarChart mChart; - - /** paint for drawing the web */ - protected Paint mWebPaint; - - public RadarChartRenderer(RadarChart chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - - mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHighlightPaint.setStyle(Paint.Style.STROKE); - mHighlightPaint.setStrokeWidth(2f); - mHighlightPaint.setColor(Color.rgb(255, 187, 115)); - - mWebPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mWebPaint.setStyle(Paint.Style.STROKE); - } - - public Paint getWebPaint() { - return mWebPaint; - } - - @Override - public void drawData(Canvas c) { - - RadarData radarData = mChart.getData(); - - for (RadarDataSet set : radarData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - } - - protected void drawDataSet(Canvas c, RadarDataSet dataSet) { - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - ArrayList entries = dataSet.getYVals(); - - Path surface = new Path(); - - for (int j = 0; j < entries.size(); j++) { - - mRenderPaint.setColor(dataSet.getColor(j)); - - Entry e = entries.get(j); - - PointF p = Utils.getPosition(center, (e.getVal() - mChart.getYChartMin()) * factor, - sliceangle * j + mChart.getRotationAngle()); - - if (j == 0) - surface.moveTo(p.x, p.y); - else - surface.lineTo(p.x, p.y); - } - - surface.close(); - - // draw filled - if (dataSet.isDrawFilledEnabled()) { - mRenderPaint.setStyle(Paint.Style.FILL); - mRenderPaint.setAlpha(dataSet.getFillAlpha()); - c.drawPath(surface, mRenderPaint); - mRenderPaint.setAlpha(255); - } - - mRenderPaint.setStrokeWidth(dataSet.getLineWidth()); - mRenderPaint.setStyle(Paint.Style.STROKE); - - // draw the line (only if filled is disabled or alpha is below 255) - if (!dataSet.isDrawFilledEnabled() || dataSet.getFillAlpha() < 255) - c.drawPath(surface, mRenderPaint); - } - - @Override - public void drawValues(Canvas c) { - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - float yoffset = Utils.convertDpToPixel(5f); - - for (int i = 0; i < mChart.getData().getDataSetCount(); i++) { - - RadarDataSet dataSet = mChart.getData().getDataSetByIndex(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - ArrayList entries = dataSet.getYVals(); - - for (int j = 0; j < entries.size(); j++) { - - Entry e = entries.get(j); - - PointF p = Utils.getPosition(center, (e.getVal() - mChart.getYChartMin()) * factor, - sliceangle * j + mChart.getRotationAngle()); - - c.drawText(dataSet.getValueFormatter().getFormattedValue(e.getVal()), - p.x, p.y - yoffset, mValuePaint); - } - } - } - - @Override - public void drawExtras(Canvas c) { - drawWeb(c); - } - - protected void drawWeb(Canvas c) { - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - float rotationangle = mChart.getRotationAngle(); - - PointF center = mChart.getCenterOffsets(); - - // draw the web lines that come from the center - mWebPaint.setStrokeWidth(mChart.getWebLineWidth()); - mWebPaint.setColor(mChart.getWebColor()); - mWebPaint.setAlpha(mChart.getWebAlpha()); - - for (int i = 0; i < mChart.getData().getXValCount(); i++) { - - PointF p = Utils.getPosition(center, mChart.getYRange() * factor, sliceangle * i - + rotationangle); - - c.drawLine(center.x, center.y, p.x, p.y, mWebPaint); - } - - // draw the inner-web - mWebPaint.setStrokeWidth(mChart.getWebLineWidthInner()); - mWebPaint.setColor(mChart.getWebColorInner()); - mWebPaint.setAlpha(mChart.getWebAlpha()); - - int labelCount = mChart.getYAxis().mEntryCount; - - for (int j = 0; j < labelCount; j++) { - - for (int i = 0; i < mChart.getData().getXValCount(); i++) { - - float r = (mChart.getYAxis().mEntries[j] - mChart.getYChartMin()) * factor; - - PointF p1 = Utils.getPosition(center, r, sliceangle * i + rotationangle); - PointF p2 = Utils.getPosition(center, r, sliceangle * (i + 1) + rotationangle); - - c.drawLine(p1.x, p1.y, p2.x, p2.y, mWebPaint); - } - } - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - float sliceangle = mChart.getSliceAngle(); - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - for (int i = 0; i < indices.length; i++) { - - RadarDataSet set = mChart.getData() - .getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null) - continue; - - mHighlightPaint.setColor(set.getHighLightColor()); - - // get the index to highlight - int xIndex = indices[i].getXIndex(); - - Entry e = set.getEntryForXIndex(xIndex); - int j = set.getEntryPosition(e); - float y = (e.getVal() - mChart.getYChartMin()); - - PointF p = Utils.getPosition(center, y * factor, - sliceangle * j + mChart.getRotationAngle()); - - float[] pts = new float[] { - p.x, 0, p.x, mViewPortHandler.getChartHeight(), 0, p.y, - mViewPortHandler.getChartWidth(), p.y - }; - - c.drawLines(pts, mHighlightPaint); - } - } - -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java deleted file mode 100644 index 88b353a6a4..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ /dev/null @@ -1,197 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Path; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.interfaces.ScatterDataProvider; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Transformer; - -import java.util.ArrayList; - -public class ScatterChartRenderer extends DataRenderer { - - protected ScatterDataProvider mChart; - - public ScatterChartRenderer(ScatterDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - } - - @Override - public void drawData(Canvas c) { - - ScatterData scatterData = mChart.getScatterData(); - - for (ScatterDataSet set : scatterData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - } - - protected void drawDataSet(Canvas c, ScatterDataSet dataSet) { - - ArrayList entries = dataSet.getYVals(); - - float shapeHalf = dataSet.getScatterShapeSize() / 2f; - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float[] valuePoints = trans.generateTransformedValuesScatter(entries, - mAnimator.getPhaseY()); - - ScatterShape shape = dataSet.getScatterShape(); - - for (int j = 0; j < valuePoints.length * mAnimator.getPhaseX(); j += 2) { - - if (!mViewPortHandler.isInBoundsRight(valuePoints[j])) - break; - - // make sure the lines don't do shitty things outside bounds - if (j != 0 && !mViewPortHandler.isInBoundsLeft(valuePoints[j - 1]) - && !mViewPortHandler.isInBoundsY(valuePoints[j + 1])) - continue; - - // Set the color for the currently drawn value. If the index is - // out of bounds, reuse colors. - mRenderPaint.setColor(dataSet.getColor(j)); - - if (shape == ScatterShape.SQUARE) { - - c.drawRect(valuePoints[j] - shapeHalf, - valuePoints[j + 1] - shapeHalf, valuePoints[j] - + shapeHalf, valuePoints[j + 1] - + shapeHalf, mRenderPaint); - - } else if (shape == ScatterShape.CIRCLE) { - - c.drawCircle(valuePoints[j], valuePoints[j + 1], shapeHalf, - mRenderPaint); - - } else if (shape == ScatterShape.CROSS) { - - c.drawLine(valuePoints[j] - shapeHalf, valuePoints[j + 1], - valuePoints[j] + shapeHalf, - valuePoints[j + 1], mRenderPaint); - c.drawLine(valuePoints[j], valuePoints[j + 1] - shapeHalf, - valuePoints[j], valuePoints[j + 1] - + shapeHalf, mRenderPaint); - - } else if (shape == ScatterShape.TRIANGLE) { - - // create a triangle path - Path tri = new Path(); - tri.moveTo(valuePoints[j], valuePoints[j + 1] - shapeHalf); - tri.lineTo(valuePoints[j] + shapeHalf, valuePoints[j + 1] + shapeHalf); - tri.lineTo(valuePoints[j] - shapeHalf, valuePoints[j + 1] + shapeHalf); - tri.close(); - - c.drawPath(tri, mRenderPaint); - - } else if (shape == ScatterShape.CUSTOM) { - - Path customShape = dataSet.getCustomScatterShape(); - - if (customShape == null) - return; - - // transform the provided custom path - trans.pathValueToPixel(customShape); - c.drawPath(customShape, mRenderPaint); - } - } - } - - @Override - public void drawValues(Canvas c) { - - // if values are drawn - if (mChart.getScatterData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX()) { - - ArrayList dataSets = mChart.getScatterData().getDataSets(); - - for (int i = 0; i < mChart.getScatterData().getDataSetCount(); i++) { - - ScatterDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - ArrayList entries = dataSet.getYVals(); - - float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) - .generateTransformedValuesScatter(entries, - mAnimator.getPhaseY()); - - float shapeSize = dataSet.getScatterShapeSize(); - - for (int j = 0; j < positions.length * mAnimator.getPhaseX(); j += 2) { - - if (!mViewPortHandler.isInBoundsRight(positions[j])) - break; - - // make sure the lines don't do shitty things outside bounds - if (j != 0 && (!mViewPortHandler.isInBoundsLeft(positions[j]) - || !mViewPortHandler.isInBoundsY(positions[j + 1]))) - continue; - - float val = entries.get(j / 2).getVal(); - - c.drawText(dataSet.getValueFormatter().getFormattedValue(val), positions[j], - positions[j + 1] - shapeSize, - mValuePaint); - } - } - } - } - - @Override - public void drawExtras(Canvas c) { - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - for (int i = 0; i < indices.length; i++) { - - ScatterDataSet set = mChart.getScatterData().getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null) - continue; - - mHighlightPaint.setColor(set.getHighLightColor()); - - int xIndex = indices[i].getXIndex(); // get the - // x-position - - if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) - continue; - - float y = set.getYValForXIndex(xIndex) * mAnimator.getPhaseY(); // get - // the - // y-position - - float[] pts = new float[] { - xIndex, mChart.getYChartMax(), xIndex, mChart.getYChartMin(), 0, y, - mChart.getXChartMax(), y - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); - // draw the highlight lines - c.drawLines(pts, mHighlightPaint); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ViewPortHandler.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ViewPortHandler.java deleted file mode 100644 index f0970ec379..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/ViewPortHandler.java +++ /dev/null @@ -1,478 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Matrix; -import android.graphics.PointF; -import android.graphics.RectF; - -import com.github.mikephil.charting.interfaces.ChartInterface; -import com.github.mikephil.charting.utils.Utils; - -public class ViewPortHandler { - - /** matrix used for touch events */ - protected final Matrix mMatrixTouch = new Matrix(); - - /** this rectangle defines the area in which graph values can be drawn */ - protected RectF mContentRect = new RectF(); - - protected float mChartWidth = 0f; - protected float mChartHeight = 0f; - - /** minimum scale value on the y-axis */ - private float mMinScaleY = 1f; - - /** minimum scale value on the x-axis */ - private float mMinScaleX = 1f; - - /** contains the current scale factor of the x-axis */ - private float mScaleX = 1f; - - /** contains the current scale factor of the y-axis */ - private float mScaleY = 1f; - - /** offset that allows the chart to be dragged over its bounds on the x-axis */ - private float mTransOffsetX = 0f; - - /** offset that allows the chart to be dragged over its bounds on the x-axis */ - private float mTransOffsetY = 0f; - - public ViewPortHandler() { - - } - -// public ViewPortHandler(float width, float height) { -// mChartHeight = height; -// mChartWidth = width; -// } - - public void setChartDimens(float width, float height) { - mChartHeight = height; - mChartWidth = width; - } - - public void restrainViewPort(float offsetLeft, float offsetTop, float offsetRight, float offsetBottom) { - mContentRect.set(offsetLeft, offsetTop, mChartWidth - offsetRight, mChartHeight - offsetBottom); - } - - public float offsetLeft() { - return mContentRect.left; - } - - public float offsetRight() { - return mChartWidth - mContentRect.right; - } - - public float offsetTop() { - return mContentRect.top; - } - - public float offsetBottom() { - return mChartHeight - mContentRect.bottom; - } - - public float contentTop() { - return mContentRect.top; - } - - public float contentLeft() { - return mContentRect.left; - } - - public float contentRight() { - return mContentRect.right; - } - - public float contentBottom() { - return mContentRect.bottom; - } - - public float contentWidth() { - return mContentRect.width(); - } - - public float contentHeight() { - return mContentRect.height(); - } - - public RectF getContentRect() { - return mContentRect; - } - - public PointF getContentCenter() { - return new PointF(mContentRect.centerX(), mContentRect.centerY()); - } - - public float getChartHeight() { - return mChartHeight; - } - - public float getChartWidth() { - return mChartWidth; - } - - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW THIS RELATED TO SCALING AND GESTURES */ - - /** - * Zooms in by 1.4f, x and y are the coordinates (in pixels) of the zoom - * center. - * - * @param x - * @param y - */ - public Matrix zoomIn(float x, float y) { - - Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(1.4f, 1.4f, x, y); - - return save; - } - - /** - * Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom - * center. - */ - public Matrix zoomOut(float x, float y) { - - Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(0.7f, 0.7f, x, y); - - return save; - } - - /** - * Zooms in or out by the given scale factor. x and y are the coordinates - * (in pixels) of the zoom center. - * - * @param scaleX if < 1f --> zoom out, if > 1f --> zoom in - * @param scaleY if < 1f --> zoom out, if > 1f --> zoom in - * @param x - * @param y - */ - public Matrix zoom(float scaleX, float scaleY, float x, float y) { - - Matrix save = new Matrix(); - save.set(mMatrixTouch); - - // Log.i(LOG_TAG, "Zooming, x: " + x + ", y: " + y); - - save.postScale(scaleX, scaleY, x, y); - - return save; - } - - /** - * Resets all zooming and dragging and makes the chart fit exactly it's - * bounds. - */ - public Matrix fitScreen() { - - Matrix save = new Matrix(); - save.set(mMatrixTouch); - - float[] vals = new float[9]; - - save.getValues(vals); - - // reset all translations and scaling - vals[Matrix.MTRANS_X] = 0f; - vals[Matrix.MTRANS_Y] = 0f; - vals[Matrix.MSCALE_X] = 1f; - vals[Matrix.MSCALE_Y] = 1f; - - save.setValues(vals); - - return save; - } - - /** - * Centers the viewport around the specified position (x-index and y-value) - * in the chart. Centering the viewport outside the bounds of the chart is - * not possible. Makes most sense in combination with the - * setScaleMinima(...) method. - * - * @param pts the position to center view viewport to - * @param chart - * @return save - */ - public synchronized void centerViewPort(final float[] transformedPts, final ChartInterface chart) { - - Matrix save = new Matrix(); - save.set(mMatrixTouch); - - final float x = transformedPts[0] - offsetLeft(); - final float y = transformedPts[1] - offsetTop(); - - save.postTranslate(-x, -y); - - refresh(save, chart, false); - -// final View v = chart.getChartView(); -// -// v.post(new Runnable() { -// -// @Override -// public void run() { -// Matrix save = new Matrix(); -// save.set(mMatrixTouch); -// -// final float x = transformedPts[0] - offsetLeft(); -// final float y = transformedPts[1] - offsetTop(); -// -// save.postTranslate(-x, -y); -// -// refresh(save, chart, false); -// } -// }); - } - - /** - * call this method to refresh the graph with a given matrix - * - * @param newMatrix - * @return - */ - public Matrix refresh(Matrix newMatrix, ChartInterface chart, boolean invalidate) { - - mMatrixTouch.set(newMatrix); - - // make sure scale and translation are within their bounds - limitTransAndScale(mMatrixTouch, mContentRect); - - chart.getChartView().invalidate(); - - newMatrix.set(mMatrixTouch); - return newMatrix; - } - - /** - * limits the maximum scale and X translation of the given matrix - * - * @param matrix - */ - public void limitTransAndScale(Matrix matrix, RectF content) { - - float[] vals = new float[9]; - matrix.getValues(vals); - - float curTransX = vals[Matrix.MTRANS_X]; - float curScaleX = vals[Matrix.MSCALE_X]; - - float curTransY = vals[Matrix.MTRANS_Y]; - float curScaleY = vals[Matrix.MSCALE_Y]; - - // min scale-x is 1f - mScaleX = Math.max(mMinScaleX, curScaleX); - - // min scale-y is 1f - mScaleY = Math.max(mMinScaleY, curScaleY); - - float width = 0f; - float height = 0f; - - if (content != null) { - width = content.width(); - height = content.height(); - } - - float maxTransX = -width * (mScaleX - 1f); - float newTransX = Math.min(Math.max(curTransX, maxTransX - mTransOffsetX), mTransOffsetX); - - // if(curScaleX < mMinScaleX) { - // newTransX = (-width * (mScaleX - 1f)) / 2f; - // } - - float maxTransY = height * (mScaleY - 1f); - float newTransY = Math.max(Math.min(curTransY, maxTransY + mTransOffsetY), -mTransOffsetY); - - // if(curScaleY < mMinScaleY) { - // newTransY = (height * (mScaleY - 1f)) / 2f; - // } - - vals[Matrix.MTRANS_X] = newTransX; - vals[Matrix.MSCALE_X] = mScaleX; - - vals[Matrix.MTRANS_Y] = newTransY; - vals[Matrix.MSCALE_Y] = mScaleY; - - matrix.setValues(vals); - } - - public void setMinimumScaleX(float xScale) { - - if (xScale < 1f) - xScale = 1f; - - mMinScaleX = xScale; - - limitTransAndScale(mMatrixTouch, mContentRect); - } - - public void setMinimumScaleY(float yScale) { - - if (yScale < 1f) - yScale = 1f; - - mMinScaleY = yScale; - - limitTransAndScale(mMatrixTouch, mContentRect); - } - -// /** -// * Sets the minimum scale values for both axes. This limits the extent to -// * which the user can zoom-out. -// * -// * @param scaleXmin -// * @param scaleYmin -// */ -// public void setScaleMinima(float scaleXmin, float scaleYmin, ChartInterface chart) { -// -// if (scaleXmin < 1f) -// scaleXmin = 1f; -// if (scaleYmin < 1f) -// scaleYmin = 1f; -// -// mMinScaleX = scaleXmin; -// mMinScaleY = scaleYmin; -//// -//// Matrix save = zoom(mMinScaleX, mMinScaleY, 0f, 0f); -//// refresh(mMatrixTouch, chart); -// -// limitTransAndScale(mMatrixTouch, mContentRect); -// } - - public Matrix getMatrixTouch() { - return mMatrixTouch; - } - - /** - * ################ ################ ################ ################ - */ - /** BELOW METHODS FOR BOUNDS CHECK */ - - public boolean isInBoundsX(float x) { - if (isInBoundsLeft(x) && isInBoundsRight(x)) - return true; - else - return false; - } - - public boolean isInBoundsY(float y) { - if (isInBoundsTop(y) && isInBoundsBottom(y)) - return true; - else - return false; - } - - public boolean isInBounds(float x, float y) { - if (isInBoundsX(x) && isInBoundsY(y)) - return true; - else - return false; - } - - public boolean isInBoundsLeft(float x) { - return mContentRect.left <= x ? true : false; - } - - public boolean isInBoundsRight(float x) { - return mContentRect.right >= x ? true : false; - } - - public boolean isInBoundsTop(float y) { - return mContentRect.top <= y ? true : false; - } - - public boolean isInBoundsBottom(float y) { - return mContentRect.bottom >= y ? true : false; - } - - - /** - * returns the current x-scale factor - */ - public float getScaleX() { - return mScaleX; - } - - /** - * returns the current y-scale factor - */ - public float getScaleY() { - return mScaleY; - } - - /** - * if the chart is fully zoomed out, return true - * - * @return - */ - public boolean isFullyZoomedOut() { - - if (isFullyZoomedOutX() && isFullyZoomedOutY()) - return true; - else - return false; - } - - /** - * Returns true if the chart is fully zoomed out on it's y-axis (vertical). - * - * @return - */ - public boolean isFullyZoomedOutY() { - if (mScaleY > mMinScaleY || mMinScaleY > 1f) - return false; - else - return true; - } - - /** - * Returns true if the chart is fully zoomed out on it's x-axis - * (horizontal). - * - * @return - */ - public boolean isFullyZoomedOutX() { - if (mScaleX > mMinScaleX || mMinScaleX > 1f) - return false; - else - return true; - } - - /** - * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the x-axis. - * - * @param offset - */ - public void setDragOffsetX(float offset) { - mTransOffsetX = Utils.convertDpToPixel(offset); - } - - /** - * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the y-axis. - * - * @param offset - */ - public void setDragOffsetY(float offset) { - mTransOffsetY = Utils.convertDpToPixel(offset); - } - - /** - * Returns true if both drag offsets (x and y) are zero or smaller. - * - * @return - */ - public boolean hasNoDragOffset() { - return mTransOffsetX <= 0 && mTransOffsetY <= 0 ? true : false; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java deleted file mode 100644 index a559a298f0..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ /dev/null @@ -1,183 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint.Align; - -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class XAxisRenderer extends AxisRenderer { - - protected XAxis mXAxis; - - public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans) { - super(viewPortHandler, trans); - - this.mXAxis = xAxis; - - mAxisPaint.setColor(Color.BLACK); - mAxisPaint.setTextAlign(Align.CENTER); - mAxisPaint.setTextSize(Utils.convertDpToPixel(10f)); - } - - public void computeAxis(float xValAverageLength, ArrayList xValues) { - - mAxisPaint.setTypeface(mXAxis.getTypeface()); - mAxisPaint.setTextSize(mXAxis.getTextSize()); - - StringBuffer a = new StringBuffer(); - - int max = (int) Math.round(xValAverageLength - + mXAxis.getSpaceBetweenLabels()); - - for (int i = 0; i < max; i++) { - a.append("h"); - } - - mXAxis.mLabelWidth = Utils.calcTextWidth(mAxisPaint, a.toString()); - mXAxis.mLabelHeight = Utils.calcTextHeight(mAxisPaint, "Q"); - mXAxis.setValues(xValues); - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) - return; - - float yoffset = Utils.convertDpToPixel(4f); - - mAxisPaint.setTypeface(mXAxis.getTypeface()); - mAxisPaint.setTextSize(mXAxis.getTextSize()); - mAxisPaint.setColor(mXAxis.getTextColor()); - - if (mXAxis.getPosition() == XAxisPosition.TOP) { - - drawLabels(c, mViewPortHandler.offsetTop() - yoffset); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - drawLabels(c, mViewPortHandler.contentBottom() + mXAxis.mLabelHeight + yoffset * 1.5f); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { - - drawLabels(c, mViewPortHandler.contentBottom() - yoffset); - - } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { - - drawLabels(c, mViewPortHandler.offsetTop() + yoffset + mXAxis.mLabelHeight); - - } else { // BOTH SIDED - - drawLabels(c, mViewPortHandler.offsetTop() - yoffset); - drawLabels(c, mViewPortHandler.contentBottom() + mXAxis.mLabelHeight + yoffset * 1.6f); - } - } - - @Override - public void renderAxisLine(Canvas c) { - - if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled()) - return; - - mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); - mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); - - if (mXAxis.getPosition() == XAxisPosition.TOP - || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), - mViewPortHandler.contentTop(), mAxisLinePaint); - } - - if (mXAxis.getPosition() == XAxisPosition.BOTTOM - || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom(), mViewPortHandler.contentRight(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } - } - - /** - * draws the x-labels on the specified y-position - * - * @param pos - */ - protected void drawLabels(Canvas c, float pos) { - - // pre allocate to save performance (dont allocate in loop) - float[] position = new float[] { - 0f, 0f - }; - - for (int i = 0; i < mXAxis.getValues().size(); i += mXAxis.mAxisLabelModulus) { - - position[0] = i; - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsX(position[0])) { - - String label = mXAxis.getValues().get(i); - - if (mXAxis.isAvoidFirstLastClippingEnabled()) { - - // avoid clipping of the last - if (i == mXAxis.getValues().size() - 1 && mXAxis.getValues().size() > 1) { - float width = Utils.calcTextWidth(mAxisPaint, label); - - if (width > mViewPortHandler.offsetRight() * 2 - && position[0] + width > mViewPortHandler.getChartWidth()) - position[0] -= width / 2; - - // avoid clipping of the first - } else if (i == 0) { - - float width = Utils.calcTextWidth(mAxisPaint, label); - position[0] += width / 2; - } - } - - c.drawText(label, position[0], - pos, - mAxisPaint); - } - } - } - - @Override - public void renderGridLines(Canvas c) { - - if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) - return; - - float[] position = new float[] { - 0f, 0f - }; - - mGridPaint.setColor(mXAxis.getGridColor()); - mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); - - for (int i = 0; i < mXAxis.getValues().size(); i += mXAxis.mAxisLabelModulus) { - - position[0] = i; - - mTrans.pointValuesToPixel(position); - - if (position[0] >= mViewPortHandler.offsetLeft() - && position[0] <= mViewPortHandler.getChartWidth()) { - - c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0], - mViewPortHandler.contentBottom(), mGridPaint); - } - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java deleted file mode 100644 index 8960ab3eb2..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java +++ /dev/null @@ -1,110 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; - -public class XAxisRendererBarChart extends XAxisRenderer { - - protected BarChart mChart; - - public XAxisRendererBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, - BarChart chart) { - super(viewPortHandler, xAxis, trans); - - this.mChart = chart; - } - - /** - * draws the x-labels on the specified y-position - * - * @param pos - */ - @Override - protected void drawLabels(Canvas c, float pos) { - - // pre allocate to save performance (dont allocate in loop) - float[] position = new float[] { - 0f, 0f - }; - - BarData bd = mChart.getData(); - int step = bd.getDataSetCount(); - - for (int i = 0; i < mXAxis.getValues().size(); i += mXAxis.mAxisLabelModulus) { - - position[0] = i * step + i * bd.getGroupSpace() - + bd.getGroupSpace() / 2f; - - // consider groups (center label for each group) - if (step > 1) { - position[0] += ((float) step - 1f) / 2f; - } - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsX(position[0])) { - - String label = mXAxis.getValues().get(i); - - if (mXAxis.isAvoidFirstLastClippingEnabled()) { - - // avoid clipping of the last - if (i == mXAxis.getValues().size() - 1) { - float width = Utils.calcTextWidth(mAxisPaint, label); - - if (width > mViewPortHandler.offsetRight() * 2 - && position[0] + width > mViewPortHandler.getChartWidth()) - position[0] -= width / 2; - - // avoid clipping of the first - } else if (i == 0) { - - float width = Utils.calcTextWidth(mAxisPaint, label); - position[0] += width / 2; - } - } - - c.drawText(label, position[0], - pos, - mAxisPaint); - } - } - } - - @Override - public void renderGridLines(Canvas c) { - - if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) - return; - - float[] position = new float[] { - 0f, 0f - }; - - mGridPaint.setColor(mXAxis.getGridColor()); - mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); - - BarData bd = mChart.getData(); - // take into consideration that multiple DataSets increase mDeltaX - int step = bd.getDataSetCount(); - - for (int i = 0; i < mXAxis.getValues().size(); i += mXAxis.mAxisLabelModulus) { - - position[0] = i * step + i * bd.getGroupSpace() - 0.5f; - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsX(position[0])) { - - c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0], - mViewPortHandler.contentBottom(), mGridPaint); - } - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java deleted file mode 100644 index 119f2fcebc..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ /dev/null @@ -1,167 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint.Align; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class XAxisRendererHorizontalBarChart extends XAxisRendererBarChart { - - public XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, - Transformer trans, BarChart chart) { - super(viewPortHandler, xAxis, trans, chart); - } - - @Override - public void computeAxis(float xValAverageLength, ArrayList xValues) { - - mAxisPaint.setTypeface(mXAxis.getTypeface()); - mAxisPaint.setTextSize(mXAxis.getTextSize()); - mXAxis.setValues(xValues); - - String longest = mXAxis.getLongestLabel(); - mXAxis.mLabelWidth = (int) (Utils.calcTextWidth(mAxisPaint, longest) + mXAxis.getXOffset() * 3.5f); - mXAxis.mLabelHeight = Utils.calcTextHeight(mAxisPaint, longest); - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) - return; - - float xoffset = mXAxis.getXOffset(); - - mAxisPaint.setTypeface(mXAxis.getTypeface()); - mAxisPaint.setTextSize(mXAxis.getTextSize()); - mAxisPaint.setColor(mXAxis.getTextColor()); - - if (mXAxis.getPosition() == XAxisPosition.TOP) { - - mAxisPaint.setTextAlign(Align.LEFT); - drawLabels(c, mViewPortHandler.contentRight() + xoffset); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - mAxisPaint.setTextAlign(Align.RIGHT); - drawLabels(c, mViewPortHandler.contentLeft() - xoffset); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { - - mAxisPaint.setTextAlign(Align.LEFT); - drawLabels(c, mViewPortHandler.contentLeft() + xoffset); - - } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { - - mAxisPaint.setTextAlign(Align.RIGHT); - drawLabels(c, mViewPortHandler.contentRight() - xoffset); - - } else { // BOTH SIDED - - drawLabels(c, mViewPortHandler.contentLeft()); - drawLabels(c, mViewPortHandler.contentRight()); - } - } - - /** - * draws the x-labels on the specified y-position - * - * @param pos - */ - @Override - protected void drawLabels(Canvas c, float pos) { - - // pre allocate to save performance (dont allocate in loop) - float[] position = new float[] { - 0f, 0f - }; - - BarData bd = mChart.getData(); - int step = bd.getDataSetCount(); - - for (int i = 0; i < mXAxis.getValues().size(); i += mXAxis.mAxisLabelModulus) { - - position[1] = i * step + i * bd.getGroupSpace() - + bd.getGroupSpace() / 2f; - - // consider groups (center label for each group) - if (step > 1) { - position[1] += ((float) step - 1f) / 2f; - } - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsY(position[1])) { - - String label = mXAxis.getValues().get(i); - c.drawText(label, pos, position[1] + mXAxis.mLabelHeight / 2f, - mAxisPaint); - } - } - } - - @Override - public void renderGridLines(Canvas c) { - - if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) - return; - - float[] position = new float[] { - 0f, 0f - }; - - mGridPaint.setColor(mXAxis.getGridColor()); - mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); - - BarData bd = mChart.getData(); - // take into consideration that multiple DataSets increase mDeltaX - int step = bd.getDataSetCount(); - - for (int i = 0; i < mXAxis.getValues().size(); i += mXAxis.mAxisLabelModulus) { - - position[1] = i * step + i * bd.getGroupSpace() - 0.5f; - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsY(position[1])) { - - c.drawLine(mViewPortHandler.contentLeft(), position[1], - mViewPortHandler.contentRight(), position[1], mGridPaint); - } - } - } - - @Override - public void renderAxisLine(Canvas c) { - - if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled()) - return; - - mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); - mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); - - if (mXAxis.getPosition() == XAxisPosition.TOP - || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(mViewPortHandler.contentRight(), - mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } - - if (mXAxis.getPosition() == XAxisPosition.BOTTOM - || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop(), mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java deleted file mode 100644 index e3b0572f97..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java +++ /dev/null @@ -1,51 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.PointF; - -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.utils.Utils; - -public class XAxisRendererRadarChart extends XAxisRenderer { - - private RadarChart mChart; - - public XAxisRendererRadarChart(ViewPortHandler viewPortHandler, XAxis xAxis, RadarChart chart) { - super(viewPortHandler, xAxis, null); - - mChart = chart; - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) - return; - - mAxisPaint.setTypeface(mXAxis.getTypeface()); - mAxisPaint.setTextSize(mXAxis.getTextSize()); - mAxisPaint.setColor(mXAxis.getTextColor()); - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - for (int i = 0; i < mXAxis.getValues().size(); i++) { - - String text = mXAxis.getValues().get(i); - - float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; - - PointF p = Utils.getPosition(center, mChart.getYRange() * factor - + mXAxis.mLabelWidth / 2f, angle); - - c.drawText(text, p.x, p.y + mXAxis.mLabelHeight / 2f, mAxisPaint); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java deleted file mode 100644 index a0038787f1..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ /dev/null @@ -1,324 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; - -import com.github.mikephil.charting.components.LimitLine; -import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; -import com.github.mikephil.charting.utils.PointD; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class YAxisRenderer extends AxisRenderer { - - /** paint used for the limit lines */ - protected Paint mLimitLinePaint; - - protected YAxis mYAxis; - - public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer trans) { - super(viewPortHandler, trans); - - this.mYAxis = yAxis; - - mAxisPaint.setColor(Color.BLACK); - mAxisPaint.setTextSize(Utils.convertDpToPixel(10f)); - - mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mLimitLinePaint.setStyle(Paint.Style.FILL_AND_STROKE); - } - - /** - * Computes the axis values. - * - * @param yMin - the minimum y-value in the data object for this axis - * @param yMax - the maximum y-value in the data object for this axis - */ - public void computeAxis(float yMin, float yMax) { - - // calculate the starting and entry point of the y-labels (depending on - // zoom / contentrect bounds) - if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { - - PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop()); - PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom()); - - if (!mYAxis.isInverted()) { - yMin = (float) p2.y; - yMax = (float) p1.y; - } else { - - if (!mYAxis.isStartAtZeroEnabled()) - yMin = (float) Math.min(p1.y, p2.y); - else - yMin = 0; - yMax = (float) Math.max(p1.y, p2.y); - } - } - - computeAxisValues(yMin, yMax); - } - - /** - * Sets up the y-axis labels. Computes the desired number of labels between - * the two given extremes. Unlike the papareXLabels() method, this method - * needs to be called upon every refresh of the view. - * - * @return - */ - protected void computeAxisValues(float min, float max) { - - float yMin = min; - float yMax = max; - - int labelCount = mYAxis.getLabelCount(); - double range = Math.abs(yMax - yMin); - - if (labelCount == 0 || range <= 0) { - mYAxis.mEntries = new float[] {}; - mYAxis.mEntryCount = 0; - return; - } - - double rawInterval = range / labelCount; - double interval = Utils.roundToNextSignificant(rawInterval); - double intervalMagnitude = Math.pow(10, (int) Math.log10(interval)); - int intervalSigDigit = (int) (interval / intervalMagnitude); - if (intervalSigDigit > 5) { - // Use one order of magnitude higher, to avoid intervals like 0.9 or - // 90 - interval = Math.floor(10 * intervalMagnitude); - } - - // if the labels should only show min and max - if (mYAxis.isShowOnlyMinMaxEnabled()) { - - mYAxis.mEntryCount = 2; - mYAxis.mEntries = new float[2]; - mYAxis.mEntries[0] = yMin; - mYAxis.mEntries[1] = yMax; - - } else { - - double first = Math.ceil(yMin / interval) * interval; - double last = Utils.nextUp(Math.floor(yMax / interval) * interval); - - double f; - int i; - int n = 0; - for (f = first; f <= last; f += interval) { - ++n; - } - - mYAxis.mEntryCount = n; - - if (mYAxis.mEntries.length < n) { - // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[n]; - } - - for (f = first, i = 0; i < n; f += interval, ++i) { - mYAxis.mEntries[i] = (float) f; - } - } - - if (interval < 1) { - mYAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); - } else { - mYAxis.mDecimals = 0; - } - } - - /** - * draws the y-axis labels to the screen - */ - @Override - public void renderAxisLabels(Canvas c) { - - if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) - return; - - float[] positions = new float[mYAxis.mEntryCount * 2]; - - for (int i = 0; i < positions.length; i += 2) { - // only fill y values, x values are not needed since the y-labels - // are - // static on the x-axis - positions[i + 1] = mYAxis.mEntries[i / 2]; - } - - mTrans.pointValuesToPixel(positions); - - mAxisPaint.setTypeface(mYAxis.getTypeface()); - mAxisPaint.setTextSize(mYAxis.getTextSize()); - mAxisPaint.setColor(mYAxis.getTextColor()); - - float xoffset = mYAxis.getXOffset(); - float yoffset = Utils.calcTextHeight(mAxisPaint, "A") / 2.5f; - - AxisDependency dependency = mYAxis.getAxisDependency(); - YAxisLabelPosition labelPosition = mYAxis.getLabelPosition(); - - float xPos = 0f; - - if (dependency == AxisDependency.LEFT) { - - if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { - mAxisPaint.setTextAlign(Align.RIGHT); - xPos = mViewPortHandler.offsetLeft() - xoffset; - } else { - mAxisPaint.setTextAlign(Align.LEFT); - xPos = mViewPortHandler.offsetLeft() + xoffset; - } - - } else { - - if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { - mAxisPaint.setTextAlign(Align.LEFT); - xPos = mViewPortHandler.contentRight() + xoffset; - } else { - mAxisPaint.setTextAlign(Align.RIGHT); - xPos = mViewPortHandler.contentRight() - xoffset; - } - } - - drawYLabels(c, xPos, positions, yoffset); - } - - @Override - public void renderAxisLine(Canvas c) { - - if (!mYAxis.isEnabled() || !mYAxis.isDrawAxisLineEnabled()) - return; - - mAxisLinePaint.setColor(mYAxis.getAxisLineColor()); - mAxisLinePaint.setStrokeWidth(mYAxis.getAxisLineWidth()); - - if (mYAxis.getAxisDependency() == AxisDependency.LEFT) { - c.drawLine(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop(), mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } else { - c.drawLine(mViewPortHandler.contentRight(), - mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } - } - - /** - * draws the y-labels on the specified x-position - * - * @param fixedPosition - * @param positions - */ - protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) { - - // draw - for (int i = 0; i < mYAxis.mEntryCount; i++) { - - String text = mYAxis.getFormattedLabel(i); - - if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1) - return; - - c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisPaint); - } - } - - @Override - public void renderGridLines(Canvas c) { - - if (!mYAxis.isDrawGridLinesEnabled() || !mYAxis.isEnabled()) - return; - - // pre alloc - float[] position = new float[2]; - - mGridPaint.setColor(mYAxis.getGridColor()); - mGridPaint.setStrokeWidth(mYAxis.getGridLineWidth()); - - // draw the horizontal grid - for (int i = 0; i < mYAxis.mEntryCount; i++) { - - position[1] = mYAxis.mEntries[i]; - mTrans.pointValuesToPixel(position); - - c.drawLine(mViewPortHandler.offsetLeft(), position[1], mViewPortHandler.contentRight(), - position[1], - mGridPaint); - } - } - - /** - * Draws the LimitLines associated with this axis to the screen. - * - * @param c - */ - public void renderLimitLines(Canvas c) { - - ArrayList limitLines = mYAxis.getLimitLines(); - - if (limitLines == null || limitLines.size() <= 0) - return; - - float[] pts = new float[4]; - - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - pts[1] = l.getLimit(); - pts[3] = l.getLimit(); - - mTrans.pointValuesToPixel(pts); - - pts[0] = mViewPortHandler.contentLeft(); - pts[2] = mViewPortHandler.contentRight(); - - mLimitLinePaint.setColor(l.getLineColor()); - mLimitLinePaint.setPathEffect(l.getDashPathEffect()); - mLimitLinePaint.setStrokeWidth(l.getLineWidth()); - - c.drawLines(pts, mLimitLinePaint); - - String label = l.getLabel(); - - // if drawing the limit-value label is enabled - if (label != null && !label.equals("")) { - - float xOffset = Utils.convertDpToPixel(4f); - float yOffset = l.getLineWidth() + Utils.calcTextHeight(mLimitLinePaint, label) - / 2f; - - mLimitLinePaint.setPathEffect(null); - mLimitLinePaint.setColor(l.getTextColor()); - mLimitLinePaint.setStrokeWidth(0.5f); - mLimitLinePaint.setTextSize(l.getTextSize()); - - if (l.getLabelPosition() == LimitLabelPosition.POS_RIGHT) { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - c.drawText(label, mViewPortHandler.contentRight() - - xOffset, - pts[1] - yOffset, mLimitLinePaint); - - } else { - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, mViewPortHandler.offsetLeft() - + xOffset, - pts[1] - yOffset, mLimitLinePaint); - } - } - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java deleted file mode 100644 index a960cb7553..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ /dev/null @@ -1,177 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint.Align; - -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; -import com.github.mikephil.charting.utils.PointD; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; - -public class YAxisRendererHorizontalBarChart extends YAxisRenderer { - - public YAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, YAxis yAxis, - Transformer trans) { - super(viewPortHandler, yAxis, trans); - } - - /** - * Computes the axis values. - * - * @param yMin - the minimum y-value in the data object for this axis - * @param yMax - the maximum y-value in the data object for this axis - */ - public void computeAxis(float yMin, float yMax) { - - // calculate the starting and entry point of the y-labels (depending on - // zoom / contentrect bounds) - if (mViewPortHandler.contentHeight() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { - - PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop()); - PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), - mViewPortHandler.contentTop()); - - if (!mYAxis.isInverted()) { - yMin = (float) p1.x; - yMax = (float) p2.x; - } else { - - if (!mYAxis.isStartAtZeroEnabled()) - yMin = (float) Math.min(p1.x, p2.x); - else - yMin = 0; - yMax = (float) Math.max(p1.x, p2.x); - } - } - - computeAxisValues(yMin, yMax); - } - - /** - * draws the y-axis labels to the screen - */ - @Override - public void renderAxisLabels(Canvas c) { - - if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) - return; - - float[] positions = new float[mYAxis.mEntryCount * 2]; - - for (int i = 0; i < positions.length; i += 2) { - // only fill y values, x values are not needed since the y-labels - // are - // static on the x-axis - positions[i] = mYAxis.mEntries[i / 2]; - } - - mTrans.pointValuesToPixel(positions); - - mAxisPaint.setTypeface(mYAxis.getTypeface()); - mAxisPaint.setTextSize(mYAxis.getTextSize()); - mAxisPaint.setColor(mYAxis.getTextColor()); - mAxisPaint.setTextAlign(Align.CENTER); - - float yoffset = Utils.calcTextHeight(mAxisPaint, "A") + mYAxis.getYOffset(); - - AxisDependency dependency = mYAxis.getAxisDependency(); - YAxisLabelPosition labelPosition = mYAxis.getLabelPosition(); - - float yPos = 0f; - - if (dependency == AxisDependency.LEFT) { - - if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { - yoffset = Utils.convertDpToPixel(3f); - yPos = mViewPortHandler.contentTop(); - } else { - yoffset = yoffset * -1f; - yPos = mViewPortHandler.contentTop(); - } - - } else { - - if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { - yoffset = yoffset * -1f; - yPos = mViewPortHandler.contentBottom(); - } else { - yoffset = Utils.convertDpToPixel(4f); - yPos = mViewPortHandler.contentBottom(); - } - } - - drawYLabels(c, yPos, positions, yoffset); - } - - @Override - public void renderAxisLine(Canvas c) { - - if (!mYAxis.isEnabled() || !mYAxis.isDrawAxisLineEnabled()) - return; - - mAxisLinePaint.setColor(mYAxis.getAxisLineColor()); - mAxisLinePaint.setStrokeWidth(mYAxis.getAxisLineWidth()); - - if (mYAxis.getAxisDependency() == AxisDependency.LEFT) { - c.drawLine(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), - mViewPortHandler.contentTop(), mAxisLinePaint); - } else { - c.drawLine(mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom(), mViewPortHandler.contentRight(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } - } - - /** - * draws the y-labels on the specified x-position - * - * @param fixedPosition - * @param positions - */ - @Override - protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) { - - mAxisPaint.setTypeface(mYAxis.getTypeface()); - mAxisPaint.setTextSize(mYAxis.getTextSize()); - mAxisPaint.setColor(mYAxis.getTextColor()); - - for (int i = 0; i < mYAxis.mEntryCount; i++) { - - String text = mYAxis.getFormattedLabel(i); - - if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1) - return; - - c.drawText(text, positions[i * 2], fixedPosition - offset, mAxisPaint); - } - } - - @Override - public void renderGridLines(Canvas c) { - - if (!mYAxis.isDrawGridLinesEnabled() || !mYAxis.isEnabled()) - return; - - // pre alloc - float[] position = new float[2]; - - mGridPaint.setColor(mYAxis.getGridColor()); - mGridPaint.setStrokeWidth(mYAxis.getGridLineWidth()); - - // draw the horizontal grid - for (int i = 0; i < mYAxis.mEntryCount; i++) { - - position[0] = mYAxis.mEntries[i]; - mTrans.pointValuesToPixel(position); - - c.drawLine(position[0], mViewPortHandler.contentTop(), position[0], - mViewPortHandler.contentBottom(), - mGridPaint); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java deleted file mode 100644 index c19653be17..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ /dev/null @@ -1,172 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Path; -import android.graphics.PointF; - -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.components.LimitLine; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; - -public class YAxisRendererRadarChart extends YAxisRenderer { - - private RadarChart mChart; - - public YAxisRendererRadarChart(ViewPortHandler viewPortHandler, YAxis yAxis, RadarChart chart) { - super(viewPortHandler, yAxis, null); - - this.mChart = chart; - } - - @Override - public void computeAxis(float yMin, float yMax) { - computeAxisValues(yMin, yMax); - } - - @Override - protected void computeAxisValues(float min, float max) { - float yMin = min; - float yMax = max; - - int labelCount = mYAxis.getLabelCount(); - double range = Math.abs(yMax - yMin); - - if (labelCount == 0 || range <= 0) { - mYAxis.mEntries = new float[] {}; - mYAxis.mEntryCount = 0; - return; - } - - double rawInterval = range / labelCount; - double interval = Utils.roundToNextSignificant(rawInterval); - double intervalMagnitude = Math.pow(10, (int) Math.log10(interval)); - int intervalSigDigit = (int) (interval / intervalMagnitude); - if (intervalSigDigit > 5) { - // Use one order of magnitude higher, to avoid intervals like 0.9 or - // 90 - interval = Math.floor(10 * intervalMagnitude); - } - - // if the labels should only show min and max - if (mYAxis.isShowOnlyMinMaxEnabled()) { - - mYAxis.mEntryCount = 2; - mYAxis.mEntries = new float[2]; - mYAxis.mEntries[0] = yMin; - mYAxis.mEntries[1] = yMax; - - } else { - - double first = Math.ceil(yMin / interval) * interval; - double last = Utils.nextUp(Math.floor(yMax / interval) * interval); - - double f; - int i; - int n = 0; - for (f = first; f <= last; f += interval) { - ++n; - } - - if (Float.isNaN(mYAxis.getAxisMaxValue())) - n += 1; - - mYAxis.mEntryCount = n; - - if (mYAxis.mEntries.length < n) { - // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[n]; - } - - for (f = first, i = 0; i < n; f += interval, ++i) { - mYAxis.mEntries[i] = (float) f; - } - } - - if (interval < 1) { - mYAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); - } else { - mYAxis.mDecimals = 0; - } - - mYAxis.mAxisMaximum = mYAxis.mEntries[mYAxis.mEntryCount - 1]; - mYAxis.mAxisRange = Math.abs(mYAxis.mAxisMaximum - mYAxis.mAxisMinimum); - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) - return; - - mAxisPaint.setTypeface(mYAxis.getTypeface()); - mAxisPaint.setTextSize(mYAxis.getTextSize()); - mAxisPaint.setColor(mYAxis.getTextColor()); - - PointF center = mChart.getCenterOffsets(); - float factor = mChart.getFactor(); - - int labelCount = mYAxis.mEntryCount; - - for (int j = 0; j < labelCount; j++) { - - if (j == labelCount - 1 && mYAxis.isDrawTopYLabelEntryEnabled() == false) - break; - - float r = (mYAxis.mEntries[j] - mYAxis.mAxisMinimum) * factor; - - PointF p = Utils.getPosition(center, r, mChart.getRotationAngle()); - - String label = mYAxis.getFormattedLabel(j); - - c.drawText(label, p.x + 10, p.y, mAxisPaint); - } - } - - @Override - public void renderLimitLines(Canvas c) { - - ArrayList limitLines = mYAxis.getLimitLines(); - - if (limitLines == null) - return; - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - mLimitLinePaint.setColor(l.getLineColor()); - mLimitLinePaint.setPathEffect(l.getDashPathEffect()); - mLimitLinePaint.setStrokeWidth(l.getLineWidth()); - - float r = (l.getLimit() - mChart.getYChartMin()) * factor; - - Path limitPath = new Path(); - - for (int j = 0; j < mChart.getData().getXValCount(); j++) { - - PointF p = Utils.getPosition(center, r, sliceangle * j + mChart.getRotationAngle()); - - if (j == 0) - limitPath.moveTo(p.x, p.y); - else - limitPath.lineTo(p.x, p.y); - } - - limitPath.close(); - - c.drawPath(limitPath, mLimitLinePaint); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ColorFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/ColorFormatter.java deleted file mode 100644 index f646d0e4a9..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ColorFormatter.java +++ /dev/null @@ -1,15 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import com.github.mikephil.charting.data.Entry; - -/** - * Interface that can be used to return a customized color instead of setting - * colors via the setColor(...) method of the DataSet. - * - * @author Philipp Jahoda - */ -public interface ColorFormatter { - - public int getColor(Entry e, int index); -} \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/DefaultValueFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/DefaultValueFormatter.java deleted file mode 100644 index f63c77edc7..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/DefaultValueFormatter.java +++ /dev/null @@ -1,40 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import java.text.DecimalFormat; - -/** - * Default formatter used for formatting values. Uses a DecimalFormat with - * pre-calculated number of digits (depending on max and min value). - * - * @author Philipp Jahoda - */ -public class DefaultValueFormatter implements ValueFormatter { - - /** decimalformat for formatting */ - private DecimalFormat mFormat; - - /** - * Constructor that specifies to how many digits the value should be - * formatted. - * - * @param digits - */ - public DefaultValueFormatter(int digits) { - - StringBuffer b = new StringBuffer(); - for (int i = 0; i < digits; i++) { - if (i == 0) - b.append("."); - b.append("0"); - } - - mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); - } - - @Override - public String getFormattedValue(float value) { - // avoid memory allocations here (for performance) - return mFormat.format(value); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/FillFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/FillFormatter.java deleted file mode 100644 index 0fe37914b3..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/FillFormatter.java +++ /dev/null @@ -1,27 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; - -/** - * Interface for providing a custom logic to where the filling line of a DataSet - * should end. If setFillEnabled(...) is set to true. - * - * @author Philipp Jahoda - */ -public interface FillFormatter { - - /** - * Returns the vertical (y-axis) position where the filled-line of the - * DataSet should end. - * - * @param dataSet - * @param data - * @param chartMaxY - * @param chartMinY - * @return - */ - public float getFillLinePosition(LineDataSet dataSet, LineData data, float chartMaxY, - float chartMinY); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Highlight.java b/MPChartLib/src/com/github/mikephil/charting/utils/Highlight.java deleted file mode 100644 index 63c4457d34..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Highlight.java +++ /dev/null @@ -1,99 +0,0 @@ - -package com.github.mikephil.charting.utils; - -/** - * Contains information needed to determine the highlighted value. - * - * @author Philipp Jahoda - */ -public class Highlight { - - /** the x-index of the highlighted value */ - private int mXIndex; - - /** the index of the dataset the highlighted value is in */ - private int mDataSetIndex; - - /** index which value of a stacked bar entry is highlighted, default -1 */ - private int mStackIndex = -1; - - /** - * constructor - * - * @param x the index of the highlighted value on the x-axis - * @param val the value at the position the user touched - * @param dataSet the index of the DataSet the highlighted value belongs to - */ - public Highlight(int x, int dataSet) { - this.mXIndex = x; - this.mDataSetIndex = dataSet; - } - - /** - * Constructor, only used for stacked-barchart. - * - * @param x the index of the highlighted value on the x-axis - * @param val the value at the position the user touched - * @param dataSet the index of the DataSet the highlighted value belongs to - * @param stackIndex references which value of a stacked-bar entry has been - * selected - */ - public Highlight(int x, int dataSet, int stackIndex) { - this(x, dataSet); - mStackIndex = stackIndex; - } - - /** - * returns the index of the DataSet the highlighted value is in - * - * @return - */ - public int getDataSetIndex() { - return mDataSetIndex; - } - - /** - * returns the index of the highlighted value on the x-axis - * - * @return - */ - public int getXIndex() { - return mXIndex; - } - - /** - * Only needed if a stacked-barchart entry was highlighted. References the - * selected value within the stacked-entry. - * - * @return - */ - public int getStackIndex() { - return mStackIndex; - } - - /** - * returns true if this highlight object is equal to the other (compares - * xIndex and dataSetIndex) - * - * @param h - * @return - */ - public boolean equalTo(Highlight h) { - - if (h == null) - return false; - else { - if (this.mDataSetIndex == h.mDataSetIndex && this.mXIndex == h.mXIndex - && this.mStackIndex == h.mStackIndex) - return true; - else - return false; - } - } - - @Override - public String toString() { - return "Highlight, xIndex: " + mXIndex + ", dataSetIndex: " + mDataSetIndex - + ", stackIndex (only stacked barentry): " + mStackIndex; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/LargeValueFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/LargeValueFormatter.java deleted file mode 100644 index 608c8b7b36..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/LargeValueFormatter.java +++ /dev/null @@ -1,51 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import java.text.DecimalFormat; - -/** - * Predefined value-formatter that formats large numbers in a pretty way. - * Outputs: 856 = 856; 1000 = 1k; 5821 = 5.8k; 10500 = 10k; 101800 = 102k; - * 2000000 = 2m; 7800000 = 7.8m; 92150000 = 92m; 123200000 = 123m; 9999999 = - * 10m; 1000000000 = 1b; Special thanks to Roman Gromov - * (https://github.com/romangromov) for this piece of code. - * - * @author Philipp Jahoda - */ -public class LargeValueFormatter implements ValueFormatter { - - private static String[] SUFFIX = new String[] { - "", "k", "m", "b", "t" - }; - - private static int MAX_LENGTH = 4; - - private DecimalFormat mFormat; - - public LargeValueFormatter() { - - mFormat = new DecimalFormat("###E0"); - } - - @Override - public String getFormattedValue(float value) { - return makePretty(value); - } - - /** - * Formats each number properly. Special thanks to Roman Gromov - * (https://github.com/romangromov) for this piece of code. - */ - private String makePretty(double number) { - - String r = mFormat.format(number); - - r = r.replaceAll("E[0-9]", SUFFIX[Character.getNumericValue(r.charAt(r.length() - 1)) / 3]); - - while (r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]")) { - r = r.substring(0, r.length() - 2) + r.substring(r.length() - 1); - } - - return r; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/PercentFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/PercentFormatter.java deleted file mode 100644 index b69b3e9ba9..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/PercentFormatter.java +++ /dev/null @@ -1,24 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import java.text.DecimalFormat; - -/** - * This ValueFormatter is just for convenience and simply puts a "%" sign after - * each value. (Recommeded for PieChart) - * - * @author Philipp Jahoda - */ -public class PercentFormatter implements ValueFormatter { - - protected DecimalFormat mFormat; - - public PercentFormatter() { - mFormat = new DecimalFormat("###,###,##0.0"); - } - - @Override - public String getFormattedValue(float value) { - return mFormat.format(value) + " %"; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/PointD.java b/MPChartLib/src/com/github/mikephil/charting/utils/PointD.java deleted file mode 100644 index 97c4aeead8..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/PointD.java +++ /dev/null @@ -1,23 +0,0 @@ - -package com.github.mikephil.charting.utils; - -/** - * Point encapsulating two double values. - * - * @author Philipp Jahoda - */ -public class PointD { - - public double x; - public double y; - - public PointD(double x, double y) { - this.x = x; - this.y = y; - } - - /** returns a string representation of the object */ - public String toString() { - return "PointD, x: " + x + ", y: " + y; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/SelInfo.java b/MPChartLib/src/com/github/mikephil/charting/utils/SelInfo.java deleted file mode 100644 index 4c3749a9a1..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/SelInfo.java +++ /dev/null @@ -1,25 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import com.github.mikephil.charting.data.DataSet; - -/** - * Class that encapsulates information of a value that has been - * selected/highlighted and its DataSet index. The SelInfo objects give - * information about the value at the selected index and the DataSet it belongs - * to. Needed only for highlighting onTouch(). - * - * @author Philipp Jahoda - */ -public class SelInfo { - - public float val; - public int dataSetIndex; - public DataSet dataSet; - - public SelInfo(float val, int dataSetIndex, DataSet set) { - this.val = val; - this.dataSetIndex = dataSetIndex; - this.dataSet = set; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java deleted file mode 100644 index 192f510aa6..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java +++ /dev/null @@ -1,420 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import android.graphics.Matrix; -import android.graphics.Path; -import android.graphics.RectF; - -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.CandleEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.renderer.ViewPortHandler; - -import java.util.ArrayList; - -/** - * Transformer class that contains all matrices and is responsible for - * transforming values into pixels on the screen and backwards. - * - * @author Philipp Jahoda - */ -public class Transformer { - - /** matrix to map the values to the screen pixels */ - protected Matrix mMatrixValueToPx = new Matrix(); - - /** matrix for handling the different offsets of the chart */ - protected Matrix mMatrixOffset = new Matrix(); - - private ViewPortHandler mViewPortHandler; - - public Transformer(ViewPortHandler viewPortHandler) { - this.mViewPortHandler = viewPortHandler; - } - - /** - * Prepares the matrix that transforms values to pixels. Calculates the - * scale factors from the charts size and offsets. - * - * @param chart - */ - public void prepareMatrixValuePx(float xChartMin, float deltaX, float deltaY, float yChartMin) { - - float scaleX = (float) ((mViewPortHandler.getChartWidth() - mViewPortHandler.offsetRight() - mViewPortHandler - .offsetLeft()) / deltaX); - float scaleY = (float) ((mViewPortHandler.getChartHeight() - mViewPortHandler.offsetTop() - mViewPortHandler - .offsetBottom()) / deltaY); - - // setup all matrices - mMatrixValueToPx.reset(); - mMatrixValueToPx.postTranslate(-xChartMin, -yChartMin); - mMatrixValueToPx.postScale(scaleX, -scaleY); - } - - // /** - // * Prepares the transformation matrix with the specified scales. - // * - // * @param chart - // * @param scaleX - // * @param scaleY - // */ - // public void prepareMatrixValuePx(ChartInterface chart, float scaleX, - // float scaleY) { - // - // mMatrixValueToPx.reset(); - // mMatrixValueToPx.postTranslate(0, -chart.getYChartMin()); - // mMatrixValueToPx.postScale(scaleX, -scaleY); - // } - - /** - * Prepares the matrix that contains all offsets. - * - * @param chart - */ - public void prepareMatrixOffset(boolean inverted) { - - mMatrixOffset.reset(); - - // offset.postTranslate(mOffsetLeft, getHeight() - mOffsetBottom); - - if (!inverted) - mMatrixOffset.postTranslate(mViewPortHandler.offsetLeft(), - mViewPortHandler.getChartHeight() - mViewPortHandler.offsetBottom()); - else { - mMatrixOffset - .setTranslate(mViewPortHandler.offsetLeft(), -mViewPortHandler.offsetTop()); - mMatrixOffset.postScale(1.0f, -1.0f); - } - - // mMatrixOffset.set(offset); - - // mMatrixOffset.reset(); - // - // mMatrixOffset.postTranslate(mOffsetLeft, getHeight() - - // mOffsetBottom); - } - - /** - * Transforms an arraylist of Entry into a float array containing the x and - * y values transformed with all matrices for the SCATTERCHART. - * - * @param entries - * @return - */ - public float[] generateTransformedValuesScatter(ArrayList entries, - float phaseY) { - - float[] valuePoints = new float[entries.size() * 2]; - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = entries.get(j / 2); - - if (e != null) { - valuePoints[j] = e.getXIndex(); - valuePoints[j + 1] = e.getVal() * phaseY; - } - } - - pointValuesToPixel(valuePoints); - - return valuePoints; - } - - /** - * Transforms an arraylist of Entry into a float array containing the x and - * y values transformed with all matrices for the LINECHART. - * - * @param entries - * @return - */ - public float[] generateTransformedValuesLine(ArrayList entries, - float phaseY) { - - float[] valuePoints = new float[entries.size() * 2]; - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = entries.get(j / 2); - - if (e != null) { - valuePoints[j] = e.getXIndex(); - valuePoints[j + 1] = e.getVal() * phaseY; - } - } - - pointValuesToPixel(valuePoints); - - return valuePoints; - } - - /** - * Transforms an arraylist of Entry into a float array containing the x and - * y values transformed with all matrices for the CANDLESTICKCHART. - * - * @param entries - * @return - */ - public float[] generateTransformedValuesCandle(ArrayList entries, - float phaseY) { - - float[] valuePoints = new float[entries.size() * 2]; - - for (int j = 0; j < valuePoints.length; j += 2) { - - CandleEntry e = entries.get(j / 2); - - if (e != null) { - valuePoints[j] = e.getXIndex(); - valuePoints[j + 1] = e.getHigh() * phaseY; - } - } - - pointValuesToPixel(valuePoints); - - return valuePoints; - } - - /** - * Transforms an arraylist of Entry into a float array containing the x and - * y values transformed with all matrices for the BARCHART. - * - * @param entries - * @param dataSet the dataset index - * @return - */ - public float[] generateTransformedValuesBarChart(ArrayList entries, - int dataSet, BarData bd, float phaseY) { - - float[] valuePoints = new float[entries.size() * 2]; - - int setCount = bd.getDataSetCount(); - float space = bd.getGroupSpace(); - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = entries.get(j / 2); - - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + (j / 2 * (setCount - 1)) + dataSet + space * (j / 2) - + space / 2f; - float y = e.getVal(); - - valuePoints[j] = x; - valuePoints[j + 1] = y * phaseY; - } - - pointValuesToPixel(valuePoints); - - return valuePoints; - } - - /** - * Transforms an arraylist of Entry into a float array containing the x and - * y values transformed with all matrices for the BARCHART. - * - * @param entries - * @param dataSet the dataset index - * @return - */ - public float[] generateTransformedValuesHorizontalBarChart(ArrayList entries, - int dataSet, BarData bd, float phaseY) { - - float[] valuePoints = new float[entries.size() * 2]; - - int setCount = bd.getDataSetCount(); - float space = bd.getGroupSpace(); - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = entries.get(j / 2); - - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + (j / 2 * (setCount - 1)) + dataSet + space * (j / 2) - + space / 2f ; - float y = e.getVal(); - - valuePoints[j] = y * phaseY; - valuePoints[j + 1] = x; - } - - pointValuesToPixel(valuePoints); - - return valuePoints; - } - - /** - * transform a path with all the given matrices VERY IMPORTANT: keep order - * to value-touch-offset - * - * @param path - */ - public void pathValueToPixel(Path path) { - - path.transform(mMatrixValueToPx); - path.transform(mViewPortHandler.getMatrixTouch()); - path.transform(mMatrixOffset); - } - - /** - * Transforms multiple paths will all matrices. - * - * @param paths - */ - public void pathValuesToPixel(ArrayList paths) { - - for (int i = 0; i < paths.size(); i++) { - pathValueToPixel(paths.get(i)); - } - } - - /** - * Transform an array of points with all matrices. VERY IMPORTANT: Keep - * matrix order "value-touch-offset" when transforming. - * - * @param pts - */ - public void pointValuesToPixel(float[] pts) { - - mMatrixValueToPx.mapPoints(pts); - mViewPortHandler.getMatrixTouch().mapPoints(pts); - mMatrixOffset.mapPoints(pts); - } - - /** - * Transform a rectangle with all matrices. - * - * @param r - */ - public void rectValueToPixel(RectF r) { - - mMatrixValueToPx.mapRect(r); - mViewPortHandler.getMatrixTouch().mapRect(r); - mMatrixOffset.mapRect(r); - } - - /** - * Transform a rectangle with all matrices with potential animation phases. - * - * @param r - * @param phaseY - */ - public void rectValueToPixel(RectF r, float phaseY) { - - // multiply the height of the rect with the phase - if (r.top > 0) - r.top *= phaseY; - else - r.bottom *= phaseY; - - mMatrixValueToPx.mapRect(r); - mViewPortHandler.getMatrixTouch().mapRect(r); - mMatrixOffset.mapRect(r); - } - - /** - * Transform a rectangle with all matrices with potential animation phases. - * - * @param r - * @param phaseY - */ - public void rectValueToPixelHorizontal(RectF r, float phaseY) { - - // multiply the height of the rect with the phase - if (r.left > 0) - r.left *= phaseY; - else - r.right *= phaseY; - - mMatrixValueToPx.mapRect(r); - mViewPortHandler.getMatrixTouch().mapRect(r); - mMatrixOffset.mapRect(r); - } - - /** - * transforms multiple rects with all matrices - * - * @param rects - */ - public void rectValuesToPixel(ArrayList rects) { - - for (int i = 0; i < rects.size(); i++) - rectValueToPixel(rects.get(i)); - } - - /** - * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) - * into values on the chart. - * - * @param pixels - */ - public void pixelsToValue(float[] pixels) { - - Matrix tmp = new Matrix(); - - // invert all matrixes to convert back to the original value - mMatrixOffset.invert(tmp); - tmp.mapPoints(pixels); - - mViewPortHandler.getMatrixTouch().invert(tmp); - tmp.mapPoints(pixels); - - mMatrixValueToPx.invert(tmp); - tmp.mapPoints(pixels); - } - - /** - * Returns the x and y values in the chart at the given touch point - * (encapsulated in a PointD). This method transforms pixel coordinates to - * coordinates / values in the chart. This is the opposite method to - * getPixelsForValues(...). - * - * @param x - * @param y - * @return - */ - public PointD getValuesByTouchPoint(float x, float y) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; - - pixelsToValue(pts); - - double xTouchVal = pts[0]; - double yTouchVal = pts[1]; - - return new PointD(xTouchVal, yTouchVal); - } - - // /** - // * transforms the given rect objects with the touch matrix only - // * - // * @param paths - // */ - // public void transformRectsTouch(ArrayList rects) { - // for (int i = 0; i < rects.size(); i++) { - // mMatrixTouch.mapRect(rects.get(i)); - // } - // } - // - // /** - // * transforms the given path objects with the touch matrix only - // * - // * @param paths - // */ - // public void transformPathsTouch(ArrayList paths) { - // for (int i = 0; i < paths.size(); i++) { - // paths.get(i).transform(mMatrixTouch); - // } - // } - - public Matrix getValueMatrix() { - return mMatrixValueToPx; - } - - public Matrix getOffsetMatrix() { - return mMatrixOffset; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java deleted file mode 100644 index 0513dfb91d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ /dev/null @@ -1,434 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import android.content.res.Resources; -import android.graphics.Paint; -import android.graphics.PointF; -import android.graphics.Rect; -import android.util.DisplayMetrics; -import android.util.Log; - -import com.github.mikephil.charting.components.YAxis.AxisDependency; - -import java.text.DecimalFormat; -import java.util.ArrayList; - -/** - * Utilities class that has some helper methods. Needs to be initialized by - * calling Utils.init(...) before usage. Inside the Chart.init() method, this is - * done, if the Utils are used before that, Utils.init(...) needs to be called - * manually. - * - * @author Philipp Jahoda - */ -public abstract class Utils { - - private static DisplayMetrics mMetrics; - - /** - * initialize method, called inside the Chart.init() method. - * - * @param res - */ - public static void init(Resources res) { - mMetrics = res.getDisplayMetrics(); - } - - /** - * format a number properly with the given number of digits - * - * @param number the number to format - * @param digits the number of digits - * @return - */ - public static String formatDecimal(double number, int digits) { - - StringBuffer a = new StringBuffer(); - for (int i = 0; i < digits; i++) { - if (i == 0) - a.append("."); - a.append("0"); - } - - DecimalFormat nf = new DecimalFormat("###,###,###,##0" + a.toString()); - String formatted = nf.format(number); - - return formatted; - } - - /** - * This method converts dp unit to equivalent pixels, depending on device - * density. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. - * - * @param dp A value in dp (density independent pixels) unit. Which we need - * to convert into pixels - * @return A float value to represent px equivalent to dp depending on - * device density - */ - public static float convertDpToPixel(float dp) { - - if (mMetrics == null) { - - Log.e("MPChartLib-Utils", - "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before calling Utils.convertDpToPixel(...). Otherwise conversion does not take place."); - return dp; - // throw new IllegalStateException( - // "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before calling Utils.convertDpToPixel(...)."); - } - - DisplayMetrics metrics = mMetrics; - float px = dp * (metrics.densityDpi / 160f); - return px; - } - - /** - * This method converts device specific pixels to density independent - * pixels. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. - * - * @param px A value in px (pixels) unit. Which we need to convert into db - * @return A float value to represent dp equivalent to px value - */ - public static float convertPixelsToDp(float px) { - - if (mMetrics == null) { - - Log.e("MPChartLib-Utils", - "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before calling Utils.convertPixelsToDp(...). Otherwise conversion does not take place."); - return px; - // throw new IllegalStateException( - // "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before calling Utils.convertPixelsToDp(...)."); - } - - DisplayMetrics metrics = mMetrics; - float dp = px / (metrics.densityDpi / 160f); - return dp; - } - - /** - * calculates the approximate width of a text, depending on a demo text - * avoid repeated calls (e.g. inside drawing methods) - * - * @param paint - * @param demoText - * @return - */ - public static int calcTextWidth(Paint paint, String demoText) { - return (int) paint.measureText(demoText); - } - - /** - * calculates the approximate height of a text, depending on a demo text - * avoid repeated calls (e.g. inside drawing methods) - * - * @param paint - * @param demoText - * @return - */ - public static int calcTextHeight(Paint paint, String demoText) { - - Rect r = new Rect(); - paint.getTextBounds(demoText, 0, demoText.length(), r); - return r.height(); - } - - // /** - // * returns the appropriate number of format digits for a delta value - // * - // * @param delta - // * @return - // */ - // public static int getFormatDigits(float delta) { - // - // if (delta < 0.1) { - // return 6; - // } else if (delta <= 1) { - // return 4; - // } else if (delta < 4) { - // return 3; - // } else if (delta < 20) { - // return 2; - // } else if (delta < 60) { - // return 1; - // } else { - // return 0; - // } - // } - - /** - * returns the appropriate number of format digits for a legend value - * - * @param delta - * @param bonus - additional digits - * @return - */ - public static int getLegendFormatDigits(float step, int bonus) { - - if (step < 0.0000099) { - return 6 + bonus; - } else if (step < 0.000099) { - return 5 + bonus; - } else if (step < 0.00099) { - return 4 + bonus; - } else if (step < 0.0099) { - return 3 + bonus; - } else if (step < 0.099) { - return 2 + bonus; - } else if (step < 0.99) { - return 1 + bonus; - } else { - return 0 + bonus; - } - } - - /** - * Math.pow(...) is very expensive, so avoid calling it and create it - * yourself. - */ - private static final int POW_10[] = { - 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 - }; - - /** - * Formats the given number to the given number of decimals, and returns the - * number as a string, maximum 35 characters. - * - * @param number - * @param digitCount - * @param separateTousands set this to true to separate thousands values - * @return - */ - public static String formatNumber(float number, int digitCount, boolean separateThousands) { - - char[] out = new char[35]; - - boolean neg = false; - if (number == 0) { - return "0"; - } - - boolean zero = false; - if (number < 1 && number > -1) { - zero = true; - } - - if (number < 0) { - neg = true; - number = -number; - } - - if (digitCount > POW_10.length) { - digitCount = POW_10.length - 1; - } - - number *= POW_10[digitCount]; - long lval = Math.round(number); - int ind = out.length - 1; - int charCount = 0; - boolean decimalPointAdded = false; - - while (lval != 0 || charCount < (digitCount + 1)) { - int digit = (int) (lval % 10); - lval = lval / 10; - out[ind--] = (char) (digit + '0'); - charCount++; - - // add decimal point - if (charCount == digitCount) { - out[ind--] = ','; - charCount++; - decimalPointAdded = true; - - // add thousand separators - } else if (separateThousands && lval != 0 && charCount > digitCount) { - - if (decimalPointAdded) { - - if ((charCount - digitCount) % 4 == 0) { - out[ind--] = '.'; - charCount++; - } - - } else { - - if ((charCount - digitCount) % 4 == 3) { - out[ind--] = '.'; - charCount++; - } - } - } - } - - // if number around zero (between 1 and -1) - if (zero) { - out[ind--] = '0'; - charCount += 1; - } - - // if the number is negative - if (neg) { - out[ind--] = '-'; - charCount += 1; - } - - int start = out.length - charCount; - - // use this instead of "new String(...)" because of issue < Android 4.0 - return String.valueOf(out, start, out.length - start); - } - - /** - * rounds the given number to the next significant number - * - * @param number - * @return - */ - public static float roundToNextSignificant(double number) { - final float d = (float) Math.ceil((float) Math.log10(number < 0 ? -number : number)); - final int pw = 1 - (int) d; - final float magnitude = (float) Math.pow(10, pw); - final long shifted = Math.round(number * magnitude); - return shifted / magnitude; - } - - /** - * Returns the appropriate number of decimals to be used for the provided - * number. - * - * @param number - * @return - */ - public static int getDecimals(float number) { - - float i = roundToNextSignificant(number); - return (int) Math.ceil(-Math.log10(i)) + 2; - } - - /** - * Converts the provided Integer ArrayList to an int array. - * - * @param integers - * @return - */ - public static int[] convertIntegers(ArrayList integers) { - - int[] ret = new int[integers.size()]; - - for (int i = 0; i < ret.length; i++) { - ret[i] = integers.get(i).intValue(); - } - - return ret; - } - - /** - * Converts the provided String ArrayList to a String array. - * - * @param labels - * @return - */ - public static String[] convertStrings(ArrayList strings) { - - String[] ret = new String[strings.size()]; - - for (int i = 0; i < ret.length; i++) { - ret[i] = strings.get(i); - } - - return ret; - } - - /** - * Replacement for the Math.nextUp(...) method that is only available in - * HONEYCOMB and higher. Dat's some seeeeek sheeet. - * - * @param d - * @return - */ - public static double nextUp(double d) { - if (d == Double.POSITIVE_INFINITY) - return d; - else { - d += 0.0d; - return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + - ((d >= 0.0d) ? +1L : -1L)); - } - } - - /** - * Returns the index of the DataSet that contains the closest value on the - * y-axis. This is needed for highlighting. - * - * @param valsAtIndex all the values at a specific index - * @return - */ - public static int getClosestDataSetIndex(ArrayList valsAtIndex, float val, - AxisDependency axis) { - - int index = -1; - float distance = Float.MAX_VALUE; - - for (int i = 0; i < valsAtIndex.size(); i++) { - - SelInfo sel = valsAtIndex.get(i); - - if (axis == null || sel.dataSet.getAxisDependency() == axis) { - - float cdistance = Math.abs((float) sel.val - val); - if (cdistance < distance) { - index = valsAtIndex.get(i).dataSetIndex; - distance = cdistance; - } - } - } - - // Log.i(LOG_TAG, "Closest DataSet index: " + index); - - return index; - } - - /** - * Returns the minimum distance from a touch-y-value (in pixels) to the - * closest y-value (in pixels) that is displayed in the chart. - * - * @param valsAtIndex - * @param val - * @param axis - * @return - */ - public static float getMinimumDistance(ArrayList valsAtIndex, float val, - AxisDependency axis) { - - float distance = Float.MAX_VALUE; - - for (int i = 0; i < valsAtIndex.size(); i++) { - - SelInfo sel = valsAtIndex.get(i); - - if (sel.dataSet.getAxisDependency() == axis) { - - float cdistance = Math.abs((float) sel.val - val); - if (cdistance < distance) { - distance = cdistance; - } - } - } - - return distance; - } - - /** - * Calculates the position around a center point, depending on the distance - * from the center, and the angle of the position around the center. - * - * @param center - * @param dist - * @param angle in degrees, converted to radians internally - * @return - */ - public static PointF getPosition(PointF center, float dist, float angle) { - - PointF p = new PointF((float) (center.x + dist * Math.cos(Math.toRadians(angle))), - (float) (center.y + dist * Math.sin(Math.toRadians(angle)))); - return p; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ValueFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/ValueFormatter.java deleted file mode 100644 index 843fa9c7d6..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ValueFormatter.java +++ /dev/null @@ -1,23 +0,0 @@ - -package com.github.mikephil.charting.utils; - -/** - * Interface that allows custom formatting of all values and value-labels - * displayed inside the chart. Simply create your own formatting class and let - * it implement ValueFormatter. Then override the getFormattedLabel(...) method - * and return whatever you want. - * - * @author Philipp Jahoda - */ -public interface ValueFormatter { - - /** - * Called when a value (from labels, or inside the chart) is formatted - * before being drawn. For performance reasons, avoid excessive calculations - * and memory allocations inside this method. - * - * @param value the value to be formatted - * @return the formatted label ready for being drawn - */ - public String getFormattedValue(float value); -} diff --git a/MPChartLib/AndroidManifest.xml b/MPChartLib/src/main/AndroidManifest.xml similarity index 56% rename from MPChartLib/AndroidManifest.xml rename to MPChartLib/src/main/AndroidManifest.xml index 57ddacc805..d75f87c7e2 100644 --- a/MPChartLib/AndroidManifest.xml +++ b/MPChartLib/src/main/AndroidManifest.xml @@ -1,12 +1,5 @@ - - - + zoom out, if > 1f --> zoom in + * @param scaleY if < 1f --> zoom out, if > 1f --> zoom in + * @param x + * @param y + */ + public void zoom(float scaleX, float scaleY, float x, float y) { + + mViewPortHandler.zoom(scaleX, scaleY, x, -y, mZoomMatrixBuffer); + mViewPortHandler.refresh(mZoomMatrixBuffer, this, false); + + // Range might have changed, which means that Y-axis labels + // could have changed in size, affecting Y-axis size. + // So we need to recalculate offsets. + calculateOffsets(); + postInvalidate(); + } + + /** + * Zooms in or out by the given scale factor. + * x and y are the values (NOT PIXELS) of the zoom center.. + * + * @param scaleX + * @param scaleY + * @param xValue + * @param yValue + * @param axis the axis relative to which the zoom should take place + */ + public void zoom(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis) { + + Runnable job = ZoomJob.getInstance(mViewPortHandler, scaleX, scaleY, xValue, yValue, getTransformer(axis), axis, this); + addViewportJob(job); + } + + /** + * Zooms to the center of the chart with the given scale factor. + * + * @param scaleX + * @param scaleY + */ + public void zoomToCenter(float scaleX, float scaleY) { + + MPPointF center = getCenterOffsets(); + + Matrix save = mZoomMatrixBuffer; + mViewPortHandler.zoom(scaleX, scaleY, center.x, -center.y, save); + mViewPortHandler.refresh(save, this, false); + } + + /** + * Zooms by the specified scale factor to the specified values on the specified axis. + * + * @param scaleX + * @param scaleY + * @param xValue + * @param yValue + * @param axis + * @param duration + */ + @TargetApi(11) + public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis, + long duration) { + + if (android.os.Build.VERSION.SDK_INT >= 11) { + + MPPointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + + Runnable job = AnimatedZoomJob.getInstance(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis + .mAxisRange, scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), + xValue, yValue, (float) origin.x, (float) origin.y, duration); + addViewportJob(job); + + MPPointD.recycleInstance(origin); + + } else { + Log.e(LOG_TAG, "Unable to execute zoomAndCenterAnimated(...) on API level < 11"); + } + } + + protected Matrix mFitScreenMatrixBuffer = new Matrix(); + + /** + * Resets all zooming and dragging and makes the chart fit exactly it's + * bounds. + */ + public void fitScreen() { + Matrix save = mFitScreenMatrixBuffer; + mViewPortHandler.fitScreen(save); + mViewPortHandler.refresh(save, this, false); + + calculateOffsets(); + postInvalidate(); + } + + /** + * Sets the minimum scale factor value to which can be zoomed out. 1f = + * fitScreen + * + * @param scaleX + * @param scaleY + */ + public void setScaleMinima(float scaleX, float scaleY) { + mViewPortHandler.setMinimumScaleX(scaleX); + mViewPortHandler.setMinimumScaleY(scaleY); + } + + /** + * Sets the size of the area (range on the x-axis) that should be maximum + * visible at once (no further zooming out allowed). If this is e.g. set to + * 10, no more than a range of 10 on the x-axis can be viewed at once without + * scrolling. + * + * @param maxXRange The maximum visible range of x-values. + */ + public void setVisibleXRangeMaximum(float maxXRange) { + float xScale = mXAxis.mAxisRange / (maxXRange); + mViewPortHandler.setMinimumScaleX(xScale); + } + + /** + * Sets the size of the area (range on the x-axis) that should be minimum + * visible at once (no further zooming in allowed). If this is e.g. set to + * 10, no less than a range of 10 on the x-axis can be viewed at once without + * scrolling. + * + * @param minXRange The minimum visible range of x-values. + */ + public void setVisibleXRangeMinimum(float minXRange) { + float xScale = mXAxis.mAxisRange / (minXRange); + mViewPortHandler.setMaximumScaleX(xScale); + } + + /** + * Limits the maximum and minimum x range that can be visible by pinching and zooming. e.g. minRange=10, maxRange=100 the + * smallest range to be displayed at once is 10, and no more than a range of 100 values can be viewed at once without + * scrolling + * + * @param minXRange + * @param maxXRange + */ + public void setVisibleXRange(float minXRange, float maxXRange) { + float minScale = mXAxis.mAxisRange / minXRange; + float maxScale = mXAxis.mAxisRange / maxXRange; + mViewPortHandler.setMinMaxScaleX(minScale, maxScale); + } + + /** + * Sets the size of the area (range on the y-axis) that should be maximum + * visible at once. + * + * @param maxYRange the maximum visible range on the y-axis + * @param axis the axis for which this limit should apply + */ + public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { + float yScale = getAxisRange(axis) / maxYRange; + mViewPortHandler.setMinimumScaleY(yScale); + } + + /** + * Sets the size of the area (range on the y-axis) that should be minimum visible at once, no further zooming in possible. + * + * @param minYRange + * @param axis the axis for which this limit should apply + */ + public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { + float yScale = getAxisRange(axis) / minYRange; + mViewPortHandler.setMaximumScaleY(yScale); + } + + /** + * Limits the maximum and minimum y range that can be visible by pinching and zooming. + * + * @param minYRange + * @param maxYRange + * @param axis + */ + public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) { + float minScale = getAxisRange(axis) / minYRange; + float maxScale = getAxisRange(axis) / maxYRange; + mViewPortHandler.setMinMaxScaleY(minScale, maxScale); + } + + + /** + * Moves the left side of the current viewport to the specified x-position. + * This also refreshes the chart by calling invalidate(). + * + * @param xValue + */ + public void moveViewToX(float xValue) { + + Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, 0f, + getTransformer(AxisDependency.LEFT), this); + + addViewportJob(job); + } + + /** + * This will move the left side of the current viewport to the specified + * x-value on the x-axis, and center the viewport to the specified y value on the y-axis. + * This also refreshes the chart by calling invalidate(). + * + * @param xValue + * @param yValue + * @param axis - which axis should be used as a reference for the y-axis + */ + public void moveViewTo(float xValue, float yValue, AxisDependency axis) { + + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + + Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, + getTransformer(axis), this); + + addViewportJob(job); + } + + /** + * This will move the left side of the current viewport to the specified x-value + * and center the viewport to the y value animated. + * This also refreshes the chart by calling invalidate(). + * + * @param xValue + * @param yValue + * @param axis + * @param duration the duration of the animation in milliseconds + */ + @TargetApi(11) + public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { + + if (android.os.Build.VERSION.SDK_INT >= 11) { + + MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + + Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, + getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); + + addViewportJob(job); + + MPPointD.recycleInstance(bounds); + } else { + Log.e(LOG_TAG, "Unable to execute moveViewToAnimated(...) on API level < 11"); + } + } + + /** + * Centers the viewport to the specified y value on the y-axis. + * This also refreshes the chart by calling invalidate(). + * + * @param yValue + * @param axis - which axis should be used as a reference for the y-axis + */ + public void centerViewToY(float yValue, AxisDependency axis) { + + float valsInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + + Runnable job = MoveViewJob.getInstance(mViewPortHandler, 0f, yValue + valsInView / 2f, + getTransformer(axis), this); + + addViewportJob(job); + } + + /** + * This will move the center of the current viewport to the specified + * x and y value. + * This also refreshes the chart by calling invalidate(). + * + * @param xValue + * @param yValue + * @param axis - which axis should be used as a reference for the y axis + */ + public void centerViewTo(float xValue, float yValue, AxisDependency axis) { + + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); + + Runnable job = MoveViewJob.getInstance(mViewPortHandler, + xValue - xInView / 2f, yValue + yInView / 2f, + getTransformer(axis), this); + + addViewportJob(job); + } + + /** + * This will move the center of the current viewport to the specified + * x and y value animated. + * + * @param xValue + * @param yValue + * @param axis + * @param duration the duration of the animation in milliseconds + */ + @TargetApi(11) + public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { + + if (android.os.Build.VERSION.SDK_INT >= 11) { + + MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); + + Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, + xValue - xInView / 2f, yValue + yInView / 2f, + getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); + + addViewportJob(job); + + MPPointD.recycleInstance(bounds); + } else { + Log.e(LOG_TAG, "Unable to execute centerViewToAnimated(...) on API level < 11"); + } + } + + /** + * flag that indicates if a custom viewport offset has been set + */ + private boolean mCustomViewPortEnabled = false; + + /** + * Sets custom offsets for the current ViewPort (the offsets on the sides of + * the actual chart window). Setting this will prevent the chart from + * automatically calculating it's offsets. Use resetViewPortOffsets() to + * undo this. ONLY USE THIS WHEN YOU KNOW WHAT YOU ARE DOING, else use + * setExtraOffsets(...). + * + * @param left + * @param top + * @param right + * @param bottom + */ + public void setViewPortOffsets(final float left, final float top, + final float right, final float bottom) { + + mCustomViewPortEnabled = true; + post(new Runnable() { + + @Override + public void run() { + + mViewPortHandler.restrainViewPort(left, top, right, bottom); + prepareOffsetMatrix(); + prepareValuePxMatrix(); + } + }); + } + + /** + * Resets all custom offsets set via setViewPortOffsets(...) method. Allows + * the chart to again calculate all offsets automatically. + */ + public void resetViewPortOffsets() { + mCustomViewPortEnabled = false; + calculateOffsets(); + } + + /** + * ################ ################ ################ ################ + */ + /** CODE BELOW IS GETTERS AND SETTERS */ + + /** + * Returns the range of the specified axis. + * + * @param axis + * @return + */ + protected float getAxisRange(AxisDependency axis) { + if (axis == AxisDependency.LEFT) + return mAxisLeft.mAxisRange; + else + return mAxisRight.mAxisRange; + } + + /** + * Sets the OnDrawListener + * + * @param drawListener + */ + public void setOnDrawListener(OnDrawListener drawListener) { + this.mDrawListener = drawListener; + } + + /** + * Gets the OnDrawListener. May be null. + * + * @return + */ + public OnDrawListener getDrawListener() { + return mDrawListener; + } + + protected float[] mGetPositionBuffer = new float[2]; + + /** + * Returns a recyclable MPPointF instance. + * Returns the position (in pixels) the provided Entry has inside the chart + * view or null, if the provided Entry is null. + * + * @param e + * @return + */ + public MPPointF getPosition(Entry e, AxisDependency axis) { + + if (e == null) + return null; + + mGetPositionBuffer[0] = e.getX(); + mGetPositionBuffer[1] = e.getY(); + + getTransformer(axis).pointValuesToPixel(mGetPositionBuffer); + + return MPPointF.getInstance(mGetPositionBuffer[0], mGetPositionBuffer[1]); + } + + /** + * sets the number of maximum visible drawn values on the chart only active + * when setDrawValues() is enabled + * + * @param count + */ + public void setMaxVisibleValueCount(int count) { + this.mMaxVisibleCount = count; + } + + public int getMaxVisibleCount() { + return mMaxVisibleCount; + } + + /** + * Set this to true to allow highlighting per dragging over the chart + * surface when it is fully zoomed out. Default: true + * + * @param enabled + */ + public void setHighlightPerDragEnabled(boolean enabled) { + mHighlightPerDragEnabled = enabled; + } + + public boolean isHighlightPerDragEnabled() { + return mHighlightPerDragEnabled; + } + + /** + * Sets the color for the background of the chart-drawing area (everything + * behind the grid lines). + * + * @param color + */ + public void setGridBackgroundColor(int color) { + mGridBackgroundPaint.setColor(color); + } + + /** + * Set this to true to enable dragging (moving the chart with the finger) + * for the chart (this does not effect scaling). + * + * @param enabled + */ + public void setDragEnabled(boolean enabled) { + this.mDragEnabled = enabled; + } + + /** + * Returns true if dragging is enabled for the chart, false if not. + * + * @return + */ + public boolean isDragEnabled() { + return mDragEnabled; + } + + /** + * Set this to true to enable scaling (zooming in and out by gesture) for + * the chart (this does not effect dragging) on both X- and Y-Axis. + * + * @param enabled + */ + public void setScaleEnabled(boolean enabled) { + this.mScaleXEnabled = enabled; + this.mScaleYEnabled = enabled; + } + + public void setScaleXEnabled(boolean enabled) { + mScaleXEnabled = enabled; + } + + public void setScaleYEnabled(boolean enabled) { + mScaleYEnabled = enabled; + } + + public boolean isScaleXEnabled() { + return mScaleXEnabled; + } + + public boolean isScaleYEnabled() { + return mScaleYEnabled; + } + + /** + * Set this to true to enable zooming in by double-tap on the chart. + * Default: enabled + * + * @param enabled + */ + public void setDoubleTapToZoomEnabled(boolean enabled) { + mDoubleTapToZoomEnabled = enabled; + } + + /** + * Returns true if zooming via double-tap is enabled false if not. + * + * @return + */ + public boolean isDoubleTapToZoomEnabled() { + return mDoubleTapToZoomEnabled; + } + + /** + * set this to true to draw the grid background, false if not + * + * @param enabled + */ + public void setDrawGridBackground(boolean enabled) { + mDrawGridBackground = enabled; + } + + /** + * When enabled, the borders rectangle will be rendered. + * If this is enabled, there is no point drawing the axis-lines of x- and y-axis. + * + * @param enabled + */ + public void setDrawBorders(boolean enabled) { + mDrawBorders = enabled; + } + + /** + * When enabled, the borders rectangle will be rendered. + * If this is enabled, there is no point drawing the axis-lines of x- and y-axis. + * + * @return + */ + public boolean isDrawBordersEnabled() { + return mDrawBorders; + } + + /** + * When enabled, the values will be clipped to contentRect, + * otherwise they can bleed outside the content rect. + * + * @param enabled + */ + public void setClipValuesToContent(boolean enabled) { + mClipValuesToContent = enabled; + } + + /** + * When enabled, the values will be clipped to contentRect, + * otherwise they can bleed outside the content rect. + * + * @return + */ + public boolean isClipValuesToContentEnabled() { + return mClipValuesToContent; + } + + /** + * Sets the width of the border lines in dp. + * + * @param width + */ + public void setBorderWidth(float width) { + mBorderPaint.setStrokeWidth(Utils.convertDpToPixel(width)); + } + + /** + * Sets the color of the chart border lines. + * + * @param color + */ + public void setBorderColor(int color) { + mBorderPaint.setColor(color); + } + + /** + * Gets the minimum offset (padding) around the chart, defaults to 15.f + */ + public float getMinOffset() { + return mMinOffset; + } + + /** + * Sets the minimum offset (padding) around the chart, defaults to 15.f + */ + public void setMinOffset(float minOffset) { + mMinOffset = minOffset; + } + + /** + * Returns true if keeping the position on rotation is enabled and false if not. + */ + public boolean isKeepPositionOnRotation() { + return mKeepPositionOnRotation; + } + + /** + * Sets whether the chart should keep its position (zoom / scroll) after a rotation (orientation change) + */ + public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { + mKeepPositionOnRotation = keepPositionOnRotation; + } + + /** + * Returns a recyclable MPPointD instance + * Returns the x and y values in the chart at the given touch point + * (encapsulated in a MPPointD). This method transforms pixel coordinates to + * coordinates / values in the chart. This is the opposite method to + * getPixelForValues(...). + * + * @param x + * @param y + * @return + */ + public MPPointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { + MPPointD result = MPPointD.getInstance(0, 0); + getValuesByTouchPoint(x, y, axis, result); + return result; + } + + public void getValuesByTouchPoint(float x, float y, AxisDependency axis, MPPointD outputPoint) { + getTransformer(axis).getValuesByTouchPoint(x, y, outputPoint); + } + + /** + * Returns a recyclable MPPointD instance + * Transforms the given chart values into pixels. This is the opposite + * method to getValuesByTouchPoint(...). + * + * @param x + * @param y + * @return + */ + public MPPointD getPixelForValues(float x, float y, AxisDependency axis) { + return getTransformer(axis).getPixelForValues(x, y); + } + + /** + * returns the Entry object displayed at the touched position of the chart + * + * @param x + * @param y + * @return + */ + public Entry getEntryByTouchPoint(float x, float y) { + Highlight h = getHighlightByTouchPoint(x, y); + if (h != null) { + return mData.getEntryForHighlight(h); + } + return null; + } + + /** + * returns the DataSet object displayed at the touched position of the chart + * + * @param x + * @param y + * @return + */ + public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float y) { + Highlight h = getHighlightByTouchPoint(x, y); + if (h != null) { + return mData.getDataSetByIndex(h.getDataSetIndex()); + } + return null; + } + + /** + * buffer for storing lowest visible x point + */ + protected MPPointD posForGetLowestVisibleX = MPPointD.getInstance(0, 0); + + /** + * Returns the lowest x-index (value on the x-axis) that is still visible on + * the chart. + * + * @return + */ + @Override + public float getLowestVisibleX() { + getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), posForGetLowestVisibleX); + float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.x); + return result; + } + + /** + * buffer for storing highest visible x point + */ + protected MPPointD posForGetHighestVisibleX = MPPointD.getInstance(0, 0); + + /** + * Returns the highest x-index (value on the x-axis) that is still visible + * on the chart. + * + * @return + */ + @Override + public float getHighestVisibleX() { + getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), posForGetHighestVisibleX); + float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.x); + return result; + } + + /** + * Returns the range visible on the x-axis. + * + * @return + */ + public float getVisibleXRange() { + return Math.abs(getHighestVisibleX() - getLowestVisibleX()); + } + + /** + * returns the current x-scale factor + */ + public float getScaleX() { + if (mViewPortHandler == null) + return 1f; + else + return mViewPortHandler.getScaleX(); + } + + /** + * returns the current y-scale factor + */ + public float getScaleY() { + if (mViewPortHandler == null) + return 1f; + else + return mViewPortHandler.getScaleY(); + } + + /** + * if the chart is fully zoomed out, return true + * + * @return + */ + public boolean isFullyZoomedOut() { + return mViewPortHandler.isFullyZoomedOut(); + } + + /** + * Returns the left y-axis object. In the horizontal bar-chart, this is the + * top axis. + * + * @return + */ + public YAxis getAxisLeft() { + return mAxisLeft; + } + + /** + * Returns the right y-axis object. In the horizontal bar-chart, this is the + * bottom axis. + * + * @return + */ + public YAxis getAxisRight() { + return mAxisRight; + } + + /** + * Returns the y-axis object to the corresponding AxisDependency. In the + * horizontal bar-chart, LEFT == top, RIGHT == BOTTOM + * + * @param axis + * @return + */ + public YAxis getAxis(AxisDependency axis) { + if (axis == AxisDependency.LEFT) + return mAxisLeft; + else + return mAxisRight; + } + + @Override + public boolean isInverted(AxisDependency axis) { + return getAxis(axis).isInverted(); + } + + /** + * If set to true, both x and y axis can be scaled simultaneously with 2 fingers, if false, + * x and y axis can be scaled separately. default: false + * + * @param enabled + */ + public void setPinchZoom(boolean enabled) { + mPinchZoomEnabled = enabled; + } + + /** + * returns true if pinch-zoom is enabled, false if not + * + * @return + */ + public boolean isPinchZoomEnabled() { + return mPinchZoomEnabled; + } + + /** + * Set an offset in dp that allows the user to drag the chart over it's + * bounds on the x-axis. + * + * @param offset + */ + public void setDragOffsetX(float offset) { + mViewPortHandler.setDragOffsetX(offset); + } + + /** + * Set an offset in dp that allows the user to drag the chart over it's + * bounds on the y-axis. + * + * @param offset + */ + public void setDragOffsetY(float offset) { + mViewPortHandler.setDragOffsetY(offset); + } + + /** + * Returns true if both drag offsets (x and y) are zero or smaller. + * + * @return + */ + public boolean hasNoDragOffset() { + return mViewPortHandler.hasNoDragOffset(); + } + + public XAxisRenderer getRendererXAxis() { + return mXAxisRenderer; + } + + /** + * Sets a custom XAxisRenderer and overrides the existing (default) one. + * + * @param xAxisRenderer + */ + public void setXAxisRenderer(XAxisRenderer xAxisRenderer) { + mXAxisRenderer = xAxisRenderer; + } + + public YAxisRenderer getRendererLeftYAxis() { + return mAxisRendererLeft; + } + + /** + * Sets a custom axis renderer for the left axis and overwrites the existing one. + * + * @param rendererLeftYAxis + */ + public void setRendererLeftYAxis(YAxisRenderer rendererLeftYAxis) { + mAxisRendererLeft = rendererLeftYAxis; + } + + public YAxisRenderer getRendererRightYAxis() { + return mAxisRendererRight; + } + + /** + * Sets a custom axis renderer for the right acis and overwrites the existing one. + * + * @param rendererRightYAxis + */ + public void setRendererRightYAxis(YAxisRenderer rendererRightYAxis) { + mAxisRendererRight = rendererRightYAxis; + } + + @Override + public float getYChartMax() { + return Math.max(mAxisLeft.mAxisMaximum, mAxisRight.mAxisMaximum); + } + + @Override + public float getYChartMin() { + return Math.min(mAxisLeft.mAxisMinimum, mAxisRight.mAxisMinimum); + } + + /** + * Returns true if either the left or the right or both axes are inverted. + * + * @return + */ + public boolean isAnyAxisInverted() { + if (mAxisLeft.isInverted()) + return true; + if (mAxisRight.isInverted()) + return true; + return false; + } + + /** + * Flag that indicates if auto scaling on the y axis is enabled. This is + * especially interesting for charts displaying financial data. + * + * @param enabled the y axis automatically adjusts to the min and max y + * values of the current x axis range whenever the viewport + * changes + */ + public void setAutoScaleMinMaxEnabled(boolean enabled) { + mAutoScaleMinMaxEnabled = enabled; + } + + /** + * @return true if auto scaling on the y axis is enabled. + * @default false + */ + public boolean isAutoScaleMinMaxEnabled() { + return mAutoScaleMinMaxEnabled; + } + + @Override + public void setPaint(Paint p, int which) { + super.setPaint(p, which); + + switch (which) { + case PAINT_GRID_BACKGROUND: + mGridBackgroundPaint = p; + break; + } + } + + @Override + public Paint getPaint(int which) { + Paint p = super.getPaint(which); + if (p != null) + return p; + + switch (which) { + case PAINT_GRID_BACKGROUND: + return mGridBackgroundPaint; + } + + return null; + } + + protected float[] mOnSizeChangedBuffer = new float[2]; + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + + // Saving current position of chart. + mOnSizeChangedBuffer[0] = mOnSizeChangedBuffer[1] = 0; + + if (mKeepPositionOnRotation) { + mOnSizeChangedBuffer[0] = mViewPortHandler.contentLeft(); + mOnSizeChangedBuffer[1] = mViewPortHandler.contentTop(); + getTransformer(AxisDependency.LEFT).pixelsToValue(mOnSizeChangedBuffer); + } + + //Superclass transforms chart. + super.onSizeChanged(w, h, oldw, oldh); + + if (mKeepPositionOnRotation) { + + //Restoring old position of chart. + getTransformer(AxisDependency.LEFT).pointValuesToPixel(mOnSizeChangedBuffer); + mViewPortHandler.centerViewPort(mOnSizeChangedBuffer, this); + } else { + mViewPortHandler.refresh(mViewPortHandler.getMatrixTouch(), this, true); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java new file mode 100644 index 0000000000..23dac5780f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java @@ -0,0 +1,43 @@ + +package com.github.mikephil.charting.charts; + +import android.content.Context; +import android.util.AttributeSet; + +import com.github.mikephil.charting.data.BubbleData; +import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; +import com.github.mikephil.charting.renderer.BubbleChartRenderer; + +/** + * The BubbleChart. Draws bubbles. Bubble chart implementation: Copyright 2015 + * Pierre-Marc Airoldi Licensed under Apache License 2.0. In the BubbleChart, it + * is the area of the bubble, not the radius or diameter of the bubble that + * conveys the data. + * + * @author Philipp Jahoda + */ +public class BubbleChart extends BarLineChartBase implements BubbleDataProvider { + + public BubbleChart(Context context) { + super(context); + } + + public BubbleChart(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BubbleChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init() { + super.init(); + + mRenderer = new BubbleChartRenderer(this, mAnimator, mViewPortHandler); + } + + public BubbleData getBubbleData() { + return mData; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java similarity index 73% rename from MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java index 28c373e5a6..fa36e3522f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java @@ -5,12 +5,12 @@ import android.util.AttributeSet; import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.interfaces.CandleDataProvider; +import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; import com.github.mikephil.charting.renderer.CandleStickChartRenderer; /** - * Financial chart type that draws candle-sticks. - * + * Financial chart type that draws candle-sticks (OHCL chart). + * * @author Philipp Jahoda */ public class CandleStickChart extends BarLineChartBase implements CandleDataProvider { @@ -26,23 +26,17 @@ public CandleStickChart(Context context, AttributeSet attrs) { public CandleStickChart(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } - + @Override protected void init() { super.init(); - - mRenderer = new CandleStickChartRenderer(this, mAnimator, mViewPortHandler); - mXChartMin = -0.5f; - } - @Override - protected void calcMinMax() { - super.calcMinMax(); + mRenderer = new CandleStickChartRenderer(this, mAnimator, mViewPortHandler); - mXChartMax += 0.5f; - mDeltaX = Math.abs(mXChartMax - mXChartMin); + getXAxis().setSpaceMin(0.5f); + getXAxis().setSpaceMax(0.5f); } - + @Override public CandleData getCandleData() { return mData; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java new file mode 100644 index 0000000000..79f699320b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -0,0 +1,1761 @@ + +package com.github.mikephil.charting.charts; + +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.SuppressLint; +import android.content.ContentValues; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.CompressFormat; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.os.Environment; +import android.provider.MediaStore.Images; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.animation.EasingFunction; +import com.github.mikephil.charting.components.Description; +import com.github.mikephil.charting.components.IMarker; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.DefaultValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.highlight.ChartHighlighter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.IHighlighter; +import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.listener.ChartTouchListener; +import com.github.mikephil.charting.listener.OnChartGestureListener; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.renderer.DataRenderer; +import com.github.mikephil.charting.renderer.LegendRenderer; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; + +/** + * Baseclass of all Chart-Views. + * + * @author Philipp Jahoda + */ +@SuppressLint("NewApi") +public abstract class Chart>> extends + ViewGroup + implements ChartInterface { + + public static final String LOG_TAG = "MPAndroidChart"; + + /** + * flag that indicates if logging is enabled or not + */ + protected boolean mLogEnabled = false; + + /** + * object that holds all data that was originally set for the chart, before + * it was modified or any filtering algorithms had been applied + */ + protected T mData = null; + + /** + * Flag that indicates if highlighting per tap (touch) is enabled + */ + protected boolean mHighLightPerTapEnabled = true; + + /** + * If set to true, chart continues to scroll after touch up + */ + private boolean mDragDecelerationEnabled = true; + + /** + * Deceleration friction coefficient in [0 ; 1] interval, higher values + * indicate that speed will decrease slowly, for example if it set to 0, it + * will stop immediately. 1 is an invalid value, and will be converted to + * 0.999f automatically. + */ + private float mDragDecelerationFrictionCoef = 0.9f; + + /** + * default value-formatter, number of digits depends on provided chart-data + */ + protected DefaultValueFormatter mDefaultValueFormatter = new DefaultValueFormatter(0); + + /** + * paint object used for drawing the description text in the bottom right + * corner of the chart + */ + protected Paint mDescPaint; + + /** + * paint object for drawing the information text when there are no values in + * the chart + */ + protected Paint mInfoPaint; + + /** + * the object representing the labels on the x-axis + */ + protected XAxis mXAxis; + + /** + * if true, touch gestures are enabled on the chart + */ + protected boolean mTouchEnabled = true; + + /** + * the object responsible for representing the description text + */ + protected Description mDescription; + + /** + * the legend object containing all data associated with the legend + */ + protected Legend mLegend; + + /** + * listener that is called when a value on the chart is selected + */ + protected OnChartValueSelectedListener mSelectionListener; + + protected ChartTouchListener mChartTouchListener; + + /** + * text that is displayed when the chart is empty + */ + private String mNoDataText = "No chart data available."; + + /** + * Gesture listener for custom callbacks when making gestures on the chart. + */ + private OnChartGestureListener mGestureListener; + + protected LegendRenderer mLegendRenderer; + + /** + * object responsible for rendering the data + */ + protected DataRenderer mRenderer; + + protected IHighlighter mHighlighter; + + /** + * object that manages the bounds and drawing constraints of the chart + */ + protected ViewPortHandler mViewPortHandler = new ViewPortHandler(); + + /** + * object responsible for animations + */ + protected ChartAnimator mAnimator; + + /** + * Extra offsets to be appended to the viewport + */ + private float mExtraTopOffset = 0.f, + mExtraRightOffset = 0.f, + mExtraBottomOffset = 0.f, + mExtraLeftOffset = 0.f; + + /** + * default constructor for initialization in code + */ + public Chart(Context context) { + super(context); + init(); + } + + /** + * constructor for initialization in xml + */ + public Chart(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + /** + * even more awesome constructor + */ + public Chart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + /** + * initialize all paints and stuff + */ + protected void init() { + + setWillNotDraw(false); + // setLayerType(View.LAYER_TYPE_HARDWARE, null); + + if (android.os.Build.VERSION.SDK_INT < 11) + mAnimator = new ChartAnimator(); + else + mAnimator = new ChartAnimator(new AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + // ViewCompat.postInvalidateOnAnimation(Chart.this); + postInvalidate(); + } + }); + + // initialize the utils + Utils.init(getContext()); + mMaxHighlightDistance = Utils.convertDpToPixel(500f); + + mDescription = new Description(); + mLegend = new Legend(); + + mLegendRenderer = new LegendRenderer(mViewPortHandler, mLegend); + + mXAxis = new XAxis(); + + mDescPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + mInfoPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mInfoPaint.setColor(Color.rgb(247, 189, 51)); // orange + mInfoPaint.setTextAlign(Align.CENTER); + mInfoPaint.setTextSize(Utils.convertDpToPixel(12f)); + + if (mLogEnabled) + Log.i("", "Chart.init()"); + } + + // public void initWithDummyData() { + // ColorTemplate template = new ColorTemplate(); + // template.addColorsForDataSets(ColorTemplate.COLORFUL_COLORS, + // getContext()); + // + // setColorTemplate(template); + // setDrawYValues(false); + // + // ArrayList xVals = new ArrayList(); + // Calendar calendar = Calendar.getInstance(); + // for (int i = 0; i < 12; i++) { + // xVals.add(calendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, + // Locale.getDefault())); + // } + // + // ArrayList dataSets = new ArrayList(); + // for (int i = 0; i < 3; i++) { + // + // ArrayList yVals = new ArrayList(); + // + // for (int j = 0; j < 12; j++) { + // float val = (float) (Math.random() * 100); + // yVals.add(new Entry(val, j)); + // } + // + // DataSet set = new DataSet(yVals, "DataSet " + i); + // dataSets.add(set); // add the datasets + // } + // // create a data object with the datasets + // ChartData data = new ChartData(xVals, dataSets); + // setData(data); + // invalidate(); + // } + + /** + * Sets a new data object for the chart. The data object contains all values + * and information needed for displaying. + * + * @param data + */ + public void setData(T data) { + + mData = data; + mOffsetsCalculated = false; + + if (data == null) { + return; + } + + // calculate how many digits are needed + setupDefaultFormatter(data.getYMin(), data.getYMax()); + + for (IDataSet set : mData.getDataSets()) { + if (set.needsFormatter() || set.getValueFormatter() == mDefaultValueFormatter) + set.setValueFormatter(mDefaultValueFormatter); + } + + // let the chart know there is new data + notifyDataSetChanged(); + + if (mLogEnabled) + Log.i(LOG_TAG, "Data is set."); + } + + /** + * Clears the chart from all data (sets it to null) and refreshes it (by + * calling invalidate()). + */ + public void clear() { + mData = null; + mOffsetsCalculated = false; + mIndicesToHighlight = null; + invalidate(); + } + + /** + * Removes all DataSets (and thereby Entries) from the chart. Does not set the data object to null. Also refreshes the + * chart by calling invalidate(). + */ + public void clearValues() { + mData.clearValues(); + invalidate(); + } + + /** + * Returns true if the chart is empty (meaning it's data object is either + * null or contains no entries). + * + * @return + */ + public boolean isEmpty() { + + if (mData == null) + return true; + else { + + if (mData.getEntryCount() <= 0) + return true; + else + return false; + } + } + + /** + * Lets the chart know its underlying data has changed and performs all + * necessary recalculations. It is crucial that this method is called + * everytime data is changed dynamically. Not calling this method can lead + * to crashes or unexpected behaviour. + */ + public abstract void notifyDataSetChanged(); + + /** + * Calculates the offsets of the chart to the border depending on the + * position of an eventual legend or depending on the length of the y-axis + * and x-axis labels and their position + */ + protected abstract void calculateOffsets(); + + /** + * Calculates the y-min and y-max value and the y-delta and x-delta value + */ + protected abstract void calcMinMax(); + + /** + * Calculates the required number of digits for the values that might be + * drawn in the chart (if enabled), and creates the default-value-formatter + */ + protected void setupDefaultFormatter(float min, float max) { + + float reference = 0f; + + if (mData == null || mData.getEntryCount() < 2) { + + reference = Math.max(Math.abs(min), Math.abs(max)); + } else { + reference = Math.abs(max - min); + } + + int digits = Utils.getDecimals(reference); + + // setup the formatter with a new number of digits + mDefaultValueFormatter.setup(digits); + } + + /** + * flag that indicates if offsets calculation has already been done or not + */ + private boolean mOffsetsCalculated = false; + + @Override + protected void onDraw(Canvas canvas) { + // super.onDraw(canvas); + + if (mData == null) { + + boolean hasText = !TextUtils.isEmpty(mNoDataText); + + if (hasText) { + MPPointF c = getCenter(); + canvas.drawText(mNoDataText, c.x, c.y, mInfoPaint); + } + + return; + } + + if (!mOffsetsCalculated) { + + calculateOffsets(); + mOffsetsCalculated = true; + } + } + + /** + * Draws the description text in the bottom right corner of the chart (per default) + */ + protected void drawDescription(Canvas c) { + + // check if description should be drawn + if (mDescription != null && mDescription.isEnabled()) { + + MPPointF position = mDescription.getPosition(); + + mDescPaint.setTypeface(mDescription.getTypeface()); + mDescPaint.setTextSize(mDescription.getTextSize()); + mDescPaint.setColor(mDescription.getTextColor()); + mDescPaint.setTextAlign(mDescription.getTextAlign()); + + float x, y; + + // if no position specified, draw on default position + if (position == null) { + x = getWidth() - mViewPortHandler.offsetRight() - mDescription.getXOffset(); + y = getHeight() - mViewPortHandler.offsetBottom() - mDescription.getYOffset(); + } else { + x = position.x; + y = position.y; + } + + c.drawText(mDescription.getText(), x, y, mDescPaint); + } + } + + /** + * ################ ################ ################ ################ + */ + /** BELOW THIS CODE FOR HIGHLIGHTING */ + + /** + * array of Highlight objects that reference the highlighted slices in the + * chart + */ + protected Highlight[] mIndicesToHighlight; + + /** + * The maximum distance in dp away from an entry causing it to highlight. + */ + protected float mMaxHighlightDistance = 0f; + + @Override + public float getMaxHighlightDistance() { + return mMaxHighlightDistance; + } + + /** + * Sets the maximum distance in screen dp a touch can be away from an entry to cause it to get highlighted. + * Default: 500dp + * + * @param distDp + */ + public void setMaxHighlightDistance(float distDp) { + mMaxHighlightDistance = Utils.convertDpToPixel(distDp); + } + + /** + * Returns the array of currently highlighted values. This might a null or + * empty array if nothing is highlighted. + * + * @return + */ + public Highlight[] getHighlighted() { + return mIndicesToHighlight; + } + + /** + * Returns true if values can be highlighted via tap gesture, false if not. + * + * @return + */ + public boolean isHighlightPerTapEnabled() { + return mHighLightPerTapEnabled; + } + + /** + * Set this to false to prevent values from being highlighted by tap gesture. + * Values can still be highlighted via drag or programmatically. Default: true + * + * @param enabled + */ + public void setHighlightPerTapEnabled(boolean enabled) { + mHighLightPerTapEnabled = enabled; + } + + /** + * Returns true if there are values to highlight, false if there are no + * values to highlight. Checks if the highlight array is null, has a length + * of zero or if the first object is null. + * + * @return + */ + public boolean valuesToHighlight() { + return mIndicesToHighlight == null || mIndicesToHighlight.length <= 0 + || mIndicesToHighlight[0] == null ? false + : true; + } + + /** + * Sets the last highlighted value for the touchlistener. + * + * @param highs + */ + protected void setLastHighlighted(Highlight[] highs) { + + if (highs == null || highs.length <= 0 || highs[0] == null) { + mChartTouchListener.setLastHighlighted(null); + } else { + mChartTouchListener.setLastHighlighted(highs[0]); + } + } + + /** + * Highlights the values at the given indices in the given DataSets. Provide + * null or an empty array to undo all highlighting. This should be used to + * programmatically highlight values. + * This method *will not* call the listener. + * + * @param highs + */ + public void highlightValues(Highlight[] highs) { + + // set the indices to highlight + mIndicesToHighlight = highs; + + setLastHighlighted(highs); + + // redraw the chart + invalidate(); + } + + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * This method will call the listener. + * @param x The x-value to highlight + * @param dataSetIndex The dataset index to search in + */ + public void highlightValue(float x, int dataSetIndex) { + highlightValue(x, dataSetIndex, true); + } + + /** + * Highlights the value at the given x-value and y-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * This method will call the listener. + * @param x The x-value to highlight + * @param y The y-value to highlight. Supply `NaN` for "any" + * @param dataSetIndex The dataset index to search in + */ + public void highlightValue(float x, float y, int dataSetIndex) { + highlightValue(x, y, dataSetIndex, true); + } + + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * @param x The x-value to highlight + * @param dataSetIndex The dataset index to search in + * @param callListener Should the listener be called for this change + */ + public void highlightValue(float x, int dataSetIndex, boolean callListener) { + highlightValue(x, Float.NaN, dataSetIndex, callListener); + } + + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * @param x The x-value to highlight + * @param y The y-value to highlight. Supply `NaN` for "any" + * @param dataSetIndex The dataset index to search in + * @param callListener Should the listener be called for this change + */ + public void highlightValue(float x, float y, int dataSetIndex, boolean callListener) { + + if (dataSetIndex < 0 || dataSetIndex >= mData.getDataSetCount()) { + highlightValue(null, callListener); + } else { + highlightValue(new Highlight(x, y, dataSetIndex), callListener); + } + } + + /** + * Highlights the values represented by the provided Highlight object + * This method *will not* call the listener. + * + * @param highlight contains information about which entry should be highlighted + */ + public void highlightValue(Highlight highlight) { + highlightValue(highlight, false); + } + + /** + * Highlights the value selected by touch gesture. Unlike + * highlightValues(...), this generates a callback to the + * OnChartValueSelectedListener. + * + * @param high - the highlight object + * @param callListener - call the listener + */ + public void highlightValue(Highlight high, boolean callListener) { + + Entry e = null; + + if (high == null) + mIndicesToHighlight = null; + else { + + if (mLogEnabled) + Log.i(LOG_TAG, "Highlighted: " + high.toString()); + + e = mData.getEntryForHighlight(high); + if (e == null) { + mIndicesToHighlight = null; + high = null; + } else { + + // set the indices to highlight + mIndicesToHighlight = new Highlight[]{ + high + }; + } + } + + setLastHighlighted(mIndicesToHighlight); + + if (callListener && mSelectionListener != null) { + + if (!valuesToHighlight()) + mSelectionListener.onNothingSelected(); + else { + // notify the listener + mSelectionListener.onValueSelected(e, high); + } + } + + // redraw the chart + invalidate(); + } + + /** + * Returns the Highlight object (contains x-index and DataSet index) of the + * selected value at the given touch point inside the Line-, Scatter-, or + * CandleStick-Chart. + * + * @param x + * @param y + * @return + */ + public Highlight getHighlightByTouchPoint(float x, float y) { + + if (mData == null) { + Log.e(LOG_TAG, "Can't select by touch. No data set."); + return null; + } else + return getHighlighter().getHighlight(x, y); + } + + /** + * Set a new (e.g. custom) ChartTouchListener NOTE: make sure to + * setTouchEnabled(true); if you need touch gestures on the chart + * + * @param l + */ + public void setOnTouchListener(ChartTouchListener l) { + this.mChartTouchListener = l; + } + + /** + * Returns an instance of the currently active touch listener. + * + * @return + */ + public ChartTouchListener getOnTouchListener() { + return mChartTouchListener; + } + + /** + * ################ ################ ################ ################ + */ + /** BELOW CODE IS FOR THE MARKER VIEW */ + + /** + * if set to true, the marker view is drawn when a value is clicked + */ + protected boolean mDrawMarkers = true; + + /** + * the view that represents the marker + */ + protected IMarker mMarker; + + /** + * draws all MarkerViews on the highlighted positions + */ + protected void drawMarkers(Canvas canvas) { + + // if there is no marker view or drawing marker is disabled + if (mMarker == null || !isDrawMarkersEnabled() || !valuesToHighlight()) + return; + + for (int i = 0; i < mIndicesToHighlight.length; i++) { + + Highlight highlight = mIndicesToHighlight[i]; + + IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex()); + + Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]); + int entryIndex = set.getEntryIndex(e); + + // make sure entry not null + if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX()) + continue; + + float[] pos = getMarkerPosition(highlight); + + // check bounds + if (!mViewPortHandler.isInBounds(pos[0], pos[1])) + continue; + + // callbacks to update the content + mMarker.refreshContent(e, highlight); + + // draw the marker + mMarker.draw(canvas, pos[0], pos[1]); + } + } + + /** + * Returns the actual position in pixels of the MarkerView for the given + * Highlight object. + * + * @param high + * @return + */ + protected float[] getMarkerPosition(Highlight high) { + return new float[]{high.getDrawX(), high.getDrawY()}; + } + + /** + * ################ ################ ################ ################ + * ANIMATIONS ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. + */ + /** CODE BELOW THIS RELATED TO ANIMATION */ + + /** + * Returns the animator responsible for animating chart values. + * + * @return + */ + public ChartAnimator getAnimator() { + return mAnimator; + } + + /** + * If set to true, chart continues to scroll after touch up default: true + */ + public boolean isDragDecelerationEnabled() { + return mDragDecelerationEnabled; + } + + /** + * If set to true, chart continues to scroll after touch up. Default: true. + * + * @param enabled + */ + public void setDragDecelerationEnabled(boolean enabled) { + mDragDecelerationEnabled = enabled; + } + + /** + * Returns drag deceleration friction coefficient + * + * @return + */ + public float getDragDecelerationFrictionCoef() { + return mDragDecelerationFrictionCoef; + } + + /** + * Deceleration friction coefficient in [0 ; 1] interval, higher values + * indicate that speed will decrease slowly, for example if it set to 0, it + * will stop immediately. 1 is an invalid value, and will be converted to + * 0.999f automatically. + * + * @param newValue + */ + public void setDragDecelerationFrictionCoef(float newValue) { + + if (newValue < 0.f) + newValue = 0.f; + + if (newValue >= 1f) + newValue = 0.999f; + + mDragDecelerationFrictionCoef = newValue; + } + + /** + * ################ ################ ################ ################ + * ANIMATIONS ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. + */ + /** CODE BELOW FOR PROVIDING EASING FUNCTIONS */ + + /** + * Animates the drawing / rendering of the chart on both x- and y-axis with + * the specified animation time. If animate(...) is called, no further + * calling of invalidate() is necessary to refresh the chart. ANIMATIONS + * ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. + * + * @param durationMillisX + * @param durationMillisY + * @param easingX a custom easing function to be used on the animation phase + * @param easingY a custom easing function to be used on the animation phase + */ + public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easingX, + EasingFunction easingY) { + mAnimator.animateXY(durationMillisX, durationMillisY, easingX, easingY); + } + + /** + * Animates the rendering of the chart on the x-axis with the specified + * animation time. If animate(...) is called, no further calling of + * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR + * API LEVEL 11 (Android 3.0.x) AND HIGHER. + * + * @param durationMillis + * @param easing a custom easing function to be used on the animation phase + */ + public void animateX(int durationMillis, EasingFunction easing) { + mAnimator.animateX(durationMillis, easing); + } + + /** + * Animates the rendering of the chart on the y-axis with the specified + * animation time. If animate(...) is called, no further calling of + * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR + * API LEVEL 11 (Android 3.0.x) AND HIGHER. + * + * @param durationMillis + * @param easing a custom easing function to be used on the animation phase + */ + public void animateY(int durationMillis, EasingFunction easing) { + mAnimator.animateY(durationMillis, easing); + } + + /** + * ################ ################ ################ ################ + * ANIMATIONS ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. + */ + /** CODE BELOW FOR PREDEFINED EASING OPTIONS */ + + /** + * Animates the drawing / rendering of the chart on both x- and y-axis with + * the specified animation time. If animate(...) is called, no further + * calling of invalidate() is necessary to refresh the chart. ANIMATIONS + * ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. + * + * @param durationMillisX + * @param durationMillisY + * @param easingX a predefined easing option + * @param easingY a predefined easing option + */ + public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOption easingX, + Easing.EasingOption easingY) { + mAnimator.animateXY(durationMillisX, durationMillisY, easingX, easingY); + } + + /** + * Animates the rendering of the chart on the x-axis with the specified + * animation time. If animate(...) is called, no further calling of + * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR + * API LEVEL 11 (Android 3.0.x) AND HIGHER. + * + * @param durationMillis + * @param easing a predefined easing option + */ + public void animateX(int durationMillis, Easing.EasingOption easing) { + mAnimator.animateX(durationMillis, easing); + } + + /** + * Animates the rendering of the chart on the y-axis with the specified + * animation time. If animate(...) is called, no further calling of + * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR + * API LEVEL 11 (Android 3.0.x) AND HIGHER. + * + * @param durationMillis + * @param easing a predefined easing option + */ + public void animateY(int durationMillis, Easing.EasingOption easing) { + mAnimator.animateY(durationMillis, easing); + } + + /** + * ################ ################ ################ ################ + * ANIMATIONS ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. + */ + /** CODE BELOW FOR ANIMATIONS WITHOUT EASING */ + + /** + * Animates the rendering of the chart on the x-axis with the specified + * animation time. If animate(...) is called, no further calling of + * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR + * API LEVEL 11 (Android 3.0.x) AND HIGHER. + * + * @param durationMillis + */ + public void animateX(int durationMillis) { + mAnimator.animateX(durationMillis); + } + + /** + * Animates the rendering of the chart on the y-axis with the specified + * animation time. If animate(...) is called, no further calling of + * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR + * API LEVEL 11 (Android 3.0.x) AND HIGHER. + * + * @param durationMillis + */ + public void animateY(int durationMillis) { + mAnimator.animateY(durationMillis); + } + + /** + * Animates the drawing / rendering of the chart on both x- and y-axis with + * the specified animation time. If animate(...) is called, no further + * calling of invalidate() is necessary to refresh the chart. ANIMATIONS + * ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. + * + * @param durationMillisX + * @param durationMillisY + */ + public void animateXY(int durationMillisX, int durationMillisY) { + mAnimator.animateXY(durationMillisX, durationMillisY); + } + + /** + * ################ ################ ################ ################ + */ + /** BELOW THIS ONLY GETTERS AND SETTERS */ + + + /** + * Returns the object representing all x-labels, this method can be used to + * acquire the XAxis object and modify it (e.g. change the position of the + * labels, styling, etc.) + * + * @return + */ + public XAxis getXAxis() { + return mXAxis; + } + + /** + * Returns the default IValueFormatter that has been determined by the chart + * considering the provided minimum and maximum values. + * + * @return + */ + public IValueFormatter getDefaultValueFormatter() { + return mDefaultValueFormatter; + } + + /** + * set a selection listener for the chart + * + * @param l + */ + public void setOnChartValueSelectedListener(OnChartValueSelectedListener l) { + this.mSelectionListener = l; + } + + /** + * Sets a gesture-listener for the chart for custom callbacks when executing + * gestures on the chart surface. + * + * @param l + */ + public void setOnChartGestureListener(OnChartGestureListener l) { + this.mGestureListener = l; + } + + /** + * Returns the custom gesture listener. + * + * @return + */ + public OnChartGestureListener getOnChartGestureListener() { + return mGestureListener; + } + + /** + * returns the current y-max value across all DataSets + * + * @return + */ + public float getYMax() { + return mData.getYMax(); + } + + /** + * returns the current y-min value across all DataSets + * + * @return + */ + public float getYMin() { + return mData.getYMin(); + } + + @Override + public float getXChartMax() { + return mXAxis.mAxisMaximum; + } + + @Override + public float getXChartMin() { + return mXAxis.mAxisMinimum; + } + + @Override + public float getXRange() { + return mXAxis.mAxisRange; + } + + /** + * Returns a recyclable MPPointF instance. + * Returns the center point of the chart (the whole View) in pixels. + * + * @return + */ + public MPPointF getCenter() { + return MPPointF.getInstance(getWidth() / 2f, getHeight() / 2f); + } + + /** + * Returns a recyclable MPPointF instance. + * Returns the center of the chart taking offsets under consideration. + * (returns the center of the content rectangle) + * + * @return + */ + @Override + public MPPointF getCenterOffsets() { + return mViewPortHandler.getContentCenter(); + } + + /** + * Sets extra offsets (around the chart view) to be appended to the + * auto-calculated offsets. + * + * @param left + * @param top + * @param right + * @param bottom + */ + public void setExtraOffsets(float left, float top, float right, float bottom) { + setExtraLeftOffset(left); + setExtraTopOffset(top); + setExtraRightOffset(right); + setExtraBottomOffset(bottom); + } + + /** + * Set an extra offset to be appended to the viewport's top + */ + public void setExtraTopOffset(float offset) { + mExtraTopOffset = Utils.convertDpToPixel(offset); + } + + /** + * @return the extra offset to be appended to the viewport's top + */ + public float getExtraTopOffset() { + return mExtraTopOffset; + } + + /** + * Set an extra offset to be appended to the viewport's right + */ + public void setExtraRightOffset(float offset) { + mExtraRightOffset = Utils.convertDpToPixel(offset); + } + + /** + * @return the extra offset to be appended to the viewport's right + */ + public float getExtraRightOffset() { + return mExtraRightOffset; + } + + /** + * Set an extra offset to be appended to the viewport's bottom + */ + public void setExtraBottomOffset(float offset) { + mExtraBottomOffset = Utils.convertDpToPixel(offset); + } + + /** + * @return the extra offset to be appended to the viewport's bottom + */ + public float getExtraBottomOffset() { + return mExtraBottomOffset; + } + + /** + * Set an extra offset to be appended to the viewport's left + */ + public void setExtraLeftOffset(float offset) { + mExtraLeftOffset = Utils.convertDpToPixel(offset); + } + + /** + * @return the extra offset to be appended to the viewport's left + */ + public float getExtraLeftOffset() { + return mExtraLeftOffset; + } + + /** + * Set this to true to enable logcat outputs for the chart. Beware that + * logcat output decreases rendering performance. Default: disabled. + * + * @param enabled + */ + public void setLogEnabled(boolean enabled) { + mLogEnabled = enabled; + } + + /** + * Returns true if log-output is enabled for the chart, fals if not. + * + * @return + */ + public boolean isLogEnabled() { + return mLogEnabled; + } + + /** + * Sets the text that informs the user that there is no data available with + * which to draw the chart. + * + * @param text + */ + public void setNoDataText(String text) { + mNoDataText = text; + } + + /** + * Sets the color of the no data text. + * + * @param color + */ + public void setNoDataTextColor(int color) { + mInfoPaint.setColor(color); + } + + /** + * Sets the typeface to be used for the no data text. + * + * @param tf + */ + public void setNoDataTextTypeface(Typeface tf) { + mInfoPaint.setTypeface(tf); + } + + /** + * Set this to false to disable all gestures and touches on the chart, + * default: true + * + * @param enabled + */ + public void setTouchEnabled(boolean enabled) { + this.mTouchEnabled = enabled; + } + + /** + * sets the marker that is displayed when a value is clicked on the chart + * + * @param marker + */ + public void setMarker(IMarker marker) { + mMarker = marker; + } + + /** + * returns the marker that is set as a marker view for the chart + * + * @return + */ + public IMarker getMarker() { + return mMarker; + } + + @Deprecated + public void setMarkerView(IMarker v) { + setMarker(v); + } + + @Deprecated + public IMarker getMarkerView() { + return getMarker(); + } + + /** + * Sets a new Description object for the chart. + * + * @param desc + */ + public void setDescription(Description desc) { + this.mDescription = desc; + } + + /** + * Returns the Description object of the chart that is responsible for holding all information related + * to the description text that is displayed in the bottom right corner of the chart (by default). + * + * @return + */ + public Description getDescription() { + return mDescription; + } + + /** + * Returns the Legend object of the chart. This method can be used to get an + * instance of the legend in order to customize the automatically generated + * Legend. + * + * @return + */ + public Legend getLegend() { + return mLegend; + } + + /** + * Returns the renderer object responsible for rendering / drawing the + * Legend. + * + * @return + */ + public LegendRenderer getLegendRenderer() { + return mLegendRenderer; + } + + /** + * Returns the rectangle that defines the borders of the chart-value surface + * (into which the actual values are drawn). + * + * @return + */ + @Override + public RectF getContentRect() { + return mViewPortHandler.getContentRect(); + } + + /** + * disables intercept touchevents + */ + public void disableScroll() { + ViewParent parent = getParent(); + if (parent != null) + parent.requestDisallowInterceptTouchEvent(true); + } + + /** + * enables intercept touchevents + */ + public void enableScroll() { + ViewParent parent = getParent(); + if (parent != null) + parent.requestDisallowInterceptTouchEvent(false); + } + + /** + * paint for the grid background (only line and barchart) + */ + public static final int PAINT_GRID_BACKGROUND = 4; + + /** + * paint for the info text that is displayed when there are no values in the + * chart + */ + public static final int PAINT_INFO = 7; + + /** + * paint for the description text in the bottom right corner + */ + public static final int PAINT_DESCRIPTION = 11; + + /** + * paint for the hole in the middle of the pie chart + */ + public static final int PAINT_HOLE = 13; + + /** + * paint for the text in the middle of the pie chart + */ + public static final int PAINT_CENTER_TEXT = 14; + + /** + * paint used for the legend + */ + public static final int PAINT_LEGEND_LABEL = 18; + + /** + * set a new paint object for the specified parameter in the chart e.g. + * Chart.PAINT_VALUES + * + * @param p the new paint object + * @param which Chart.PAINT_VALUES, Chart.PAINT_GRID, Chart.PAINT_VALUES, + * ... + */ + public void setPaint(Paint p, int which) { + + switch (which) { + case PAINT_INFO: + mInfoPaint = p; + break; + case PAINT_DESCRIPTION: + mDescPaint = p; + break; + } + } + + /** + * Returns the paint object associated with the provided constant. + * + * @param which e.g. Chart.PAINT_LEGEND_LABEL + * @return + */ + public Paint getPaint(int which) { + switch (which) { + case PAINT_INFO: + return mInfoPaint; + case PAINT_DESCRIPTION: + return mDescPaint; + } + + return null; + } + + @Deprecated + public boolean isDrawMarkerViewsEnabled() { + return isDrawMarkersEnabled(); + } + + @Deprecated + public void setDrawMarkerViews(boolean enabled) { + setDrawMarkers(enabled); + } + + /** + * returns true if drawing the marker is enabled when tapping on values + * (use the setMarker(IMarker marker) method to specify a marker) + * + * @return + */ + public boolean isDrawMarkersEnabled() { + return mDrawMarkers; + } + + /** + * Set this to true to draw a user specified marker when tapping on + * chart values (use the setMarker(IMarker marker) method to specify a + * marker). Default: true + * + * @param enabled + */ + public void setDrawMarkers(boolean enabled) { + mDrawMarkers = enabled; + } + + /** + * Returns the ChartData object that has been set for the chart. + * + * @return + */ + public T getData() { + return mData; + } + + /** + * Returns the ViewPortHandler of the chart that is responsible for the + * content area of the chart and its offsets and dimensions. + * + * @return + */ + public ViewPortHandler getViewPortHandler() { + return mViewPortHandler; + } + + /** + * Returns the Renderer object the chart uses for drawing data. + * + * @return + */ + public DataRenderer getRenderer() { + return mRenderer; + } + + /** + * Sets a new DataRenderer object for the chart. + * + * @param renderer + */ + public void setRenderer(DataRenderer renderer) { + + if (renderer != null) + mRenderer = renderer; + } + + public IHighlighter getHighlighter() { + return mHighlighter; + } + + /** + * Sets a custom highligher object for the chart that handles / processes + * all highlight touch events performed on the chart-view. + * + * @param highlighter + */ + public void setHighlighter(ChartHighlighter highlighter) { + mHighlighter = highlighter; + } + + /** + * Returns a recyclable MPPointF instance. + * + * @return + */ + @Override + public MPPointF getCenterOfView() { + return getCenter(); + } + + /** + * Returns the bitmap that represents the chart. + * + * @return + */ + public Bitmap getChartBitmap() { + // Define a bitmap with the same size as the view + Bitmap returnedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565); + // Bind a canvas to it + Canvas canvas = new Canvas(returnedBitmap); + // Get the view's background + Drawable bgDrawable = getBackground(); + if (bgDrawable != null) + // has background drawable, then draw it on the canvas + bgDrawable.draw(canvas); + else + // does not have background drawable, then draw white background on + // the canvas + canvas.drawColor(Color.WHITE); + // draw the view on the canvas + draw(canvas); + // return the bitmap + return returnedBitmap; + } + + /** + * Saves the current chart state with the given name to the given path on + * the sdcard leaving the path empty "" will put the saved file directly on + * the SD card chart is saved as a PNG image, example: + * saveToPath("myfilename", "foldername1/foldername2"); + * + * @param title + * @param pathOnSD e.g. "folder1/folder2/folder3" + * @return returns true on success, false on error + */ + public boolean saveToPath(String title, String pathOnSD) { + + Bitmap b = getChartBitmap(); + + OutputStream stream = null; + try { + stream = new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + + pathOnSD + "/" + title + + ".png"); + + /* + * Write bitmap to file using JPEG or PNG and 40% quality hint for + * JPEG. + */ + b.compress(CompressFormat.PNG, 40, stream); + + stream.close(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + return true; + } + + /** + * Saves the current state of the chart to the gallery as an image type. The + * compression must be set for JPEG only. 0 == maximum compression, 100 = low + * compression (high quality). NOTE: Needs permission WRITE_EXTERNAL_STORAGE + * + * @param fileName e.g. "my_image" + * @param subFolderPath e.g. "ChartPics" + * @param fileDescription e.g. "Chart details" + * @param format e.g. Bitmap.CompressFormat.PNG + * @param quality e.g. 50, min = 0, max = 100 + * @return returns true if saving was successful, false if not + */ + public boolean saveToGallery(String fileName, String subFolderPath, String fileDescription, Bitmap.CompressFormat + format, int quality) { + // restrain quality + if (quality < 0 || quality > 100) + quality = 50; + + long currentTime = System.currentTimeMillis(); + + File extBaseDir = Environment.getExternalStorageDirectory(); + File file = new File(extBaseDir.getAbsolutePath() + "/DCIM/" + subFolderPath); + if (!file.exists()) { + if (!file.mkdirs()) { + return false; + } + } + + String mimeType = ""; + switch (format) { + case PNG: + mimeType = "image/png"; + if (!fileName.endsWith(".png")) + fileName += ".png"; + break; + case WEBP: + mimeType = "image/webp"; + if (!fileName.endsWith(".webp")) + fileName += ".webp"; + break; + case JPEG: + default: + mimeType = "image/jpeg"; + if (!(fileName.endsWith(".jpg") || fileName.endsWith(".jpeg"))) + fileName += ".jpg"; + break; + } + + String filePath = file.getAbsolutePath() + "/" + fileName; + FileOutputStream out = null; + try { + out = new FileOutputStream(filePath); + + Bitmap b = getChartBitmap(); + b.compress(format, quality, out); + + out.flush(); + out.close(); + + } catch (IOException e) { + e.printStackTrace(); + + return false; + } + + long size = new File(filePath).length(); + + ContentValues values = new ContentValues(8); + + // store the details + values.put(Images.Media.TITLE, fileName); + values.put(Images.Media.DISPLAY_NAME, fileName); + values.put(Images.Media.DATE_ADDED, currentTime); + values.put(Images.Media.MIME_TYPE, mimeType); + values.put(Images.Media.DESCRIPTION, fileDescription); + values.put(Images.Media.ORIENTATION, 0); + values.put(Images.Media.DATA, filePath); + values.put(Images.Media.SIZE, size); + + return getContext().getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values) != null; + } + + /** + * Saves the current state of the chart to the gallery as a JPEG image. The + * filename and compression can be set. 0 == maximum compression, 100 = low + * compression (high quality). NOTE: Needs permission WRITE_EXTERNAL_STORAGE + * + * @param fileName e.g. "my_image" + * @param quality e.g. 50, min = 0, max = 100 + * @return returns true if saving was successful, false if not + */ + public boolean saveToGallery(String fileName, int quality) { + return saveToGallery(fileName, "", "MPAndroidChart-Library Save", Bitmap.CompressFormat.JPEG, quality); + } + + /** + * tasks to be done after the view is setup + */ + protected ArrayList mJobs = new ArrayList(); + + public void removeViewportJob(Runnable job) { + mJobs.remove(job); + } + + public void clearAllViewportJobs() { + mJobs.clear(); + } + + /** + * Either posts a job immediately if the chart has already setup it's + * dimensions or adds the job to the execution queue. + * + * @param job + */ + public void addViewportJob(Runnable job) { + + if (mViewPortHandler.hasChartDimens()) { + post(job); + } else { + mJobs.add(job); + } + } + + /** + * Returns all jobs that are scheduled to be executed after + * onSizeChanged(...). + * + * @return + */ + public ArrayList getJobs() { + return mJobs; + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + + for (int i = 0; i < getChildCount(); i++) { + getChildAt(i).layout(left, top, right, bottom); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int size = (int) Utils.convertDpToPixel(50f); + setMeasuredDimension( + Math.max(getSuggestedMinimumWidth(), + resolveSize(size, + widthMeasureSpec)), + Math.max(getSuggestedMinimumHeight(), + resolveSize(size, + heightMeasureSpec))); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + if (mLogEnabled) + Log.i(LOG_TAG, "OnSizeChanged()"); + + if (w > 0 && h > 0 && w < 10000 && h < 10000) { + + mViewPortHandler.setChartDimens(w, h); + + if (mLogEnabled) + Log.i(LOG_TAG, "Setting chart dimens, width: " + w + ", height: " + h); + + for (Runnable r : mJobs) { + post(r); + } + + mJobs.clear(); + } + + notifyDataSetChanged(); + + super.onSizeChanged(w, h, oldw, oldh); + } + + /** + * Setting this to true will set the layer-type HARDWARE for the view, false + * will set layer-type SOFTWARE. + * + * @param enabled + */ + public void setHardwareAccelerationEnabled(boolean enabled) { + + if (android.os.Build.VERSION.SDK_INT >= 11) { + + if (enabled) + setLayerType(View.LAYER_TYPE_HARDWARE, null); + else + setLayerType(View.LAYER_TYPE_SOFTWARE, null); + } else { + Log.e(LOG_TAG, + "Cannot enable/disable hardware acceleration for devices below API level 11."); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + //Log.i(LOG_TAG, "Detaching..."); + + if (mUnbind) + unbindDrawables(this); + } + + /** + * unbind flag + */ + private boolean mUnbind = false; + + /** + * Unbind all drawables to avoid memory leaks. + * Link: http://stackoverflow.com/a/6779164/1590502 + * + * @param view + */ + private void unbindDrawables(View view) { + + if (view.getBackground() != null) { + view.getBackground().setCallback(null); + } + if (view instanceof ViewGroup) { + for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { + unbindDrawables(((ViewGroup) view).getChildAt(i)); + } + ((ViewGroup) view).removeAllViews(); + } + } + + /** + * Set this to true to enable "unbinding" of drawables. When a View is detached + * from a window. This helps avoid memory leaks. + * Default: false + * Link: http://stackoverflow.com/a/6779164/1590502 + * + * @param enabled + */ + public void setUnbindEnabled(boolean enabled) { + this.mUnbind = enabled; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java new file mode 100644 index 0000000000..b1975b973e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java @@ -0,0 +1,229 @@ + +package com.github.mikephil.charting.charts; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; + +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BubbleData; +import com.github.mikephil.charting.data.CandleData; +import com.github.mikephil.charting.data.CombinedData; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.highlight.CombinedHighlighter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider; +import com.github.mikephil.charting.renderer.CombinedChartRenderer; + +/** + * This chart class allows the combination of lines, bars, scatter and candle + * data all displayed in one chart area. + * + * @author Philipp Jahoda + */ +public class CombinedChart extends BarLineChartBase implements CombinedDataProvider { + + /** + * if set to true, all values are drawn above their bars, instead of below + * their top + */ + private boolean mDrawValueAboveBar = true; + + + /** + * flag that indicates whether the highlight should be full-bar oriented, or single-value? + */ + protected boolean mHighlightFullBarEnabled = false; + + /** + * if set to true, a grey area is drawn behind each bar that indicates the + * maximum value + */ + private boolean mDrawBarShadow = false; + + protected DrawOrder[] mDrawOrder; + + /** + * enum that allows to specify the order in which the different data objects + * for the combined-chart are drawn + */ + public enum DrawOrder { + BAR, BUBBLE, LINE, CANDLE, SCATTER + } + + public CombinedChart(Context context) { + super(context); + } + + public CombinedChart(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CombinedChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init() { + super.init(); + + // Default values are not ready here yet + mDrawOrder = new DrawOrder[]{ + DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.LINE, DrawOrder.CANDLE, DrawOrder.SCATTER + }; + + setHighlighter(new CombinedHighlighter(this, this)); + + // Old default behaviour + setHighlightFullBarEnabled(true); + + mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler); + } + + @Override + public CombinedData getCombinedData() { + return mData; + } + + @Override + public void setData(CombinedData data) { + super.setData(data); + setHighlighter(new CombinedHighlighter(this, this)); + ((CombinedChartRenderer)mRenderer).createRenderers(); + mRenderer.initBuffers(); + } + + /** + * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch + * point + * inside the CombinedChart. + * + * @param x + * @param y + * @return + */ + @Override + public Highlight getHighlightByTouchPoint(float x, float y) { + + if (mData == null) { + Log.e(LOG_TAG, "Can't select by touch. No data set."); + return null; + } else { + Highlight h = getHighlighter().getHighlight(x, y); + if (h == null || !isHighlightFullBarEnabled()) return h; + + // For isHighlightFullBarEnabled, remove stackIndex + return new Highlight(h.getX(), h.getY(), + h.getXPx(), h.getYPx(), + h.getDataSetIndex(), -1, h.getAxis()); + } + } + + @Override + public LineData getLineData() { + if (mData == null) + return null; + return mData.getLineData(); + } + + @Override + public BarData getBarData() { + if (mData == null) + return null; + return mData.getBarData(); + } + + @Override + public ScatterData getScatterData() { + if (mData == null) + return null; + return mData.getScatterData(); + } + + @Override + public CandleData getCandleData() { + if (mData == null) + return null; + return mData.getCandleData(); + } + + @Override + public BubbleData getBubbleData() { + if (mData == null) + return null; + return mData.getBubbleData(); + } + + @Override + public boolean isDrawBarShadowEnabled() { + return mDrawBarShadow; + } + + @Override + public boolean isDrawValueAboveBarEnabled() { + return mDrawValueAboveBar; + } + + /** + * If set to true, all values are drawn above their bars, instead of below + * their top. + * + * @param enabled + */ + public void setDrawValueAboveBar(boolean enabled) { + mDrawValueAboveBar = enabled; + } + + + /** + * If set to true, a grey area is drawn behind each bar that indicates the + * maximum value. Enabling his will reduce performance by about 50%. + * + * @param enabled + */ + public void setDrawBarShadow(boolean enabled) { + mDrawBarShadow = enabled; + } + + /** + * Set this to true to make the highlight operation full-bar oriented, + * false to make it highlight single values (relevant only for stacked). + * + * @param enabled + */ + public void setHighlightFullBarEnabled(boolean enabled) { + mHighlightFullBarEnabled = enabled; + } + + /** + * @return true the highlight operation is be full-bar oriented, false if single-value + */ + @Override + public boolean isHighlightFullBarEnabled() { + return mHighlightFullBarEnabled; + } + + /** + * Returns the currently set draw order. + * + * @return + */ + public DrawOrder[] getDrawOrder() { + return mDrawOrder; + } + + /** + * Sets the order in which the provided data objects should be drawn. The + * earlier you place them in the provided array, the further they will be in + * the background. e.g. if you provide new DrawOrer[] { DrawOrder.BAR, + * DrawOrder.LINE }, the bars will be drawn behind the lines. + * + * @param order + */ + public void setDrawOrder(DrawOrder[] order) { + if (order == null || order.length <= 0) + return; + mDrawOrder = order; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java new file mode 100644 index 0000000000..e4ec309d9f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java @@ -0,0 +1,268 @@ +package com.github.mikephil.charting.charts; + +import android.content.Context; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.Log; + +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.HorizontalBarHighlighter; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer; +import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart; +import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart; +import com.github.mikephil.charting.utils.HorizontalViewPortHandler; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.TransformerHorizontalBarChart; +import com.github.mikephil.charting.utils.Utils; + +/** + * BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched, meaning the YAxis class + * represents the horizontal values and the XAxis class represents the vertical values. + * + * @author Philipp Jahoda + */ +public class HorizontalBarChart extends BarChart { + + public HorizontalBarChart(Context context) { + super(context); + } + + public HorizontalBarChart(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public HorizontalBarChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init() { + + mViewPortHandler = new HorizontalViewPortHandler(); + + super.init(); + + mLeftAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler); + mRightAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler); + + mRenderer = new HorizontalBarChartRenderer(this, mAnimator, mViewPortHandler); + setHighlighter(new HorizontalBarHighlighter(this)); + + mAxisRendererLeft = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, mLeftAxisTransformer); + mAxisRendererRight = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, mRightAxisTransformer); + mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, this); + } + + private RectF mOffsetsBuffer = new RectF(); + + @Override + public void calculateOffsets() { + + float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; + + calculateLegendOffsets(mOffsetsBuffer); + + offsetLeft += mOffsetsBuffer.left; + offsetTop += mOffsetsBuffer.top; + offsetRight += mOffsetsBuffer.right; + offsetBottom += mOffsetsBuffer.bottom; + + // offsets for y-labels + if (mAxisLeft.needsOffset()) { + offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.getPaintAxisLabels()); + } + + if (mAxisRight.needsOffset()) { + offsetBottom += mAxisRight.getRequiredHeightSpace(mAxisRendererRight.getPaintAxisLabels()); + } + + float xlabelwidth = mXAxis.mLabelRotatedWidth; + + if (mXAxis.isEnabled()) { + + // offsets for x-labels + if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { + + offsetLeft += xlabelwidth; + + } else if (mXAxis.getPosition() == XAxisPosition.TOP) { + + offsetRight += xlabelwidth; + + } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { + + offsetLeft += xlabelwidth; + offsetRight += xlabelwidth; + } + } + + offsetTop += getExtraTopOffset(); + offsetRight += getExtraRightOffset(); + offsetBottom += getExtraBottomOffset(); + offsetLeft += getExtraLeftOffset(); + + float minOffset = Utils.convertDpToPixel(mMinOffset); + + mViewPortHandler.restrainViewPort( + Math.max(minOffset, offsetLeft), + Math.max(minOffset, offsetTop), + Math.max(minOffset, offsetRight), + Math.max(minOffset, offsetBottom)); + + if (mLogEnabled) { + Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop + ", offsetRight: " + + offsetRight + ", offsetBottom: " + + offsetBottom); + Log.i(LOG_TAG, "Content: " + mViewPortHandler.getContentRect().toString()); + } + + prepareOffsetMatrix(); + prepareValuePxMatrix(); + } + + @Override + protected void prepareValuePxMatrix() { + mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mXAxis.mAxisRange, + mXAxis.mAxisMinimum); + mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mXAxis.mAxisRange, + mXAxis.mAxisMinimum); + } + + @Override + protected float[] getMarkerPosition(Highlight high) { + return new float[]{high.getDrawY(), high.getDrawX()}; + } + + @Override + public void getBarBounds(BarEntry e, RectF outputRect) { + + RectF bounds = outputRect; + IBarDataSet set = mData.getDataSetForEntry(e); + + if (set == null) { + outputRect.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE); + return; + } + + float y = e.getY(); + float x = e.getX(); + + float barWidth = mData.getBarWidth(); + + float top = x - barWidth / 2f; + float bottom = x + barWidth / 2f; + float left = y >= 0 ? y : 0; + float right = y <= 0 ? y : 0; + + bounds.set(left, top, right, bottom); + + getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); + + } + + protected float[] mGetPositionBuffer = new float[2]; + + /** + * Returns a recyclable MPPointF instance. + * + * @param e + * @param axis + * @return + */ + @Override + public MPPointF getPosition(Entry e, AxisDependency axis) { + + if (e == null) + return null; + + float[] vals = mGetPositionBuffer; + vals[0] = e.getY(); + vals[1] = e.getX(); + + getTransformer(axis).pointValuesToPixel(vals); + + return MPPointF.getInstance(vals[0], vals[1]); + } + + /** + * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point + * inside the BarChart. + * + * @param x + * @param y + * @return + */ + @Override + public Highlight getHighlightByTouchPoint(float x, float y) { + + if (mData == null) { + if (mLogEnabled) + Log.e(LOG_TAG, "Can't select by touch. No data set."); + return null; + } else + return getHighlighter().getHighlight(y, x); // switch x and y + } + + @Override + public float getLowestVisibleX() { + getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), posForGetLowestVisibleX); + float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.y); + return result; + } + + @Override + public float getHighestVisibleX() { + getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop(), posForGetHighestVisibleX); + float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.y); + return result; + } + + /** + * ###### VIEWPORT METHODS BELOW THIS ###### + */ + + @Override + public void setVisibleXRangeMaximum(float maxXRange) { + float xScale = mXAxis.mAxisRange / (maxXRange); + mViewPortHandler.setMinimumScaleY(xScale); + } + + @Override + public void setVisibleXRangeMinimum(float minXRange) { + float xScale = mXAxis.mAxisRange / (minXRange); + mViewPortHandler.setMaximumScaleY(xScale); + } + + @Override + public void setVisibleXRange(float minXRange, float maxXRange) { + float minScale = mXAxis.mAxisRange / minXRange; + float maxScale = mXAxis.mAxisRange / maxXRange; + mViewPortHandler.setMinMaxScaleY(minScale, maxScale); + } + + @Override + public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { + float yScale = getAxisRange(axis) / maxYRange; + mViewPortHandler.setMinimumScaleX(yScale); + } + + @Override + public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { + float yScale = getAxisRange(axis) / minYRange; + mViewPortHandler.setMaximumScaleX(yScale); + } + + @Override + public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) { + float minScale = getAxisRange(axis) / minYRange; + float maxScale = getAxisRange(axis) / maxYRange; + mViewPortHandler.setMinMaxScaleX(minScale, maxScale); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java new file mode 100644 index 0000000000..aa7afc4c85 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java @@ -0,0 +1,50 @@ + +package com.github.mikephil.charting.charts; + +import android.content.Context; +import android.util.AttributeSet; + +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.renderer.LineChartRenderer; + +/** + * Chart that draws lines, surfaces, circles, ... + * + * @author Philipp Jahoda + */ +public class LineChart extends BarLineChartBase implements LineDataProvider { + + public LineChart(Context context) { + super(context); + } + + public LineChart(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LineChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init() { + super.init(); + + mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler); + } + + @Override + public LineData getLineData() { + return mData; + } + + @Override + protected void onDetachedFromWindow() { + // releases the bitmap in the renderer to avoid oom error + if (mRenderer != null && mRenderer instanceof LineChartRenderer) { + ((LineChartRenderer) mRenderer).releaseBitmap(); + } + super.onDetachedFromWindow(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java new file mode 100644 index 0000000000..660fd29bdc --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java @@ -0,0 +1,730 @@ + +package com.github.mikephil.charting.charts; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.util.AttributeSet; + +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.PieHighlighter; +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; +import com.github.mikephil.charting.renderer.PieChartRenderer; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; + +import java.util.List; + +/** + * View that represents a pie chart. Draws cake like slices. + * + * @author Philipp Jahoda + */ +public class PieChart extends PieRadarChartBase { + + /** + * rect object that represents the bounds of the piechart, needed for + * drawing the circle + */ + private RectF mCircleBox = new RectF(); + + /** + * flag indicating if entry labels should be drawn or not + */ + private boolean mDrawEntryLabels = true; + + /** + * array that holds the width of each pie-slice in degrees + */ + private float[] mDrawAngles = new float[1]; + + /** + * array that holds the absolute angle in degrees of each slice + */ + private float[] mAbsoluteAngles = new float[1]; + + /** + * if true, the white hole inside the chart will be drawn + */ + private boolean mDrawHole = true; + + /** + * if true, the hole will see-through to the inner tips of the slices + */ + private boolean mDrawSlicesUnderHole = false; + + /** + * if true, the values inside the piechart are drawn as percent values + */ + private boolean mUsePercentValues = false; + + /** + * if true, the slices of the piechart are rounded + */ + private boolean mDrawRoundedSlices = false; + + /** + * variable for the text that is drawn in the center of the pie-chart + */ + private CharSequence mCenterText = ""; + + private MPPointF mCenterTextOffset = MPPointF.getInstance(0, 0); + + /** + * indicates the size of the hole in the center of the piechart, default: + * radius / 2 + */ + private float mHoleRadiusPercent = 50f; + + /** + * the radius of the transparent circle next to the chart-hole in the center + */ + protected float mTransparentCircleRadiusPercent = 55f; + + /** + * if enabled, centertext is drawn + */ + private boolean mDrawCenterText = true; + + private float mCenterTextRadiusPercent = 100.f; + + protected float mMaxAngle = 360f; + + public PieChart(Context context) { + super(context); + } + + public PieChart(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PieChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init() { + super.init(); + + mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler); + mXAxis = null; + + mHighlighter = new PieHighlighter(this); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mData == null) + return; + + mRenderer.drawData(canvas); + + if (valuesToHighlight()) + mRenderer.drawHighlighted(canvas, mIndicesToHighlight); + + mRenderer.drawExtras(canvas); + + mRenderer.drawValues(canvas); + + mLegendRenderer.renderLegend(canvas); + + drawDescription(canvas); + + drawMarkers(canvas); + } + + @Override + public void calculateOffsets() { + super.calculateOffsets(); + + // prevent nullpointer when no data set + if (mData == null) + return; + + float diameter = getDiameter(); + float radius = diameter / 2f; + + MPPointF c = getCenterOffsets(); + + float shift = mData.getDataSet().getSelectionShift(); + + // create the circle box that will contain the pie-chart (the bounds of + // the pie-chart) + mCircleBox.set(c.x - radius + shift, + c.y - radius + shift, + c.x + radius - shift, + c.y + radius - shift); + + MPPointF.recycleInstance(c); + } + + @Override + protected void calcMinMax() { + calcAngles(); + } + + @Override + protected float[] getMarkerPosition(Highlight highlight) { + + MPPointF center = getCenterCircleBox(); + float r = getRadius(); + + float off = r / 10f * 3.6f; + + if (isDrawHoleEnabled()) { + off = (r - (r / 100f * getHoleRadius())) / 2f; + } + + r -= off; // offset to keep things inside the chart + + float rotationAngle = getRotationAngle(); + + int entryIndex = (int) highlight.getX(); + + // offset needed to center the drawn text in the slice + float offset = mDrawAngles[entryIndex] / 2; + + // calculate the text position + float x = (float) (r + * Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) + * mAnimator.getPhaseY())) + center.x); + float y = (float) (r + * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) + * mAnimator.getPhaseY())) + center.y); + + MPPointF.recycleInstance(center); + return new float[]{x, y}; + } + + /** + * calculates the needed angles for the chart slices + */ + private void calcAngles() { + + int entryCount = mData.getEntryCount(); + + if (mDrawAngles.length != entryCount) { + mDrawAngles = new float[entryCount]; + } else { + for (int i = 0; i < entryCount; i++) { + mDrawAngles[i] = 0; + } + } + if (mAbsoluteAngles.length != entryCount) { + mAbsoluteAngles = new float[entryCount]; + } else { + for (int i = 0; i < entryCount; i++) { + mAbsoluteAngles[i] = 0; + } + } + + float yValueSum = mData.getYValueSum(); + + List dataSets = mData.getDataSets(); + + int cnt = 0; + + for (int i = 0; i < mData.getDataSetCount(); i++) { + + IPieDataSet set = dataSets.get(i); + + for (int j = 0; j < set.getEntryCount(); j++) { + + mDrawAngles[cnt] = calcAngle(Math.abs(set.getEntryForIndex(j).getY()), yValueSum); + + if (cnt == 0) { + mAbsoluteAngles[cnt] = mDrawAngles[cnt]; + } else { + mAbsoluteAngles[cnt] = mAbsoluteAngles[cnt - 1] + mDrawAngles[cnt]; + } + + cnt++; + } + } + + } + + /** + * Checks if the given index is set to be highlighted. + * + * @param index + * @return + */ + public boolean needsHighlight(int index) { + + // no highlight + if (!valuesToHighlight()) + return false; + + for (int i = 0; i < mIndicesToHighlight.length; i++) + + // check if the xvalue for the given dataset needs highlight + if ((int) mIndicesToHighlight[i].getX() == index) + return true; + + return false; + } + + /** + * calculates the needed angle for a given value + * + * @param value + * @return + */ + private float calcAngle(float value) { + return calcAngle(value, mData.getYValueSum()); + } + + /** + * calculates the needed angle for a given value + * + * @param value + * @param yValueSum + * @return + */ + private float calcAngle(float value, float yValueSum) { + return value / yValueSum * mMaxAngle; + } + + /** + * This will throw an exception, PieChart has no XAxis object. + * + * @return + */ + @Deprecated + @Override + public XAxis getXAxis() { + throw new RuntimeException("PieChart has no XAxis"); + } + + @Override + public int getIndexForAngle(float angle) { + + // take the current angle of the chart into consideration + float a = Utils.getNormalizedAngle(angle - getRotationAngle()); + + for (int i = 0; i < mAbsoluteAngles.length; i++) { + if (mAbsoluteAngles[i] > a) + return i; + } + + return -1; // return -1 if no index found + } + + /** + * Returns the index of the DataSet this x-index belongs to. + * + * @param xIndex + * @return + */ + public int getDataSetIndexForIndex(int xIndex) { + + List dataSets = mData.getDataSets(); + + for (int i = 0; i < dataSets.size(); i++) { + if (dataSets.get(i).getEntryForXValue(xIndex, Float.NaN) != null) + return i; + } + + return -1; + } + + /** + * returns an integer array of all the different angles the chart slices + * have the angles in the returned array determine how much space (of 360°) + * each slice takes + * + * @return + */ + public float[] getDrawAngles() { + return mDrawAngles; + } + + /** + * returns the absolute angles of the different chart slices (where the + * slices end) + * + * @return + */ + public float[] getAbsoluteAngles() { + return mAbsoluteAngles; + } + + /** + * Sets the color for the hole that is drawn in the center of the PieChart + * (if enabled). + * + * @param color + */ + public void setHoleColor(int color) { + ((PieChartRenderer) mRenderer).getPaintHole().setColor(color); + } + + /** + * Enable or disable the visibility of the inner tips of the slices behind the hole + */ + public void setDrawSlicesUnderHole(boolean enable) { + mDrawSlicesUnderHole = enable; + } + + /** + * Returns true if the inner tips of the slices are visible behind the hole, + * false if not. + * + * @return true if slices are visible behind the hole. + */ + public boolean isDrawSlicesUnderHoleEnabled() { + return mDrawSlicesUnderHole; + } + + /** + * set this to true to draw the pie center empty + * + * @param enabled + */ + public void setDrawHoleEnabled(boolean enabled) { + this.mDrawHole = enabled; + } + + /** + * returns true if the hole in the center of the pie-chart is set to be + * visible, false if not + * + * @return + */ + public boolean isDrawHoleEnabled() { + return mDrawHole; + } + + /** + * Sets the text String that is displayed in the center of the PieChart. + * + * @param text + */ + public void setCenterText(CharSequence text) { + if (text == null) + mCenterText = ""; + else + mCenterText = text; + } + + /** + * returns the text that is drawn in the center of the pie-chart + * + * @return + */ + public CharSequence getCenterText() { + return mCenterText; + } + + /** + * set this to true to draw the text that is displayed in the center of the + * pie chart + * + * @param enabled + */ + public void setDrawCenterText(boolean enabled) { + this.mDrawCenterText = enabled; + } + + /** + * returns true if drawing the center text is enabled + * + * @return + */ + public boolean isDrawCenterTextEnabled() { + return mDrawCenterText; + } + + @Override + protected float getRequiredLegendOffset() { + return mLegendRenderer.getLabelPaint().getTextSize() * 2.f; + } + + @Override + protected float getRequiredBaseOffset() { + return 0; + } + + @Override + public float getRadius() { + if (mCircleBox == null) + return 0; + else + return Math.min(mCircleBox.width() / 2f, mCircleBox.height() / 2f); + } + + /** + * returns the circlebox, the boundingbox of the pie-chart slices + * + * @return + */ + public RectF getCircleBox() { + return mCircleBox; + } + + /** + * returns the center of the circlebox + * + * @return + */ + public MPPointF getCenterCircleBox() { + return MPPointF.getInstance(mCircleBox.centerX(), mCircleBox.centerY()); + } + + /** + * sets the typeface for the center-text paint + * + * @param t + */ + public void setCenterTextTypeface(Typeface t) { + ((PieChartRenderer) mRenderer).getPaintCenterText().setTypeface(t); + } + + /** + * Sets the size of the center text of the PieChart in dp. + * + * @param sizeDp + */ + public void setCenterTextSize(float sizeDp) { + ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize( + Utils.convertDpToPixel(sizeDp)); + } + + /** + * Sets the size of the center text of the PieChart in pixels. + * + * @param sizePixels + */ + public void setCenterTextSizePixels(float sizePixels) { + ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(sizePixels); + } + + /** + * Sets the offset the center text should have from it's original position in dp. Default x = 0, y = 0 + * + * @param x + * @param y + */ + public void setCenterTextOffset(float x, float y) { + mCenterTextOffset.x = Utils.convertDpToPixel(x); + mCenterTextOffset.y = Utils.convertDpToPixel(y); + } + + /** + * Returns the offset on the x- and y-axis the center text has in dp. + * + * @return + */ + public MPPointF getCenterTextOffset() { + return MPPointF.getInstance(mCenterTextOffset.x, mCenterTextOffset.y); + } + + /** + * Sets the color of the center text of the PieChart. + * + * @param color + */ + public void setCenterTextColor(int color) { + ((PieChartRenderer) mRenderer).getPaintCenterText().setColor(color); + } + + /** + * sets the radius of the hole in the center of the piechart in percent of + * the maximum radius (max = the radius of the whole chart), default 50% + * + * @param percent + */ + public void setHoleRadius(final float percent) { + mHoleRadiusPercent = percent; + } + + /** + * Returns the size of the hole radius in percent of the total radius. + * + * @return + */ + public float getHoleRadius() { + return mHoleRadiusPercent; + } + + /** + * Sets the color the transparent-circle should have. + * + * @param color + */ + public void setTransparentCircleColor(int color) { + + Paint p = ((PieChartRenderer) mRenderer).getPaintTransparentCircle(); + int alpha = p.getAlpha(); + p.setColor(color); + p.setAlpha(alpha); + } + + /** + * sets the radius of the transparent circle that is drawn next to the hole + * in the piechart in percent of the maximum radius (max = the radius of the + * whole chart), default 55% -> means 5% larger than the center-hole by + * default + * + * @param percent + */ + public void setTransparentCircleRadius(final float percent) { + mTransparentCircleRadiusPercent = percent; + } + + public float getTransparentCircleRadius() { + return mTransparentCircleRadiusPercent; + } + + /** + * Sets the amount of transparency the transparent circle should have 0 = fully transparent, + * 255 = fully opaque. + * Default value is 100. + * + * @param alpha 0-255 + */ + public void setTransparentCircleAlpha(int alpha) { + ((PieChartRenderer) mRenderer).getPaintTransparentCircle().setAlpha(alpha); + } + + /** + * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class). + * Deprecated -> use setDrawEntryLabels(...) instead. + * + * @param enabled + */ + @Deprecated + public void setDrawSliceText(boolean enabled) { + mDrawEntryLabels = enabled; + } + + /** + * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class). + * + * @param enabled + */ + public void setDrawEntryLabels(boolean enabled) { + mDrawEntryLabels = enabled; + } + + /** + * Returns true if drawing the entry labels is enabled, false if not. + * + * @return + */ + public boolean isDrawEntryLabelsEnabled() { + return mDrawEntryLabels; + } + + /** + * Sets the color the entry labels are drawn with. + * + * @param color + */ + public void setEntryLabelColor(int color) { + ((PieChartRenderer) mRenderer).getPaintEntryLabels().setColor(color); + } + + /** + * Sets a custom Typeface for the drawing of the entry labels. + * + * @param tf + */ + public void setEntryLabelTypeface(Typeface tf) { + ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTypeface(tf); + } + + /** + * Sets the size of the entry labels in dp. Default: 13dp + * + * @param size + */ + public void setEntryLabelTextSize(float size) { + ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTextSize(Utils.convertDpToPixel(size)); + } + + /** + * Returns true if the chart is set to draw each end of a pie-slice + * "rounded". + * + * @return + */ + public boolean isDrawRoundedSlicesEnabled() { + return mDrawRoundedSlices; + } + + /** + * If this is enabled, values inside the PieChart are drawn in percent and + * not with their original value. Values provided for the IValueFormatter to + * format are then provided in percent. + * + * @param enabled + */ + public void setUsePercentValues(boolean enabled) { + mUsePercentValues = enabled; + } + + /** + * Returns true if using percentage values is enabled for the chart. + * + * @return + */ + public boolean isUsePercentValuesEnabled() { + return mUsePercentValues; + } + + /** + * the rectangular radius of the bounding box for the center text, as a percentage of the pie + * hole + * default 1.f (100%) + */ + public void setCenterTextRadiusPercent(float percent) { + mCenterTextRadiusPercent = percent; + } + + /** + * the rectangular radius of the bounding box for the center text, as a percentage of the pie + * hole + * default 1.f (100%) + */ + public float getCenterTextRadiusPercent() { + return mCenterTextRadiusPercent; + } + + public float getMaxAngle() { + return mMaxAngle; + } + + /** + * Sets the max angle that is used for calculating the pie-circle. 360f means + * it's a full PieChart, 180f results in a half-pie-chart. Default: 360f + * + * @param maxangle min 90, max 360 + */ + public void setMaxAngle(float maxangle) { + + if (maxangle > 360) + maxangle = 360f; + + if (maxangle < 90) + maxangle = 90f; + + this.mMaxAngle = maxangle; + } + + @Override + protected void onDetachedFromWindow() { + // releases the bitmap in the renderer to avoid oom error + if (mRenderer != null && mRenderer instanceof PieChartRenderer) { + ((PieChartRenderer) mRenderer).releaseBitmap(); + } + super.onDetachedFromWindow(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java new file mode 100644 index 0000000000..d403a752cc --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -0,0 +1,501 @@ + +package com.github.mikephil.charting.charts; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.listener.PieRadarChartTouchListener; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; + +/** + * Baseclass of PieChart and RadarChart. + * + * @author Philipp Jahoda + */ +public abstract class PieRadarChartBase>> + extends Chart { + + /** + * holds the normalized version of the current rotation angle of the chart + */ + private float mRotationAngle = 270f; + + /** + * holds the raw version of the current rotation angle of the chart + */ + private float mRawRotationAngle = 270f; + + /** + * flag that indicates if rotation is enabled or not + */ + protected boolean mRotateEnabled = true; + + /** + * Sets the minimum offset (padding) around the chart, defaults to 0.f + */ + protected float mMinOffset = 0.f; + + public PieRadarChartBase(Context context) { + super(context); + } + + public PieRadarChartBase(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PieRadarChartBase(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init() { + super.init(); + + mChartTouchListener = new PieRadarChartTouchListener(this); + } + + @Override + protected void calcMinMax() { + //mXAxis.mAxisRange = mData.getXVals().size() - 1; + } + + @Override + public int getMaxVisibleCount() { + return mData.getEntryCount(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + // use the pie- and radarchart listener own listener + if (mTouchEnabled && mChartTouchListener != null) + return mChartTouchListener.onTouch(this, event); + else + return super.onTouchEvent(event); + } + + @Override + public void computeScroll() { + + if (mChartTouchListener instanceof PieRadarChartTouchListener) + ((PieRadarChartTouchListener) mChartTouchListener).computeScroll(); + } + + @Override + public void notifyDataSetChanged() { + if (mData == null) + return; + + calcMinMax(); + + if (mLegend != null) + mLegendRenderer.computeLegend(mData); + + calculateOffsets(); + } + + @Override + public void calculateOffsets() { + + float legendLeft = 0f, legendRight = 0f, legendBottom = 0f, legendTop = 0f; + + if (mLegend != null && mLegend.isEnabled() && !mLegend.isDrawInsideEnabled()) { + + float fullLegendWidth = Math.min(mLegend.mNeededWidth, + mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()); + + switch (mLegend.getOrientation()) { + case VERTICAL: { + float xLegendOffset = 0.f; + + if (mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.LEFT + || mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.RIGHT) { + if (mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.CENTER) { + // this is the space between the legend and the chart + final float spacing = Utils.convertDpToPixel(13f); + + xLegendOffset = fullLegendWidth + spacing; + + } else { + // this is the space between the legend and the chart + float spacing = Utils.convertDpToPixel(8f); + + float legendWidth = fullLegendWidth + spacing; + float legendHeight = mLegend.mNeededHeight + mLegend.mTextHeightMax; + + MPPointF center = getCenter(); + + float bottomX = mLegend.getHorizontalAlignment() == + Legend.LegendHorizontalAlignment.RIGHT + ? getWidth() - legendWidth + 15.f + : legendWidth - 15.f; + float bottomY = legendHeight + 15.f; + float distLegend = distanceToCenter(bottomX, bottomY); + + MPPointF reference = getPosition(center, getRadius(), + getAngleForPoint(bottomX, bottomY)); + + float distReference = distanceToCenter(reference.x, reference.y); + float minOffset = Utils.convertDpToPixel(5f); + + if (bottomY >= center.y && getHeight() - legendWidth > getWidth()) { + xLegendOffset = legendWidth; + } else if (distLegend < distReference) { + + float diff = distReference - distLegend; + xLegendOffset = minOffset + diff; + } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(reference); + } + } + + switch (mLegend.getHorizontalAlignment()) { + case LEFT: + legendLeft = xLegendOffset; + break; + + case RIGHT: + legendRight = xLegendOffset; + break; + + case CENTER: + switch (mLegend.getVerticalAlignment()) { + case TOP: + legendTop = Math.min(mLegend.mNeededHeight, + mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); + break; + case BOTTOM: + legendBottom = Math.min(mLegend.mNeededHeight, + mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); + break; + } + break; + } + } + break; + + case HORIZONTAL: + float yLegendOffset = 0.f; + + if (mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.TOP || + mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.BOTTOM) { + + // It's possible that we do not need this offset anymore as it + // is available through the extraOffsets, but changing it can mean + // changing default visibility for existing apps. + float yOffset = getRequiredLegendOffset(); + + yLegendOffset = Math.min(mLegend.mNeededHeight + yOffset, + mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); + + switch (mLegend.getVerticalAlignment()) { + case TOP: + legendTop = yLegendOffset; + break; + case BOTTOM: + legendBottom = yLegendOffset; + break; + } + } + break; + } + + legendLeft += getRequiredBaseOffset(); + legendRight += getRequiredBaseOffset(); + legendTop += getRequiredBaseOffset(); + legendBottom += getRequiredBaseOffset(); + } + + float minOffset = Utils.convertDpToPixel(mMinOffset); + + if (this instanceof RadarChart) { + XAxis x = this.getXAxis(); + + if (x.isEnabled() && x.isDrawLabelsEnabled()) { + minOffset = Math.max(minOffset, x.mLabelRotatedWidth); + } + } + + legendTop += getExtraTopOffset(); + legendRight += getExtraRightOffset(); + legendBottom += getExtraBottomOffset(); + legendLeft += getExtraLeftOffset(); + + float offsetLeft = Math.max(minOffset, legendLeft); + float offsetTop = Math.max(minOffset, legendTop); + float offsetRight = Math.max(minOffset, legendRight); + float offsetBottom = Math.max(minOffset, Math.max(getRequiredBaseOffset(), legendBottom)); + + mViewPortHandler.restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom); + + if (mLogEnabled) + Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop + + ", offsetRight: " + offsetRight + ", offsetBottom: " + offsetBottom); + } + + /** + * returns the angle relative to the chart center for the given point on the + * chart in degrees. The angle is always between 0 and 360°, 0° is NORTH, + * 90° is EAST, ... + * + * @param x + * @param y + * @return + */ + public float getAngleForPoint(float x, float y) { + + MPPointF c = getCenterOffsets(); + + double tx = x - c.x, ty = y - c.y; + double length = Math.sqrt(tx * tx + ty * ty); + double r = Math.acos(ty / length); + + float angle = (float) Math.toDegrees(r); + + if (x > c.x) + angle = 360f - angle; + + // add 90° because chart starts EAST + angle = angle + 90f; + + // neutralize overflow + if (angle > 360f) + angle = angle - 360f; + + MPPointF.recycleInstance(c); + + return angle; + } + + /** + * Returns a recyclable MPPointF instance. + * Calculates the position around a center point, depending on the distance + * from the center, and the angle of the position around the center. + * + * @param center + * @param dist + * @param angle in degrees, converted to radians internally + * @return + */ + public MPPointF getPosition(MPPointF center, float dist, float angle) { + + MPPointF p = MPPointF.getInstance(0, 0); + getPosition(center, dist, angle, p); + return p; + } + + public void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint) { + outputPoint.x = (float) (center.x + dist * Math.cos(Math.toRadians(angle))); + outputPoint.y = (float) (center.y + dist * Math.sin(Math.toRadians(angle))); + } + + /** + * Returns the distance of a certain point on the chart to the center of the + * chart. + * + * @param x + * @param y + * @return + */ + public float distanceToCenter(float x, float y) { + + MPPointF c = getCenterOffsets(); + + float dist = 0f; + + float xDist = 0f; + float yDist = 0f; + + if (x > c.x) { + xDist = x - c.x; + } else { + xDist = c.x - x; + } + + if (y > c.y) { + yDist = y - c.y; + } else { + yDist = c.y - y; + } + + // pythagoras + dist = (float) Math.sqrt(Math.pow(xDist, 2.0) + Math.pow(yDist, 2.0)); + + MPPointF.recycleInstance(c); + + return dist; + } + + /** + * Returns the xIndex for the given angle around the center of the chart. + * Returns -1 if not found / outofbounds. + * + * @param angle + * @return + */ + public abstract int getIndexForAngle(float angle); + + /** + * Set an offset for the rotation of the RadarChart in degrees. Default 270f + * --> top (NORTH) + * + * @param angle + */ + public void setRotationAngle(float angle) { + mRawRotationAngle = angle; + mRotationAngle = Utils.getNormalizedAngle(mRawRotationAngle); + } + + /** + * gets the raw version of the current rotation angle of the pie chart the + * returned value could be any value, negative or positive, outside of the + * 360 degrees. this is used when working with rotation direction, mainly by + * gestures and animations. + * + * @return + */ + public float getRawRotationAngle() { + return mRawRotationAngle; + } + + /** + * gets a normalized version of the current rotation angle of the pie chart, + * which will always be between 0.0 < 360.0 + * + * @return + */ + public float getRotationAngle() { + return mRotationAngle; + } + + /** + * Set this to true to enable the rotation / spinning of the chart by touch. + * Set it to false to disable it. Default: true + * + * @param enabled + */ + public void setRotationEnabled(boolean enabled) { + mRotateEnabled = enabled; + } + + /** + * Returns true if rotation of the chart by touch is enabled, false if not. + * + * @return + */ + public boolean isRotationEnabled() { + return mRotateEnabled; + } + + /** + * Gets the minimum offset (padding) around the chart, defaults to 0.f + */ + public float getMinOffset() { + return mMinOffset; + } + + /** + * Sets the minimum offset (padding) around the chart, defaults to 0.f + */ + public void setMinOffset(float minOffset) { + mMinOffset = minOffset; + } + + /** + * returns the diameter of the pie- or radar-chart + * + * @return + */ + public float getDiameter() { + RectF content = mViewPortHandler.getContentRect(); + content.left += getExtraLeftOffset(); + content.top += getExtraTopOffset(); + content.right -= getExtraRightOffset(); + content.bottom -= getExtraBottomOffset(); + return Math.min(content.width(), content.height()); + } + + /** + * Returns the radius of the chart in pixels. + * + * @return + */ + public abstract float getRadius(); + + /** + * Returns the required offset for the chart legend. + * + * @return + */ + protected abstract float getRequiredLegendOffset(); + + /** + * Returns the base offset needed for the chart without calculating the + * legend size. + * + * @return + */ + protected abstract float getRequiredBaseOffset(); + + @Override + public float getYChartMax() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public float getYChartMin() { + // TODO Auto-generated method stub + return 0; + } + + /** + * ################ ################ ################ ################ + */ + /** CODE BELOW THIS RELATED TO ANIMATION */ + + /** + * Applys a spin animation to the Chart. + * + * @param durationmillis + * @param fromangle + * @param toangle + */ + @SuppressLint("NewApi") + public void spin(int durationmillis, float fromangle, float toangle, Easing.EasingOption easing) { + + if (android.os.Build.VERSION.SDK_INT < 11) + return; + + setRotationAngle(fromangle); + + ObjectAnimator spinAnimator = ObjectAnimator.ofFloat(this, "rotationAngle", fromangle, + toangle); + spinAnimator.setDuration(durationmillis); + spinAnimator.setInterpolator(Easing.getEasingFunctionFromOption(easing)); + + spinAnimator.addUpdateListener(new AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + postInvalidate(); + } + }); + spinAnimator.start(); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java similarity index 61% rename from MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java index c2c02c4287..3c9aec0dde 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java @@ -4,16 +4,13 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; -import android.util.Log; -import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.RadarData; +import com.github.mikephil.charting.highlight.RadarHighlighter; import com.github.mikephil.charting.renderer.RadarChartRenderer; import com.github.mikephil.charting.renderer.XAxisRendererRadarChart; import com.github.mikephil.charting.renderer.YAxisRendererRadarChart; @@ -22,34 +19,50 @@ /** * Implementation of the RadarChart, a "spidernet"-like chart. It works best * when displaying 5-10 entries per DataSet. - * + * * @author Philipp Jahoda */ public class RadarChart extends PieRadarChartBase { - /** width of the main web lines */ + /** + * width of the main web lines + */ private float mWebLineWidth = 2.5f; - /** width of the inner web lines */ + /** + * width of the inner web lines + */ private float mInnerWebLineWidth = 1.5f; - /** color for the main web lines */ + /** + * color for the main web lines + */ private int mWebColor = Color.rgb(122, 122, 122); - /** color for the inner web */ + /** + * color for the inner web + */ private int mWebColorInner = Color.rgb(122, 122, 122); - /** transparency the grid is drawn with (0-255) */ + /** + * transparency the grid is drawn with (0-255) + */ private int mWebAlpha = 150; - /** flag indicating if the web lines should be drawn or not */ + /** + * flag indicating if the web lines should be drawn or not + */ private boolean mDrawWeb = true; - /** the object reprsenting the y-axis labels */ - private YAxis mYAxis; + /** + * modulus that determines how many labels and web-lines are skipped before the next is drawn + */ + private int mSkipWebLineCount = 0; - /** the object representing the x-axis labels */ - private XAxis mXAxis; + /** + * the object reprsenting the y-axis labels + */ + private YAxis mYAxis; protected YAxisRendererRadarChart mYAxisRenderer; protected XAxisRendererRadarChart mXAxisRenderer; @@ -71,8 +84,6 @@ protected void init() { super.init(); mYAxis = new YAxis(AxisDependency.LEFT); - mXAxis = new XAxis(); - mXAxis.setSpaceBetweenLabels(0); mWebLineWidth = Utils.convertDpToPixel(1.5f); mInnerWebLineWidth = Utils.convertDpToPixel(0.75f); @@ -80,68 +91,30 @@ protected void init() { mRenderer = new RadarChartRenderer(this, mAnimator, mViewPortHandler); mYAxisRenderer = new YAxisRendererRadarChart(mViewPortHandler, mYAxis, this); mXAxisRenderer = new XAxisRendererRadarChart(mViewPortHandler, mXAxis, this); + + mHighlighter = new RadarHighlighter(this); } @Override protected void calcMinMax() { super.calcMinMax(); - float minLeft = mData.getYMin(AxisDependency.LEFT); - float maxLeft = mData.getYMax(AxisDependency.LEFT); - - mXChartMax = mData.getXVals().size() - 1; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - - float leftRange = Math.abs(maxLeft - (mYAxis.isStartAtZeroEnabled() ? 0 : minLeft)); - - float topSpaceLeft = leftRange / 100f * mYAxis.getSpaceTop(); - float bottomSpaceLeft = leftRange / 100f * mYAxis.getSpaceBottom(); - - mXChartMax = mData.getXVals().size() - 1; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - - mYAxis.mAxisMaximum = !Float.isNaN(mYAxis.getAxisMaxValue()) ? mYAxis - .getAxisMaxValue() : maxLeft + topSpaceLeft; - mYAxis.mAxisMinimum = !Float.isNaN(mYAxis.getAxisMinValue()) ? mYAxis - .getAxisMinValue() : minLeft - bottomSpaceLeft; - - // consider starting at zero (0) - if (mYAxis.isStartAtZeroEnabled()) - mYAxis.mAxisMinimum = 0f; - - mYAxis.mAxisRange = Math.abs(mYAxis.mAxisMaximum - mYAxis.mAxisMinimum); - } - - @Override - protected float[] getMarkerPosition(Entry e, int dataSetIndex) { - - float angle = getSliceAngle() * e.getXIndex() + getRotationAngle(); - float val = e.getVal() * getFactor(); - PointF c = getCenterOffsets(); - - PointF p = new PointF((float) (c.x + val * Math.cos(Math.toRadians(angle))), - (float) (c.y + val * Math.sin(Math.toRadians(angle)))); - - return new float[] { - p.x, p.y - }; + mYAxis.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); + mXAxis.calculate(0, mData.getMaxEntryCountSet().getEntryCount()); } @Override public void notifyDataSetChanged() { - if (mDataNotSet) + if (mData == null) return; calcMinMax(); - if (mYAxis.needsDefaultFormatter()) { - mYAxis.setValueFormatter(mDefaultFormatter); - } - - mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum); - mXAxisRenderer.computeAxis(mData.getXValAverageLength(), mData.getXVals()); + mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); + mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); - mLegend = mLegendRenderer.computeLegend(mData, mLegend); + if (mLegend != null && !mLegend.isLegendCustom()) + mLegendRenderer.computeLegend(mData); calculateOffsets(); } @@ -150,92 +123,98 @@ public void notifyDataSetChanged() { protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if (mDataNotSet) + if (mData == null) return; - mXAxisRenderer.renderAxisLabels(mDrawCanvas); +// if (mYAxis.isEnabled()) +// mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); + + if (mXAxis.isEnabled()) + mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); + + mXAxisRenderer.renderAxisLabels(canvas); if (mDrawWeb) - mRenderer.drawExtras(mDrawCanvas); + mRenderer.drawExtras(canvas); - mYAxisRenderer.renderLimitLines(mDrawCanvas); + if (mYAxis.isEnabled() && mYAxis.isDrawLimitLinesBehindDataEnabled()) + mYAxisRenderer.renderLimitLines(canvas); - mRenderer.drawData(mDrawCanvas); + mRenderer.drawData(canvas); - if (mHighlightEnabled && valuesToHighlight()) - mRenderer.drawHighlighted(mDrawCanvas, mIndicesToHightlight); + if (valuesToHighlight()) + mRenderer.drawHighlighted(canvas, mIndicesToHighlight); - mYAxisRenderer.renderAxisLabels(mDrawCanvas); + if (mYAxis.isEnabled() && !mYAxis.isDrawLimitLinesBehindDataEnabled()) + mYAxisRenderer.renderLimitLines(canvas); - mRenderer.drawValues(mDrawCanvas); + mYAxisRenderer.renderAxisLabels(canvas); - mLegendRenderer.renderLegend(mDrawCanvas, mLegend); + mRenderer.drawValues(canvas); - drawDescription(); + mLegendRenderer.renderLegend(canvas); - drawMarkers(); + drawDescription(canvas); - canvas.drawBitmap(mDrawBitmap, 0, 0, mDrawPaint); + drawMarkers(canvas); } /** * Returns the factor that is needed to transform values into pixels. - * + * * @return */ public float getFactor() { RectF content = mViewPortHandler.getContentRect(); - return (float) Math.min(content.width() / 2f, content.height() / 2f) - / mYAxis.mAxisRange; + return Math.min(content.width() / 2f, content.height() / 2f) / mYAxis.mAxisRange; } /** * Returns the angle that each slice in the radar chart occupies. - * + * * @return */ public float getSliceAngle() { - return 360f / (float) mData.getXValCount(); + return 360f / (float) mData.getMaxEntryCountSet().getEntryCount(); } @Override public int getIndexForAngle(float angle) { // take the current angle of the chart into consideration - float a = (angle - mRotationAngle + 360) % 360f; + float a = Utils.getNormalizedAngle(angle - getRotationAngle()); float sliceangle = getSliceAngle(); - for (int i = 0; i < mData.getXValCount(); i++) { - if (sliceangle * (i + 1) - sliceangle / 2f > a) - return i; + int max = mData.getMaxEntryCountSet().getEntryCount(); + + int index = 0; + + for (int i = 0; i < max; i++) { + + float referenceAngle = sliceangle * (i + 1) - sliceangle / 2f; + + if (referenceAngle > a) { + index = i; + break; + } } - return 0; + return index; } /** * Returns the object that represents all y-labels of the RadarChart. - * + * * @return */ public YAxis getYAxis() { return mYAxis; } - /** - * Returns the object that represents all x-labels that are placed around - * the RadarChart. - * - * @return - */ - public XAxis getXAxis() { - return mXAxis; - } - /** * Sets the width of the web lines that come from the center. - * + * * @param width */ public void setWebLineWidth(float width) { @@ -249,7 +228,7 @@ public float getWebLineWidth() { /** * Sets the width of the web lines that are in between the lines coming from * the center. - * + * * @param width */ public void setWebLineWidthInner(float width) { @@ -263,7 +242,7 @@ public float getWebLineWidthInner() { /** * Sets the transparency (alpha) value for all web lines, default: 150, 255 * = 100% opaque, 0 = 100% transparent - * + * * @param alpha */ public void setWebAlpha(int alpha) { @@ -272,7 +251,7 @@ public void setWebAlpha(int alpha) { /** * Returns the alpha value for all web lines. - * + * * @return */ public int getWebAlpha() { @@ -283,7 +262,7 @@ public int getWebAlpha() { * Sets the color for the web lines that come from the center. Don't forget * to use getResources().getColor(...) when loading a color from the * resources. Default: Color.rgb(122, 122, 122) - * + * * @param color */ public void setWebColor(int color) { @@ -298,7 +277,7 @@ public int getWebColor() { * Sets the color for the web lines in between the lines that come from the * center. Don't forget to use getResources().getColor(...) when loading a * color from the resources. Default: Color.rgb(122, 122, 122) - * + * * @param color */ public void setWebColorInner(int color) { @@ -312,21 +291,43 @@ public int getWebColorInner() { /** * If set to true, drawing the web is enabled, if set to false, drawing the * whole web is disabled. Default: true - * + * * @param enabled */ public void setDrawWeb(boolean enabled) { mDrawWeb = enabled; } + /** + * Sets the number of web-lines that should be skipped on chart web before the + * next one is drawn. This targets the lines that come from the center of the RadarChart. + * + * @param count if count = 1 -> 1 line is skipped in between + */ + public void setSkipWebLineCount(int count) { + + mSkipWebLineCount = Math.max(0, count); + } + + /** + * Returns the modulus that is used for skipping web-lines. + * + * @return + */ + public int getSkipWebLineCount() { + return mSkipWebLineCount; + } + @Override - protected float getRequiredBottomOffset() { - return mLegendRenderer.getLabelPaint().getTextSize() * 6.5f; + protected float getRequiredLegendOffset() { + return mLegendRenderer.getLabelPaint().getTextSize() * 4.f; } @Override protected float getRequiredBaseOffset() { - return mXAxis.mLabelWidth; + return mXAxis.isEnabled() && mXAxis.isDrawLabelsEnabled() ? + mXAxis.mLabelRotatedWidth : + Utils.convertDpToPixel(10f); } @Override @@ -351,7 +352,7 @@ public float getYChartMin() { /** * Returns the range of y-values this chart can display. - * + * * @return */ public float getYRange() { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java new file mode 100644 index 0000000000..decf1e855a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java @@ -0,0 +1,74 @@ + +package com.github.mikephil.charting.charts; + +import android.content.Context; +import android.util.AttributeSet; + +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; +import com.github.mikephil.charting.renderer.ScatterChartRenderer; + +/** + * The ScatterChart. Draws dots, triangles, squares and custom shapes into the + * Chart-View. CIRCLE and SCQUARE offer the best performance, TRIANGLE has the + * worst performance. + * + * @author Philipp Jahoda + */ +public class ScatterChart extends BarLineChartBase implements ScatterDataProvider { + + public ScatterChart(Context context) { + super(context); + } + + public ScatterChart(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ScatterChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + + @Override + protected void init() { + super.init(); + + mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); + } + + @Override + public ScatterData getScatterData() { + return mData; + } + + /** + * Predefined ScatterShapes that allow the specification of a shape a ScatterDataSet should be drawn with. + * If a ScatterShape is specified for a ScatterDataSet, the required renderer is set. + */ + public enum ScatterShape { + + SQUARE("SQUARE"), + CIRCLE("CIRCLE"), + TRIANGLE("TRIANGLE"), + CROSS("CROSS"), + X("X"), + CHEVRON_UP("CHEVRON_UP"), + CHEVRON_DOWN("CHEVRON_DOWN"); + + private final String shapeIdentifier; + + ScatterShape(final String shapeIdentifier) { + this.shapeIdentifier = shapeIdentifier; + } + + @Override + public String toString() { + return shapeIdentifier; + } + + public static ScatterShape[] getAllDefaultShapes() { + return new ScatterShape[]{SQUARE, CIRCLE, TRIANGLE, CROSS, X, CHEVRON_UP, CHEVRON_DOWN}; + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java new file mode 100644 index 0000000000..40835f7347 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -0,0 +1,766 @@ + +package com.github.mikephil.charting.components; + +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.util.Log; + +import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.utils.Utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Base-class of all axes (previously called labels). + * + * @author Philipp Jahoda + */ +public abstract class AxisBase extends ComponentBase { + + /** + * custom formatter that is used instead of the auto-formatter if set + */ + protected IAxisValueFormatter mAxisValueFormatter; + + private int mGridColor = Color.GRAY; + + private float mGridLineWidth = 1f; + + private int mAxisLineColor = Color.GRAY; + + private float mAxisLineWidth = 1f; + + /** + * the actual array of entries + */ + public float[] mEntries = new float[]{}; + + /** + * axis label entries only used for centered labels + */ + public float[] mCenteredEntries = new float[]{}; + + /** + * the number of entries the legend contains + */ + public int mEntryCount; + + /** + * the number of decimal digits to use + */ + public int mDecimals; + + /** + * the number of label entries the axis should have, default 6 + */ + private int mLabelCount = 6; + + /** + * the minimum interval between axis values + */ + protected float mGranularity = 1.0f; + + /** + * When true, axis labels are controlled by the `granularity` property. + * When false, axis values could possibly be repeated. + * This could happen if two adjacent axis values are rounded to same value. + * If using granularity this could be avoided by having fewer axis values visible. + */ + protected boolean mGranularityEnabled = false; + + /** + * if true, the set number of y-labels will be forced + */ + protected boolean mForceLabels = false; + + /** + * flag indicating if the grid lines for this axis should be drawn + */ + protected boolean mDrawGridLines = true; + + /** + * flag that indicates if the line alongside the axis is drawn or not + */ + protected boolean mDrawAxisLine = true; + + /** + * flag that indicates of the labels of this axis should be drawn or not + */ + protected boolean mDrawLabels = true; + + protected boolean mCenterAxisLabels = false; + + /** + * the path effect of the axis line that makes dashed lines possible + */ + private DashPathEffect mAxisLineDashPathEffect = null; + + /** + * the path effect of the grid lines that makes dashed lines possible + */ + private DashPathEffect mGridDashPathEffect = null; + + /** + * array of limit lines that can be set for the axis + */ + protected List mLimitLines; + + /** + * flag indicating the limit lines layer depth + */ + protected boolean mDrawLimitLineBehindData = false; + + /** + * Extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum` + */ + protected float mSpaceMin = 0.f; + + /** + * Extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum` + */ + protected float mSpaceMax = 0.f; + + /** + * flag indicating that the axis-min value has been customized + */ + protected boolean mCustomAxisMin = false; + + /** + * flag indicating that the axis-max value has been customized + */ + protected boolean mCustomAxisMax = false; + + /** + * don't touch this direclty, use setter + */ + public float mAxisMaximum = 0f; + + /** + * don't touch this directly, use setter + */ + public float mAxisMinimum = 0f; + + /** + * the total range of values this axis covers + */ + public float mAxisRange = 0f; + + /** + * default constructor + */ + public AxisBase() { + this.mTextSize = Utils.convertDpToPixel(10f); + this.mXOffset = Utils.convertDpToPixel(5f); + this.mYOffset = Utils.convertDpToPixel(5f); + this.mLimitLines = new ArrayList(); + } + + /** + * Set this to true to enable drawing the grid lines for this axis. + * + * @param enabled + */ + public void setDrawGridLines(boolean enabled) { + mDrawGridLines = enabled; + } + + /** + * Returns true if drawing grid lines is enabled for this axis. + * + * @return + */ + public boolean isDrawGridLinesEnabled() { + return mDrawGridLines; + } + + /** + * Set this to true if the line alongside the axis should be drawn or not. + * + * @param enabled + */ + public void setDrawAxisLine(boolean enabled) { + mDrawAxisLine = enabled; + } + + /** + * Returns true if the line alongside the axis should be drawn. + * + * @return + */ + public boolean isDrawAxisLineEnabled() { + return mDrawAxisLine; + } + + /** + * Centers the axis labels instead of drawing them at their original position. + * This is useful especially for grouped BarChart. + * + * @param enabled + */ + public void setCenterAxisLabels(boolean enabled) { + mCenterAxisLabels = enabled; + } + + public boolean isCenterAxisLabelsEnabled() { + return mCenterAxisLabels && mEntryCount > 0; + } + + /** + * Sets the color of the grid lines for this axis (the horizontal lines + * coming from each label). + * + * @param color + */ + public void setGridColor(int color) { + mGridColor = color; + } + + /** + * Returns the color of the grid lines for this axis (the horizontal lines + * coming from each label). + * + * @return + */ + public int getGridColor() { + return mGridColor; + } + + /** + * Sets the width of the border surrounding the chart in dp. + * + * @param width + */ + public void setAxisLineWidth(float width) { + mAxisLineWidth = Utils.convertDpToPixel(width); + } + + /** + * Returns the width of the axis line (line alongside the axis). + * + * @return + */ + public float getAxisLineWidth() { + return mAxisLineWidth; + } + + /** + * Sets the width of the grid lines that are drawn away from each axis + * label. + * + * @param width + */ + public void setGridLineWidth(float width) { + mGridLineWidth = Utils.convertDpToPixel(width); + } + + /** + * Returns the width of the grid lines that are drawn away from each axis + * label. + * + * @return + */ + public float getGridLineWidth() { + return mGridLineWidth; + } + + /** + * Sets the color of the border surrounding the chart. + * + * @param color + */ + public void setAxisLineColor(int color) { + mAxisLineColor = color; + } + + /** + * Returns the color of the axis line (line alongside the axis). + * + * @return + */ + public int getAxisLineColor() { + return mAxisLineColor; + } + + /** + * Set this to true to enable drawing the labels of this axis (this will not + * affect drawing the grid lines or axis lines). + * + * @param enabled + */ + public void setDrawLabels(boolean enabled) { + mDrawLabels = enabled; + } + + /** + * Returns true if drawing the labels is enabled for this axis. + * + * @return + */ + public boolean isDrawLabelsEnabled() { + return mDrawLabels; + } + + /** + * Sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware + * that this number is not fixed. + * + * @param count the number of y-axis labels that should be displayed + */ + public void setLabelCount(int count) { + + if (count > 25) + count = 25; + if (count < 2) + count = 2; + + mLabelCount = count; + mForceLabels = false; + } + + /** + * sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware + * that this number is not + * fixed (if force == false) and can only be approximated. + * + * @param count the number of y-axis labels that should be displayed + * @param force if enabled, the set label count will be forced, meaning that the exact + * specified count of labels will + * be drawn and evenly distributed alongside the axis - this might cause labels + * to have uneven values + */ + public void setLabelCount(int count, boolean force) { + + setLabelCount(count); + mForceLabels = force; + } + + /** + * Returns true if focing the y-label count is enabled. Default: false + * + * @return + */ + public boolean isForceLabelsEnabled() { + return mForceLabels; + } + + /** + * Returns the number of label entries the y-axis should have + * + * @return + */ + public int getLabelCount() { + return mLabelCount; + } + + /** + * @return true if granularity is enabled + */ + public boolean isGranularityEnabled() { + return mGranularityEnabled; + } + + /** + * Enabled/disable granularity control on axis value intervals. If enabled, the axis + * interval is not allowed to go below a certain granularity. Default: false + * + * @param enabled + */ + public void setGranularityEnabled(boolean enabled) { + mGranularityEnabled = enabled; + } + + /** + * @return the minimum interval between axis values + */ + public float getGranularity() { + return mGranularity; + } + + /** + * Set a minimum interval for the axis when zooming in. The axis is not allowed to go below + * that limit. This can be used to avoid label duplicating when zooming in. + * + * @param granularity + */ + public void setGranularity(float granularity) { + mGranularity = granularity; + // set this to true if it was disabled, as it makes no sense to call this method with granularity disabled + mGranularityEnabled = true; + } + + /** + * Adds a new LimitLine to this axis. + * + * @param l + */ + public void addLimitLine(LimitLine l) { + mLimitLines.add(l); + + if (mLimitLines.size() > 6) { + Log.e("MPAndroiChart", + "Warning! You have more than 6 LimitLines on your axis, do you really want " + + "that?"); + } + } + + /** + * Removes the specified LimitLine from the axis. + * + * @param l + */ + public void removeLimitLine(LimitLine l) { + mLimitLines.remove(l); + } + + /** + * Removes all LimitLines from the axis. + */ + public void removeAllLimitLines() { + mLimitLines.clear(); + } + + /** + * Returns the LimitLines of this axis. + * + * @return + */ + public List getLimitLines() { + return mLimitLines; + } + + /** + * If this is set to true, the LimitLines are drawn behind the actual data, + * otherwise on top. Default: false + * + * @param enabled + */ + public void setDrawLimitLinesBehindData(boolean enabled) { + mDrawLimitLineBehindData = enabled; + } + + public boolean isDrawLimitLinesBehindDataEnabled() { + return mDrawLimitLineBehindData; + } + + /** + * Returns the longest formatted label (in terms of characters), this axis + * contains. + * + * @return + */ + public String getLongestLabel() { + + String longest = ""; + + for (int i = 0; i < mEntries.length; i++) { + String text = getFormattedLabel(i); + + if (text != null && longest.length() < text.length()) + longest = text; + } + + return longest; + } + + public String getFormattedLabel(int index) { + + if (index < 0 || index >= mEntries.length) + return ""; + else + return getValueFormatter().getFormattedValue(mEntries[index], this); + } + + /** + * Sets the formatter to be used for formatting the axis labels. If no formatter is set, the + * chart will + * automatically determine a reasonable formatting (concerning decimals) for all the values + * that are drawn inside + * the chart. Use chart.getDefaultValueFormatter() to use the formatter calculated by the chart. + * + * @param f + */ + public void setValueFormatter(IAxisValueFormatter f) { + + if (f == null) + mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); + else + mAxisValueFormatter = f; + } + + /** + * Returns the formatter used for formatting the axis labels. + * + * @return + */ + public IAxisValueFormatter getValueFormatter() { + + if (mAxisValueFormatter == null || + (mAxisValueFormatter instanceof DefaultAxisValueFormatter && + ((DefaultAxisValueFormatter)mAxisValueFormatter).getDecimalDigits() != mDecimals)) + mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); + + return mAxisValueFormatter; + } + + /** + * Enables the grid line to be drawn in dashed mode, e.g. like this + * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. + * Keep in mind that hardware acceleration boosts performance. + * + * @param lineLength the length of the line pieces + * @param spaceLength the length of space in between the pieces + * @param phase offset, in degrees (normally, use 0) + */ + public void enableGridDashedLine(float lineLength, float spaceLength, float phase) { + mGridDashPathEffect = new DashPathEffect(new float[]{ + lineLength, spaceLength + }, phase); + } + + /** + * Enables the grid line to be drawn in dashed mode, e.g. like this + * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. + * Keep in mind that hardware acceleration boosts performance. + * + * @param effect the DashPathEffect + */ + public void setGridDashedLine(DashPathEffect effect) { + mGridDashPathEffect = effect; + } + + /** + * Disables the grid line to be drawn in dashed mode. + */ + public void disableGridDashedLine() { + mGridDashPathEffect = null; + } + + /** + * Returns true if the grid dashed-line effect is enabled, false if not. + * + * @return + */ + public boolean isGridDashedLineEnabled() { + return mGridDashPathEffect == null ? false : true; + } + + /** + * returns the DashPathEffect that is set for grid line + * + * @return + */ + public DashPathEffect getGridDashPathEffect() { + return mGridDashPathEffect; + } + + + /** + * Enables the axis line to be drawn in dashed mode, e.g. like this + * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. + * Keep in mind that hardware acceleration boosts performance. + * + * @param lineLength the length of the line pieces + * @param spaceLength the length of space in between the pieces + * @param phase offset, in degrees (normally, use 0) + */ + public void enableAxisLineDashedLine(float lineLength, float spaceLength, float phase) { + mAxisLineDashPathEffect = new DashPathEffect(new float[]{ + lineLength, spaceLength + }, phase); + } + + /** + * Enables the axis line to be drawn in dashed mode, e.g. like this + * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. + * Keep in mind that hardware acceleration boosts performance. + * + * @param effect the DashPathEffect + */ + public void setAxisLineDashedLine(DashPathEffect effect) { + mAxisLineDashPathEffect = effect; + } + + /** + * Disables the axis line to be drawn in dashed mode. + */ + public void disableAxisLineDashedLine() { + mAxisLineDashPathEffect = null; + } + + /** + * Returns true if the axis dashed-line effect is enabled, false if not. + * + * @return + */ + public boolean isAxisLineDashedLineEnabled() { + return mAxisLineDashPathEffect == null ? false : true; + } + + /** + * returns the DashPathEffect that is set for axis line + * + * @return + */ + public DashPathEffect getAxisLineDashPathEffect() { + return mAxisLineDashPathEffect; + } + + /** + * ###### BELOW CODE RELATED TO CUSTOM AXIS VALUES ###### + */ + + public float getAxisMaximum() { + return mAxisMaximum; + } + + public float getAxisMinimum() { + return mAxisMinimum; + } + + /** + * By calling this method, any custom maximum value that has been previously set is reseted, + * and the calculation is + * done automatically. + */ + public void resetAxisMaximum() { + mCustomAxisMax = false; + } + + /** + * Returns true if the axis max value has been customized (and is not calculated automatically) + * + * @return + */ + public boolean isAxisMaxCustom() { + return mCustomAxisMax; + } + + /** + * By calling this method, any custom minimum value that has been previously set is reseted, + * and the calculation is + * done automatically. + */ + public void resetAxisMinimum() { + mCustomAxisMin = false; + } + + /** + * Returns true if the axis min value has been customized (and is not calculated automatically) + * + * @return + */ + public boolean isAxisMinCustom() { + return mCustomAxisMin; + } + + /** + * Set a custom minimum value for this axis. If set, this value will not be calculated + * automatically depending on + * the provided data. Use resetAxisMinValue() to undo this. Do not forget to call + * setStartAtZero(false) if you use + * this method. Otherwise, the axis-minimum value will still be forced to 0. + * + * @param min + */ + public void setAxisMinimum(float min) { + mCustomAxisMin = true; + mAxisMinimum = min; + this.mAxisRange = Math.abs(mAxisMaximum - min); + } + + /** + * Use setAxisMinimum(...) instead. + * + * @param min + */ + @Deprecated + public void setAxisMinValue(float min) { + setAxisMinimum(min); + } + + /** + * Set a custom maximum value for this axis. If set, this value will not be calculated + * automatically depending on + * the provided data. Use resetAxisMaxValue() to undo this. + * + * @param max + */ + public void setAxisMaximum(float max) { + mCustomAxisMax = true; + mAxisMaximum = max; + this.mAxisRange = Math.abs(max - mAxisMinimum); + } + + /** + * Use setAxisMaximum(...) instead. + * + * @param max + */ + @Deprecated + public void setAxisMaxValue(float max) { + setAxisMaximum(max); + } + + /** + * Calculates the minimum / maximum and range values of the axis with the given + * minimum and maximum values from the chart data. + * + * @param dataMin the min value according to chart data + * @param dataMax the max value according to chart data + */ + public void calculate(float dataMin, float dataMax) { + + // if custom, use value as is, else use data value + float min = mCustomAxisMin ? mAxisMinimum : (dataMin - mSpaceMin); + float max = mCustomAxisMax ? mAxisMaximum : (dataMax + mSpaceMax); + + // temporary range (before calculations) + float range = Math.abs(max - min); + + // in case all values are equal + if (range == 0f) { + max = max + 1f; + min = min - 1f; + } + + this.mAxisMinimum = min; + this.mAxisMaximum = max; + + // actual range + this.mAxisRange = Math.abs(max - min); + } + + /** + * Gets extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum` + */ + public float getSpaceMin() + { + return mSpaceMin; + } + + /** + * Sets extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum` + */ + public void setSpaceMin(float mSpaceMin) + { + this.mSpaceMin = mSpaceMin; + } + + /** + * Gets extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum` + */ + public float getSpaceMax() + { + return mSpaceMax; + } + + /** + * Sets extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum` + */ + public void setSpaceMax(float mSpaceMax) + { + this.mSpaceMax = mSpaceMax; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java similarity index 74% rename from MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java index bdcaa4a56e..d3a1d4d82a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java @@ -7,30 +7,43 @@ import com.github.mikephil.charting.utils.Utils; /** - * This class encapsulates everything both Axis and Legend have in common. - * + * This class encapsulates everything both Axis, Legend and LimitLines have in common. + * * @author Philipp Jahoda */ public abstract class ComponentBase { - /** flag that indicates if this axis / legend is enabled or not */ + /** + * flag that indicates if this axis / legend is enabled or not + */ protected boolean mEnabled = true; - /** the offset in pixels this axis labels have on the x-axis */ + /** + * the offset in pixels this component has on the x-axis + */ protected float mXOffset = 5f; - /** the offset in pixels this axis labels have on the Y-axis */ + /** + * the offset in pixels this component has on the Y-axis + */ protected float mYOffset = 5f; - /** the typeface used for the labels */ + /** + * the typeface used for the labels + */ protected Typeface mTypeface = null; - /** the text size of the labels */ - protected float mTextSize = 10f; + /** + * the text size of the labels + */ + protected float mTextSize = Utils.convertDpToPixel(10f); - /** the text color to use for the labels */ + /** + * the text color to use for the labels + */ protected int mTextColor = Color.BLACK; + public ComponentBase() { } @@ -38,7 +51,7 @@ public ComponentBase() { /** * Returns the used offset on the x-axis for drawing the axis or legend * labels. This offset is applied before and after the label. - * + * * @return */ public float getXOffset() { @@ -47,7 +60,7 @@ public float getXOffset() { /** * Sets the used x-axis offset for the labels on this axis. - * + * * @param xOffset */ public void setXOffset(float xOffset) { @@ -57,7 +70,7 @@ public void setXOffset(float xOffset) { /** * Returns the used offset on the x-axis for drawing the axis labels. This * offset is applied before and after the label. - * + * * @return */ public float getYOffset() { @@ -65,9 +78,11 @@ public float getYOffset() { } /** - * Sets the used x-axis offset for the labels on this axis. - * - * @param xOffset + * Sets the used y-axis offset for the labels on this axis. For the legend, + * higher offset means the legend as a whole will be placed further away + * from the top. + * + * @param yOffset */ public void setYOffset(float yOffset) { mYOffset = Utils.convertDpToPixel(yOffset); @@ -75,7 +90,7 @@ public void setYOffset(float yOffset) { /** * returns the Typeface used for the labels, returns null if none is set - * + * * @return */ public Typeface getTypeface() { @@ -84,7 +99,7 @@ public Typeface getTypeface() { /** * sets a specific Typeface for the labels - * + * * @param tf */ public void setTypeface(Typeface tf) { @@ -92,10 +107,10 @@ public void setTypeface(Typeface tf) { } /** - * sets the size of the label text in pixels min = 6f, max = 24f, default + * sets the size of the label text in density pixels min = 6f, max = 24f, default * 10f - * - * @param size + * + * @param size the text size, in DP */ public void setTextSize(float size) { @@ -108,18 +123,19 @@ public void setTextSize(float size) { } /** - * returns the text size that is currently set for the labels - * + * returns the text size that is currently set for the labels, in pixels + * * @return */ public float getTextSize() { return mTextSize; } + /** * Sets the text color to use for the labels. Make sure to use * getResources().getColor(...) when using a color from the resources. - * + * * @param color */ public void setTextColor(int color) { @@ -128,7 +144,7 @@ public void setTextColor(int color) { /** * Returns the text color that is set for the labels. - * + * * @return */ public int getTextColor() { @@ -139,7 +155,7 @@ public int getTextColor() { * Set this to true if this component should be enabled (should be drawn), * false if not. If disabled, nothing of this component will be drawn. * Default: true - * + * * @param enabled */ public void setEnabled(boolean enabled) { @@ -148,7 +164,7 @@ public void setEnabled(boolean enabled) { /** * Returns true if this comonent is enabled (should be drawn), false if not. - * + * * @return */ public boolean isEnabled() { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java new file mode 100644 index 0000000000..18294a3270 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java @@ -0,0 +1,95 @@ +package com.github.mikephil.charting.components; + +import android.graphics.Paint; + +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; + +/** + * Created by Philipp Jahoda on 17/09/16. + */ +public class Description extends ComponentBase { + + /** + * the text used in the description + */ + private String text = "Description Label"; + + /** + * the custom position of the description text + */ + private MPPointF mPosition; + + /** + * the alignment of the description text + */ + private Paint.Align mTextAlign = Paint.Align.RIGHT; + + public Description() { + super(); + + // default size + mTextSize = Utils.convertDpToPixel(8f); + } + + /** + * Sets the text to be shown as the description. + * Never set this to null as this will cause nullpointer exception when drawing with Android Canvas. + * + * @param text + */ + public void setText(String text) { + this.text = text; + } + + /** + * Returns the description text. + * + * @return + */ + public String getText() { + return text; + } + + /** + * Sets a custom position for the description text in pixels on the screen. + * + * @param x - xcoordinate + * @param y - ycoordinate + */ + public void setPosition(float x, float y) { + if (mPosition == null) { + mPosition = MPPointF.getInstance(x, y); + } else { + mPosition.x = x; + mPosition.y = y; + } + } + + /** + * Returns the customized position of the description, or null if none set. + * + * @return + */ + public MPPointF getPosition() { + return mPosition; + } + + /** + * Sets the text alignment of the description text. Default RIGHT. + * + * @param align + */ + public void setTextAlign(Paint.Align align) { + this.mTextAlign = align; + } + + /** + * Returns the text alignment of the description. + * + * @return + */ + public Paint.Align getTextAlign() { + return mTextAlign; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java new file mode 100644 index 0000000000..3b8ca43c81 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java @@ -0,0 +1,47 @@ +package com.github.mikephil.charting.components; + +import android.graphics.Canvas; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; + +public interface IMarker { + + /** + * @return The desired (general) offset you wish the IMarker to have on the x- and y-axis. + * By returning x: -(width / 2) you will center the IMarker horizontally. + * By returning y: -(height / 2) you will center the IMarker vertically. + */ + MPPointF getOffset(); + + /** + * @return The offset for drawing at the specific `point`. This allows conditional adjusting of the Marker position. + * If you have no adjustments to make, return getOffset(). + * + * @param posX This is the X position at which the marker wants to be drawn. + * You can adjust the offset conditionally based on this argument. + * @param posY This is the X position at which the marker wants to be drawn. + * You can adjust the offset conditionally based on this argument. + */ + MPPointF getOffsetForDrawingAtPoint(float posX, float posY); + + /** + * This method enables a specified custom IMarker to update it's content every time the IMarker is redrawn. + * + * @param e The Entry the IMarker belongs to. This can also be any subclass of Entry, like BarEntry or + * CandleEntry, simply cast it at runtime. + * @param highlight The highlight object contains information about the highlighted value such as it's dataset-index, the + * selected range or stack-index (only stacked bar entries). + */ + void refreshContent(Entry e, Highlight highlight); + + /** + * Draws the IMarker on the given position on the screen with the given Canvas object. + * + * @param canvas + * @param posX + * @param posY + */ + void draw(Canvas canvas, float posX, float posY); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java new file mode 100644 index 0000000000..fb1afc8bba --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java @@ -0,0 +1,1030 @@ + +package com.github.mikephil.charting.components; + +import android.graphics.DashPathEffect; +import android.graphics.Paint; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class representing the legend of the chart. The legend will contain one entry + * per color and DataSet. Multiple colors in one DataSet are grouped together. + * The legend object is NOT available before setting data to the chart. + * + * @author Philipp Jahoda + */ +public class Legend extends ComponentBase { + + /** + * This property is deprecated - Use `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, + * `direction`. + */ + @Deprecated + public enum LegendPosition { + RIGHT_OF_CHART, RIGHT_OF_CHART_CENTER, RIGHT_OF_CHART_INSIDE, + LEFT_OF_CHART, LEFT_OF_CHART_CENTER, LEFT_OF_CHART_INSIDE, + BELOW_CHART_LEFT, BELOW_CHART_RIGHT, BELOW_CHART_CENTER, + ABOVE_CHART_LEFT, ABOVE_CHART_RIGHT, ABOVE_CHART_CENTER, + PIECHART_CENTER + } + + public enum LegendForm { + /** + * Avoid drawing a form + */ + NONE, + + /** + * Do not draw the a form, but leave space for it + */ + EMPTY, + + /** + * Use default (default dataset's form to the legend's form) + */ + DEFAULT, + + /** + * Draw a square + */ + SQUARE, + + /** + * Draw a circle + */ + CIRCLE, + + /** + * Draw a horizontal line + */ + LINE + } + + public enum LegendHorizontalAlignment { + LEFT, CENTER, RIGHT + } + + public enum LegendVerticalAlignment { + TOP, CENTER, BOTTOM + } + + public enum LegendOrientation { + HORIZONTAL, VERTICAL + } + + public enum LegendDirection { + LEFT_TO_RIGHT, RIGHT_TO_LEFT + } + + /** + * The legend entries array + */ + private LegendEntry[] mEntries = new LegendEntry[]{}; + + /** + * Entries that will be appended to the end of the auto calculated entries after calculating the legend. + * (if the legend has already been calculated, you will need to call notifyDataSetChanged() to let the changes take effect) + */ + private LegendEntry[] mExtraEntries; + + /** + * Are the legend labels/colors a custom value or auto calculated? If false, + * then it's auto, if true, then custom. default false (automatic legend) + */ + private boolean mIsLegendCustom = false; + + private LegendHorizontalAlignment mHorizontalAlignment = LegendHorizontalAlignment.LEFT; + private LegendVerticalAlignment mVerticalAlignment = LegendVerticalAlignment.BOTTOM; + private LegendOrientation mOrientation = LegendOrientation.HORIZONTAL; + private boolean mDrawInside = false; + + /** + * the text direction for the legend + */ + private LegendDirection mDirection = LegendDirection.LEFT_TO_RIGHT; + + /** + * the shape/form the legend colors are drawn in + */ + private LegendForm mShape = LegendForm.SQUARE; + + /** + * the size of the legend forms/shapes + */ + private float mFormSize = 8f; + + /** + * the size of the legend forms/shapes + */ + private float mFormLineWidth = 3f; + + /** + * Line dash path effect used for shapes that consist of lines. + */ + private DashPathEffect mFormLineDashEffect = null; + + /** + * the space between the legend entries on a horizontal axis, default 6f + */ + private float mXEntrySpace = 6f; + + /** + * the space between the legend entries on a vertical axis, default 5f + */ + private float mYEntrySpace = 0f; + + /** + * the space between the legend entries on a vertical axis, default 2f + * private float mYEntrySpace = 2f; /** the space between the form and the + * actual label/text + */ + private float mFormToTextSpace = 5f; + + /** + * the space that should be left between stacked forms + */ + private float mStackSpace = 3f; + + /** + * the maximum relative size out of the whole chart view in percent + */ + private float mMaxSizePercent = 0.95f; + + /** + * default constructor + */ + public Legend() { + + this.mTextSize = Utils.convertDpToPixel(10f); + this.mXOffset = Utils.convertDpToPixel(5f); + this.mYOffset = Utils.convertDpToPixel(3f); // 2 + } + + /** + * Constructor. Provide entries for the legend. + * + * @param entries + */ + public Legend(LegendEntry[] entries) { + this(); + + if (entries == null) { + throw new IllegalArgumentException("entries array is NULL"); + } + + this.mEntries = entries; + } + + @Deprecated + public Legend(int[] colors, String[] labels) { + this(); + + if (colors == null || labels == null) { + throw new IllegalArgumentException("colors array or labels array is NULL"); + } + + if (colors.length != labels.length) { + throw new IllegalArgumentException( + "colors array and labels array need to be of same size"); + } + + List entries = new ArrayList<>(); + + for (int i = 0; i < Math.min(colors.length, labels.length); i++) { + final LegendEntry entry = new LegendEntry(); + entry.formColor = colors[i]; + entry.label = labels[i]; + + if (entry.formColor == ColorTemplate.COLOR_SKIP) + entry.form = LegendForm.NONE; + else if (entry.formColor == ColorTemplate.COLOR_NONE || + entry.formColor == 0) + entry.form = LegendForm.EMPTY; + + entries.add(entry); + } + + mEntries = entries.toArray(new LegendEntry[entries.size()]); + } + + @Deprecated + public Legend(List colors, List labels) { + this(Utils.convertIntegers(colors), Utils.convertStrings(labels)); + } + + /** + * This method sets the automatically computed colors for the legend. Use setCustom(...) to set custom colors. + * + * @param entries + */ + public void setEntries(List entries) { + mEntries = entries.toArray(new LegendEntry[entries.size()]); + } + + public LegendEntry[] getEntries() { + return mEntries; + } + + /** + * returns the maximum length in pixels across all legend labels + formsize + * + formtotextspace + * + * @param p the paint object used for rendering the text + * @return + */ + public float getMaximumEntryWidth(Paint p) { + + float max = 0f; + float maxFormSize = 0f; + float formToTextSpace = Utils.convertDpToPixel(mFormToTextSpace); + + for (LegendEntry entry : mEntries) { + final float formSize = Utils.convertDpToPixel( + Float.isNaN(entry.formSize) + ? mFormSize : entry.formSize); + if (formSize > maxFormSize) + maxFormSize = formSize; + + String label = entry.label; + if (label == null) continue; + + float length = (float) Utils.calcTextWidth(p, label); + + if (length > max) + max = length; + } + + return max + maxFormSize + formToTextSpace; + } + + /** + * returns the maximum height in pixels across all legend labels + * + * @param p the paint object used for rendering the text + * @return + */ + public float getMaximumEntryHeight(Paint p) { + + float max = 0f; + + for (LegendEntry entry : mEntries) { + String label = entry.label; + if (label == null) continue; + + float length = (float) Utils.calcTextHeight(p, label); + + if (length > max) + max = length; + } + + return max; + } + + @Deprecated + public int[] getColors() { + + int[] old = new int[mEntries.length]; + for (int i = 0; i < mEntries.length; i++) { + old[i] = mEntries[i].form == LegendForm.NONE ? ColorTemplate.COLOR_SKIP : + (mEntries[i].form == LegendForm.EMPTY ? ColorTemplate.COLOR_NONE : + mEntries[i].formColor); + } + return old; + } + + @Deprecated + public String[] getLabels() { + + String[] old = new String[mEntries.length]; + for (int i = 0; i < mEntries.length; i++) { + old[i] = mEntries[i].label; + } + return old; + } + + @Deprecated + public int[] getExtraColors() { + + int[] old = new int[mExtraEntries.length]; + for (int i = 0; i < mExtraEntries.length; i++) { + old[i] = mExtraEntries[i].form == LegendForm.NONE ? ColorTemplate.COLOR_SKIP : + (mExtraEntries[i].form == LegendForm.EMPTY ? ColorTemplate.COLOR_NONE : + mExtraEntries[i].formColor); + } + return old; + } + + @Deprecated + public String[] getExtraLabels() { + + String[] old = new String[mExtraEntries.length]; + for (int i = 0; i < mExtraEntries.length; i++) { + old[i] = mExtraEntries[i].label; + } + return old; + } + + public LegendEntry[] getExtraEntries() { + + return mExtraEntries; + } + + public void setExtra(List entries) { + mExtraEntries = entries.toArray(new LegendEntry[entries.size()]); + } + + public void setExtra(LegendEntry[] entries) { + if (entries == null) + entries = new LegendEntry[]{}; + mExtraEntries = entries; + } + + @Deprecated + public void setExtra(List colors, List labels) { + setExtra(Utils.convertIntegers(colors), Utils.convertStrings(labels)); + } + + /** + * Entries that will be appended to the end of the auto calculated + * entries after calculating the legend. + * (if the legend has already been calculated, you will need to call notifyDataSetChanged() + * to let the changes take effect) + */ + public void setExtra(int[] colors, String[] labels) { + + List entries = new ArrayList<>(); + + for (int i = 0; i < Math.min(colors.length, labels.length); i++) { + final LegendEntry entry = new LegendEntry(); + entry.formColor = colors[i]; + entry.label = labels[i]; + + if (entry.formColor == ColorTemplate.COLOR_SKIP || + entry.formColor == 0) + entry.form = LegendForm.NONE; + else if (entry.formColor == ColorTemplate.COLOR_NONE) + entry.form = LegendForm.EMPTY; + + entries.add(entry); + } + + mExtraEntries = entries.toArray(new LegendEntry[entries.size()]); + } + + /** + * Sets a custom legend's entries array. + * * A null label will start a group. + * This will disable the feature that automatically calculates the legend + * entries from the datasets. + * Call resetCustom() to re-enable automatic calculation (and then + * notifyDataSetChanged() is needed to auto-calculate the legend again) + */ + public void setCustom(LegendEntry[] entries) { + + mEntries = entries; + mIsLegendCustom = true; + } + + /** + * Sets a custom legend's entries array. + * * A null label will start a group. + * This will disable the feature that automatically calculates the legend + * entries from the datasets. + * Call resetCustom() to re-enable automatic calculation (and then + * notifyDataSetChanged() is needed to auto-calculate the legend again) + */ + public void setCustom(List entries) { + + mEntries = entries.toArray(new LegendEntry[entries.size()]); + mIsLegendCustom = true; + } + + /** + * Calling this will disable the custom legend entries (set by + * setCustom(...)). Instead, the entries will again be calculated + * automatically (after notifyDataSetChanged() is called). + */ + public void resetCustom() { + mIsLegendCustom = false; + } + + /** + * @return true if a custom legend entries has been set default + * false (automatic legend) + */ + public boolean isLegendCustom() { + return mIsLegendCustom; + } + + /** + * This property is deprecated - Use `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, + * `direction`. + */ + @Deprecated + public LegendPosition getPosition() { + + if (mOrientation == LegendOrientation.VERTICAL + && mHorizontalAlignment == LegendHorizontalAlignment.CENTER + && mVerticalAlignment == LegendVerticalAlignment.CENTER) { + return LegendPosition.PIECHART_CENTER; + } else if (mOrientation == LegendOrientation.HORIZONTAL) { + if (mVerticalAlignment == LegendVerticalAlignment.TOP) + return mHorizontalAlignment == LegendHorizontalAlignment.LEFT + ? LegendPosition.ABOVE_CHART_LEFT + : (mHorizontalAlignment == LegendHorizontalAlignment.RIGHT + ? LegendPosition.ABOVE_CHART_RIGHT + : LegendPosition.ABOVE_CHART_CENTER); + else + return mHorizontalAlignment == LegendHorizontalAlignment.LEFT + ? LegendPosition.BELOW_CHART_LEFT + : (mHorizontalAlignment == LegendHorizontalAlignment.RIGHT + ? LegendPosition.BELOW_CHART_RIGHT + : LegendPosition.BELOW_CHART_CENTER); + } else { + if (mHorizontalAlignment == LegendHorizontalAlignment.LEFT) + return mVerticalAlignment == LegendVerticalAlignment.TOP && mDrawInside + ? LegendPosition.LEFT_OF_CHART_INSIDE + : (mVerticalAlignment == LegendVerticalAlignment.CENTER + ? LegendPosition.LEFT_OF_CHART_CENTER + : LegendPosition.LEFT_OF_CHART); + else + return mVerticalAlignment == LegendVerticalAlignment.TOP && mDrawInside + ? LegendPosition.RIGHT_OF_CHART_INSIDE + : (mVerticalAlignment == LegendVerticalAlignment.CENTER + ? LegendPosition.RIGHT_OF_CHART_CENTER + : LegendPosition.RIGHT_OF_CHART); + } + } + + /** + * This property is deprecated - Use `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, + * `direction`. + */ + @Deprecated + public void setPosition(LegendPosition newValue) { + + switch (newValue) { + case LEFT_OF_CHART: + case LEFT_OF_CHART_INSIDE: + case LEFT_OF_CHART_CENTER: + mHorizontalAlignment = LegendHorizontalAlignment.LEFT; + mVerticalAlignment = newValue == LegendPosition.LEFT_OF_CHART_CENTER + ? LegendVerticalAlignment.CENTER + : LegendVerticalAlignment.TOP; + mOrientation = LegendOrientation.VERTICAL; + break; + + case RIGHT_OF_CHART: + case RIGHT_OF_CHART_INSIDE: + case RIGHT_OF_CHART_CENTER: + mHorizontalAlignment = LegendHorizontalAlignment.RIGHT; + mVerticalAlignment = newValue == LegendPosition.RIGHT_OF_CHART_CENTER + ? LegendVerticalAlignment.CENTER + : LegendVerticalAlignment.TOP; + mOrientation = LegendOrientation.VERTICAL; + break; + + case ABOVE_CHART_LEFT: + case ABOVE_CHART_CENTER: + case ABOVE_CHART_RIGHT: + mHorizontalAlignment = newValue == LegendPosition.ABOVE_CHART_LEFT + ? LegendHorizontalAlignment.LEFT + : (newValue == LegendPosition.ABOVE_CHART_RIGHT + ? LegendHorizontalAlignment.RIGHT + : LegendHorizontalAlignment.CENTER); + mVerticalAlignment = LegendVerticalAlignment.TOP; + mOrientation = LegendOrientation.HORIZONTAL; + break; + + case BELOW_CHART_LEFT: + case BELOW_CHART_CENTER: + case BELOW_CHART_RIGHT: + mHorizontalAlignment = newValue == LegendPosition.BELOW_CHART_LEFT + ? LegendHorizontalAlignment.LEFT + : (newValue == LegendPosition.BELOW_CHART_RIGHT + ? LegendHorizontalAlignment.RIGHT + : LegendHorizontalAlignment.CENTER); + mVerticalAlignment = LegendVerticalAlignment.BOTTOM; + mOrientation = LegendOrientation.HORIZONTAL; + break; + + case PIECHART_CENTER: + mHorizontalAlignment = LegendHorizontalAlignment.CENTER; + mVerticalAlignment = LegendVerticalAlignment.CENTER; + mOrientation = LegendOrientation.VERTICAL; + break; + } + + mDrawInside = newValue == LegendPosition.LEFT_OF_CHART_INSIDE + || newValue == LegendPosition.RIGHT_OF_CHART_INSIDE; + } + + /** + * returns the horizontal alignment of the legend + * + * @return + */ + public LegendHorizontalAlignment getHorizontalAlignment() { + return mHorizontalAlignment; + } + + /** + * sets the horizontal alignment of the legend + * + * @param value + */ + public void setHorizontalAlignment(LegendHorizontalAlignment value) { + mHorizontalAlignment = value; + } + + /** + * returns the vertical alignment of the legend + * + * @return + */ + public LegendVerticalAlignment getVerticalAlignment() { + return mVerticalAlignment; + } + + /** + * sets the vertical alignment of the legend + * + * @param value + */ + public void setVerticalAlignment(LegendVerticalAlignment value) { + mVerticalAlignment = value; + } + + /** + * returns the orientation of the legend + * + * @return + */ + public LegendOrientation getOrientation() { + return mOrientation; + } + + /** + * sets the orientation of the legend + * + * @param value + */ + public void setOrientation(LegendOrientation value) { + mOrientation = value; + } + + /** + * returns whether the legend will draw inside the chart or outside + * + * @return + */ + public boolean isDrawInsideEnabled() { + return mDrawInside; + } + + /** + * sets whether the legend will draw inside the chart or outside + * + * @param value + */ + public void setDrawInside(boolean value) { + mDrawInside = value; + } + + /** + * returns the text direction of the legend + * + * @return + */ + public LegendDirection getDirection() { + return mDirection; + } + + /** + * sets the text direction of the legend + * + * @param pos + */ + public void setDirection(LegendDirection pos) { + mDirection = pos; + } + + /** + * returns the current form/shape that is set for the legend + * + * @return + */ + public LegendForm getForm() { + return mShape; + } + + /** + * sets the form/shape of the legend forms + * + * @param shape + */ + public void setForm(LegendForm shape) { + mShape = shape; + } + + /** + * sets the size in dp of the legend forms, default 8f + * + * @param size + */ + public void setFormSize(float size) { + mFormSize = size; + } + + /** + * returns the size in dp of the legend forms + * + * @return + */ + public float getFormSize() { + return mFormSize; + } + + /** + * sets the line width in dp for forms that consist of lines, default 3f + * + * @param size + */ + public void setFormLineWidth(float size) { + mFormLineWidth = size; + } + + /** + * returns the line width in dp for drawing forms that consist of lines + * + * @return + */ + public float getFormLineWidth() { + return mFormLineWidth; + } + + /** + * Sets the line dash path effect used for shapes that consist of lines. + * + * @param dashPathEffect + */ + public void setFormLineDashEffect(DashPathEffect dashPathEffect) { + mFormLineDashEffect = dashPathEffect; + } + + /** + * @return The line dash path effect used for shapes that consist of lines. + */ + public DashPathEffect getFormLineDashEffect() { + return mFormLineDashEffect; + } + + /** + * returns the space between the legend entries on a horizontal axis in + * pixels + * + * @return + */ + public float getXEntrySpace() { + return mXEntrySpace; + } + + /** + * sets the space between the legend entries on a horizontal axis in pixels, + * converts to dp internally + * + * @param space + */ + public void setXEntrySpace(float space) { + mXEntrySpace = space; + } + + /** + * returns the space between the legend entries on a vertical axis in pixels + * + * @return + */ + public float getYEntrySpace() { + return mYEntrySpace; + } + + /** + * sets the space between the legend entries on a vertical axis in pixels, + * converts to dp internally + * + * @param space + */ + public void setYEntrySpace(float space) { + mYEntrySpace = space; + } + + /** + * returns the space between the form and the actual label/text + * + * @return + */ + public float getFormToTextSpace() { + return mFormToTextSpace; + } + + /** + * sets the space between the form and the actual label/text, converts to dp + * internally + * + * @param space + */ + public void setFormToTextSpace(float space) { + this.mFormToTextSpace = space; + } + + /** + * returns the space that is left out between stacked forms (with no label) + * + * @return + */ + public float getStackSpace() { + return mStackSpace; + } + + /** + * sets the space that is left out between stacked forms (with no label) + * + * @param space + */ + public void setStackSpace(float space) { + mStackSpace = space; + } + + /** + * the total width of the legend (needed width space) + */ + public float mNeededWidth = 0f; + + /** + * the total height of the legend (needed height space) + */ + public float mNeededHeight = 0f; + + public float mTextHeightMax = 0f; + + public float mTextWidthMax = 0f; + + /** + * flag that indicates if word wrapping is enabled + */ + private boolean mWordWrapEnabled = false; + + /** + * Should the legend word wrap? / this is currently supported only for: + * BelowChartLeft, BelowChartRight, BelowChartCenter. / note that word + * wrapping a legend takes a toll on performance. / you may want to set + * maxSizePercent when word wrapping, to set the point where the text wraps. + * / default: false + * + * @param enabled + */ + public void setWordWrapEnabled(boolean enabled) { + mWordWrapEnabled = enabled; + } + + /** + * If this is set, then word wrapping the legend is enabled. This means the + * legend will not be cut off if too long. + * + * @return + */ + public boolean isWordWrapEnabled() { + return mWordWrapEnabled; + } + + /** + * The maximum relative size out of the whole chart view. / If the legend is + * to the right/left of the chart, then this affects the width of the + * legend. / If the legend is to the top/bottom of the chart, then this + * affects the height of the legend. / If the legend is the center of the + * piechart, then this defines the size of the rectangular bounds out of the + * size of the "hole". / default: 0.95f (95%) + * + * @return + */ + public float getMaxSizePercent() { + return mMaxSizePercent; + } + + /** + * The maximum relative size out of the whole chart view. / If + * the legend is to the right/left of the chart, then this affects the width + * of the legend. / If the legend is to the top/bottom of the chart, then + * this affects the height of the legend. / default: 0.95f (95%) + * + * @param maxSize + */ + public void setMaxSizePercent(float maxSize) { + mMaxSizePercent = maxSize; + } + + private List mCalculatedLabelSizes = new ArrayList<>(16); + private List mCalculatedLabelBreakPoints = new ArrayList<>(16); + private List mCalculatedLineSizes = new ArrayList<>(16); + + public List getCalculatedLabelSizes() { + return mCalculatedLabelSizes; + } + + public List getCalculatedLabelBreakPoints() { + return mCalculatedLabelBreakPoints; + } + + public List getCalculatedLineSizes() { + return mCalculatedLineSizes; + } + + /** + * Calculates the dimensions of the Legend. This includes the maximum width + * and height of a single entry, as well as the total width and height of + * the Legend. + * + * @param labelpaint + */ + public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandler) { + + float defaultFormSize = Utils.convertDpToPixel(mFormSize); + float stackSpace = Utils.convertDpToPixel(mStackSpace); + float formToTextSpace = Utils.convertDpToPixel(mFormToTextSpace); + float xEntrySpace = Utils.convertDpToPixel(mXEntrySpace); + float yEntrySpace = Utils.convertDpToPixel(mYEntrySpace); + boolean wordWrapEnabled = mWordWrapEnabled; + LegendEntry[] entries = mEntries; + int entryCount = entries.length; + + mTextWidthMax = getMaximumEntryWidth(labelpaint); + mTextHeightMax = getMaximumEntryHeight(labelpaint); + + switch (mOrientation) { + case VERTICAL: { + + float maxWidth = 0f, maxHeight = 0f, width = 0f; + float labelLineHeight = Utils.getLineHeight(labelpaint); + boolean wasStacked = false; + + for (int i = 0; i < entryCount; i++) { + + LegendEntry e = entries[i]; + boolean drawingForm = e.form != LegendForm.NONE; + float formSize = Float.isNaN(e.formSize) + ? defaultFormSize + : Utils.convertDpToPixel(e.formSize); + String label = e.label; + + if (!wasStacked) + width = 0.f; + + if (drawingForm) { + if (wasStacked) + width += stackSpace; + width += formSize; + } + + // grouped forms have null labels + if (label != null) { + + // make a step to the left + if (drawingForm && !wasStacked) + width += formToTextSpace; + else if (wasStacked) { + maxWidth = Math.max(maxWidth, width); + maxHeight += labelLineHeight + yEntrySpace; + width = 0.f; + wasStacked = false; + } + + width += Utils.calcTextWidth(labelpaint, label); + + if (i < entryCount - 1) + maxHeight += labelLineHeight + yEntrySpace; + } else { + wasStacked = true; + width += formSize; + if (i < entryCount - 1) + width += stackSpace; + } + + maxWidth = Math.max(maxWidth, width); + } + + mNeededWidth = maxWidth; + mNeededHeight = maxHeight; + + break; + } + case HORIZONTAL: { + + float labelLineHeight = Utils.getLineHeight(labelpaint); + float labelLineSpacing = Utils.getLineSpacing(labelpaint) + yEntrySpace; + float contentWidth = viewPortHandler.contentWidth() * mMaxSizePercent; + + // Start calculating layout + float maxLineWidth = 0.f; + float currentLineWidth = 0.f; + float requiredWidth = 0.f; + int stackedStartIndex = -1; + + mCalculatedLabelBreakPoints.clear(); + mCalculatedLabelSizes.clear(); + mCalculatedLineSizes.clear(); + + for (int i = 0; i < entryCount; i++) { + + LegendEntry e = entries[i]; + boolean drawingForm = e.form != LegendForm.NONE; + float formSize = Float.isNaN(e.formSize) + ? defaultFormSize + : Utils.convertDpToPixel(e.formSize); + String label = e.label; + + mCalculatedLabelBreakPoints.add(false); + + if (stackedStartIndex == -1) { + // we are not stacking, so required width is for this label + // only + requiredWidth = 0.f; + } else { + // add the spacing appropriate for stacked labels/forms + requiredWidth += stackSpace; + } + + // grouped forms have null labels + if (label != null) { + + mCalculatedLabelSizes.add(Utils.calcTextSize(labelpaint, label)); + requiredWidth += drawingForm ? formToTextSpace + formSize : 0.f; + requiredWidth += mCalculatedLabelSizes.get(i).width; + } else { + + mCalculatedLabelSizes.add(FSize.getInstance(0.f, 0.f)); + requiredWidth += drawingForm ? formSize : 0.f; + + if (stackedStartIndex == -1) { + // mark this index as we might want to break here later + stackedStartIndex = i; + } + } + + if (label != null || i == entryCount - 1) { + + float requiredSpacing = currentLineWidth == 0.f ? 0.f : xEntrySpace; + + if (!wordWrapEnabled // No word wrapping, it must fit. + // The line is empty, it must fit + || currentLineWidth == 0.f + // It simply fits + || (contentWidth - currentLineWidth >= + requiredSpacing + requiredWidth)) { + // Expand current line + currentLineWidth += requiredSpacing + requiredWidth; + } else { // It doesn't fit, we need to wrap a line + + // Add current line size to array + mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight)); + maxLineWidth = Math.max(maxLineWidth, currentLineWidth); + + // Start a new line + mCalculatedLabelBreakPoints.set( + stackedStartIndex > -1 ? stackedStartIndex + : i, true); + currentLineWidth = requiredWidth; + } + + if (i == entryCount - 1) { + // Add last line size to array + mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight)); + maxLineWidth = Math.max(maxLineWidth, currentLineWidth); + } + } + + stackedStartIndex = label != null ? -1 : stackedStartIndex; + } + + mNeededWidth = maxLineWidth; + mNeededHeight = labelLineHeight + * (float) (mCalculatedLineSizes.size()) + + labelLineSpacing * + (float) (mCalculatedLineSizes.size() == 0 + ? 0 + : (mCalculatedLineSizes.size() - 1)); + + break; + } + } + + mNeededHeight += mYOffset; + mNeededWidth += mXOffset; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java new file mode 100644 index 0000000000..3acec0f461 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java @@ -0,0 +1,78 @@ +package com.github.mikephil.charting.components; + + +import android.graphics.DashPathEffect; + +import com.github.mikephil.charting.utils.ColorTemplate; + +public class LegendEntry { + public LegendEntry() { + + } + + /** + * + * @param label The legend entry text. A `null` label will start a group. + * @param form The form to draw for this entry. + * @param formSize Set to NaN to use the legend's default. + * @param formLineWidth Set to NaN to use the legend's default. + * @param formLineDashEffect Set to nil to use the legend's default. + * @param formColor The color for drawing the form. + */ + public LegendEntry(String label, + Legend.LegendForm form, + float formSize, + float formLineWidth, + DashPathEffect formLineDashEffect, + int formColor) + { + this.label = label; + this.form = form; + this.formSize = formSize; + this.formLineWidth = formLineWidth; + this.formLineDashEffect = formLineDashEffect; + this.formColor = formColor; + } + + /** + * The legend entry text. + * A `null` label will start a group. + */ + public String label; + + /** + * The form to draw for this entry. + * + * `NONE` will avoid drawing a form, and any related space. + * `EMPTY` will avoid drawing a form, but keep its space. + * `DEFAULT` will use the Legend's default. + */ + public Legend.LegendForm form = Legend.LegendForm.DEFAULT; + + /** + * Form size will be considered except for when .None is used + * + * Set as NaN to use the legend's default + */ + public float formSize = Float.NaN; + + /** + * Line width used for shapes that consist of lines. + * + * Set as NaN to use the legend's default + */ + public float formLineWidth = Float.NaN; + + /** + * Line dash path effect used for shapes that consist of lines. + * + * Set to null to use the legend's default + */ + public DashPathEffect formLineDashEffect = null; + + /** + * The color for drawing the form + */ + public int formColor = ColorTemplate.COLOR_NONE; + +} \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/components/LimitLine.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java similarity index 81% rename from MPChartLib/src/com/github/mikephil/charting/components/LimitLine.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java index 2f00ef7a80..8fcdee8fc1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/LimitLine.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java @@ -3,19 +3,21 @@ import android.graphics.Color; import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.graphics.Typeface; import com.github.mikephil.charting.utils.Utils; /** * The limit line is an additional feature for all Line-, Bar- and * ScatterCharts. It allows the displaying of an additional line in the chart - * that marks a certain maximum / limit. + * that marks a certain maximum / limit on the specified axis (x- or y-axis). * * @author Philipp Jahoda */ -public class LimitLine { +public class LimitLine extends ComponentBase { - /** limit / maximum (the y-position) */ + /** limit / maximum (the y-value or xIndex) */ private float mLimit = 0f; /** the width of the limit line */ @@ -24,11 +26,8 @@ public class LimitLine { /** the color of the limit line */ private int mLineColor = Color.rgb(237, 91, 91); - /** the color of the value-text */ - private int mValueTextColor = Color.BLACK; - - /** the size of the label text */ - private float mTextSize = 13f; + /** the style of the label text */ + private Paint.Style mTextStyle = Paint.Style.FILL_AND_STROKE; /** label string that is drawn next to the limit line */ private String mLabel = ""; @@ -37,18 +36,18 @@ public class LimitLine { private DashPathEffect mDashPathEffect = null; /** indicates the position of the LimitLine label */ - private LimitLabelPosition mLabelPosition = LimitLabelPosition.POS_RIGHT; + private LimitLabelPosition mLabelPosition = LimitLabelPosition.RIGHT_TOP; /** enum that indicates the position of the LimitLine label */ public enum LimitLabelPosition { - POS_LEFT, POS_RIGHT + LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM } /** * Constructor with limit. * - * @param limit - the position (the value) on the y-axis where this line - * should appear + * @param limit - the position (the value) on the y-axis (y-value) or x-axis + * (xIndex) where this line should appear */ public LimitLine(float limit) { mLimit = limit; @@ -57,8 +56,8 @@ public LimitLine(float limit) { /** * Constructor with limit and label. * - * @param limit - the position (the value) on the y-axis where this line - * should appear + * @param limit - the position (the value) on the y-axis (y-value) or x-axis + * (xIndex) where this line should appear * @param label - provide "" if no label is required */ public LimitLine(float limit, String label) { @@ -159,20 +158,21 @@ public DashPathEffect getDashPathEffect() { /** * Sets the color of the value-text that is drawn next to the LimitLine. - * - * @param color + * Default: Paint.Style.FILL_AND_STROKE + * + * @param style */ - public void setTextColor(int color) { - mValueTextColor = color; + public void setTextStyle(Paint.Style style) { + this.mTextStyle = style; } /** * Returns the color of the value-text that is drawn next to the LimitLine. - * + * * @return */ - public int getTextColor() { - return mValueTextColor; + public Paint.Style getTextStyle() { + return mTextStyle; } /** @@ -212,22 +212,4 @@ public void setLabel(String label) { public String getLabel() { return mLabel; } - - /** - * Sets the size of the label-text. - * - * @param size - */ - public void setTextSize(float size) { - mTextSize = Utils.convertDpToPixel(size); - } - - /** - * Returns the size of the label text. - * - * @return - */ - public float getTextSize() { - return mTextSize; - } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java new file mode 100644 index 0000000000..f164d8341f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java @@ -0,0 +1,167 @@ +package com.github.mikephil.charting.components; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.RelativeLayout; + +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointF; + +import java.lang.ref.WeakReference; + +/** + * View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your + * markers. + * + * @author Philipp Jahoda + */ +public class MarkerImage implements IMarker { + + private Context mContext; + private Drawable mDrawable; + + private MPPointF mOffset = new MPPointF(); + private MPPointF mOffset2 = new MPPointF(); + private WeakReference mWeakChart; + + private FSize mSize = new FSize(); + private Rect mDrawableBoundsCache = new Rect(); + + /** + * Constructor. Sets up the MarkerView with a custom layout resource. + * + * @param context + * @param drawableResourceId the drawable resource to render + */ + public MarkerImage(Context context, int drawableResourceId) { + mContext = context; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + { + mDrawable = mContext.getResources().getDrawable(drawableResourceId, null); + } + else + { + mDrawable = mContext.getResources().getDrawable(drawableResourceId); + } + } + + public void setOffset(MPPointF offset) { + mOffset = offset; + + if (mOffset == null) { + mOffset = new MPPointF(); + } + } + + public void setOffset(float offsetX, float offsetY) { + mOffset.x = offsetX; + mOffset.y = offsetY; + } + + @Override + public MPPointF getOffset() { + return mOffset; + } + + public void setSize(FSize size) { + mSize = size; + + if (mSize == null) { + mSize = new FSize(); + } + } + + public FSize getSize() { + return mSize; + } + + public void setChartView(Chart chart) { + mWeakChart = new WeakReference<>(chart); + } + + public Chart getChartView() { + return mWeakChart == null ? null : mWeakChart.get(); + } + + @Override + public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) { + + MPPointF offset = getOffset(); + mOffset2.x = offset.x; + mOffset2.y = offset.y; + + Chart chart = getChartView(); + + float width = mSize.width; + float height = mSize.height; + + if (width == 0.f && mDrawable != null) { + width = mDrawable.getIntrinsicWidth(); + } + if (height == 0.f && mDrawable != null) { + height = mDrawable.getIntrinsicHeight(); + } + + if (posX + mOffset2.x < 0) { + mOffset2.x = - posX; + } else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) { + mOffset2.x = chart.getWidth() - posX - width; + } + + if (posY + mOffset2.y < 0) { + mOffset2.y = - posY; + } else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) { + mOffset2.y = chart.getHeight() - posY - height; + } + + return mOffset2; + } + + @Override + public void refreshContent(Entry e, Highlight highlight) { + + } + + @Override + public void draw(Canvas canvas, float posX, float posY) { + + if (mDrawable == null) return; + + MPPointF offset = getOffsetForDrawingAtPoint(posX, posY); + + float width = mSize.width; + float height = mSize.height; + + if (width == 0.f && mDrawable != null) { + width = mDrawable.getIntrinsicWidth(); + } + if (height == 0.f && mDrawable != null) { + height = mDrawable.getIntrinsicHeight(); + } + + mDrawable.copyBounds(mDrawableBoundsCache); + mDrawable.setBounds( + mDrawableBoundsCache.left, + mDrawableBoundsCache.top, + mDrawableBoundsCache.left + (int)width, + mDrawableBoundsCache.top + (int)height); + + int saveId = canvas.save(); + // translate to the correct position and draw + canvas.translate(posX + offset.x, posY + offset.y); + mDrawable.draw(canvas); + canvas.restoreToCount(saveId); + + mDrawable.setBounds(mDrawableBoundsCache); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java new file mode 100644 index 0000000000..162e88e33c --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java @@ -0,0 +1,129 @@ +package com.github.mikephil.charting.components; + +import android.content.Context; +import android.graphics.Canvas; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.RelativeLayout; + +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointF; + +import java.lang.ref.WeakReference; + +/** + * View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your + * markers. + * + * @author Philipp Jahoda + */ +public class MarkerView extends RelativeLayout implements IMarker { + + private MPPointF mOffset = new MPPointF(); + private MPPointF mOffset2 = new MPPointF(); + private WeakReference mWeakChart; + + /** + * Constructor. Sets up the MarkerView with a custom layout resource. + * + * @param context + * @param layoutResource the layout resource to use for the MarkerView + */ + public MarkerView(Context context, int layoutResource) { + super(context); + setupLayoutResource(layoutResource); + } + + /** + * Sets the layout resource for a custom MarkerView. + * + * @param layoutResource + */ + private void setupLayoutResource(int layoutResource) { + + View inflated = LayoutInflater.from(getContext()).inflate(layoutResource, this); + + inflated.setLayoutParams(new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT)); + inflated.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + + // measure(getWidth(), getHeight()); + inflated.layout(0, 0, inflated.getMeasuredWidth(), inflated.getMeasuredHeight()); + } + + public void setOffset(MPPointF offset) { + mOffset = offset; + + if (mOffset == null) { + mOffset = new MPPointF(); + } + } + + public void setOffset(float offsetX, float offsetY) { + mOffset.x = offsetX; + mOffset.y = offsetY; + } + + @Override + public MPPointF getOffset() { + return mOffset; + } + + public void setChartView(Chart chart) { + mWeakChart = new WeakReference<>(chart); + } + + public Chart getChartView() { + return mWeakChart == null ? null : mWeakChart.get(); + } + + @Override + public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) { + + MPPointF offset = getOffset(); + mOffset2.x = offset.x; + mOffset2.y = offset.y; + + Chart chart = getChartView(); + + float width = getWidth(); + float height = getHeight(); + + if (posX + mOffset2.x < 0) { + mOffset2.x = - posX; + } else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) { + mOffset2.x = chart.getWidth() - posX - width; + } + + if (posY + mOffset2.y < 0) { + mOffset2.y = - posY; + } else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) { + mOffset2.y = chart.getHeight() - posY - height; + } + + return mOffset2; + } + + @Override + public void refreshContent(Entry e, Highlight highlight) { + + measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); + + } + + @Override + public void draw(Canvas canvas, float posX, float posY) { + + MPPointF offset = getOffsetForDrawingAtPoint(posX, posY); + + int saveId = canvas.save(); + // translate to the correct position and draw + canvas.translate(posX + offset.x, posY + offset.y); + draw(canvas); + canvas.restoreToCount(saveId); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java new file mode 100644 index 0000000000..77d4aaf98f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -0,0 +1,118 @@ + +package com.github.mikephil.charting.components; + +import com.github.mikephil.charting.utils.Utils; + +/** + * Class representing the x-axis labels settings. Only use the setter methods to + * modify it. Do not access public variables directly. Be aware that not all + * features the XLabels class provides are suitable for the RadarChart. + * + * @author Philipp Jahoda + */ +public class XAxis extends AxisBase { + + /** + * width of the x-axis labels in pixels - this is automatically + * calculated by the computeSize() methods in the renderers + */ + public int mLabelWidth = 1; + + /** + * height of the x-axis labels in pixels - this is automatically + * calculated by the computeSize() methods in the renderers + */ + public int mLabelHeight = 1; + + /** + * width of the (rotated) x-axis labels in pixels - this is automatically + * calculated by the computeSize() methods in the renderers + */ + public int mLabelRotatedWidth = 1; + + /** + * height of the (rotated) x-axis labels in pixels - this is automatically + * calculated by the computeSize() methods in the renderers + */ + public int mLabelRotatedHeight = 1; + + /** + * This is the angle for drawing the X axis labels (in degrees) + */ + protected float mLabelRotationAngle = 0f; + + /** + * if set to true, the chart will avoid that the first and last label entry + * in the chart "clip" off the edge of the chart + */ + private boolean mAvoidFirstLastClipping = false; + + /** + * the position of the x-labels relative to the chart + */ + private XAxisPosition mPosition = XAxisPosition.TOP; + + /** + * enum for the position of the x-labels relative to the chart + */ + public enum XAxisPosition { + TOP, BOTTOM, BOTH_SIDED, TOP_INSIDE, BOTTOM_INSIDE + } + + public XAxis() { + super(); + + mYOffset = Utils.convertDpToPixel(4.f); // -3 + } + + /** + * returns the position of the x-labels + */ + public XAxisPosition getPosition() { + return mPosition; + } + + /** + * sets the position of the x-labels + * + * @param pos + */ + public void setPosition(XAxisPosition pos) { + mPosition = pos; + } + + /** + * returns the angle for drawing the X axis labels (in degrees) + */ + public float getLabelRotationAngle() { + return mLabelRotationAngle; + } + + /** + * sets the angle for drawing the X axis labels (in degrees) + * + * @param angle the angle in degrees + */ + public void setLabelRotationAngle(float angle) { + mLabelRotationAngle = angle; + } + + /** + * if set to true, the chart will avoid that the first and last label entry + * in the chart "clip" off the edge of the chart or the screen + * + * @param enabled + */ + public void setAvoidFirstLastClipping(boolean enabled) { + mAvoidFirstLastClipping = enabled; + } + + /** + * returns true if avoid-first-lastclipping is enabled, false if not + * + * @return + */ + public boolean isAvoidFirstLastClippingEnabled() { + return mAvoidFirstLastClipping; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java new file mode 100644 index 0000000000..e84caab76b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -0,0 +1,393 @@ +package com.github.mikephil.charting.components; + +import android.graphics.Color; +import android.graphics.Paint; + +import com.github.mikephil.charting.utils.Utils; + +/** + * Class representing the y-axis labels settings and its entries. Only use the setter methods to + * modify it. Do not + * access public variables directly. Be aware that not all features the YLabels class provides + * are suitable for the + * RadarChart. Customizations that affect the value range of the axis need to be applied before + * setting data for the + * chart. + * + * @author Philipp Jahoda + */ +public class YAxis extends AxisBase { + + /** + * indicates if the bottom y-label entry is drawn or not + */ + private boolean mDrawBottomYLabelEntry = true; + + /** + * indicates if the top y-label entry is drawn or not + */ + private boolean mDrawTopYLabelEntry = true; + + /** + * flag that indicates if the axis is inverted or not + */ + protected boolean mInverted = false; + + /** + * flag that indicates if the zero-line should be drawn regardless of other grid lines + */ + protected boolean mDrawZeroLine = false; + + /** + * Color of the zero line + */ + protected int mZeroLineColor = Color.GRAY; + + /** + * Width of the zero line in pixels + */ + protected float mZeroLineWidth = 1f; + + /** + * axis space from the largest value to the top in percent of the total axis range + */ + protected float mSpacePercentTop = 10f; + + /** + * axis space from the smallest value to the bottom in percent of the total axis range + */ + protected float mSpacePercentBottom = 10f; + + /** + * the position of the y-labels relative to the chart + */ + private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART; + + /** + * enum for the position of the y-labels relative to the chart + */ + public enum YAxisLabelPosition { + OUTSIDE_CHART, INSIDE_CHART + } + + /** + * the side this axis object represents + */ + private AxisDependency mAxisDependency; + + /** + * the minimum width that the axis should take (in dp). + *

+ * default: 0.0 + */ + protected float mMinWidth = 0.f; + + /** + * the maximum width that the axis can take (in dp). + * use Inifinity for disabling the maximum + * default: Float.POSITIVE_INFINITY (no maximum specified) + */ + protected float mMaxWidth = Float.POSITIVE_INFINITY; + + /** + * Enum that specifies the axis a DataSet should be plotted against, either LEFT or RIGHT. + * + * @author Philipp Jahoda + */ + public enum AxisDependency { + LEFT, RIGHT + } + + public YAxis() { + super(); + + // default left + this.mAxisDependency = AxisDependency.LEFT; + this.mYOffset = 0f; + } + + public YAxis(AxisDependency position) { + super(); + this.mAxisDependency = position; + this.mYOffset = 0f; + } + + public AxisDependency getAxisDependency() { + return mAxisDependency; + } + + /** + * @return the minimum width that the axis should take (in dp). + */ + public float getMinWidth() { + return mMinWidth; + } + + /** + * Sets the minimum width that the axis should take (in dp). + * + * @param minWidth + */ + public void setMinWidth(float minWidth) { + mMinWidth = minWidth; + } + + /** + * @return the maximum width that the axis can take (in dp). + */ + public float getMaxWidth() { + return mMaxWidth; + } + + /** + * Sets the maximum width that the axis can take (in dp). + * + * @param maxWidth + */ + public void setMaxWidth(float maxWidth) { + mMaxWidth = maxWidth; + } + + /** + * returns the position of the y-labels + */ + public YAxisLabelPosition getLabelPosition() { + return mPosition; + } + + /** + * sets the position of the y-labels + * + * @param pos + */ + public void setPosition(YAxisLabelPosition pos) { + mPosition = pos; + } + + /** + * returns true if drawing the top y-axis label entry is enabled + * + * @return + */ + public boolean isDrawTopYLabelEntryEnabled() { + return mDrawTopYLabelEntry; + } + + /** + * returns true if drawing the bottom y-axis label entry is enabled + * + * @return + */ + public boolean isDrawBottomYLabelEntryEnabled() { + return mDrawBottomYLabelEntry; + } + + /** + * set this to true to enable drawing the top y-label entry. Disabling this can be helpful + * when the top y-label and + * left x-label interfere with each other. default: true + * + * @param enabled + */ + public void setDrawTopYLabelEntry(boolean enabled) { + mDrawTopYLabelEntry = enabled; + } + + /** + * If this is set to true, the y-axis is inverted which means that low values are on top of + * the chart, high values + * on bottom. + * + * @param enabled + */ + public void setInverted(boolean enabled) { + mInverted = enabled; + } + + /** + * If this returns true, the y-axis is inverted. + * + * @return + */ + public boolean isInverted() { + return mInverted; + } + + /** + * This method is deprecated. + * Use setAxisMinimum(...) / setAxisMaximum(...) instead. + * + * @param startAtZero + */ + @Deprecated + public void setStartAtZero(boolean startAtZero) { + if (startAtZero) + setAxisMinimum(0f); + else + resetAxisMinimum(); + } + + /** + * Sets the top axis space in percent of the full range. Default 10f + * + * @param percent + */ + public void setSpaceTop(float percent) { + mSpacePercentTop = percent; + } + + /** + * Returns the top axis space in percent of the full range. Default 10f + * + * @return + */ + public float getSpaceTop() { + return mSpacePercentTop; + } + + /** + * Sets the bottom axis space in percent of the full range. Default 10f + * + * @param percent + */ + public void setSpaceBottom(float percent) { + mSpacePercentBottom = percent; + } + + /** + * Returns the bottom axis space in percent of the full range. Default 10f + * + * @return + */ + public float getSpaceBottom() { + return mSpacePercentBottom; + } + + public boolean isDrawZeroLineEnabled() { + return mDrawZeroLine; + } + + /** + * Set this to true to draw the zero-line regardless of weather other + * grid-lines are enabled or not. Default: false + * + * @param mDrawZeroLine + */ + public void setDrawZeroLine(boolean mDrawZeroLine) { + this.mDrawZeroLine = mDrawZeroLine; + } + + public int getZeroLineColor() { + return mZeroLineColor; + } + + /** + * Sets the color of the zero line + * + * @param color + */ + public void setZeroLineColor(int color) { + mZeroLineColor = color; + } + + public float getZeroLineWidth() { + return mZeroLineWidth; + } + + /** + * Sets the width of the zero line in dp + * + * @param width + */ + public void setZeroLineWidth(float width) { + this.mZeroLineWidth = Utils.convertDpToPixel(width); + } + + /** + * This is for normal (not horizontal) charts horizontal spacing. + * + * @param p + * @return + */ + public float getRequiredWidthSpace(Paint p) { + + p.setTextSize(mTextSize); + + String label = getLongestLabel(); + float width = (float) Utils.calcTextWidth(p, label) + getXOffset() * 2f; + + float minWidth = getMinWidth(); + float maxWidth = getMaxWidth(); + + if (minWidth > 0.f) + minWidth = Utils.convertDpToPixel(minWidth); + + if (maxWidth > 0.f && maxWidth != Float.POSITIVE_INFINITY) + maxWidth = Utils.convertDpToPixel(maxWidth); + + width = Math.max(minWidth, Math.min(width, maxWidth > 0.0 ? maxWidth : width)); + + return width; + } + + /** + * This is for HorizontalBarChart vertical spacing. + * + * @param p + * @return + */ + public float getRequiredHeightSpace(Paint p) { + + p.setTextSize(mTextSize); + + String label = getLongestLabel(); + return (float) Utils.calcTextHeight(p, label) + getYOffset() * 2f; + } + + /** + * Returns true if this axis needs horizontal offset, false if no offset is needed. + * + * @return + */ + public boolean needsOffset() { + if (isEnabled() && isDrawLabelsEnabled() && getLabelPosition() == YAxisLabelPosition + .OUTSIDE_CHART) + return true; + else + return false; + } + + @Override + public void calculate(float dataMin, float dataMax) { + + // if custom, use value as is, else use data value + float min = mCustomAxisMin ? mAxisMinimum : dataMin; + float max = mCustomAxisMax ? mAxisMaximum : dataMax; + + // temporary range (before calculations) + float range = Math.abs(max - min); + + // in case all values are equal + if (range == 0f) { + max = max + 1f; + min = min - 1f; + } + + // bottom-space only effects non-custom min + if (!mCustomAxisMin) { + + float bottomSpace = range / 100f * getSpaceBottom(); + this.mAxisMinimum = (min - bottomSpace); + } + + // top-space only effects non-custom max + if (!mCustomAxisMax) { + + float topSpace = range / 100f * getSpaceTop(); + this.mAxisMaximum = (max + topSpace); + } + + // calc actual range + this.mAxisRange = Math.abs(this.mAxisMaximum - this.mAxisMinimum); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java new file mode 100644 index 0000000000..16d60f6f9c --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java @@ -0,0 +1,119 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; + +import java.util.List; + +/** + * Data object that represents all data for the BarChart. + * + * @author Philipp Jahoda + */ +public class BarData extends BarLineScatterCandleBubbleData { + + /** + * the width of the bars on the x-axis, in values (not pixels) + */ + private float mBarWidth = 0.85f; + + public BarData() { + super(); + } + + public BarData(IBarDataSet... dataSets) { + super(dataSets); + } + + public BarData(List dataSets) { + super(dataSets); + } + + /** + * Sets the width each bar should have on the x-axis (in values, not pixels). + * Default 0.85f + * + * @param mBarWidth + */ + public void setBarWidth(float mBarWidth) { + this.mBarWidth = mBarWidth; + } + + public float getBarWidth() { + return mBarWidth; + } + + /** + * Groups all BarDataSet objects this data object holds together by modifying the x-value of their entries. + * Previously set x-values of entries will be overwritten. Leaves space between bars and groups as specified + * by the parameters. + * Do not forget to call notifyDataSetChanged() on your BarChart object after calling this method. + * + * @param fromX the starting point on the x-axis where the grouping should begin + * @param groupSpace the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f + * @param barSpace the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f + */ + public void groupBars(float fromX, float groupSpace, float barSpace) { + + int setCount = mDataSets.size(); + if (setCount <= 1) { + throw new RuntimeException("BarData needs to hold at least 2 BarDataSets to allow grouping."); + } + + IBarDataSet max = getMaxEntryCountSet(); + int maxEntryCount = max.getEntryCount(); + + float groupSpaceWidthHalf = groupSpace / 2f; + float barSpaceHalf = barSpace / 2f; + float barWidthHalf = mBarWidth / 2f; + + float interval = getGroupWidth(groupSpace, barSpace); + + for (int i = 0; i < maxEntryCount; i++) { + + float start = fromX; + fromX += groupSpaceWidthHalf; + + for (IBarDataSet set : mDataSets) { + + fromX += barSpaceHalf; + fromX += barWidthHalf; + + if (i < set.getEntryCount()) { + + BarEntry entry = set.getEntryForIndex(i); + + if (entry != null) { + entry.setX(fromX); + } + } + + fromX += barWidthHalf; + fromX += barSpaceHalf; + } + + fromX += groupSpaceWidthHalf; + float end = fromX; + float innerInterval = end - start; + float diff = interval - innerInterval; + + // correct rounding errors + if (diff > 0 || diff < 0) { + fromX += diff; + } + } + + notifyDataChanged(); + } + + /** + * In case of grouped bars, this method returns the space an individual group of bar needs on the x-axis. + * + * @param groupSpace + * @param barSpace + * @return + */ + public float getGroupWidth(float groupSpace, float barSpace) { + return mDataSets.size() * (mBarWidth + barSpace) + groupSpace; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java similarity index 60% rename from MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java index 899f9f0de9..ae11c97b3c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java @@ -3,12 +3,12 @@ import android.graphics.Color; -import java.util.ArrayList; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -public class BarDataSet extends BarLineScatterCandleDataSet { +import java.util.ArrayList; +import java.util.List; - /** space indicator between the bars 0.1f == 10 % */ - private float mBarSpace = 0.15f; +public class BarDataSet extends BarLineScatterCandleBubbleDataSet implements IBarDataSet { /** * the maximum number of bars that are stacked upon each other, this value @@ -16,10 +16,18 @@ public class BarDataSet extends BarLineScatterCandleDataSet { */ private int mStackSize = 1; - /** the color used for drawing the bar shadows */ + /** + * the color used for drawing the bar shadows + */ private int mBarShadowColor = Color.rgb(215, 215, 215); - /** the alpha value used to draw the highlight indicator bar */ + private float mBarBorderWidth = 0.0f; + + private int mBarBorderColor = Color.BLACK; + + /** + * the alpha value used to draw the highlight indicator bar + */ private int mHighLightAlpha = 120; /** @@ -30,11 +38,11 @@ public class BarDataSet extends BarLineScatterCandleDataSet { /** * array of labels used to describe the different values of the stacked bars */ - private String[] mStackLabels = new String[] { + private String[] mStackLabels = new String[]{ "Stack" }; - public BarDataSet(ArrayList yVals, String label) { + public BarDataSet(List yVals, String label) { super(yVals, label); mHighLightColor = Color.rgb(0, 0, 0); @@ -46,16 +54,16 @@ public BarDataSet(ArrayList yVals, String label) { @Override public DataSet copy() { - ArrayList yVals = new ArrayList(); + List yVals = new ArrayList(); + yVals.clear(); - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(((BarEntry) mYVals.get(i)).copy()); + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); } BarDataSet copied = new BarDataSet(yVals, getLabel()); copied.mColors = mColors; copied.mStackSize = mStackSize; - copied.mBarSpace = mBarSpace; copied.mBarShadowColor = mBarShadowColor; copied.mStackLabels = mStackLabels; copied.mHighLightColor = mHighLightColor; @@ -68,13 +76,13 @@ public DataSet copy() { * Calculates the total number of entries this DataSet represents, including * stacks. All values belonging to a stack are calculated separately. */ - private void calcEntryCountIncludingStacks(ArrayList yVals) { + private void calcEntryCountIncludingStacks(List yVals) { mEntryCountStacks = 0; for (int i = 0; i < yVals.size(); i++) { - float[] vals = yVals.get(i).getVals(); + float[] vals = yVals.get(i).getYVals(); if (vals == null) mEntryCountStacks++; @@ -87,32 +95,48 @@ private void calcEntryCountIncludingStacks(ArrayList yVals) { * calculates the maximum stacksize that occurs in the Entries array of this * DataSet */ - private void calcStackSize(ArrayList yVals) { + private void calcStackSize(List yVals) { for (int i = 0; i < yVals.size(); i++) { - float[] vals = yVals.get(i).getVals(); + float[] vals = yVals.get(i).getYVals(); if (vals != null && vals.length > mStackSize) mStackSize = vals.length; } } - /** - * Returns the maximum number of bars that can be stacked upon another in - * this DataSet. - * - * @return - */ + @Override + protected void calcMinMax(BarEntry e) { + + if (e != null && !Float.isNaN(e.getY())) { + + if (e.getYVals() == null) { + + if (e.getY() < mYMin) + mYMin = e.getY(); + + if (e.getY() > mYMax) + mYMax = e.getY(); + } else { + + if (-e.getNegativeSum() < mYMin) + mYMin = -e.getNegativeSum(); + + if (e.getPositiveSum() > mYMax) + mYMax = e.getPositiveSum(); + } + + calcMinMaxX(e); + } + } + + @Override public int getStackSize() { return mStackSize; } - /** - * Returns true if this DataSet is stacked (stacksize > 1) or not. - * - * @return - */ + @Override public boolean isStacked() { return mStackSize > 1 ? true : false; } @@ -120,7 +144,7 @@ public boolean isStacked() { /** * returns the overall entry count, including counting each stack-value * individually - * + * * @return */ public int getEntryCountStacks() { @@ -128,87 +152,86 @@ public int getEntryCountStacks() { } /** - * returns the space between bars in percent of the whole width of one value - * - * @return + * Sets the color used for drawing the bar-shadows. The bar shadows is a + * surface behind the bar that indicates the maximum value. Don't for get to + * use getResources().getColor(...) to set this. Or Color.rgb(...). + * + * @param color */ - public float getBarSpacePercent() { - return mBarSpace * 100f; + public void setBarShadowColor(int color) { + mBarShadowColor = color; + } + + @Override + public int getBarShadowColor() { + return mBarShadowColor; } /** - * returns the space between bars as the actual value (0 - 1.0f) - * + * Sets the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. + * * @return */ - public float getBarSpace() { - return mBarSpace; + public void setBarBorderWidth(float width) { + mBarBorderWidth = width; } /** - * sets the space between the bars in percent of the total bar width - * - * @param percent + * Returns the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. + * + * @return */ - public void setBarSpacePercent(float percent) { - mBarSpace = percent / 100f; + @Override + public float getBarBorderWidth() { + return mBarBorderWidth; } /** - * Sets the color used for drawing the bar-shadows. The bar shadows is a - * surface behind the bar that indicates the maximum value. Don't for get to - * use getResources().getColor(...) to set this. Or Color.rgb(...). - * - * @param color + * Sets the color drawing borders around the bars. + * + * @return */ - public void setBarShadowColor(int color) { - mBarShadowColor = color; + public void setBarBorderColor(int color) { + mBarBorderColor = color; } /** - * Returns the color used for drawing the bar-shadows. The bar shadows is a - * surface behind the bar that indicates the maximum value. - * + * Returns the color drawing borders around the bars. + * * @return */ - public int getBarShadowColor() { - return mBarShadowColor; + @Override + public int getBarBorderColor() { + return mBarBorderColor; } /** * Set the alpha value (transparency) that is used for drawing the highlight * indicator bar. min = 0 (fully transparent), max = 255 (fully opaque) - * + * * @param alpha */ public void setHighLightAlpha(int alpha) { mHighLightAlpha = alpha; } - /** - * Returns the alpha value (transparency) that is used for drawing the - * highlight indicator. - * - * @return - */ + @Override public int getHighLightAlpha() { return mHighLightAlpha; } /** * Sets labels for different values of bar-stacks, in case there are one. - * + * * @param labels */ public void setStackLabels(String[] labels) { mStackLabels = labels; } - /** - * returns the labels used for the different value-stacks - * - * @return - */ + @Override public String[] getStackLabels() { return mStackLabels; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java new file mode 100644 index 0000000000..365ef51a2d --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java @@ -0,0 +1,310 @@ +package com.github.mikephil.charting.data; + +import android.annotation.SuppressLint; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.highlight.Range; + +/** + * Entry class for the BarChart. (especially stacked bars) + * + * @author Philipp Jahoda + */ +@SuppressLint("ParcelCreator") +public class BarEntry extends Entry { + + /** + * the values the stacked barchart holds + */ + private float[] mYVals; + + /** + * the ranges for the individual stack values - automatically calculated + */ + private Range[] mRanges; + + /** + * the sum of all negative values this entry (if stacked) contains + */ + private float mNegativeSum; + + /** + * the sum of all positive values this entry (if stacked) contains + */ + private float mPositiveSum; + + /** + * Constructor for normal bars (not stacked). + * + * @param x + * @param y + */ + public BarEntry(float x, float y) { + super(x, y); + } + + /** + * Constructor for normal bars (not stacked). + * + * @param x + * @param y + * @param data - Spot for additional data this Entry represents. + */ + public BarEntry(float x, float y, Object data) { + super(x, y, data); + } + + /** + * Constructor for normal bars (not stacked). + * + * @param x + * @param y + * @param icon - icon image + */ + public BarEntry(float x, float y, Drawable icon) { + super(x, y, icon); + } + + /** + * Constructor for normal bars (not stacked). + * + * @param x + * @param y + * @param icon - icon image + * @param data - Spot for additional data this Entry represents. + */ + public BarEntry(float x, float y, Drawable icon, Object data) { + super(x, y, icon, data); + } + + /** + * Constructor for stacked bar entries. One data object for whole stack + * + * @param x + * @param vals - the stack values, use at least 2 + */ + public BarEntry(float x, float[] vals) { + super(x, calcSum(vals)); + + this.mYVals = vals; + calcPosNegSum(); + calcRanges(); + } + + /** + * Constructor for stacked bar entries. One data object for whole stack + * + * @param x + * @param vals - the stack values, use at least 2 + * @param data - Spot for additional data this Entry represents. + */ + public BarEntry(float x, float[] vals, Object data) { + super(x, calcSum(vals), data); + + this.mYVals = vals; + calcPosNegSum(); + calcRanges(); + } + + /** + * Constructor for stacked bar entries. One data object for whole stack + * + * @param x + * @param vals - the stack values, use at least 2 + * @param icon - icon image + */ + public BarEntry(float x, float[] vals, Drawable icon) { + super(x, calcSum(vals), icon); + + this.mYVals = vals; + calcPosNegSum(); + calcRanges(); + } + + /** + * Constructor for stacked bar entries. One data object for whole stack + * + * @param x + * @param vals - the stack values, use at least 2 + * @param icon - icon image + * @param data - Spot for additional data this Entry represents. + */ + public BarEntry(float x, float[] vals, Drawable icon, Object data) { + super(x, calcSum(vals), icon, data); + + this.mYVals = vals; + calcPosNegSum(); + calcRanges(); + } + + /** + * Returns an exact copy of the BarEntry. + */ + public BarEntry copy() { + + BarEntry copied = new BarEntry(getX(), getY(), getData()); + copied.setVals(mYVals); + return copied; + } + + /** + * Returns the stacked values this BarEntry represents, or null, if only a single value is represented (then, use + * getY()). + * + * @return + */ + public float[] getYVals() { + return mYVals; + } + + /** + * Set the array of values this BarEntry should represent. + * + * @param vals + */ + public void setVals(float[] vals) { + setY(calcSum(vals)); + mYVals = vals; + calcPosNegSum(); + calcRanges(); + } + + /** + * Returns the value of this BarEntry. If the entry is stacked, it returns the positive sum of all values. + * + * @return + */ + @Override + public float getY() { + return super.getY(); + } + + /** + * Returns the ranges of the individual stack-entries. Will return null if this entry is not stacked. + * + * @return + */ + public Range[] getRanges() { + return mRanges; + } + + /** + * Returns true if this BarEntry is stacked (has a values array), false if not. + * + * @return + */ + public boolean isStacked() { + return mYVals != null; + } + + /** + * Use `getSumBelow(stackIndex)` instead. + */ + @Deprecated + public float getBelowSum(int stackIndex) { + return getSumBelow(stackIndex); + } + + public float getSumBelow(int stackIndex) { + + if (mYVals == null) + return 0; + + float remainder = 0f; + int index = mYVals.length - 1; + + while (index > stackIndex && index >= 0) { + remainder += mYVals[index]; + index--; + } + + return remainder; + } + + /** + * Reuturns the sum of all positive values this entry (if stacked) contains. + * + * @return + */ + public float getPositiveSum() { + return mPositiveSum; + } + + /** + * Returns the sum of all negative values this entry (if stacked) contains. (this is a positive number) + * + * @return + */ + public float getNegativeSum() { + return mNegativeSum; + } + + private void calcPosNegSum() { + + if (mYVals == null) { + mNegativeSum = 0; + mPositiveSum = 0; + return; + } + + float sumNeg = 0f; + float sumPos = 0f; + + for (float f : mYVals) { + if (f <= 0f) + sumNeg += Math.abs(f); + else + sumPos += f; + } + + mNegativeSum = sumNeg; + mPositiveSum = sumPos; + } + + /** + * Calculates the sum across all values of the given stack. + * + * @param vals + * @return + */ + private static float calcSum(float[] vals) { + + if (vals == null) + return 0f; + + float sum = 0f; + + for (float f : vals) + sum += f; + + return sum; + } + + protected void calcRanges() { + + float[] values = getYVals(); + + if (values == null || values.length == 0) + return; + + mRanges = new Range[values.length]; + + float negRemain = -getNegativeSum(); + float posRemain = 0f; + + for (int i = 0; i < mRanges.length; i++) { + + float value = values[i]; + + if (value < 0) { + mRanges[i] = new Range(negRemain, negRemain - value); + negRemain -= value; + } else { + mRanges[i] = new Range(posRemain, posRemain + value); + posRemain += value; + } + } + } +} + + diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java new file mode 100644 index 0000000000..c09eadec9b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java @@ -0,0 +1,27 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; + +import java.util.List; + +/** + * Baseclass for all Line, Bar, Scatter, Candle and Bubble data. + * + * @author Philipp Jahoda + */ +public abstract class BarLineScatterCandleBubbleData> + extends ChartData { + + public BarLineScatterCandleBubbleData() { + super(); + } + + public BarLineScatterCandleBubbleData(T... sets) { + super(sets); + } + + public BarLineScatterCandleBubbleData(List sets) { + super(sets); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java similarity index 67% rename from MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java index 43c6173ddd..fba8216c3d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java @@ -3,19 +3,21 @@ import android.graphics.Color; -import java.util.ArrayList; +import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; + +import java.util.List; /** * Baseclass of all DataSets for Bar-, Line-, Scatter- and CandleStickChart. * * @author Philipp Jahoda */ -public abstract class BarLineScatterCandleDataSet extends DataSet { +public abstract class BarLineScatterCandleBubbleDataSet extends DataSet implements IBarLineScatterCandleBubbleDataSet { /** default highlight color */ protected int mHighLightColor = Color.rgb(255, 187, 115); - public BarLineScatterCandleDataSet(ArrayList yVals, String label) { + public BarLineScatterCandleBubbleDataSet(List yVals, String label) { super(yVals, label); } @@ -30,11 +32,7 @@ public void setHighLightColor(int color) { mHighLightColor = color; } - /** - * Returns the color that is used for drawing the highlight indicators. - * - * @return - */ + @Override public int getHighLightColor() { return mHighLightColor; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java new file mode 100644 index 0000000000..3869a00895 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -0,0 +1,491 @@ +package com.github.mikephil.charting.data; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.graphics.Typeface; + +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.formatter.DefaultValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Philipp Jahoda on 21/10/15. + * This is the base dataset of all DataSets. It's purpose is to implement critical methods + * provided by the IDataSet interface. + */ +public abstract class BaseDataSet implements IDataSet { + + /** + * List representing all colors that are used for this DataSet + */ + protected List mColors = null; + + /** + * List representing all colors that are used for drawing the actual values for this DataSet + */ + protected List mValueColors = null; + + /** + * label that describes the DataSet or the data the DataSet represents + */ + private String mLabel = "DataSet"; + + /** + * this specifies which axis this DataSet should be plotted against + */ + protected YAxis.AxisDependency mAxisDependency = YAxis.AxisDependency.LEFT; + + /** + * if true, value highlightning is enabled + */ + protected boolean mHighlightEnabled = true; + + /** + * custom formatter that is used instead of the auto-formatter if set + */ + protected transient IValueFormatter mValueFormatter; + + /** + * the typeface used for the value text + */ + protected Typeface mValueTypeface; + + private Legend.LegendForm mForm = Legend.LegendForm.DEFAULT; + private float mFormSize = Float.NaN; + private float mFormLineWidth = Float.NaN; + private DashPathEffect mFormLineDashEffect = null; + + /** + * if true, y-values are drawn on the chart + */ + protected boolean mDrawValues = true; + + /** + * if true, y-icons are drawn on the chart + */ + protected boolean mDrawIcons = true; + + /** + * the offset for drawing icons (in dp) + */ + protected MPPointF mIconsOffset = new MPPointF(); + + /** + * the size of the value-text labels + */ + protected float mValueTextSize = 17f; + + /** + * flag that indicates if the DataSet is visible or not + */ + protected boolean mVisible = true; + + /** + * Default constructor. + */ + public BaseDataSet() { + mColors = new ArrayList(); + mValueColors = new ArrayList(); + + // default color + mColors.add(Color.rgb(140, 234, 255)); + mValueColors.add(Color.BLACK); + } + + /** + * Constructor with label. + * + * @param label + */ + public BaseDataSet(String label) { + this(); + this.mLabel = label; + } + + /** + * Use this method to tell the data set that the underlying data has changed. + */ + public void notifyDataSetChanged() { + calcMinMax(); + } + + + /** + * ###### ###### COLOR GETTING RELATED METHODS ##### ###### + */ + + @Override + public List getColors() { + return mColors; + } + + public List getValueColors() { + return mValueColors; + } + + @Override + public int getColor() { + return mColors.get(0); + } + + @Override + public int getColor(int index) { + return mColors.get(index % mColors.size()); + } + + /** + * ###### ###### COLOR SETTING RELATED METHODS ##### ###### + */ + + /** + * Sets the colors that should be used fore this DataSet. Colors are reused + * as soon as the number of Entries the DataSet represents is higher than + * the size of the colors array. If you are using colors from the resources, + * make sure that the colors are already prepared (by calling + * getResources().getColor(...)) before adding them to the DataSet. + * + * @param colors + */ + public void setColors(List colors) { + this.mColors = colors; + } + + /** + * Sets the colors that should be used fore this DataSet. Colors are reused + * as soon as the number of Entries the DataSet represents is higher than + * the size of the colors array. If you are using colors from the resources, + * make sure that the colors are already prepared (by calling + * getResources().getColor(...)) before adding them to the DataSet. + * + * @param colors + */ + public void setColors(int... colors) { + this.mColors = ColorTemplate.createColors(colors); + } + + /** + * Sets the colors that should be used fore this DataSet. Colors are reused + * as soon as the number of Entries the DataSet represents is higher than + * the size of the colors array. You can use + * "new int[] { R.color.red, R.color.green, ... }" to provide colors for + * this method. Internally, the colors are resolved using + * getResources().getColor(...) + * + * @param colors + */ + public void setColors(int[] colors, Context c) { + + if(mColors == null){ + mColors = new ArrayList<>(); + } + + mColors.clear(); + + for (int color : colors) { + mColors.add(c.getResources().getColor(color)); + } + } + + /** + * Adds a new color to the colors array of the DataSet. + * + * @param color + */ + public void addColor(int color) { + if (mColors == null) + mColors = new ArrayList(); + mColors.add(color); + } + + /** + * Sets the one and ONLY color that should be used for this DataSet. + * Internally, this recreates the colors array and adds the specified color. + * + * @param color + */ + public void setColor(int color) { + resetColors(); + mColors.add(color); + } + + /** + * Sets a color with a specific alpha value. + * + * @param color + * @param alpha from 0-255 + */ + public void setColor(int color, int alpha) { + setColor(Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color))); + } + + /** + * Sets colors with a specific alpha value. + * + * @param colors + * @param alpha + */ + public void setColors(int[] colors, int alpha) { + resetColors(); + for (int color : colors) { + addColor(Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color))); + } + } + + /** + * Resets all colors of this DataSet and recreates the colors array. + */ + public void resetColors() { + if(mColors == null) { + mColors = new ArrayList(); + } + mColors.clear(); + } + + /** + * ###### ###### OTHER STYLING RELATED METHODS ##### ###### + */ + + @Override + public void setLabel(String label) { + mLabel = label; + } + + @Override + public String getLabel() { + return mLabel; + } + + @Override + public void setHighlightEnabled(boolean enabled) { + mHighlightEnabled = enabled; + } + + @Override + public boolean isHighlightEnabled() { + return mHighlightEnabled; + } + + @Override + public void setValueFormatter(IValueFormatter f) { + + if (f == null) + return; + else + mValueFormatter = f; + } + + @Override + public IValueFormatter getValueFormatter() { + if (needsFormatter()) + return Utils.getDefaultValueFormatter(); + return mValueFormatter; + } + + @Override + public boolean needsFormatter() { + return mValueFormatter == null; + } + + @Override + public void setValueTextColor(int color) { + mValueColors.clear(); + mValueColors.add(color); + } + + @Override + public void setValueTextColors(List colors) { + mValueColors = colors; + } + + @Override + public void setValueTypeface(Typeface tf) { + mValueTypeface = tf; + } + + @Override + public void setValueTextSize(float size) { + mValueTextSize = Utils.convertDpToPixel(size); + } + + @Override + public int getValueTextColor() { + return mValueColors.get(0); + } + + @Override + public int getValueTextColor(int index) { + return mValueColors.get(index % mValueColors.size()); + } + + @Override + public Typeface getValueTypeface() { + return mValueTypeface; + } + + @Override + public float getValueTextSize() { + return mValueTextSize; + } + + public void setForm(Legend.LegendForm form) { + mForm = form; + } + + @Override + public Legend.LegendForm getForm() { + return mForm; + } + + public void setFormSize(float formSize) { + mFormSize = formSize; + } + + @Override + public float getFormSize() { + return mFormSize; + } + + public void setFormLineWidth(float formLineWidth) { + mFormLineWidth = formLineWidth; + } + + @Override + public float getFormLineWidth() { + return mFormLineWidth; + } + + public void setFormLineDashEffect(DashPathEffect dashPathEffect) { + mFormLineDashEffect = dashPathEffect; + } + + @Override + public DashPathEffect getFormLineDashEffect() { + return mFormLineDashEffect; + } + + @Override + public void setDrawValues(boolean enabled) { + this.mDrawValues = enabled; + } + + @Override + public boolean isDrawValuesEnabled() { + return mDrawValues; + } + + @Override + public void setDrawIcons(boolean enabled) { + mDrawIcons = enabled; + } + + @Override + public boolean isDrawIconsEnabled() { + return mDrawIcons; + } + + @Override + public void setIconsOffset(MPPointF offsetDp) { + + mIconsOffset.x = offsetDp.x; + mIconsOffset.y = offsetDp.y; + } + + @Override + public MPPointF getIconsOffset() { + return mIconsOffset; + } + + @Override + public void setVisible(boolean visible) { + mVisible = visible; + } + + @Override + public boolean isVisible() { + return mVisible; + } + + @Override + public YAxis.AxisDependency getAxisDependency() { + return mAxisDependency; + } + + @Override + public void setAxisDependency(YAxis.AxisDependency dependency) { + mAxisDependency = dependency; + } + + + /** + * ###### ###### DATA RELATED METHODS ###### ###### + */ + + @Override + public int getIndexInEntries(int xIndex) { + + for (int i = 0; i < getEntryCount(); i++) { + if (xIndex == getEntryForIndex(i).getX()) + return i; + } + + return -1; + } + + @Override + public boolean removeFirst() { + + if (getEntryCount() > 0) { + + T entry = getEntryForIndex(0); + return removeEntry(entry); + } else + return false; + } + + @Override + public boolean removeLast() { + + if (getEntryCount() > 0) { + + T e = getEntryForIndex(getEntryCount() - 1); + return removeEntry(e); + } else + return false; + } + + @Override + public boolean removeEntryByXValue(float xValue) { + + T e = getEntryForXValue(xValue, Float.NaN); + return removeEntry(e); + } + + @Override + public boolean removeEntry(int index) { + + T e = getEntryForIndex(index); + return removeEntry(e); + } + + @Override + public boolean contains(T e) { + + for (int i = 0; i < getEntryCount(); i++) { + if (getEntryForIndex(i).equals(e)) + return true; + } + + return false; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java new file mode 100644 index 0000000000..eb1ada84d8 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java @@ -0,0 +1,97 @@ +package com.github.mikephil.charting.data; + +import android.graphics.drawable.Drawable; + +/** + * Created by Philipp Jahoda on 02/06/16. + */ +public abstract class BaseEntry { + + /** the y value */ + private float y = 0f; + + /** optional spot for additional data this Entry represents */ + private Object mData = null; + + /** optional icon image */ + private Drawable mIcon = null; + + public BaseEntry() { + + } + + public BaseEntry(float y) { + this.y = y; + } + + public BaseEntry(float y, Object data) { + this(y); + this.mData = data; + } + + public BaseEntry(float y, Drawable icon) { + this(y); + this.mIcon = icon; + } + + public BaseEntry(float y, Drawable icon, Object data) { + this(y); + this.mIcon = icon; + this.mData = data; + } + + /** + * Returns the y value of this Entry. + * + * @return + */ + public float getY() { + return y; + } + + /** + * Sets the icon drawable + * + * @param icon + */ + public void setIcon(Drawable icon) { + this.mIcon = icon; + } + + /** + * Returns the icon of this Entry. + * + * @return + */ + public Drawable getIcon() { + return mIcon; + } + + /** + * Sets the y-value for the Entry. + * + * @param y + */ + public void setY(float y) { + this.y = y; + } + + /** + * Returns the data, additional information that this Entry represents, or + * null, if no data has been specified. + * + * @return + */ + public Object getData() { + return mData; + } + + /** + * Sets additional data this Entry should represent. + * + * @param data + */ + public void setData(Object data) { + this.mData = data; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java new file mode 100644 index 0000000000..8fb72f17c5 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java @@ -0,0 +1,34 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; + +import java.util.List; + +public class BubbleData extends BarLineScatterCandleBubbleData { + + public BubbleData() { + super(); + } + + public BubbleData(IBubbleDataSet... dataSets) { + super(dataSets); + } + + public BubbleData(List dataSets) { + super(dataSets); + } + + + /** + * Sets the width of the circle that surrounds the bubble when highlighted + * for all DataSet objects this data object contains, in dp. + * + * @param width + */ + public void setHighlightCircleWidth(float width) { + for (IBubbleDataSet set : mDataSets) { + set.setHighlightCircleWidth(width); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java new file mode 100644 index 0000000000..d8c0c13013 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java @@ -0,0 +1,71 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; +import com.github.mikephil.charting.utils.Utils; + +import java.util.ArrayList; +import java.util.List; + +public class BubbleDataSet extends BarLineScatterCandleBubbleDataSet implements IBubbleDataSet { + + protected float mMaxSize; + protected boolean mNormalizeSize = true; + + private float mHighlightCircleWidth = 2.5f; + + public BubbleDataSet(List yVals, String label) { + super(yVals, label); + } + + @Override + public void setHighlightCircleWidth(float width) { + mHighlightCircleWidth = Utils.convertDpToPixel(width); + } + + @Override + public float getHighlightCircleWidth() { + return mHighlightCircleWidth; + } + + @Override + protected void calcMinMax(BubbleEntry e) { + super.calcMinMax(e); + + final float size = e.getSize(); + + if (size > mMaxSize) { + mMaxSize = size; + } + } + + @Override + public DataSet copy() { + + List yVals = new ArrayList(); + + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); + } + + BubbleDataSet copied = new BubbleDataSet(yVals, getLabel()); + copied.mColors = mColors; + copied.mHighLightColor = mHighLightColor; + + return copied; + } + + @Override + public float getMaxSize() { + return mMaxSize; + } + + @Override + public boolean isNormalizeSizeEnabled() { + return mNormalizeSize; + } + + public void setNormalizeSizeEnabled(boolean normalizeSize) { + mNormalizeSize = normalizeSize; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java new file mode 100644 index 0000000000..35d8330aaa --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java @@ -0,0 +1,91 @@ + +package com.github.mikephil.charting.data; + +import android.annotation.SuppressLint; +import android.graphics.drawable.Drawable; + +/** + * Subclass of Entry that holds a value for one entry in a BubbleChart. Bubble + * chart implementation: Copyright 2015 Pierre-Marc Airoldi Licensed under + * Apache License 2.0 + * + * @author Philipp Jahoda + */ +@SuppressLint("ParcelCreator") +public class BubbleEntry extends Entry { + + /** size value */ + private float mSize = 0f; + + /** + * Constructor. + * + * @param x The value on the x-axis. + * @param y The value on the y-axis. + * @param size The size of the bubble. + */ + public BubbleEntry(float x, float y, float size) { + super(x, y); + this.mSize = size; + } + + /** + * Constructor. + * + * @param x The value on the x-axis. + * @param y The value on the y-axis. + * @param size The size of the bubble. + * @param data Spot for additional data this Entry represents. + */ + public BubbleEntry(float x, float y, float size, Object data) { + super(x, y, data); + this.mSize = size; + } + + /** + * Constructor. + * + * @param x The value on the x-axis. + * @param y The value on the y-axis. + * @param size The size of the bubble. + * @param icon Icon image + */ + public BubbleEntry(float x, float y, float size, Drawable icon) { + super(x, y, icon); + this.mSize = size; + } + + /** + * Constructor. + * + * @param x The value on the x-axis. + * @param y The value on the y-axis. + * @param size The size of the bubble. + * @param icon Icon image + * @param data Spot for additional data this Entry represents. + */ + public BubbleEntry(float x, float y, float size, Drawable icon, Object data) { + super(x, y, icon, data); + this.mSize = size; + } + + public BubbleEntry copy() { + + BubbleEntry c = new BubbleEntry(getX(), getY(), mSize, getData()); + return c; + } + + /** + * Returns the size of this entry (the size of the bubble). + * + * @return + */ + public float getSize() { + return mSize; + } + + public void setSize(float size) { + this.mSize = size; + } + +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java new file mode 100644 index 0000000000..1e90db2ba5 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java @@ -0,0 +1,21 @@ +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; + +import java.util.ArrayList; +import java.util.List; + +public class CandleData extends BarLineScatterCandleBubbleData { + + public CandleData() { + super(); + } + + public CandleData(List dataSets) { + super(dataSets); + } + + public CandleData(ICandleDataSet... dataSets) { + super(dataSets); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java new file mode 100644 index 0000000000..7574b78b27 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java @@ -0,0 +1,292 @@ + +package com.github.mikephil.charting.data; + +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.Utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * DataSet for the CandleStickChart. + * + * @author Philipp Jahoda + */ +public class CandleDataSet extends LineScatterCandleRadarDataSet implements ICandleDataSet { + + /** + * the width of the shadow of the candle + */ + private float mShadowWidth = 3f; + + /** + * should the candle bars show? + * when false, only "ticks" will show + *

+ * - default: true + */ + private boolean mShowCandleBar = true; + + /** + * the space between the candle entries, default 0.1f (10%) + */ + private float mBarSpace = 0.1f; + + /** + * use candle color for the shadow + */ + private boolean mShadowColorSameAsCandle = false; + + /** + * paint style when open < close + * increasing candlesticks are traditionally hollow + */ + protected Paint.Style mIncreasingPaintStyle = Paint.Style.STROKE; + + /** + * paint style when open > close + * descreasing candlesticks are traditionally filled + */ + protected Paint.Style mDecreasingPaintStyle = Paint.Style.FILL; + + /** + * color for open == close + */ + protected int mNeutralColor = ColorTemplate.COLOR_SKIP; + + /** + * color for open < close + */ + protected int mIncreasingColor = ColorTemplate.COLOR_SKIP; + + /** + * color for open > close + */ + protected int mDecreasingColor = ColorTemplate.COLOR_SKIP; + + /** + * shadow line color, set -1 for backward compatibility and uses default + * color + */ + protected int mShadowColor = ColorTemplate.COLOR_SKIP; + + public CandleDataSet(List yVals, String label) { + super(yVals, label); + } + + @Override + public DataSet copy() { + + List yVals = new ArrayList(); + yVals.clear(); + + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); + } + + CandleDataSet copied = new CandleDataSet(yVals, getLabel()); + copied.mColors = mColors; + copied.mShadowWidth = mShadowWidth; + copied.mShowCandleBar = mShowCandleBar; + copied.mBarSpace = mBarSpace; + copied.mHighLightColor = mHighLightColor; + copied.mIncreasingPaintStyle = mIncreasingPaintStyle; + copied.mDecreasingPaintStyle = mDecreasingPaintStyle; + copied.mShadowColor = mShadowColor; + + return copied; + } + + @Override + protected void calcMinMax(CandleEntry e) { + + if (e.getLow() < mYMin) + mYMin = e.getLow(); + + if (e.getHigh() > mYMax) + mYMax = e.getHigh(); + + calcMinMaxX(e); + } + + @Override + protected void calcMinMaxY(CandleEntry e) { + + if (e.getHigh() < mYMin) + mYMin = e.getHigh(); + + if (e.getHigh() > mYMax) + mYMax = e.getHigh(); + + if (e.getLow() < mYMin) + mYMin = e.getLow(); + + if (e.getLow() > mYMax) + mYMax = e.getLow(); + } + + /** + * Sets the space that is left out on the left and right side of each + * candle, default 0.1f (10%), max 0.45f, min 0f + * + * @param space + */ + public void setBarSpace(float space) { + + if (space < 0f) + space = 0f; + if (space > 0.45f) + space = 0.45f; + + mBarSpace = space; + } + + @Override + public float getBarSpace() { + return mBarSpace; + } + + /** + * Sets the width of the candle-shadow-line in pixels. Default 3f. + * + * @param width + */ + public void setShadowWidth(float width) { + mShadowWidth = Utils.convertDpToPixel(width); + } + + @Override + public float getShadowWidth() { + return mShadowWidth; + } + + /** + * Sets whether the candle bars should show? + * + * @param showCandleBar + */ + public void setShowCandleBar(boolean showCandleBar) { + mShowCandleBar = showCandleBar; + } + + @Override + public boolean getShowCandleBar() { + return mShowCandleBar; + } + + // TODO + /** + * It is necessary to implement ColorsList class that will encapsulate + * colors list functionality, because It's wrong to copy paste setColor, + * addColor, ... resetColors for each time when we want to add a coloring + * options for one of objects + * + * @author Mesrop + */ + + /** BELOW THIS COLOR HANDLING */ + + /** + * Sets the one and ONLY color that should be used for this DataSet when + * open == close. + * + * @param color + */ + public void setNeutralColor(int color) { + mNeutralColor = color; + } + + @Override + public int getNeutralColor() { + return mNeutralColor; + } + + /** + * Sets the one and ONLY color that should be used for this DataSet when + * open <= close. + * + * @param color + */ + public void setIncreasingColor(int color) { + mIncreasingColor = color; + } + + @Override + public int getIncreasingColor() { + return mIncreasingColor; + } + + /** + * Sets the one and ONLY color that should be used for this DataSet when + * open > close. + * + * @param color + */ + public void setDecreasingColor(int color) { + mDecreasingColor = color; + } + + @Override + public int getDecreasingColor() { + return mDecreasingColor; + } + + @Override + public Paint.Style getIncreasingPaintStyle() { + return mIncreasingPaintStyle; + } + + /** + * Sets paint style when open < close + * + * @param paintStyle + */ + public void setIncreasingPaintStyle(Paint.Style paintStyle) { + this.mIncreasingPaintStyle = paintStyle; + } + + @Override + public Paint.Style getDecreasingPaintStyle() { + return mDecreasingPaintStyle; + } + + /** + * Sets paint style when open > close + * + * @param decreasingPaintStyle + */ + public void setDecreasingPaintStyle(Paint.Style decreasingPaintStyle) { + this.mDecreasingPaintStyle = decreasingPaintStyle; + } + + @Override + public int getShadowColor() { + return mShadowColor; + } + + /** + * Sets shadow color for all entries + * + * @param shadowColor + */ + public void setShadowColor(int shadowColor) { + this.mShadowColor = shadowColor; + } + + @Override + public boolean getShadowColorSameAsCandle() { + return mShadowColorSameAsCandle; + } + + /** + * Sets shadow color to be the same color as the candle color + * + * @param shadowColorSameAsCandle + */ + public void setShadowColorSameAsCandle(boolean shadowColorSameAsCandle) { + this.mShadowColorSameAsCandle = shadowColorSameAsCandle; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java similarity index 54% rename from MPChartLib/src/com/github/mikephil/charting/data/CandleEntry.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java index e65be942da..5a049957a2 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java @@ -1,11 +1,15 @@ package com.github.mikephil.charting.data; +import android.annotation.SuppressLint; +import android.graphics.drawable.Drawable; + /** * Subclass of Entry that holds all values for one entry in a CandleStickChart. * * @author Philipp Jahoda */ +@SuppressLint("ParcelCreator") public class CandleEntry extends Entry { /** shadow-high value */ @@ -23,14 +27,34 @@ public class CandleEntry extends Entry { /** * Constructor. * - * @param xIndex The index on the x-axis. - * @param shadowH The (shadow) high value. - * @param shadowL The (shadow) low value. + * @param x The value on the x-axis + * @param shadowH The (shadow) high value + * @param shadowL The (shadow) low value + * @param open The open value + * @param close The close value + */ + public CandleEntry(float x, float shadowH, float shadowL, float open, float close) { + super(x, (shadowH + shadowL) / 2f); + + this.mShadowHigh = shadowH; + this.mShadowLow = shadowL; + this.mOpen = open; + this.mClose = close; + } + + /** + * Constructor. + * + * @param x The value on the x-axis + * @param shadowH The (shadow) high value + * @param shadowL The (shadow) low value * @param open * @param close + * @param data Spot for additional data this Entry represents */ - public CandleEntry(int xIndex, float shadowH, float shadowL, float open, float close) { - super((shadowH + shadowL) / 2f, xIndex); + public CandleEntry(float x, float shadowH, float shadowL, float open, float close, + Object data) { + super(x, (shadowH + shadowL) / 2f, data); this.mShadowHigh = shadowH; this.mShadowLow = shadowL; @@ -40,17 +64,38 @@ public CandleEntry(int xIndex, float shadowH, float shadowL, float open, float c /** * Constructor. - * - * @param xIndex The index on the x-axis. - * @param shadowH The (shadow) high value. - * @param shadowL The (shadow) low value. + * + * @param x The value on the x-axis + * @param shadowH The (shadow) high value + * @param shadowL The (shadow) low value + * @param open + * @param close + * @param icon Icon image + */ + public CandleEntry(float x, float shadowH, float shadowL, float open, float close, + Drawable icon) { + super(x, (shadowH + shadowL) / 2f, icon); + + this.mShadowHigh = shadowH; + this.mShadowLow = shadowL; + this.mOpen = open; + this.mClose = close; + } + + /** + * Constructor. + * + * @param x The value on the x-axis + * @param shadowH The (shadow) high value + * @param shadowL The (shadow) low value * @param open * @param close - * @param data Spot for additional data this Entry represents. + * @param icon Icon image + * @param data Spot for additional data this Entry represents */ - public CandleEntry(int xIndex, float shadowH, float shadowL, float open, float close, - Object data) { - super((shadowH + shadowL) / 2f, xIndex, data); + public CandleEntry(float x, float shadowH, float shadowL, float open, float close, + Drawable icon, Object data) { + super(x, (shadowH + shadowL) / 2f, icon, data); this.mShadowHigh = shadowH; this.mShadowLow = shadowL; @@ -82,13 +127,13 @@ public float getBodyRange() { * low) */ @Override - public float getVal() { - return super.getVal(); + public float getY() { + return super.getY(); } public CandleEntry copy() { - CandleEntry c = new CandleEntry(getXIndex(), mShadowHigh, mShadowLow, mOpen, + CandleEntry c = new CandleEntry(getX(), mShadowHigh, mShadowLow, mOpen, mClose, getData()); return c; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java new file mode 100644 index 0000000000..60d89f4753 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -0,0 +1,821 @@ + +package com.github.mikephil.charting.data; + +import android.graphics.Typeface; +import android.util.Log; + +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class that holds all relevant data that represents the chart. That involves + * at least one (or more) DataSets, and an array of x-values. + * + * @author Philipp Jahoda + */ +public abstract class ChartData> { + + /** + * maximum y-value in the value array across all axes + */ + protected float mYMax = -Float.MAX_VALUE; + + /** + * the minimum y-value in the value array across all axes + */ + protected float mYMin = Float.MAX_VALUE; + + /** + * maximum x-value in the value array + */ + protected float mXMax = -Float.MAX_VALUE; + + /** + * minimum x-value in the value array + */ + protected float mXMin = Float.MAX_VALUE; + + + protected float mLeftAxisMax = -Float.MAX_VALUE; + + protected float mLeftAxisMin = Float.MAX_VALUE; + + protected float mRightAxisMax = -Float.MAX_VALUE; + + protected float mRightAxisMin = Float.MAX_VALUE; + + /** + * array that holds all DataSets the ChartData object represents + */ + protected List mDataSets; + + /** + * Default constructor. + */ + public ChartData() { + mDataSets = new ArrayList(); + } + + /** + * Constructor taking single or multiple DataSet objects. + * + * @param dataSets + */ + public ChartData(T... dataSets) { + mDataSets = arrayToList(dataSets); + notifyDataChanged(); + } + + /** + * Created because Arrays.asList(...) does not support modification. + * + * @param array + * @return + */ + private List arrayToList(T[] array) { + + List list = new ArrayList<>(); + + for (T set : array) { + list.add(set); + } + + return list; + } + + /** + * constructor for chart data + * + * @param sets the dataset array + */ + public ChartData(List sets) { + this.mDataSets = sets; + notifyDataChanged(); + } + + /** + * Call this method to let the ChartData know that the underlying data has + * changed. Calling this performs all necessary recalculations needed when + * the contained data has changed. + */ + public void notifyDataChanged() { + calcMinMax(); + } + + /** + * Calc minimum and maximum y-values over all DataSets. + * Tell DataSets to recalculate their min and max y-values, this is only needed for autoScaleMinMax. + * + * @param fromX the x-value to start the calculation from + * @param toX the x-value to which the calculation should be performed + */ + public void calcMinMaxY(float fromX, float toX) { + + for (T set : mDataSets) { + set.calcMinMaxY(fromX, toX); + } + + // apply the new data + calcMinMax(); + } + + /** + * Calc minimum and maximum values (both x and y) over all DataSets. + */ + protected void calcMinMax() { + + if (mDataSets == null) + return; + + mYMax = -Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + + for (T set : mDataSets) { + calcMinMax(set); + } + + mLeftAxisMax = -Float.MAX_VALUE; + mLeftAxisMin = Float.MAX_VALUE; + mRightAxisMax = -Float.MAX_VALUE; + mRightAxisMin = Float.MAX_VALUE; + + // left axis + T firstLeft = getFirstLeft(mDataSets); + + if (firstLeft != null) { + + mLeftAxisMax = firstLeft.getYMax(); + mLeftAxisMin = firstLeft.getYMin(); + + for (T dataSet : mDataSets) { + if (dataSet.getAxisDependency() == AxisDependency.LEFT) { + if (dataSet.getYMin() < mLeftAxisMin) + mLeftAxisMin = dataSet.getYMin(); + + if (dataSet.getYMax() > mLeftAxisMax) + mLeftAxisMax = dataSet.getYMax(); + } + } + } + + // right axis + T firstRight = getFirstRight(mDataSets); + + if (firstRight != null) { + + mRightAxisMax = firstRight.getYMax(); + mRightAxisMin = firstRight.getYMin(); + + for (T dataSet : mDataSets) { + if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { + if (dataSet.getYMin() < mRightAxisMin) + mRightAxisMin = dataSet.getYMin(); + + if (dataSet.getYMax() > mRightAxisMax) + mRightAxisMax = dataSet.getYMax(); + } + } + } + } + + /** ONLY GETTERS AND SETTERS BELOW THIS */ + + /** + * returns the number of LineDataSets this object contains + * + * @return + */ + public int getDataSetCount() { + if (mDataSets == null) + return 0; + return mDataSets.size(); + } + + /** + * Returns the smallest y-value the data object contains. + * + * @return + */ + public float getYMin() { + return mYMin; + } + + /** + * Returns the minimum y-value for the specified axis. + * + * @param axis + * @return + */ + public float getYMin(AxisDependency axis) { + if (axis == AxisDependency.LEFT) { + + if (mLeftAxisMin == Float.MAX_VALUE) { + return mRightAxisMin; + } else + return mLeftAxisMin; + } else { + if (mRightAxisMin == Float.MAX_VALUE) { + return mLeftAxisMin; + } else + return mRightAxisMin; + } + } + + /** + * Returns the greatest y-value the data object contains. + * + * @return + */ + public float getYMax() { + return mYMax; + } + + /** + * Returns the maximum y-value for the specified axis. + * + * @param axis + * @return + */ + public float getYMax(AxisDependency axis) { + if (axis == AxisDependency.LEFT) { + + if (mLeftAxisMax == -Float.MAX_VALUE) { + return mRightAxisMax; + } else + return mLeftAxisMax; + } else { + if (mRightAxisMax == -Float.MAX_VALUE) { + return mLeftAxisMax; + } else + return mRightAxisMax; + } + } + + /** + * Returns the minimum x-value this data object contains. + * + * @return + */ + public float getXMin() { + return mXMin; + } + + /** + * Returns the maximum x-value this data object contains. + * + * @return + */ + public float getXMax() { + return mXMax; + } + + /** + * Returns all DataSet objects this ChartData object holds. + * + * @return + */ + public List getDataSets() { + return mDataSets; + } + + /** + * Retrieve the index of a DataSet with a specific label from the ChartData. + * Search can be case sensitive or not. IMPORTANT: This method does + * calculations at runtime, do not over-use in performance critical + * situations. + * + * @param dataSets the DataSet array to search + * @param label + * @param ignorecase if true, the search is not case-sensitive + * @return + */ + protected int getDataSetIndexByLabel(List dataSets, String label, + boolean ignorecase) { + + if (ignorecase) { + for (int i = 0; i < dataSets.size(); i++) + if (label.equalsIgnoreCase(dataSets.get(i).getLabel())) + return i; + } else { + for (int i = 0; i < dataSets.size(); i++) + if (label.equals(dataSets.get(i).getLabel())) + return i; + } + + return -1; + } + + /** + * Returns the labels of all DataSets as a string array. + * + * @return + */ + public String[] getDataSetLabels() { + + String[] types = new String[mDataSets.size()]; + + for (int i = 0; i < mDataSets.size(); i++) { + types[i] = mDataSets.get(i).getLabel(); + } + + return types; + } + + /** + * Get the Entry for a corresponding highlight object + * + * @param highlight + * @return the entry that is highlighted + */ + public Entry getEntryForHighlight(Highlight highlight) { + if (highlight.getDataSetIndex() >= mDataSets.size()) + return null; + else { + return mDataSets.get(highlight.getDataSetIndex()).getEntryForXValue(highlight.getX(), highlight.getY()); + } + } + + /** + * Returns the DataSet object with the given label. Search can be case + * sensitive or not. IMPORTANT: This method does calculations at runtime. + * Use with care in performance critical situations. + * + * @param label + * @param ignorecase + * @return + */ + public T getDataSetByLabel(String label, boolean ignorecase) { + + int index = getDataSetIndexByLabel(mDataSets, label, ignorecase); + + if (index < 0 || index >= mDataSets.size()) + return null; + else + return mDataSets.get(index); + } + + public T getDataSetByIndex(int index) { + + if (mDataSets == null || index < 0 || index >= mDataSets.size()) + return null; + + return mDataSets.get(index); + } + + /** + * Adds a DataSet dynamically. + * + * @param d + */ + public void addDataSet(T d) { + + if (d == null) + return; + + calcMinMax(d); + + mDataSets.add(d); + } + + /** + * Removes the given DataSet from this data object. Also recalculates all + * minimum and maximum values. Returns true if a DataSet was removed, false + * if no DataSet could be removed. + * + * @param d + */ + public boolean removeDataSet(T d) { + + if (d == null) + return false; + + boolean removed = mDataSets.remove(d); + + // if a DataSet was removed + if (removed) { + calcMinMax(); + } + + return removed; + } + + /** + * Removes the DataSet at the given index in the DataSet array from the data + * object. Also recalculates all minimum and maximum values. Returns true if + * a DataSet was removed, false if no DataSet could be removed. + * + * @param index + */ + public boolean removeDataSet(int index) { + + if (index >= mDataSets.size() || index < 0) + return false; + + T set = mDataSets.get(index); + return removeDataSet(set); + } + + /** + * Adds an Entry to the DataSet at the specified index. + * Entries are added to the end of the list. + * + * @param e + * @param dataSetIndex + */ + public void addEntry(Entry e, int dataSetIndex) { + + if (mDataSets.size() > dataSetIndex && dataSetIndex >= 0) { + + IDataSet set = mDataSets.get(dataSetIndex); + // add the entry to the dataset + if (!set.addEntry(e)) + return; + + calcMinMax(e, set.getAxisDependency()); + + } else { + Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low."); + } + } + + /** + * Adjusts the current minimum and maximum values based on the provided Entry object. + * + * @param e + * @param axis + */ + protected void calcMinMax(Entry e, AxisDependency axis) { + + if (mYMax < e.getY()) + mYMax = e.getY(); + if (mYMin > e.getY()) + mYMin = e.getY(); + + if (mXMax < e.getX()) + mXMax = e.getX(); + if (mXMin > e.getX()) + mXMin = e.getX(); + + if (axis == AxisDependency.LEFT) { + + if (mLeftAxisMax < e.getY()) + mLeftAxisMax = e.getY(); + if (mLeftAxisMin > e.getY()) + mLeftAxisMin = e.getY(); + } else { + if (mRightAxisMax < e.getY()) + mRightAxisMax = e.getY(); + if (mRightAxisMin > e.getY()) + mRightAxisMin = e.getY(); + } + } + + /** + * Adjusts the minimum and maximum values based on the given DataSet. + * + * @param d + */ + protected void calcMinMax(T d) { + + if (mYMax < d.getYMax()) + mYMax = d.getYMax(); + if (mYMin > d.getYMin()) + mYMin = d.getYMin(); + + if (mXMax < d.getXMax()) + mXMax = d.getXMax(); + if (mXMin > d.getXMin()) + mXMin = d.getXMin(); + + if (d.getAxisDependency() == AxisDependency.LEFT) { + + if (mLeftAxisMax < d.getYMax()) + mLeftAxisMax = d.getYMax(); + if (mLeftAxisMin > d.getYMin()) + mLeftAxisMin = d.getYMin(); + } else { + if (mRightAxisMax < d.getYMax()) + mRightAxisMax = d.getYMax(); + if (mRightAxisMin > d.getYMin()) + mRightAxisMin = d.getYMin(); + } + } + + /** + * Removes the given Entry object from the DataSet at the specified index. + * + * @param e + * @param dataSetIndex + */ + public boolean removeEntry(Entry e, int dataSetIndex) { + + // entry null, outofbounds + if (e == null || dataSetIndex >= mDataSets.size()) + return false; + + IDataSet set = mDataSets.get(dataSetIndex); + + if (set != null) { + // remove the entry from the dataset + boolean removed = set.removeEntry(e); + + if (removed) { + calcMinMax(); + } + + return removed; + } else + return false; + } + + /** + * Removes the Entry object closest to the given DataSet at the + * specified index. Returns true if an Entry was removed, false if no Entry + * was found that meets the specified requirements. + * + * @param xValue + * @param dataSetIndex + * @return + */ + public boolean removeEntry(float xValue, int dataSetIndex) { + + if (dataSetIndex >= mDataSets.size()) + return false; + + IDataSet dataSet = mDataSets.get(dataSetIndex); + Entry e = dataSet.getEntryForXValue(xValue, Float.NaN); + + if (e == null) + return false; + + return removeEntry(e, dataSetIndex); + } + + /** + * Returns the DataSet that contains the provided Entry, or null, if no + * DataSet contains this Entry. + * + * @param e + * @return + */ + public T getDataSetForEntry(Entry e) { + + if (e == null) + return null; + + for (int i = 0; i < mDataSets.size(); i++) { + + T set = mDataSets.get(i); + + for (int j = 0; j < set.getEntryCount(); j++) { + if (e.equalTo(set.getEntryForXValue(e.getX(), e.getY()))) + return set; + } + } + + return null; + } + + /** + * Returns all colors used across all DataSet objects this object + * represents. + * + * @return + */ + public int[] getColors() { + + if (mDataSets == null) + return null; + + int clrcnt = 0; + + for (int i = 0; i < mDataSets.size(); i++) { + clrcnt += mDataSets.get(i).getColors().size(); + } + + int[] colors = new int[clrcnt]; + int cnt = 0; + + for (int i = 0; i < mDataSets.size(); i++) { + + List clrs = mDataSets.get(i).getColors(); + + for (Integer clr : clrs) { + colors[cnt] = clr; + cnt++; + } + } + + return colors; + } + + /** + * Returns the index of the provided DataSet in the DataSet array of this data object, or -1 if it does not exist. + * + * @param dataSet + * @return + */ + public int getIndexOfDataSet(T dataSet) { + return mDataSets.indexOf(dataSet); + } + + /** + * Returns the first DataSet from the datasets-array that has it's dependency on the left axis. + * Returns null if no DataSet with left dependency could be found. + * + * @return + */ + protected T getFirstLeft(List sets) { + for (T dataSet : sets) { + if (dataSet.getAxisDependency() == AxisDependency.LEFT) + return dataSet; + } + return null; + } + + /** + * Returns the first DataSet from the datasets-array that has it's dependency on the right axis. + * Returns null if no DataSet with right dependency could be found. + * + * @return + */ + public T getFirstRight(List sets) { + for (T dataSet : sets) { + if (dataSet.getAxisDependency() == AxisDependency.RIGHT) + return dataSet; + } + return null; + } + + /** + * Sets a custom IValueFormatter for all DataSets this data object contains. + * + * @param f + */ + public void setValueFormatter(IValueFormatter f) { + if (f == null) + return; + else { + for (IDataSet set : mDataSets) { + set.setValueFormatter(f); + } + } + } + + /** + * Sets the color of the value-text (color in which the value-labels are + * drawn) for all DataSets this data object contains. + * + * @param color + */ + public void setValueTextColor(int color) { + for (IDataSet set : mDataSets) { + set.setValueTextColor(color); + } + } + + /** + * Sets the same list of value-colors for all DataSets this + * data object contains. + * + * @param colors + */ + public void setValueTextColors(List colors) { + for (IDataSet set : mDataSets) { + set.setValueTextColors(colors); + } + } + + /** + * Sets the Typeface for all value-labels for all DataSets this data object + * contains. + * + * @param tf + */ + public void setValueTypeface(Typeface tf) { + for (IDataSet set : mDataSets) { + set.setValueTypeface(tf); + } + } + + /** + * Sets the size (in dp) of the value-text for all DataSets this data object + * contains. + * + * @param size + */ + public void setValueTextSize(float size) { + for (IDataSet set : mDataSets) { + set.setValueTextSize(size); + } + } + + /** + * Enables / disables drawing values (value-text) for all DataSets this data + * object contains. + * + * @param enabled + */ + public void setDrawValues(boolean enabled) { + for (IDataSet set : mDataSets) { + set.setDrawValues(enabled); + } + } + + /** + * Enables / disables highlighting values for all DataSets this data object + * contains. If set to true, this means that values can + * be highlighted programmatically or by touch gesture. + */ + public void setHighlightEnabled(boolean enabled) { + for (IDataSet set : mDataSets) { + set.setHighlightEnabled(enabled); + } + } + + /** + * Returns true if highlighting of all underlying values is enabled, false + * if not. + * + * @return + */ + public boolean isHighlightEnabled() { + for (IDataSet set : mDataSets) { + if (!set.isHighlightEnabled()) + return false; + } + return true; + } + + /** + * Clears this data object from all DataSets and removes all Entries. Don't + * forget to invalidate the chart after this. + */ + public void clearValues() { + if (mDataSets != null) { + mDataSets.clear(); + } + notifyDataChanged(); + } + + /** + * Checks if this data object contains the specified DataSet. Returns true + * if so, false if not. + * + * @param dataSet + * @return + */ + public boolean contains(T dataSet) { + + for (T set : mDataSets) { + if (set.equals(dataSet)) + return true; + } + + return false; + } + + /** + * Returns the total entry count across all DataSet objects this data object contains. + * + * @return + */ + public int getEntryCount() { + + int count = 0; + + for (T set : mDataSets) { + count += set.getEntryCount(); + } + + return count; + } + + /** + * Returns the DataSet object with the maximum number of entries or null if there are no DataSets. + * + * @return + */ + public T getMaxEntryCountSet() { + + if (mDataSets == null || mDataSets.isEmpty()) + return null; + + T max = mDataSets.get(0); + + for (T set : mDataSets) { + + if (set.getEntryCount() > max.getEntryCount()) + max = set; + } + + return max; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java new file mode 100644 index 0000000000..c81097da90 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java @@ -0,0 +1,247 @@ + +package com.github.mikephil.charting.data; + +import android.util.Log; + +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; + +import java.util.ArrayList; +import java.util.List; + +/** + * Data object that allows the combination of Line-, Bar-, Scatter-, Bubble- and + * CandleData. Used in the CombinedChart class. + * + * @author Philipp Jahoda + */ +public class CombinedData extends BarLineScatterCandleBubbleData> { + + private LineData mLineData; + private BarData mBarData; + private ScatterData mScatterData; + private CandleData mCandleData; + private BubbleData mBubbleData; + + public CombinedData() { + super(); + } + + public void setData(LineData data) { + mLineData = data; + notifyDataChanged(); + } + + public void setData(BarData data) { + mBarData = data; + notifyDataChanged(); + } + + public void setData(ScatterData data) { + mScatterData = data; + notifyDataChanged(); + } + + public void setData(CandleData data) { + mCandleData = data; + notifyDataChanged(); + } + + public void setData(BubbleData data) { + mBubbleData = data; + notifyDataChanged(); + } + + @Override + public void calcMinMax() { + + if(mDataSets == null){ + mDataSets = new ArrayList<>(); + } + mDataSets.clear(); + + mYMax = -Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + + mLeftAxisMax = -Float.MAX_VALUE; + mLeftAxisMin = Float.MAX_VALUE; + mRightAxisMax = -Float.MAX_VALUE; + mRightAxisMin = Float.MAX_VALUE; + + List allData = getAllData(); + + for (ChartData data : allData) { + + data.calcMinMax(); + + List> sets = data.getDataSets(); + mDataSets.addAll(sets); + + if (data.getYMax() > mYMax) + mYMax = data.getYMax(); + + if (data.getYMin() < mYMin) + mYMin = data.getYMin(); + + if (data.getXMax() > mXMax) + mXMax = data.getXMax(); + + if (data.getXMin() < mXMin) + mXMin = data.getXMin(); + + if (data.mLeftAxisMax > mLeftAxisMax) + mLeftAxisMax = data.mLeftAxisMax; + + if (data.mLeftAxisMin < mLeftAxisMin) + mLeftAxisMin = data.mLeftAxisMin; + + if (data.mRightAxisMax > mRightAxisMax) + mRightAxisMax = data.mRightAxisMax; + + if (data.mRightAxisMin < mRightAxisMin) + mRightAxisMin = data.mRightAxisMin; + + } + } + + public BubbleData getBubbleData() { + return mBubbleData; + } + + public LineData getLineData() { + return mLineData; + } + + public BarData getBarData() { + return mBarData; + } + + public ScatterData getScatterData() { + return mScatterData; + } + + public CandleData getCandleData() { + return mCandleData; + } + + /** + * Returns all data objects in row: line-bar-scatter-candle-bubble if not null. + * + * @return + */ + public List getAllData() { + + List data = new ArrayList(); + if (mLineData != null) + data.add(mLineData); + if (mBarData != null) + data.add(mBarData); + if (mScatterData != null) + data.add(mScatterData); + if (mCandleData != null) + data.add(mCandleData); + if (mBubbleData != null) + data.add(mBubbleData); + + return data; + } + + public BarLineScatterCandleBubbleData getDataByIndex(int index) { + return getAllData().get(index); + } + + @Override + public void notifyDataChanged() { + if (mLineData != null) + mLineData.notifyDataChanged(); + if (mBarData != null) + mBarData.notifyDataChanged(); + if (mCandleData != null) + mCandleData.notifyDataChanged(); + if (mScatterData != null) + mScatterData.notifyDataChanged(); + if (mBubbleData != null) + mBubbleData.notifyDataChanged(); + + calcMinMax(); // recalculate everything + } + + /** + * Get the Entry for a corresponding highlight object + * + * @param highlight + * @return the entry that is highlighted + */ + @Override + public Entry getEntryForHighlight(Highlight highlight) { + + List dataObjects = getAllData(); + + if (highlight.getDataIndex() >= dataObjects.size()) + return null; + + ChartData data = dataObjects.get(highlight.getDataIndex()); + + if (highlight.getDataSetIndex() >= data.getDataSetCount()) + return null; + else { + // The value of the highlighted entry could be NaN - + // if we are not interested in highlighting a specific value. + + List entries = data.getDataSetByIndex(highlight.getDataSetIndex()) + .getEntriesForXValue(highlight.getX()); + for (Entry entry : entries) + if (entry.getY() == highlight.getY() || + Float.isNaN(highlight.getY())) + return entry; + + return null; + } + } + + public int getDataIndex(ChartData data) { + return getAllData().indexOf(data); + } + + @Override + public boolean removeDataSet(IBarLineScatterCandleBubbleDataSet d) { + + List datas = getAllData(); + + boolean success = false; + + for (ChartData data : datas) { + + success = data.removeDataSet(d); + + if (success) { + break; + } + } + + return success; + } + + @Deprecated + @Override + public boolean removeDataSet(int index) { + Log.e("MPAndroidChart", "removeDataSet(int index) not supported for CombinedData"); + return false; + } + + @Deprecated + @Override + public boolean removeEntry(Entry e, int dataSetIndex) { + Log.e("MPAndroidChart", "removeEntry(...) not supported for CombinedData"); + return false; + } + + @Deprecated + @Override + public boolean removeEntry(float xValue, int dataSetIndex) { + Log.e("MPAndroidChart", "removeEntry(...) not supported for CombinedData"); + return false; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java new file mode 100644 index 0000000000..a96cfdcf66 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -0,0 +1,425 @@ + +package com.github.mikephil.charting.data; + +import java.util.ArrayList; +import java.util.List; + +/** + * The DataSet class represents one group or type of entries (Entry) in the + * Chart that belong together. It is designed to logically separate different + * groups of values inside the Chart (e.g. the values for a specific line in the + * LineChart, or the values of a specific group of bars in the BarChart). + * + * @author Philipp Jahoda + */ +public abstract class DataSet extends BaseDataSet { + + /** + * the entries that this DataSet represents / holds together + */ + protected List mValues = null; + + /** + * maximum y-value in the value array + */ + protected float mYMax = -Float.MAX_VALUE; + + /** + * minimum y-value in the value array + */ + protected float mYMin = Float.MAX_VALUE; + + /** + * maximum x-value in the value array + */ + protected float mXMax = -Float.MAX_VALUE; + + /** + * minimum x-value in the value array + */ + protected float mXMin = Float.MAX_VALUE; + + + /** + * Creates a new DataSet object with the given values (entries) it represents. Also, a + * label that describes the DataSet can be specified. The label can also be + * used to retrieve the DataSet from a ChartData object. + * + * @param values + * @param label + */ + public DataSet(List values, String label) { + super(label); + this.mValues = values; + + if (mValues == null) + mValues = new ArrayList(); + + calcMinMax(); + } + + @Override + public void calcMinMax() { + + if (mValues == null || mValues.isEmpty()) + return; + + mYMax = -Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + + for (T e : mValues) { + calcMinMax(e); + } + } + + @Override + public void calcMinMaxY(float fromX, float toX) { + + if (mValues == null || mValues.isEmpty()) + return; + + mYMax = -Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; + + int indexFrom = getEntryIndex(fromX, Float.NaN, Rounding.DOWN); + int indexTo = getEntryIndex(toX, Float.NaN, Rounding.UP); + + for (int i = indexFrom; i <= indexTo; i++) { + + // only recalculate y + calcMinMaxY(mValues.get(i)); + } + } + + /** + * Updates the min and max x and y value of this DataSet based on the given Entry. + * + * @param e + */ + protected void calcMinMax(T e) { + + if (e == null) + return; + + calcMinMaxX(e); + + calcMinMaxY(e); + } + + protected void calcMinMaxX(T e) { + + if (e.getX() < mXMin) + mXMin = e.getX(); + + if (e.getX() > mXMax) + mXMax = e.getX(); + } + + protected void calcMinMaxY(T e) { + + if (e.getY() < mYMin) + mYMin = e.getY(); + + if (e.getY() > mYMax) + mYMax = e.getY(); + } + + @Override + public int getEntryCount() { + return mValues.size(); + } + + /** + * Returns the array of entries that this DataSet represents. + * + * @return + */ + public List getValues() { + return mValues; + } + + /** + * Sets the array of entries that this DataSet represents, and calls notifyDataSetChanged() + * + * @return + */ + public void setValues(List values) { + mValues = values; + notifyDataSetChanged(); + } + + /** + * Provides an exact copy of the DataSet this method is used on. + * + * @return + */ + public abstract DataSet copy(); + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(toSimpleString()); + for (int i = 0; i < mValues.size(); i++) { + buffer.append(mValues.get(i).toString() + " "); + } + return buffer.toString(); + } + + /** + * Returns a simple string representation of the DataSet with the type and + * the number of Entries. + * + * @return + */ + public String toSimpleString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("DataSet, label: " + (getLabel() == null ? "" : getLabel()) + ", entries: " + mValues.size() + + "\n"); + return buffer.toString(); + } + + @Override + public float getYMin() { + return mYMin; + } + + @Override + public float getYMax() { + return mYMax; + } + + @Override + public float getXMin() { + return mXMin; + } + + @Override + public float getXMax() { + return mXMax; + } + + @Override + public void addEntryOrdered(T e) { + + if (e == null) + return; + + if (mValues == null) { + mValues = new ArrayList(); + } + + calcMinMax(e); + + if (mValues.size() > 0 && mValues.get(mValues.size() - 1).getX() > e.getX()) { + int closestIndex = getEntryIndex(e.getX(), e.getY(), Rounding.UP); + mValues.add(closestIndex, e); + } else { + mValues.add(e); + } + } + + @Override + public void clear() { + mValues.clear(); + notifyDataSetChanged(); + } + + @Override + public boolean addEntry(T e) { + + if (e == null) + return false; + + List values = getValues(); + if (values == null) { + values = new ArrayList(); + } + + calcMinMax(e); + + // add the entry + return values.add(e); + } + + @Override + public boolean removeEntry(T e) { + + if (e == null) + return false; + + if (mValues == null) + return false; + + // remove the entry + boolean removed = mValues.remove(e); + + if (removed) { + calcMinMax(); + } + + return removed; + } + + @Override + public int getEntryIndex(Entry e) { + return mValues.indexOf(e); + } + + @Override + public T getEntryForXValue(float xValue, float closestToY, Rounding rounding) { + + int index = getEntryIndex(xValue, closestToY, rounding); + if (index > -1) + return mValues.get(index); + return null; + } + + @Override + public T getEntryForXValue(float xValue, float closestToY) { + return getEntryForXValue(xValue, closestToY, Rounding.CLOSEST); + } + + @Override + public T getEntryForIndex(int index) { + return mValues.get(index); + } + + @Override + public int getEntryIndex(float xValue, float closestToY, Rounding rounding) { + + if (mValues == null || mValues.isEmpty()) + return -1; + + int low = 0; + int high = mValues.size() - 1; + int closest = high; + + while (low < high) { + int m = (low + high) / 2; + + final float d1 = mValues.get(m).getX() - xValue, + d2 = mValues.get(m + 1).getX() - xValue, + ad1 = Math.abs(d1), ad2 = Math.abs(d2); + + if (ad2 < ad1) { + // [m + 1] is closer to xValue + // Search in an higher place + low = m + 1; + } else if (ad1 < ad2) { + // [m] is closer to xValue + // Search in a lower place + high = m; + } else { + // We have multiple sequential x-value with same distance + + if (d1 >= 0.0) { + // Search in a lower place + high = m; + } else if (d1 < 0.0) { + // Search in an higher place + low = m + 1; + } + } + + closest = high; + } + + if (closest != -1) { + float closestXValue = mValues.get(closest).getX(); + if (rounding == Rounding.UP) { + // If rounding up, and found x-value is lower than specified x, and we can go upper... + if (closestXValue < xValue && closest < mValues.size() - 1) { + ++closest; + } + } else if (rounding == Rounding.DOWN) { + // If rounding down, and found x-value is upper than specified x, and we can go lower... + if (closestXValue > xValue && closest > 0) { + --closest; + } + } + + // Search by closest to y-value + if (!Float.isNaN(closestToY)) { + while (closest > 0 && mValues.get(closest - 1).getX() == closestXValue) + closest -= 1; + + float closestYValue = mValues.get(closest).getY(); + int closestYIndex = closest; + + while (true) { + closest += 1; + if (closest >= mValues.size()) + break; + + final Entry value = mValues.get(closest); + + if (value.getX() != closestXValue) + break; + + if (Math.abs(value.getY() - closestToY) < Math.abs(closestYValue - closestToY)) { + closestYValue = closestToY; + closestYIndex = closest; + } + } + + closest = closestYIndex; + } + } + + return closest; + } + + @Override + public List getEntriesForXValue(float xValue) { + + List entries = new ArrayList(); + + int low = 0; + int high = mValues.size() - 1; + + while (low <= high) { + int m = (high + low) / 2; + T entry = mValues.get(m); + + // if we have a match + if (xValue == entry.getX()) { + while (m > 0 && mValues.get(m - 1).getX() == xValue) + m--; + + high = mValues.size(); + + // loop over all "equal" entries + for (; m < high; m++) { + entry = mValues.get(m); + if (entry.getX() == xValue) { + entries.add(entry); + } else { + break; + } + } + + break; + } else { + if (xValue > entry.getX()) + low = m + 1; + else + high = m - 1; + } + } + + return entries; + } + + /** + * Determines how to round DataSet index values for + * {@link DataSet#getEntryIndex(float, float, Rounding)} DataSet.getEntryIndex()} + * when an exact x-index is not found. + */ + public enum Rounding { + UP, + DOWN, + CLOSEST, + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java new file mode 100644 index 0000000000..b7a887990d --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java @@ -0,0 +1,173 @@ + +package com.github.mikephil.charting.data; + +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.ParcelFormatException; +import android.os.Parcelable; + +import com.github.mikephil.charting.utils.Utils; + +/** + * Class representing one entry in the chart. Might contain multiple values. + * Might only contain a single value depending on the used constructor. + * + * @author Philipp Jahoda + */ +public class Entry extends BaseEntry implements Parcelable { + + /** the x value */ + private float x = 0f; + + public Entry() { + + } + + /** + * A Entry represents one single entry in the chart. + * + * @param x the x value + * @param y the y value (the actual value of the entry) + */ + public Entry(float x, float y) { + super(y); + this.x = x; + } + + /** + * A Entry represents one single entry in the chart. + * + * @param x the x value + * @param y the y value (the actual value of the entry) + * @param data Spot for additional data this Entry represents. + */ + public Entry(float x, float y, Object data) { + super(y, data); + this.x = x; + } + + /** + * A Entry represents one single entry in the chart. + * + * @param x the x value + * @param y the y value (the actual value of the entry) + * @param icon icon image + */ + public Entry(float x, float y, Drawable icon) { + super(y, icon); + this.x = x; + } + + /** + * A Entry represents one single entry in the chart. + * + * @param x the x value + * @param y the y value (the actual value of the entry) + * @param icon icon image + * @param data Spot for additional data this Entry represents. + */ + public Entry(float x, float y, Drawable icon, Object data) { + super(y, icon, data); + this.x = x; + } + + /** + * Returns the x-value of this Entry object. + * + * @return + */ + public float getX() { + return x; + } + + /** + * Sets the x-value of this Entry object. + * + * @param x + */ + public void setX(float x) { + this.x = x; + } + + /** + * returns an exact copy of the entry + * + * @return + */ + public Entry copy() { + Entry e = new Entry(x, getY(), getData()); + return e; + } + + /** + * Compares value, xIndex and data of the entries. Returns true if entries + * are equal in those points, false if not. Does not check by hash-code like + * it's done by the "equals" method. + * + * @param e + * @return + */ + public boolean equalTo(Entry e) { + + if (e == null) + return false; + + if (e.getData() != this.getData()) + return false; + + if (Math.abs(e.x - this.x) > Utils.FLOAT_EPSILON) + return false; + + if (Math.abs(e.getY() - this.getY()) > Utils.FLOAT_EPSILON) + return false; + + return true; + } + + /** + * returns a string representation of the entry containing x-index and value + */ + @Override + public String toString() { + return "Entry, x: " + x + " y: " + getY(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(this.x); + dest.writeFloat(this.getY()); + if (getData() != null) { + if (getData() instanceof Parcelable) { + dest.writeInt(1); + dest.writeParcelable((Parcelable) this.getData(), flags); + } else { + throw new ParcelFormatException("Cannot parcel an Entry with non-parcelable data"); + } + } else { + dest.writeInt(0); + } + } + + protected Entry(Parcel in) { + this.x = in.readFloat(); + this.setY(in.readFloat()); + if (in.readInt() == 1) { + this.setData(in.readParcelable(Object.class.getClassLoader())); + } + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public Entry createFromParcel(Parcel source) { + return new Entry(source); + } + + public Entry[] newArray(int size) { + return new Entry[size]; + } + }; +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java new file mode 100644 index 0000000000..4cf544874b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java @@ -0,0 +1,27 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; + +import java.util.ArrayList; +import java.util.List; + +/** + * Data object that encapsulates all data associated with a LineChart. + * + * @author Philipp Jahoda + */ +public class LineData extends BarLineScatterCandleBubbleData { + + public LineData() { + super(); + } + + public LineData(ILineDataSet... dataSets) { + super(dataSets); + } + + public LineData(List dataSets) { + super(dataSets); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java new file mode 100644 index 0000000000..5eced95e43 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java @@ -0,0 +1,415 @@ + +package com.github.mikephil.charting.data; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.util.Log; + +import com.github.mikephil.charting.formatter.DefaultFillFormatter; +import com.github.mikephil.charting.formatter.IFillFormatter; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.Utils; + +import java.util.ArrayList; +import java.util.List; + +public class LineDataSet extends LineRadarDataSet implements ILineDataSet { + + /** + * Drawing mode for this line dataset + **/ + private LineDataSet.Mode mMode = Mode.LINEAR; + + /** + * List representing all colors that are used for the circles + */ + private List mCircleColors = null; + + /** + * the color of the inner circles + */ + private int mCircleColorHole = Color.WHITE; + + /** + * the radius of the circle-shaped value indicators + */ + private float mCircleRadius = 8f; + + /** + * the hole radius of the circle-shaped value indicators + */ + private float mCircleHoleRadius = 4f; + + /** + * sets the intensity of the cubic lines + */ + private float mCubicIntensity = 0.2f; + + /** + * the path effect of this DataSet that makes dashed lines possible + */ + private DashPathEffect mDashPathEffect = null; + + /** + * formatter for customizing the position of the fill-line + */ + private IFillFormatter mFillFormatter = new DefaultFillFormatter(); + + /** + * if true, drawing circles is enabled + */ + private boolean mDrawCircles = true; + + private boolean mDrawCircleHole = true; + + + public LineDataSet(List yVals, String label) { + super(yVals, label); + + // mCircleRadius = Utils.convertDpToPixel(4f); + // mLineWidth = Utils.convertDpToPixel(1f); + + if (mCircleColors == null) { + mCircleColors = new ArrayList(); + } + mCircleColors.clear(); + + // default colors + // mColors.add(Color.rgb(192, 255, 140)); + // mColors.add(Color.rgb(255, 247, 140)); + mCircleColors.add(Color.rgb(140, 234, 255)); + } + + @Override + public DataSet copy() { + + List yVals = new ArrayList(); + + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); + } + + LineDataSet copied = new LineDataSet(yVals, getLabel()); + copied.mMode = mMode; + copied.mColors = mColors; + copied.mCircleRadius = mCircleRadius; + copied.mCircleHoleRadius = mCircleHoleRadius; + copied.mCircleColors = mCircleColors; + copied.mDashPathEffect = mDashPathEffect; + copied.mDrawCircles = mDrawCircles; + copied.mDrawCircleHole = mDrawCircleHole; + copied.mHighLightColor = mHighLightColor; + + return copied; + } + + /** + * Returns the drawing mode for this line dataset + * + * @return + */ + @Override + public LineDataSet.Mode getMode() { + return mMode; + } + + /** + * Returns the drawing mode for this LineDataSet + * + * @return + */ + public void setMode(LineDataSet.Mode mode) { + mMode = mode; + } + + /** + * Sets the intensity for cubic lines (if enabled). Max = 1f = very cubic, + * Min = 0.05f = low cubic effect, Default: 0.2f + * + * @param intensity + */ + public void setCubicIntensity(float intensity) { + + if (intensity > 1f) + intensity = 1f; + if (intensity < 0.05f) + intensity = 0.05f; + + mCubicIntensity = intensity; + } + + @Override + public float getCubicIntensity() { + return mCubicIntensity; + } + + + /** + * Sets the radius of the drawn circles. + * Default radius = 4f, Min = 1f + * + * @param radius + */ + public void setCircleRadius(float radius) { + + if (radius >= 1f) { + mCircleRadius = Utils.convertDpToPixel(radius); + } else { + Log.e("LineDataSet", "Circle radius cannot be < 1"); + } + } + + @Override + public float getCircleRadius() { + return mCircleRadius; + } + + /** + * Sets the hole radius of the drawn circles. + * Default radius = 2f, Min = 0.5f + * + * @param holeRadius + */ + public void setCircleHoleRadius(float holeRadius) { + + if (holeRadius >= 0.5f) { + mCircleHoleRadius = Utils.convertDpToPixel(holeRadius); + } else { + Log.e("LineDataSet", "Circle radius cannot be < 0.5"); + } + } + + @Override + public float getCircleHoleRadius() { + return mCircleHoleRadius; + } + + /** + * sets the size (radius) of the circle shpaed value indicators, + * default size = 4f + *

+ * This method is deprecated because of unclarity. Use setCircleRadius instead. + * + * @param size + */ + @Deprecated + public void setCircleSize(float size) { + setCircleRadius(size); + } + + /** + * This function is deprecated because of unclarity. Use getCircleRadius instead. + */ + @Deprecated + public float getCircleSize() { + return getCircleRadius(); + } + + /** + * Enables the line to be drawn in dashed mode, e.g. like this + * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. + * Keep in mind that hardware acceleration boosts performance. + * + * @param lineLength the length of the line pieces + * @param spaceLength the length of space in between the pieces + * @param phase offset, in degrees (normally, use 0) + */ + public void enableDashedLine(float lineLength, float spaceLength, float phase) { + mDashPathEffect = new DashPathEffect(new float[]{ + lineLength, spaceLength + }, phase); + } + + /** + * Disables the line to be drawn in dashed mode. + */ + public void disableDashedLine() { + mDashPathEffect = null; + } + + @Override + public boolean isDashedLineEnabled() { + return mDashPathEffect == null ? false : true; + } + + @Override + public DashPathEffect getDashPathEffect() { + return mDashPathEffect; + } + + /** + * set this to true to enable the drawing of circle indicators for this + * DataSet, default true + * + * @param enabled + */ + public void setDrawCircles(boolean enabled) { + this.mDrawCircles = enabled; + } + + @Override + public boolean isDrawCirclesEnabled() { + return mDrawCircles; + } + + @Deprecated + @Override + public boolean isDrawCubicEnabled() { + return mMode == Mode.CUBIC_BEZIER; + } + + @Deprecated + @Override + public boolean isDrawSteppedEnabled() { + return mMode == Mode.STEPPED; + } + + /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */ + + /** + * returns all colors specified for the circles + * + * @return + */ + public List getCircleColors() { + return mCircleColors; + } + + @Override + public int getCircleColor(int index) { + return mCircleColors.get(index); + } + + @Override + public int getCircleColorCount() { + return mCircleColors.size(); + } + + /** + * Sets the colors that should be used for the circles of this DataSet. + * Colors are reused as soon as the number of Entries the DataSet represents + * is higher than the size of the colors array. Make sure that the colors + * are already prepared (by calling getResources().getColor(...)) before + * adding them to the DataSet. + * + * @param colors + */ + public void setCircleColors(List colors) { + mCircleColors = colors; + } + + /** + * Sets the colors that should be used for the circles of this DataSet. + * Colors are reused as soon as the number of Entries the DataSet represents + * is higher than the size of the colors array. Make sure that the colors + * are already prepared (by calling getResources().getColor(...)) before + * adding them to the DataSet. + * + * @param colors + */ + public void setCircleColors(int... colors) { + this.mCircleColors = ColorTemplate.createColors(colors); + } + + /** + * ets the colors that should be used for the circles of this DataSet. + * Colors are reused as soon as the number of Entries the DataSet represents + * is higher than the size of the colors array. You can use + * "new String[] { R.color.red, R.color.green, ... }" to provide colors for + * this method. Internally, the colors are resolved using + * getResources().getColor(...) + * + * @param colors + */ + public void setCircleColors(int[] colors, Context c) { + + List clrs = mCircleColors; + if (clrs == null) { + clrs = new ArrayList<>(); + } + clrs.clear(); + + for (int color : colors) { + clrs.add(c.getResources().getColor(color)); + } + + mCircleColors = clrs; + } + + /** + * Sets the one and ONLY color that should be used for this DataSet. + * Internally, this recreates the colors array and adds the specified color. + * + * @param color + */ + public void setCircleColor(int color) { + resetCircleColors(); + mCircleColors.add(color); + } + + /** + * resets the circle-colors array and creates a new one + */ + public void resetCircleColors() { + if (mCircleColors == null) { + mCircleColors = new ArrayList(); + } + mCircleColors.clear(); + } + + /** + * Sets the color of the inner circle of the line-circles. + * + * @param color + */ + public void setCircleColorHole(int color) { + mCircleColorHole = color; + } + + @Override + public int getCircleHoleColor() { + return mCircleColorHole; + } + + /** + * Set this to true to allow drawing a hole in each data circle. + * + * @param enabled + */ + public void setDrawCircleHole(boolean enabled) { + mDrawCircleHole = enabled; + } + + @Override + public boolean isDrawCircleHoleEnabled() { + return mDrawCircleHole; + } + + /** + * Sets a custom IFillFormatter to the chart that handles the position of the + * filled-line for each DataSet. Set this to null to use the default logic. + * + * @param formatter + */ + public void setFillFormatter(IFillFormatter formatter) { + + if (formatter == null) + mFillFormatter = new DefaultFillFormatter(); + else + mFillFormatter = formatter; + } + + @Override + public IFillFormatter getFillFormatter() { + return mFillFormatter; + } + + public enum Mode { + LINEAR, + STEPPED, + CUBIC_BEZIER, + HORIZONTAL_BEZIER + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java similarity index 55% rename from MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java index 048b481828..deced96ee9 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java @@ -1,61 +1,84 @@ package com.github.mikephil.charting.data; +import android.annotation.TargetApi; import android.graphics.Color; +import android.graphics.drawable.Drawable; +import com.github.mikephil.charting.interfaces.datasets.ILineRadarDataSet; import com.github.mikephil.charting.utils.Utils; -import java.util.ArrayList; +import java.util.List; /** * Base dataset for line and radar DataSets. - * + * * @author Philipp Jahoda */ -public abstract class LineRadarDataSet extends BarLineScatterCandleDataSet { +public abstract class LineRadarDataSet extends LineScatterCandleRadarDataSet implements ILineRadarDataSet { - /** the color that is used for filling the line surface */ + /** + * the color that is used for filling the line surface + */ private int mFillColor = Color.rgb(140, 234, 255); - /** transparency used for filling line surface */ + /** + * the drawable to be used for filling the line surface + */ + protected Drawable mFillDrawable; + + /** + * transparency used for filling line surface + */ private int mFillAlpha = 85; - - /** the width of the drawn data lines */ + + /** + * the width of the drawn data lines + */ private float mLineWidth = 2.5f; - - /** if true, the data will also be drawn filled */ + + /** + * if true, the data will also be drawn filled + */ private boolean mDrawFilled = false; - -// private Shader mShader; - - public LineRadarDataSet(ArrayList yVals, String label) { + + + public LineRadarDataSet(List yVals, String label) { super(yVals, label); } - /** - * returns the color that is used for filling the line surface - * - * @return - */ + @Override public int getFillColor() { return mFillColor; } /** - * sets the color that is used for filling the line surface - * + * Sets the color that is used for filling the area below the line. + * Resets an eventually set "fillDrawable". + * * @param color */ public void setFillColor(int color) { mFillColor = color; + mFillDrawable = null; + } + + @Override + public Drawable getFillDrawable() { + return mFillDrawable; } /** - * returns the alpha value that is used for filling the line surface, - * default: 85 - * - * @return + * Sets the drawable to be used to fill the area below the line. + * + * @param drawable */ + @TargetApi(18) + public void setFillDrawable(Drawable drawable) { + this.mFillDrawable = drawable; + } + + @Override public int getFillAlpha() { return mFillAlpha; } @@ -63,62 +86,40 @@ public int getFillAlpha() { /** * sets the alpha value (transparency) that is used for filling the line * surface (0-255), default: 85 - * - * @param color + * + * @param alpha */ public void setFillAlpha(int alpha) { mFillAlpha = alpha; } - + /** * set the line width of the chart (min = 0.2f, max = 10f); default 1f NOTE: * thinner line == better performance, thicker line == worse performance - * + * * @param width */ public void setLineWidth(float width) { if (width < 0.2f) - width = 0.5f; + width = 0.2f; if (width > 10.0f) width = 10.0f; mLineWidth = Utils.convertDpToPixel(width); } - /** - * returns the width of the drawn chart line - * - * @return - */ + @Override public float getLineWidth() { return mLineWidth; } - - /** - * Set to true if the DataSet should be drawn filled (surface), and not just - * as a line, disabling this will give up to 20% performance boost on large - * datasets, default: false - * - * @param filled - */ + + @Override public void setDrawFilled(boolean filled) { mDrawFilled = filled; } - /** - * returns true if filled drawing is enabled, false if not - * - * @return - */ + @Override public boolean isDrawFilledEnabled() { return mDrawFilled; } - -// public void setShader(Shader s) { -// mShader = s; -// } -// -// public Shader getShader() { -// return mShader; -// } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java new file mode 100644 index 0000000000..90a0a43fb3 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java @@ -0,0 +1,112 @@ +package com.github.mikephil.charting.data; + +import android.graphics.DashPathEffect; + +import com.github.mikephil.charting.interfaces.datasets.ILineScatterCandleRadarDataSet; +import com.github.mikephil.charting.utils.Utils; + +import java.util.List; + +/** + * Created by Philipp Jahoda on 11/07/15. + */ +public abstract class LineScatterCandleRadarDataSet extends BarLineScatterCandleBubbleDataSet implements ILineScatterCandleRadarDataSet { + + protected boolean mDrawVerticalHighlightIndicator = true; + protected boolean mDrawHorizontalHighlightIndicator = true; + + /** the width of the highlight indicator lines */ + protected float mHighlightLineWidth = 0.5f; + + /** the path effect for dashed highlight-lines */ + protected DashPathEffect mHighlightDashPathEffect = null; + + + public LineScatterCandleRadarDataSet(List yVals, String label) { + super(yVals, label); + mHighlightLineWidth = Utils.convertDpToPixel(0.5f); + } + + /** + * Enables / disables the horizontal highlight-indicator. If disabled, the indicator is not drawn. + * @param enabled + */ + public void setDrawHorizontalHighlightIndicator(boolean enabled) { + this.mDrawHorizontalHighlightIndicator = enabled; + } + + /** + * Enables / disables the vertical highlight-indicator. If disabled, the indicator is not drawn. + * @param enabled + */ + public void setDrawVerticalHighlightIndicator(boolean enabled) { + this.mDrawVerticalHighlightIndicator = enabled; + } + + /** + * Enables / disables both vertical and horizontal highlight-indicators. + * @param enabled + */ + public void setDrawHighlightIndicators(boolean enabled) { + setDrawVerticalHighlightIndicator(enabled); + setDrawHorizontalHighlightIndicator(enabled); + } + + @Override + public boolean isVerticalHighlightIndicatorEnabled() { + return mDrawVerticalHighlightIndicator; + } + + @Override + public boolean isHorizontalHighlightIndicatorEnabled() { + return mDrawHorizontalHighlightIndicator; + } + + /** + * Sets the width of the highlight line in dp. + * @param width + */ + public void setHighlightLineWidth(float width) { + mHighlightLineWidth = Utils.convertDpToPixel(width); + } + + @Override + public float getHighlightLineWidth() { + return mHighlightLineWidth; + } + + /** + * Enables the highlight-line to be drawn in dashed mode, e.g. like this "- - - - - -" + * + * @param lineLength the length of the line pieces + * @param spaceLength the length of space inbetween the line-pieces + * @param phase offset, in degrees (normally, use 0) + */ + public void enableDashedHighlightLine(float lineLength, float spaceLength, float phase) { + mHighlightDashPathEffect = new DashPathEffect(new float[] { + lineLength, spaceLength + }, phase); + } + + /** + * Disables the highlight-line to be drawn in dashed mode. + */ + public void disableDashedHighlightLine() { + mHighlightDashPathEffect = null; + } + + /** + * Returns true if the dashed-line effect is enabled for highlight lines, false if not. + * Default: disabled + * + * @return + */ + public boolean isDashedHighlightLineEnabled() { + return mHighlightDashPathEffect == null ? false : true; + } + + @Override + public DashPathEffect getDashPathEffectHighlight() { + return mHighlightDashPathEffect; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java new file mode 100644 index 0000000000..db7972a3fb --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java @@ -0,0 +1,86 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; + +import java.util.ArrayList; +import java.util.List; + +/** + * A PieData object can only represent one DataSet. Unlike all other charts, the + * legend labels of the PieChart are created from the x-values array, and not + * from the DataSet labels. Each PieData object can only represent one + * PieDataSet (multiple PieDataSets inside a single PieChart are not possible). + * + * @author Philipp Jahoda + */ +public class PieData extends ChartData { + + public PieData() { + super(); + } + + public PieData(IPieDataSet dataSet) { + super(dataSet); + } + + /** + * Sets the PieDataSet this data object should represent. + * + * @param dataSet + */ + public void setDataSet(IPieDataSet dataSet) { + mDataSets.clear(); + mDataSets.add(dataSet); + notifyDataChanged(); + } + + /** + * Returns the DataSet this PieData object represents. A PieData object can + * only contain one DataSet. + * + * @return + */ + public IPieDataSet getDataSet() { + return mDataSets.get(0); + } + + /** + * The PieData object can only have one DataSet. Use getDataSet() method instead. + * + * @param index + * @return + */ + @Override + public IPieDataSet getDataSetByIndex(int index) { + return index == 0 ? getDataSet() : null; + } + + @Override + public IPieDataSet getDataSetByLabel(String label, boolean ignorecase) { + return ignorecase ? label.equalsIgnoreCase(mDataSets.get(0).getLabel()) ? mDataSets.get(0) + : null : label.equals(mDataSets.get(0).getLabel()) ? mDataSets.get(0) : null; + } + + @Override + public Entry getEntryForHighlight(Highlight highlight) { + return getDataSet().getEntryForIndex((int) highlight.getX()); + } + + /** + * Returns the sum of all values in this PieData object. + * + * @return + */ + public float getYValueSum() { + + float sum = 0; + + for (int i = 0; i < getDataSet().getEntryCount(); i++) + sum += getDataSet().getEntryForIndex(i).getY(); + + + return sum; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java new file mode 100644 index 0000000000..010cfbddc5 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -0,0 +1,213 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; +import com.github.mikephil.charting.utils.Utils; + +import java.util.ArrayList; +import java.util.List; + +public class PieDataSet extends DataSet implements IPieDataSet { + + /** the space in pixels between the chart-slices, default 0f */ + private float mSliceSpace = 0f; + private boolean mAutomaticallyDisableSliceSpacing; + + /** indicates the selection distance of a pie slice */ + private float mShift = 18f; + + private ValuePosition mXValuePosition = ValuePosition.INSIDE_SLICE; + private ValuePosition mYValuePosition = ValuePosition.INSIDE_SLICE; + private int mValueLineColor = 0xff000000; + private float mValueLineWidth = 1.0f; + private float mValueLinePart1OffsetPercentage = 75.f; + private float mValueLinePart1Length = 0.3f; + private float mValueLinePart2Length = 0.4f; + private boolean mValueLineVariableLength = true; + + public PieDataSet(List yVals, String label) { + super(yVals, label); +// mShift = Utils.convertDpToPixel(12f); + } + + @Override + public DataSet copy() { + + List yVals = new ArrayList<>(); + + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); + } + + PieDataSet copied = new PieDataSet(yVals, getLabel()); + copied.mColors = mColors; + copied.mSliceSpace = mSliceSpace; + copied.mShift = mShift; + return copied; + } + + @Override + protected void calcMinMax(PieEntry e) { + + if (e == null) + return; + + calcMinMaxY(e); + } + + /** + * Sets the space that is left out between the piechart-slices in dp. + * Default: 0 --> no space, maximum 20f + * + * @param spaceDp + */ + public void setSliceSpace(float spaceDp) { + + if (spaceDp > 20) + spaceDp = 20f; + if (spaceDp < 0) + spaceDp = 0f; + + mSliceSpace = Utils.convertDpToPixel(spaceDp); + } + + @Override + public float getSliceSpace() { + return mSliceSpace; + } + + /** + * When enabled, slice spacing will be 0.0 when the smallest value is going to be + * smaller than the slice spacing itself. + * + * @param autoDisable + */ + public void setAutomaticallyDisableSliceSpacing(boolean autoDisable) { + mAutomaticallyDisableSliceSpacing = autoDisable; + } + + /** + * When enabled, slice spacing will be 0.0 when the smallest value is going to be + * smaller than the slice spacing itself. + * + * @return + */ + @Override + public boolean isAutomaticallyDisableSliceSpacingEnabled() { + return mAutomaticallyDisableSliceSpacing; + } + + /** + * sets the distance the highlighted piechart-slice of this DataSet is + * "shifted" away from the center of the chart, default 12f + * + * @param shift + */ + public void setSelectionShift(float shift) { + mShift = Utils.convertDpToPixel(shift); + } + + @Override + public float getSelectionShift() { + return mShift; + } + + @Override + public ValuePosition getXValuePosition() + { + return mXValuePosition; + } + + public void setXValuePosition(ValuePosition xValuePosition) + { + this.mXValuePosition = xValuePosition; + } + + @Override + public ValuePosition getYValuePosition() + { + return mYValuePosition; + } + + public void setYValuePosition(ValuePosition yValuePosition) + { + this.mYValuePosition = yValuePosition; + } + + /** When valuePosition is OutsideSlice, indicates line color */ + @Override + public int getValueLineColor() + { + return mValueLineColor; + } + + public void setValueLineColor(int valueLineColor) + { + this.mValueLineColor = valueLineColor; + } + + /** When valuePosition is OutsideSlice, indicates line width */ + @Override + public float getValueLineWidth() + { + return mValueLineWidth; + } + + public void setValueLineWidth(float valueLineWidth) + { + this.mValueLineWidth = valueLineWidth; + } + + /** When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size */ + @Override + public float getValueLinePart1OffsetPercentage() + { + return mValueLinePart1OffsetPercentage; + } + + public void setValueLinePart1OffsetPercentage(float valueLinePart1OffsetPercentage) + { + this.mValueLinePart1OffsetPercentage = valueLinePart1OffsetPercentage; + } + + /** When valuePosition is OutsideSlice, indicates length of first half of the line */ + @Override + public float getValueLinePart1Length() + { + return mValueLinePart1Length; + } + + public void setValueLinePart1Length(float valueLinePart1Length) + { + this.mValueLinePart1Length = valueLinePart1Length; + } + + /** When valuePosition is OutsideSlice, indicates length of second half of the line */ + @Override + public float getValueLinePart2Length() + { + return mValueLinePart2Length; + } + + public void setValueLinePart2Length(float valueLinePart2Length) + { + this.mValueLinePart2Length = valueLinePart2Length; + } + + /** When valuePosition is OutsideSlice, this allows variable line length */ + @Override + public boolean isValueLineVariableLength() + { + return mValueLineVariableLength; + } + + public void setValueLineVariableLength(boolean valueLineVariableLength) + { + this.mValueLineVariableLength = valueLineVariableLength; + } + + public enum ValuePosition { + INSIDE_SLICE, + OUTSIDE_SLICE + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java new file mode 100644 index 0000000000..65741ef1da --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java @@ -0,0 +1,86 @@ +package com.github.mikephil.charting.data; + +import android.annotation.SuppressLint; +import android.graphics.drawable.Drawable; +import android.util.Log; + +/** + * @author Philipp Jahoda + */ +@SuppressLint("ParcelCreator") +public class PieEntry extends Entry { + + private String label; + + public PieEntry(float value) { + super(0f, value); + } + + public PieEntry(float value, Object data) { + super(0f, value, data); + } + + public PieEntry(float value, Drawable icon) { + super(0f, value, icon); + } + + public PieEntry(float value, Drawable icon, Object data) { + super(0f, value, icon, data); + } + + public PieEntry(float value, String label) { + super(0f, value); + this.label = label; + } + + public PieEntry(float value, String label, Object data) { + super(0f, value, data); + this.label = label; + } + + public PieEntry(float value, String label, Drawable icon) { + super(0f, value, icon); + this.label = label; + } + + public PieEntry(float value, String label, Drawable icon, Object data) { + super(0f, value, icon, data); + this.label = label; + } + + /** + * This is the same as getY(). Returns the value of the PieEntry. + * + * @return + */ + public float getValue() { + return getY(); + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + @Deprecated + @Override + public void setX(float x) { + super.setX(x); + Log.i("DEPRECATED", "Pie entries do not have x values"); + } + + @Deprecated + @Override + public float getX() { + Log.i("DEPRECATED", "Pie entries do not have x values"); + return super.getX(); + } + + public PieEntry copy() { + PieEntry e = new PieEntry(getY(), label, getData()); + return e; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java new file mode 100644 index 0000000000..0c1dbe5505 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java @@ -0,0 +1,58 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Data container for the RadarChart. + * + * @author Philipp Jahoda + */ +public class RadarData extends ChartData { + + private List mLabels; + + public RadarData() { + super(); + } + + public RadarData(List dataSets) { + super(dataSets); + } + + public RadarData(IRadarDataSet... dataSets) { + super(dataSets); + } + + /** + * Sets the labels that should be drawn around the RadarChart at the end of each web line. + * + * @param labels + */ + public void setLabels(List labels) { + this.mLabels = labels; + } + + /** + * Sets the labels that should be drawn around the RadarChart at the end of each web line. + * + * @param labels + */ + public void setLabels(String... labels) { + this.mLabels = Arrays.asList(labels); + } + + public List getLabels() { + return mLabels; + } + + @Override + public Entry getEntryForHighlight(Highlight highlight) { + return getDataSetByIndex(highlight.getDataSetIndex()).getEntryForIndex((int) highlight.getX()); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java new file mode 100644 index 0000000000..f18aa8c23a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java @@ -0,0 +1,131 @@ + +package com.github.mikephil.charting.data; + +import android.graphics.Color; + +import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; + +import java.util.ArrayList; +import java.util.List; + +public class RadarDataSet extends LineRadarDataSet implements IRadarDataSet { + + /// flag indicating whether highlight circle should be drawn or not + protected boolean mDrawHighlightCircleEnabled = false; + + protected int mHighlightCircleFillColor = Color.WHITE; + + /// The stroke color for highlight circle. + /// If Utils.COLOR_NONE, the color of the dataset is taken. + protected int mHighlightCircleStrokeColor = ColorTemplate.COLOR_NONE; + + protected int mHighlightCircleStrokeAlpha = (int)(0.3 * 255); + protected float mHighlightCircleInnerRadius = 3.0f; + protected float mHighlightCircleOuterRadius = 4.0f; + protected float mHighlightCircleStrokeWidth = 2.0f; + + public RadarDataSet(List yVals, String label) { + super(yVals, label); + } + + /// Returns true if highlight circle should be drawn, false if not + @Override + public boolean isDrawHighlightCircleEnabled() + { + return mDrawHighlightCircleEnabled; + } + + /// Sets whether highlight circle should be drawn or not + @Override + public void setDrawHighlightCircleEnabled(boolean enabled) + { + mDrawHighlightCircleEnabled = enabled; + } + + @Override + public int getHighlightCircleFillColor() + { + return mHighlightCircleFillColor; + } + + public void setHighlightCircleFillColor(int color) + { + mHighlightCircleFillColor = color; + } + + /// Returns the stroke color for highlight circle. + /// If Utils.COLOR_NONE, the color of the dataset is taken. + @Override + public int getHighlightCircleStrokeColor() + { + return mHighlightCircleStrokeColor; + } + + /// Sets the stroke color for highlight circle. + /// Set to Utils.COLOR_NONE in order to use the color of the dataset; + public void setHighlightCircleStrokeColor(int color) + { + mHighlightCircleStrokeColor = color; + } + + @Override + public int getHighlightCircleStrokeAlpha() + { + return mHighlightCircleStrokeAlpha; + } + + public void setHighlightCircleStrokeAlpha(int alpha) + { + mHighlightCircleStrokeAlpha = alpha; + } + + @Override + public float getHighlightCircleInnerRadius() + { + return mHighlightCircleInnerRadius; + } + + public void setHighlightCircleInnerRadius(float radius) + { + mHighlightCircleInnerRadius = radius; + } + + @Override + public float getHighlightCircleOuterRadius() + { + return mHighlightCircleOuterRadius; + } + + public void setHighlightCircleOuterRadius(float radius) + { + mHighlightCircleOuterRadius = radius; + } + + @Override + public float getHighlightCircleStrokeWidth() + { + return mHighlightCircleStrokeWidth; + } + + public void setHighlightCircleStrokeWidth(float strokeWidth) + { + mHighlightCircleStrokeWidth = strokeWidth; + } + + @Override + public DataSet copy() { + + List yVals = new ArrayList(); + + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); + } + + RadarDataSet copied = new RadarDataSet(yVals, getLabel()); + copied.mColors = mColors; + copied.mHighLightColor = mHighLightColor; + + return copied; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java new file mode 100644 index 0000000000..02fdce7d32 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java @@ -0,0 +1,44 @@ +package com.github.mikephil.charting.data; + +import android.annotation.SuppressLint; + +/** + * Created by philipp on 13/06/16. + */ +@SuppressLint("ParcelCreator") +public class RadarEntry extends Entry { + + public RadarEntry(float value) { + super(0f, value); + } + + public RadarEntry(float value, Object data) { + super(0f, value, data); + } + + /** + * This is the same as getY(). Returns the value of the RadarEntry. + * + * @return + */ + public float getValue() { + return getY(); + } + + public RadarEntry copy() { + RadarEntry e = new RadarEntry(getY(), getData()); + return e; + } + + @Deprecated + @Override + public void setX(float x) { + super.setX(x); + } + + @Deprecated + @Override + public float getX() { + return super.getX(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java new file mode 100644 index 0000000000..ba142360f9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java @@ -0,0 +1,40 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; + +import java.util.List; + +public class ScatterData extends BarLineScatterCandleBubbleData { + + public ScatterData() { + super(); + } + + public ScatterData(List dataSets) { + super(dataSets); + } + + public ScatterData(IScatterDataSet... dataSets) { + super(dataSets); + } + + /** + * Returns the maximum shape-size across all DataSets. + * + * @return + */ + public float getGreatestShapeSize() { + + float max = 0f; + + for (IScatterDataSet set : mDataSets) { + float size = set.getScatterShapeSize(); + + if (size > max) + max = size; + } + + return max; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java new file mode 100644 index 0000000000..a9d73885b5 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java @@ -0,0 +1,155 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.charts.ScatterChart; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.scatter.ChevronDownShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.ChevronUpShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.CircleShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.CrossShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.SquareShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.TriangleShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.XShapeRenderer; +import com.github.mikephil.charting.utils.ColorTemplate; + +import java.util.ArrayList; +import java.util.List; + +public class ScatterDataSet extends LineScatterCandleRadarDataSet implements IScatterDataSet { + + /** + * the size the scattershape will have, in density pixels + */ + private float mShapeSize = 15f; + + /** + * Renderer responsible for rendering this DataSet, default: square + */ + protected IShapeRenderer mShapeRenderer = new SquareShapeRenderer(); + + /** + * The radius of the hole in the shape (applies to Square, Circle and Triangle) + * - default: 0.0 + */ + private float mScatterShapeHoleRadius = 0f; + + /** + * Color for the hole in the shape. + * Setting to `ColorTemplate.COLOR_NONE` will behave as transparent. + * - default: ColorTemplate.COLOR_NONE + */ + private int mScatterShapeHoleColor = ColorTemplate.COLOR_NONE; + + public ScatterDataSet(List yVals, String label) { + super(yVals, label); + } + + @Override + public DataSet copy() { + + List yVals = new ArrayList(); + + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); + } + + ScatterDataSet copied = new ScatterDataSet(yVals, getLabel()); + copied.mDrawValues = mDrawValues; + copied.mValueColors = mValueColors; + copied.mColors = mColors; + copied.mShapeSize = mShapeSize; + copied.mShapeRenderer = mShapeRenderer; + copied.mScatterShapeHoleRadius = mScatterShapeHoleRadius; + copied.mScatterShapeHoleColor = mScatterShapeHoleColor; + copied.mHighlightLineWidth = mHighlightLineWidth; + copied.mHighLightColor = mHighLightColor; + copied.mHighlightDashPathEffect = mHighlightDashPathEffect; + + return copied; + } + + /** + * Sets the size in density pixels the drawn scattershape will have. This + * only applies for non custom shapes. + * + * @param size + */ + public void setScatterShapeSize(float size) { + mShapeSize = size; + } + + @Override + public float getScatterShapeSize() { + return mShapeSize; + } + + /** + * Sets the ScatterShape this DataSet should be drawn with. This will search for an available IShapeRenderer and set this + * renderer for the DataSet. + * + * @param shape + */ + public void setScatterShape(ScatterChart.ScatterShape shape) { + mShapeRenderer = getRendererForShape(shape); + } + + /** + * Sets a new IShapeRenderer responsible for drawing this DataSet. + * This can also be used to set a custom IShapeRenderer aside from the default ones. + * + * @param shapeRenderer + */ + public void setShapeRenderer(IShapeRenderer shapeRenderer) { + mShapeRenderer = shapeRenderer; + } + + @Override + public IShapeRenderer getShapeRenderer() { + return mShapeRenderer; + } + + /** + * Sets the radius of the hole in the shape (applies to Square, Circle and Triangle) + * Set this to <= 0 to remove holes. + * + * @param holeRadius + */ + public void setScatterShapeHoleRadius(float holeRadius) { + mScatterShapeHoleRadius = holeRadius; + } + + @Override + public float getScatterShapeHoleRadius() { + return mScatterShapeHoleRadius; + } + + /** + * Sets the color for the hole in the shape. + * + * @param holeColor + */ + public void setScatterShapeHoleColor(int holeColor) { + mScatterShapeHoleColor = holeColor; + } + + @Override + public int getScatterShapeHoleColor() { + return mScatterShapeHoleColor; + } + + public static IShapeRenderer getRendererForShape(ScatterChart.ScatterShape shape) { + + switch (shape) { + case SQUARE: return new SquareShapeRenderer(); + case CIRCLE: return new CircleShapeRenderer(); + case TRIANGLE: return new TriangleShapeRenderer(); + case CROSS: return new CrossShapeRenderer(); + case X: return new XShapeRenderer(); + case CHEVRON_UP: return new ChevronUpShapeRenderer(); + case CHEVRON_DOWN: return new ChevronDownShapeRenderer(); + } + + return null; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java new file mode 100644 index 0000000000..542188e602 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java @@ -0,0 +1,102 @@ + +package com.github.mikephil.charting.data.filter; + +import android.annotation.TargetApi; +import android.os.Build; + +import java.util.Arrays; + +/** + * Implemented according to Wiki-Pseudocode {@link} + * http://en.wikipedia.org/wiki/Ramer�Douglas�Peucker_algorithm + * + * @author Philipp Baldauf & Phliipp Jahoda + */ +public class Approximator { + + @TargetApi(Build.VERSION_CODES.GINGERBREAD) + public float[] reduceWithDouglasPeucker(float[] points, float tolerance) { + + int greatestIndex = 0; + float greatestDistance = 0f; + + Line line = new Line(points[0], points[1], points[points.length - 2], points[points.length - 1]); + + for (int i = 2; i < points.length - 2; i += 2) { + + float distance = line.distance(points[i], points[i + 1]); + + if (distance > greatestDistance) { + greatestDistance = distance; + greatestIndex = i; + } + } + + if (greatestDistance > tolerance) { + + float[] reduced1 = reduceWithDouglasPeucker(Arrays.copyOfRange(points, 0, greatestIndex + 2), tolerance); + float[] reduced2 = reduceWithDouglasPeucker(Arrays.copyOfRange(points, greatestIndex, points.length), + tolerance); + + float[] result1 = reduced1; + float[] result2 = Arrays.copyOfRange(reduced2, 2, reduced2.length); + + return concat(result1, result2); + } else { + return line.getPoints(); + } + } + + /** + * Combine arrays. + * + * @param arrays + * @return + */ + float[] concat(float[]... arrays) { + int length = 0; + for (float[] array : arrays) { + length += array.length; + } + float[] result = new float[length]; + int pos = 0; + for (float[] array : arrays) { + for (float element : array) { + result[pos] = element; + pos++; + } + } + return result; + } + + private class Line { + + private float[] points; + + private float sxey; + private float exsy; + + private float dx; + private float dy; + + private float length; + + public Line(float x1, float y1, float x2, float y2) { + dx = x1 - x2; + dy = y1 - y2; + sxey = x1 * y2; + exsy = x2 * y1; + length = (float) Math.sqrt(dx * dx + dy * dy); + + points = new float[]{x1, y1, x2, y2}; + } + + public float distance(float x, float y) { + return Math.abs(dy * x - dx * y + sxey - exsy) / length; + } + + public float[] getPoints() { + return points; + } + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java b/MPChartLib/src/main/java/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java new file mode 100644 index 0000000000..2db66fd43f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java @@ -0,0 +1,24 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; + +/** + * Interface that can be used to return a customized color instead of setting + * colors via the setColor(...) method of the DataSet. + * + * @author Philipp Jahoda + */ +public interface ColorFormatter { + + /** + * Returns the color to be used for the given Entry at the given index (in the entries array) + * + * @param index index in the entries array + * @param e the entry to color + * @param set the DataSet the entry belongs to + * @return + */ + int getColor(int index, Entry e, IDataSet set); +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java new file mode 100644 index 0000000000..552c150e69 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java @@ -0,0 +1,56 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.components.AxisBase; + +import java.text.DecimalFormat; + +/** + * Created by philipp on 02/06/16. + */ +public class DefaultAxisValueFormatter implements IAxisValueFormatter +{ + + /** + * decimalformat for formatting + */ + protected DecimalFormat mFormat; + + /** + * the number of decimal digits this formatter uses + */ + protected int digits = 0; + + /** + * Constructor that specifies to how many digits the value should be + * formatted. + * + * @param digits + */ + public DefaultAxisValueFormatter(int digits) { + this.digits = digits; + + StringBuffer b = new StringBuffer(); + for (int i = 0; i < digits; i++) { + if (i == 0) + b.append("."); + b.append("0"); + } + + mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + // avoid memory allocations here (for performance) + return mFormat.format(value); + } + + /** + * Returns the number of decimal digits this formatter uses or -1, if unspecified. + * + * @return + */ + public int getDecimalDigits() { + return digits; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java new file mode 100644 index 0000000000..851faeb333 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java @@ -0,0 +1,45 @@ +package com.github.mikephil.charting.formatter; + + +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; + +/** + * Default formatter that calculates the position of the filled line. + * + * @author Philipp Jahoda + */ +public class DefaultFillFormatter implements IFillFormatter +{ + + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + + float fillMin = 0f; + float chartMaxY = dataProvider.getYChartMax(); + float chartMinY = dataProvider.getYChartMin(); + + LineData data = dataProvider.getLineData(); + + if (dataSet.getYMax() > 0 && dataSet.getYMin() < 0) { + fillMin = 0f; + } else { + + float max, min; + + if (data.getYMax() > 0) + max = 0f; + else + max = chartMaxY; + if (data.getYMin() < 0) + min = 0f; + else + min = chartMinY; + + fillMin = dataSet.getYMin() >= 0 ? min : max; + } + + return fillMin; + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java new file mode 100644 index 0000000000..e2fea4b079 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java @@ -0,0 +1,71 @@ + +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; + +/** + * Default formatter used for formatting values inside the chart. Uses a DecimalFormat with + * pre-calculated number of digits (depending on max and min value). + * + * @author Philipp Jahoda + */ +public class DefaultValueFormatter implements IValueFormatter +{ + + /** + * DecimalFormat for formatting + */ + protected DecimalFormat mFormat; + + protected int mDecimalDigits; + + /** + * Constructor that specifies to how many digits the value should be + * formatted. + * + * @param digits + */ + public DefaultValueFormatter(int digits) { + setup(digits); + } + + /** + * Sets up the formatter with a given number of decimal digits. + * + * @param digits + */ + public void setup(int digits) { + + this.mDecimalDigits = digits; + + StringBuffer b = new StringBuffer(); + for (int i = 0; i < digits; i++) { + if (i == 0) + b.append("."); + b.append("0"); + } + + mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); + } + + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + + // put more logic here ... + // avoid memory allocations here (for performance reasons) + + return mFormat.format(value); + } + + /** + * Returns the number of decimal digits this formatter uses. + * + * @return + */ + public int getDecimalDigits() { + return mDecimalDigits; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java new file mode 100644 index 0000000000..51939b5432 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java @@ -0,0 +1,23 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.components.AxisBase; + +/** + * Created by Philipp Jahoda on 20/09/15. + * Custom formatter interface that allows formatting of + * axis labels before they are being drawn. + */ +public interface IAxisValueFormatter +{ + + /** + * Called when a value from an axis is to be formatted + * before being drawn. For performance reasons, avoid excessive calculations + * and memory allocations inside this method. + * + * @param value the value to be formatted + * @param axis the axis the value belongs to + * @return + */ + String getFormattedValue(float value, AxisBase axis); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java new file mode 100644 index 0000000000..e096cc6528 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java @@ -0,0 +1,24 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; + +/** + * Interface for providing a custom logic to where the filling line of a LineDataSet + * should end. This of course only works if setFillEnabled(...) is set to true. + * + * @author Philipp Jahoda + */ +public interface IFillFormatter +{ + + /** + * Returns the vertical (y-axis) position where the filled-line of the + * LineDataSet should end. + * + * @param dataSet the ILineDataSet that is currently drawn + * @param dataProvider + * @return + */ + float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java new file mode 100644 index 0000000000..75d2363f26 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java @@ -0,0 +1,29 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Interface that allows custom formatting of all values inside the chart before they are + * being drawn to the screen. Simply create your own formatting class and let + * it implement IValueFormatter. Then override the getFormattedValue(...) method + * and return whatever you want. + * + * @author Philipp Jahoda + */ +public interface IValueFormatter +{ + + /** + * Called when a value (from labels inside the chart) is formatted + * before being drawn. For performance reasons, avoid excessive calculations + * and memory allocations inside this method. + * + * @param value the value to be formatted + * @param entry the entry the value belongs to - in e.g. BarChart, this is of class BarEntry + * @param dataSetIndex the index of the DataSet the entry in focus belongs to + * @param viewPortHandler provides information about the current chart state (scale, translation, ...) + * @return the formatted label ready for being drawn + */ + String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java new file mode 100644 index 0000000000..07349a6a0e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java @@ -0,0 +1,69 @@ + +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.Collection; + +/** + * This formatter is used for passing an array of x-axis labels, on whole x steps. + */ +public class IndexAxisValueFormatter implements IAxisValueFormatter +{ + private String[] mValues = new String[] {}; + private int mValueCount = 0; + + /** + * An empty constructor. + * Use `setValues` to set the axis labels. + */ + public IndexAxisValueFormatter() { + } + + /** + * Constructor that specifies axis labels. + * + * @param values The values string array + */ + public IndexAxisValueFormatter(String[] values) { + if (values != null) + setValues(values); + } + + /** + * Constructor that specifies axis labels. + * + * @param values The values string array + */ + public IndexAxisValueFormatter(Collection values) { + if (values != null) + setValues(values.toArray(new String[values.size()])); + } + + public String getFormattedValue(float value, AxisBase axis) { + int index = Math.round(value); + + if (index < 0 || index >= mValueCount || index != (int)value) + return ""; + + return mValues[index]; + } + + public String[] getValues() + { + return mValues; + } + + public void setValues(String[] values) + { + if (values == null) + values = new String[] {}; + + this.mValues = values; + this.mValueCount = values.length; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java new file mode 100644 index 0000000000..c950d640b3 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java @@ -0,0 +1,99 @@ + +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; + +/** + * Predefined value-formatter that formats large numbers in a pretty way. + * Outputs: 856 = 856; 1000 = 1k; 5821 = 5.8k; 10500 = 10k; 101800 = 102k; + * 2000000 = 2m; 7800000 = 7.8m; 92150000 = 92m; 123200000 = 123m; 9999999 = + * 10m; 1000000000 = 1b; Special thanks to Roman Gromov + * (https://github.com/romangromov) for this piece of code. + * + * @author Philipp Jahoda + * @author Oleksandr Tyshkovets + */ +public class LargeValueFormatter implements IValueFormatter, IAxisValueFormatter +{ + + private static String[] SUFFIX = new String[]{ + "", "k", "m", "b", "t" + }; + private static final int MAX_LENGTH = 5; + private DecimalFormat mFormat; + private String mText = ""; + + public LargeValueFormatter() { + mFormat = new DecimalFormat("###E00"); + } + + /** + * Creates a formatter that appends a specified text to the result string + * + * @param appendix a text that will be appended + */ + public LargeValueFormatter(String appendix) { + this(); + mText = appendix; + } + + // IValueFormatter + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return makePretty(value) + mText; + } + + // IAxisValueFormatter + @Override + public String getFormattedValue(float value, AxisBase axis) { + return makePretty(value) + mText; + } + + /** + * Set an appendix text to be added at the end of the formatted value. + * + * @param appendix + */ + public void setAppendix(String appendix) { + this.mText = appendix; + } + + /** + * Set custom suffix to be appended after the values. + * Default suffix: ["", "k", "m", "b", "t"] + * + * @param suff new suffix + */ + public void setSuffix(String[] suff) { + SUFFIX = suff; + } + + /** + * Formats each number properly. Special thanks to Roman Gromov + * (https://github.com/romangromov) for this piece of code. + */ + private String makePretty(double number) { + + String r = mFormat.format(number); + + int numericValue1 = Character.getNumericValue(r.charAt(r.length() - 1)); + int numericValue2 = Character.getNumericValue(r.charAt(r.length() - 2)); + int combined = Integer.valueOf(numericValue2 + "" + numericValue1); + + r = r.replaceAll("E[0-9][0-9]", SUFFIX[combined / 3]); + + while (r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]")) { + r = r.substring(0, r.length() - 2) + r.substring(r.length() - 1); + } + + return r; + } + + public int getDecimalDigits() { + return 0; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java new file mode 100644 index 0000000000..de8a10255a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java @@ -0,0 +1,49 @@ + +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; + +/** + * This IValueFormatter is just for convenience and simply puts a "%" sign after + * each value. (Recommeded for PieChart) + * + * @author Philipp Jahoda + */ +public class PercentFormatter implements IValueFormatter, IAxisValueFormatter +{ + + protected DecimalFormat mFormat; + + public PercentFormatter() { + mFormat = new DecimalFormat("###,###,##0.0"); + } + + /** + * Allow a custom decimalformat + * + * @param format + */ + public PercentFormatter(DecimalFormat format) { + this.mFormat = format; + } + + // IValueFormatter + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return mFormat.format(value) + " %"; + } + + // IAxisValueFormatter + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mFormat.format(value) + " %"; + } + + public int getDecimalDigits() { + return 1; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java new file mode 100644 index 0000000000..0e8351634f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java @@ -0,0 +1,75 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; + +/** + * Created by Philipp Jahoda on 28/01/16. + *

+ * A formatter specifically for stacked BarChart that allows to specify whether the all stack values + * or just the top value should be drawn. + */ +public class StackedValueFormatter implements IValueFormatter +{ + + /** + * if true, all stack values of the stacked bar entry are drawn, else only top + */ + private boolean mDrawWholeStack; + + /** + * a string that should be appended behind the value + */ + private String mAppendix; + + private DecimalFormat mFormat; + + /** + * Constructor. + * + * @param drawWholeStack if true, all stack values of the stacked bar entry are drawn, else only top + * @param appendix a string that should be appended behind the value + * @param decimals the number of decimal digits to use + */ + public StackedValueFormatter(boolean drawWholeStack, String appendix, int decimals) { + this.mDrawWholeStack = drawWholeStack; + this.mAppendix = appendix; + + StringBuffer b = new StringBuffer(); + for (int i = 0; i < decimals; i++) { + if (i == 0) + b.append("."); + b.append("0"); + } + + this.mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); + } + + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + + if (!mDrawWholeStack && entry instanceof BarEntry) { + + BarEntry barEntry = (BarEntry) entry; + float[] vals = barEntry.getYVals(); + + if (vals != null) { + + // find out if we are on top of the stack + if (vals[vals.length - 1] == value) { + + // return the "sum" across all stack values + return mFormat.format(barEntry.getY()) + mAppendix; + } else { + return ""; // return empty + } + } + } + + // return the "proposed" value + return mFormat.format(value) + mAppendix; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java new file mode 100644 index 0000000000..af83a4539f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java @@ -0,0 +1,163 @@ +package com.github.mikephil.charting.highlight; + +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.MPPointD; + +/** + * Created by Philipp Jahoda on 22/07/15. + */ +public class BarHighlighter extends ChartHighlighter { + + public BarHighlighter(BarDataProvider chart) { + super(chart); + } + + @Override + public Highlight getHighlight(float x, float y) { + Highlight high = super.getHighlight(x, y); + + if(high == null) { + return null; + } + + MPPointD pos = getValsForTouch(x, y); + + BarData barData = mChart.getBarData(); + + IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex()); + if (set.isStacked()) { + + return getStackedHighlight(high, + set, + (float) pos.x, + (float) pos.y); + } + + MPPointD.recycleInstance(pos); + + return high; + } + + /** + * This method creates the Highlight object that also indicates which value of a stacked BarEntry has been + * selected. + * + * @param high the Highlight to work with looking for stacked values + * @param set + * @param xVal + * @param yVal + * @return + */ + public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal, float yVal) { + + BarEntry entry = set.getEntryForXValue(xVal, yVal); + + if (entry == null) + return null; + + // not stacked + if (entry.getYVals() == null) { + return high; + } else { + Range[] ranges = entry.getRanges(); + + if (ranges.length > 0) { + int stackIndex = getClosestStackIndex(ranges, yVal); + + MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(high.getX(), ranges[stackIndex].to); + + Highlight stackedHigh = new Highlight( + entry.getX(), + entry.getY(), + (float) pixels.x, + (float) pixels.y, + high.getDataSetIndex(), + stackIndex, + high.getAxis() + ); + + MPPointD.recycleInstance(pixels); + + return stackedHigh; + } + } + + return null; + } + + /** + * Returns the index of the closest value inside the values array / ranges (stacked barchart) to the value + * given as + * a parameter. + * + * @param ranges + * @param value + * @return + */ + protected int getClosestStackIndex(Range[] ranges, float value) { + + if (ranges == null || ranges.length == 0) + return 0; + + int stackIndex = 0; + + for (Range range : ranges) { + if (range.contains(value)) + return stackIndex; + else + stackIndex++; + } + + int length = Math.max(ranges.length - 1, 0); + + return (value > ranges[length].to) ? length : 0; + } + +// /** +// * Splits up the stack-values of the given bar-entry into Range objects. +// * +// * @param entry +// * @return +// */ +// protected Range[] getRanges(BarEntry entry) { +// +// float[] values = entry.getYVals(); +// +// if (values == null || values.length == 0) +// return new Range[0]; +// +// Range[] ranges = new Range[values.length]; +// +// float negRemain = -entry.getNegativeSum(); +// float posRemain = 0f; +// +// for (int i = 0; i < ranges.length; i++) { +// +// float value = values[i]; +// +// if (value < 0) { +// ranges[i] = new Range(negRemain, negRemain + Math.abs(value)); +// negRemain += Math.abs(value); +// } else { +// ranges[i] = new Range(posRemain, posRemain + value); +// posRemain += value; +// } +// } +// +// return ranges; +// } + + @Override + protected float getDistance(float x1, float y1, float x2, float y2) { + return Math.abs(x1 - x2); + } + + @Override + protected BarLineScatterCandleBubbleData getData() { + return mChart.getBarData(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java new file mode 100644 index 0000000000..f889bf19d4 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java @@ -0,0 +1,246 @@ +package com.github.mikephil.charting.highlight; + +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.MPPointD; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Philipp Jahoda on 21/07/15. + */ +public class ChartHighlighter implements IHighlighter +{ + + /** + * instance of the data-provider + */ + protected T mChart; + + /** + * buffer for storing previously highlighted values + */ + protected List mHighlightBuffer = new ArrayList(); + + public ChartHighlighter(T chart) { + this.mChart = chart; + } + + @Override + public Highlight getHighlight(float x, float y) { + + MPPointD pos = getValsForTouch(x, y); + float xVal = (float) pos.x; + MPPointD.recycleInstance(pos); + + Highlight high = getHighlightForX(xVal, x, y); + return high; + } + + /** + * Returns a recyclable MPPointD instance. + * Returns the corresponding xPos for a given touch-position in pixels. + * + * @param x + * @param y + * @return + */ + protected MPPointD getValsForTouch(float x, float y) { + + // take any transformer to determine the x-axis value + MPPointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, y); + return pos; + } + + /** + * Returns the corresponding Highlight for a given xVal and x- and y-touch position in pixels. + * + * @param xVal + * @param x + * @param y + * @return + */ + protected Highlight getHighlightForX(float xVal, float x, float y) { + + List closestValues = getHighlightsAtXValue(xVal, x, y); + + if(closestValues.isEmpty()) { + return null; + } + + float leftAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.LEFT); + float rightAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.RIGHT); + + YAxis.AxisDependency axis = leftAxisMinDist < rightAxisMinDist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT; + + Highlight detail = getClosestHighlightByPixel(closestValues, x, y, axis, mChart.getMaxHighlightDistance()); + + return detail; + } + + /** + * Returns the minimum distance from a touch value (in pixels) to the + * closest value (in pixels) that is displayed in the chart. + * + * @param closestValues + * @param pos + * @param axis + * @return + */ + protected float getMinimumDistance(List closestValues, float pos, YAxis.AxisDependency axis) { + + float distance = Float.MAX_VALUE; + + for (int i = 0; i < closestValues.size(); i++) { + + Highlight high = closestValues.get(i); + + if (high.getAxis() == axis) { + + float tempDistance = Math.abs(getHighlightPos(high) - pos); + if (tempDistance < distance) { + distance = tempDistance; + } + } + } + + return distance; + } + + protected float getHighlightPos(Highlight h) { + return h.getYPx(); + } + + /** + * Returns a list of Highlight objects representing the entries closest to the given xVal. + * The returned list contains two objects per DataSet (closest rounding up, closest rounding down). + * + * @param xVal the transformed x-value of the x-touch position + * @param x touch position + * @param y touch position + * @return + */ + protected List getHighlightsAtXValue(float xVal, float x, float y) { + + mHighlightBuffer.clear(); + + BarLineScatterCandleBubbleData data = getData(); + + if (data == null) + return mHighlightBuffer; + + for (int i = 0, dataSetCount = data.getDataSetCount(); i < dataSetCount; i++) { + + IDataSet dataSet = data.getDataSetByIndex(i); + + // don't include DataSets that cannot be highlighted + if (!dataSet.isHighlightEnabled()) + continue; + + mHighlightBuffer.addAll(buildHighlights(dataSet, i, xVal, DataSet.Rounding.CLOSEST)); + } + + return mHighlightBuffer; + } + + /** + * An array of `Highlight` objects corresponding to the selected xValue and dataSetIndex. + * + * @param set + * @param dataSetIndex + * @param xVal + * @param rounding + * @return + */ + protected List buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + + ArrayList highlights = new ArrayList<>(); + + //noinspection unchecked + List entries = set.getEntriesForXValue(xVal); + if (entries.size() == 0) { + // Try to find closest x-value and take all entries for that x-value + final Entry closest = set.getEntryForXValue(xVal, Float.NaN, rounding); + if (closest != null) + { + //noinspection unchecked + entries = set.getEntriesForXValue(closest.getX()); + } + } + + if (entries.size() == 0) + return highlights; + + for (Entry e : entries) { + MPPointD pixels = mChart.getTransformer( + set.getAxisDependency()).getPixelForValues(e.getX(), e.getY()); + + highlights.add(new Highlight( + e.getX(), e.getY(), + (float) pixels.x, (float) pixels.y, + dataSetIndex, set.getAxisDependency())); + } + + return highlights; + } + + /** + * Returns the Highlight of the DataSet that contains the closest value on the + * y-axis. + * + * @param closestValues contains two Highlight objects per DataSet closest to the selected x-position (determined by + * rounding up an down) + * @param x + * @param y + * @param axis the closest axis + * @param minSelectionDistance + * @return + */ + public Highlight getClosestHighlightByPixel(List closestValues, float x, float y, + YAxis.AxisDependency axis, float minSelectionDistance) { + + Highlight closest = null; + float distance = minSelectionDistance; + + for (int i = 0; i < closestValues.size(); i++) { + + Highlight high = closestValues.get(i); + + if (axis == null || high.getAxis() == axis) { + + float cDistance = getDistance(x, y, high.getXPx(), high.getYPx()); + + if (cDistance < distance) { + closest = high; + distance = cDistance; + } + } + } + + return closest; + } + + /** + * Calculates the distance between the two given points. + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @return + */ + protected float getDistance(float x1, float y1, float x2, float y2) { + //return Math.abs(y1 - y2); + //return Math.abs(x1 - x2); + return (float) Math.hypot(x1 - x2, y1 - y2); + } + + protected BarLineScatterCandleBubbleData getData() { + return mChart.getData(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java new file mode 100644 index 0000000000..76788af6e0 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java @@ -0,0 +1,93 @@ +package com.github.mikephil.charting.highlight; + +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; + +import java.util.List; + +/** + * Created by Philipp Jahoda on 12/09/15. + */ +public class CombinedHighlighter extends ChartHighlighter implements IHighlighter +{ + + /** + * bar highlighter for supporting stacked highlighting + */ + protected BarHighlighter barHighlighter; + + public CombinedHighlighter(CombinedDataProvider chart, BarDataProvider barChart) { + super(chart); + + // if there is BarData, create a BarHighlighter + barHighlighter = barChart.getBarData() == null ? null : new BarHighlighter(barChart); + } + + @Override + protected List getHighlightsAtXValue(float xVal, float x, float y) { + + mHighlightBuffer.clear(); + + List dataObjects = mChart.getCombinedData().getAllData(); + + for (int i = 0; i < dataObjects.size(); i++) { + + ChartData dataObject = dataObjects.get(i); + + // in case of BarData, let the BarHighlighter take over + if (barHighlighter != null && dataObject instanceof BarData) { + Highlight high = barHighlighter.getHighlight(x, y); + + if (high != null) { + high.setDataIndex(i); + mHighlightBuffer.add(high); + } + } else { + + for (int j = 0, dataSetCount = dataObject.getDataSetCount(); j < dataSetCount; j++) { + + IDataSet dataSet = dataObjects.get(i).getDataSetByIndex(j); + + // don't include datasets that cannot be highlighted + if (!dataSet.isHighlightEnabled()) + continue; + + List highs = buildHighlights(dataSet, j, xVal, DataSet.Rounding.CLOSEST); + for (Highlight high : highs) + { + high.setDataIndex(i); + mHighlightBuffer.add(high); + } + } + } + } + + return mHighlightBuffer; + } + +// protected Highlight getClosest(float x, float y, Highlight... highs) { +// +// Highlight closest = null; +// float minDistance = Float.MAX_VALUE; +// +// for (Highlight high : highs) { +// +// if (high == null) +// continue; +// +// float tempDistance = getDistance(x, y, high.getXPx(), high.getYPx()); +// +// if (tempDistance < minDistance) { +// minDistance = tempDistance; +// closest = high; +// } +// } +// +// return closest; +// } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java new file mode 100644 index 0000000000..032698d5e5 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java @@ -0,0 +1,235 @@ + +package com.github.mikephil.charting.highlight; + +import com.github.mikephil.charting.components.YAxis; + +/** + * Contains information needed to determine the highlighted value. + * + * @author Philipp Jahoda + */ +public class Highlight { + + /** + * the x-value of the highlighted value + */ + private float mX = Float.NaN; + + /** + * the y-value of the highlighted value + */ + private float mY = Float.NaN; + + /** + * the x-pixel of the highlight + */ + private float mXPx; + + /** + * the y-pixel of the highlight + */ + private float mYPx; + + /** + * the index of the data object - in case it refers to more than one + */ + private int mDataIndex = -1; + + /** + * the index of the dataset the highlighted value is in + */ + private int mDataSetIndex; + + /** + * index which value of a stacked bar entry is highlighted, default -1 + */ + private int mStackIndex = -1; + + /** + * the axis the highlighted value belongs to + */ + private YAxis.AxisDependency axis; + + /** + * the x-position (pixels) on which this highlight object was last drawn + */ + private float mDrawX; + + /** + * the y-position (pixels) on which this highlight object was last drawn + */ + private float mDrawY; + + public Highlight(float x, float y, int dataSetIndex) { + this.mX = x; + this.mY = y; + this.mDataSetIndex = dataSetIndex; + } + + public Highlight(float x, int dataSetIndex, int stackIndex) { + this(x, Float.NaN, dataSetIndex); + this.mStackIndex = stackIndex; + } + + /** + * constructor + * + * @param x the x-value of the highlighted value + * @param y the y-value of the highlighted value + * @param dataSetIndex the index of the DataSet the highlighted value belongs to + */ + public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, YAxis.AxisDependency axis) { + this.mX = x; + this.mY = y; + this.mXPx = xPx; + this.mYPx = yPx; + this.mDataSetIndex = dataSetIndex; + this.axis = axis; + } + + /** + * Constructor, only used for stacked-barchart. + * + * @param x the index of the highlighted value on the x-axis + * @param y the y-value of the highlighted value + * @param dataSetIndex the index of the DataSet the highlighted value belongs to + * @param stackIndex references which value of a stacked-bar entry has been + * selected + */ + public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, int stackIndex, YAxis.AxisDependency axis) { + this(x, y, xPx, yPx, dataSetIndex, axis); + this.mStackIndex = stackIndex; + } + + /** + * returns the x-value of the highlighted value + * + * @return + */ + public float getX() { + return mX; + } + + /** + * returns the y-value of the highlighted value + * + * @return + */ + public float getY() { + return mY; + } + + /** + * returns the x-position of the highlight in pixels + */ + public float getXPx() { + return mXPx; + } + + /** + * returns the y-position of the highlight in pixels + */ + public float getYPx() { + return mYPx; + } + + /** + * the index of the data object - in case it refers to more than one + * + * @return + */ + public int getDataIndex() { + return mDataIndex; + } + + public void setDataIndex(int mDataIndex) { + this.mDataIndex = mDataIndex; + } + + /** + * returns the index of the DataSet the highlighted value is in + * + * @return + */ + public int getDataSetIndex() { + return mDataSetIndex; + } + + /** + * Only needed if a stacked-barchart entry was highlighted. References the + * selected value within the stacked-entry. + * + * @return + */ + public int getStackIndex() { + return mStackIndex; + } + + public boolean isStacked() { + return mStackIndex >= 0; + } + + /** + * Returns the axis the highlighted value belongs to. + * + * @return + */ + public YAxis.AxisDependency getAxis() { + return axis; + } + + /** + * Sets the x- and y-position (pixels) where this highlight was last drawn. + * + * @param x + * @param y + */ + public void setDraw(float x, float y) { + this.mDrawX = x; + this.mDrawY = y; + } + + /** + * Returns the x-position in pixels where this highlight object was last drawn. + * + * @return + */ + public float getDrawX() { + return mDrawX; + } + + /** + * Returns the y-position in pixels where this highlight object was last drawn. + * + * @return + */ + public float getDrawY() { + return mDrawY; + } + + /** + * Returns true if this highlight object is equal to the other (compares + * xIndex and dataSetIndex) + * + * @param h + * @return + */ + public boolean equalTo(Highlight h) { + + if (h == null) + return false; + else { + if (this.mDataSetIndex == h.mDataSetIndex && this.mX == h.mX + && this.mStackIndex == h.mStackIndex && this.mDataIndex == h.mDataIndex) + return true; + else + return false; + } + } + + @Override + public String toString() { + return "Highlight, x: " + mX + ", y: " + mY + ", dataSetIndex: " + mDataSetIndex + + ", stackIndex (only stacked barentry): " + mStackIndex; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java new file mode 100644 index 0000000000..d96298e07d --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java @@ -0,0 +1,85 @@ +package com.github.mikephil.charting.highlight; + +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.MPPointD; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Philipp Jahoda on 22/07/15. + */ +public class HorizontalBarHighlighter extends BarHighlighter { + + public HorizontalBarHighlighter(BarDataProvider chart) { + super(chart); + } + + @Override + public Highlight getHighlight(float x, float y) { + + BarData barData = mChart.getBarData(); + + MPPointD pos = getValsForTouch(y, x); + + Highlight high = getHighlightForX((float) pos.y, y, x); + if (high == null) + return null; + + IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex()); + if (set.isStacked()) { + + return getStackedHighlight(high, + set, + (float) pos.y, + (float) pos.x); + } + + MPPointD.recycleInstance(pos); + + return high; + } + + @Override + protected List buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + + ArrayList highlights = new ArrayList<>(); + + //noinspection unchecked + List entries = set.getEntriesForXValue(xVal); + if (entries.size() == 0) { + // Try to find closest x-value and take all entries for that x-value + final Entry closest = set.getEntryForXValue(xVal, Float.NaN, rounding); + if (closest != null) + { + //noinspection unchecked + entries = set.getEntriesForXValue(closest.getX()); + } + } + + if (entries.size() == 0) + return highlights; + + for (Entry e : entries) { + MPPointD pixels = mChart.getTransformer( + set.getAxisDependency()).getPixelForValues(e.getY(), e.getX()); + + highlights.add(new Highlight( + e.getX(), e.getY(), + (float) pixels.x, (float) pixels.y, + dataSetIndex, set.getAxisDependency())); + } + + return highlights; + } + + @Override + protected float getDistance(float x1, float y1, float x2, float y2) { + return Math.abs(y1 - y2); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/IHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/IHighlighter.java new file mode 100644 index 0000000000..d0ca0cfe57 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/IHighlighter.java @@ -0,0 +1,17 @@ +package com.github.mikephil.charting.highlight; + +/** + * Created by philipp on 10/06/16. + */ +public interface IHighlighter +{ + + /** + * Returns a Highlight object corresponding to the given x- and y- touch positions in pixels. + * + * @param x + * @param y + * @return + */ + Highlight getHighlight(float x, float y); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java new file mode 100644 index 0000000000..b4c5a1b24b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java @@ -0,0 +1,25 @@ +package com.github.mikephil.charting.highlight; + +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; + +/** + * Created by philipp on 12/06/16. + */ +public class PieHighlighter extends PieRadarHighlighter { + + public PieHighlighter(PieChart chart) { + super(chart); + } + + @Override + protected Highlight getClosestHighlight(int index, float x, float y) { + + IPieDataSet set = mChart.getData().getDataSet(); + + final Entry entry = set.getEntryForIndex(index); + + return new Highlight(index, entry.getY(), x, y, 0, set.getAxisDependency()); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java new file mode 100644 index 0000000000..a19906d75c --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java @@ -0,0 +1,66 @@ +package com.github.mikephil.charting.highlight; + +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.charts.PieRadarChartBase; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by philipp on 12/06/16. + */ +public abstract class PieRadarHighlighter implements IHighlighter +{ + + protected T mChart; + + /** + * buffer for storing previously highlighted values + */ + protected List mHighlightBuffer = new ArrayList(); + + public PieRadarHighlighter(T chart) { + this.mChart = chart; + } + + @Override + public Highlight getHighlight(float x, float y) { + + float touchDistanceToCenter = mChart.distanceToCenter(x, y); + + // check if a slice was touched + if (touchDistanceToCenter > mChart.getRadius()) { + + // if no slice was touched, highlight nothing + return null; + + } else { + + float angle = mChart.getAngleForPoint(x, y); + + if (mChart instanceof PieChart) { + angle /= mChart.getAnimator().getPhaseY(); + } + + int index = mChart.getIndexForAngle(angle); + + // check if the index could be found + if (index < 0 || index >= mChart.getData().getMaxEntryCountSet().getEntryCount()) { + return null; + + } else { + return getClosestHighlight(index, x, y); + } + } + } + + /** + * Returns the closest Highlight object of the given objects based on the touch position inside the chart. + * + * @param index + * @param x + * @param y + * @return + */ + protected abstract Highlight getClosestHighlight(int index, float x, float y); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java new file mode 100644 index 0000000000..3c4f6d03ac --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java @@ -0,0 +1,79 @@ +package com.github.mikephil.charting.highlight; + +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; + +import java.util.List; + +/** + * Created by philipp on 12/06/16. + */ +public class RadarHighlighter extends PieRadarHighlighter { + + public RadarHighlighter(RadarChart chart) { + super(chart); + } + + @Override + protected Highlight getClosestHighlight(int index, float x, float y) { + + List highlights = getHighlightsAtIndex(index); + + float distanceToCenter = mChart.distanceToCenter(x, y) / mChart.getFactor(); + + Highlight closest = null; + float distance = Float.MAX_VALUE; + + for (int i = 0; i < highlights.size(); i++) { + + Highlight high = highlights.get(i); + + float cdistance = Math.abs(high.getY() - distanceToCenter); + if (cdistance < distance) { + closest = high; + distance = cdistance; + } + } + + return closest; + } + /** + * Returns an array of Highlight objects for the given index. The Highlight + * objects give information about the value at the selected index and the + * DataSet it belongs to. INFORMATION: This method does calculations at + * runtime. Do not over-use in performance critical situations. + * + * @param index + * @return + */ + protected List getHighlightsAtIndex(int index) { + + mHighlightBuffer.clear(); + + float phaseX = mChart.getAnimator().getPhaseX(); + float phaseY = mChart.getAnimator().getPhaseY(); + float sliceangle = mChart.getSliceAngle(); + float factor = mChart.getFactor(); + + MPPointF pOut = MPPointF.getInstance(0,0); + for (int i = 0; i < mChart.getData().getDataSetCount(); i++) { + + IDataSet dataSet = mChart.getData().getDataSetByIndex(i); + + final Entry entry = dataSet.getEntryForIndex(index); + + float y = (entry.getY() - mChart.getYChartMin()); + + Utils.getPosition( + mChart.getCenterOffsets(), y * factor * phaseY, + sliceangle * index * phaseX + mChart.getRotationAngle(), pOut); + + mHighlightBuffer.add(new Highlight(index, entry.getY(), pOut.x, pOut.y, i, dataSet.getAxisDependency())); + } + + return mHighlightBuffer; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java new file mode 100644 index 0000000000..96dd592b8f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java @@ -0,0 +1,38 @@ +package com.github.mikephil.charting.highlight; + +/** + * Created by Philipp Jahoda on 24/07/15. Class that represents the range of one value in a stacked bar entry. e.g. + * stack values are -10, 5, 20 -> then ranges are (-10 - 0, 0 - 5, 5 - 25). + */ +public final class Range { + + public float from; + public float to; + + public Range(float from, float to) { + this.from = from; + this.to = to; + } + + /** + * Returns true if this range contains (if the value is in between) the given value, false if not. + * + * @param value + * @return + */ + public boolean contains(float value) { + + if (value > from && value <= to) + return true; + else + return false; + } + + public boolean isLarger(float value) { + return value > to; + } + + public boolean isSmaller(float value) { + return value < from; + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java new file mode 100644 index 0000000000..9dfee07f9c --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java @@ -0,0 +1,11 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import com.github.mikephil.charting.data.BarData; + +public interface BarDataProvider extends BarLineScatterCandleBubbleDataProvider { + + BarData getBarData(); + boolean isDrawBarShadowEnabled(); + boolean isDrawValueAboveBarEnabled(); + boolean isHighlightFullBarEnabled(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java new file mode 100644 index 0000000000..68beeb8d62 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java @@ -0,0 +1,16 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; +import com.github.mikephil.charting.utils.Transformer; + +public interface BarLineScatterCandleBubbleDataProvider extends ChartInterface { + + Transformer getTransformer(AxisDependency axis); + boolean isInverted(AxisDependency axis); + + float getLowestVisibleX(); + float getHighestVisibleX(); + + BarLineScatterCandleBubbleData getData(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java new file mode 100644 index 0000000000..82ee30ad7c --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java @@ -0,0 +1,8 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import com.github.mikephil.charting.data.BubbleData; + +public interface BubbleDataProvider extends BarLineScatterCandleBubbleDataProvider { + + BubbleData getBubbleData(); +} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/CandleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java similarity index 51% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/CandleDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java index 7e58121f8f..357403f98a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/CandleDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java @@ -1,8 +1,8 @@ -package com.github.mikephil.charting.interfaces; +package com.github.mikephil.charting.interfaces.dataprovider; import com.github.mikephil.charting.data.CandleData; -public interface CandleDataProvider extends BarLineScatterCandleDataProvider { +public interface CandleDataProvider extends BarLineScatterCandleBubbleDataProvider { - public CandleData getCandleData(); + CandleData getCandleData(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java new file mode 100644 index 0000000000..219b46bd82 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java @@ -0,0 +1,69 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import android.graphics.RectF; + +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.MPPointF; + +/** + * Interface that provides everything there is to know about the dimensions, + * bounds, and range of the chart. + * + * @author Philipp Jahoda + */ +public interface ChartInterface { + + /** + * Returns the minimum x value of the chart, regardless of zoom or translation. + * + * @return + */ + float getXChartMin(); + + /** + * Returns the maximum x value of the chart, regardless of zoom or translation. + * + * @return + */ + float getXChartMax(); + + float getXRange(); + + /** + * Returns the minimum y value of the chart, regardless of zoom or translation. + * + * @return + */ + float getYChartMin(); + + /** + * Returns the maximum y value of the chart, regardless of zoom or translation. + * + * @return + */ + float getYChartMax(); + + /** + * Returns the maximum distance in scren dp a touch can be away from an entry to cause it to get highlighted. + * + * @return + */ + float getMaxHighlightDistance(); + + int getWidth(); + + int getHeight(); + + MPPointF getCenterOfView(); + + MPPointF getCenterOffsets(); + + RectF getContentRect(); + + IValueFormatter getDefaultValueFormatter(); + + ChartData getData(); + + int getMaxVisibleCount(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java new file mode 100644 index 0000000000..574d26a2a2 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java @@ -0,0 +1,11 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import com.github.mikephil.charting.data.CombinedData; + +/** + * Created by philipp on 11/06/16. + */ +public interface CombinedDataProvider extends LineDataProvider, BarDataProvider, BubbleDataProvider, CandleDataProvider, ScatterDataProvider { + + CombinedData getCombinedData(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java new file mode 100644 index 0000000000..5c23ac27b4 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java @@ -0,0 +1,11 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.LineData; + +public interface LineDataProvider extends BarLineScatterCandleBubbleDataProvider { + + LineData getLineData(); + + YAxis getAxis(YAxis.AxisDependency dependency); +} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/ScatterDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java similarity index 51% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/ScatterDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java index a5570f1483..b58d5af95d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/ScatterDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java @@ -1,9 +1,8 @@ -package com.github.mikephil.charting.interfaces; +package com.github.mikephil.charting.interfaces.dataprovider; import com.github.mikephil.charting.data.ScatterData; -public interface ScatterDataProvider extends BarLineScatterCandleDataProvider { +public interface ScatterDataProvider extends BarLineScatterCandleBubbleDataProvider { - public ScatterData getScatterData(); - + ScatterData getScatterData(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java new file mode 100644 index 0000000000..fbdfd79531 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java @@ -0,0 +1,64 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import com.github.mikephil.charting.data.BarEntry; + +/** + * Created by philipp on 21/10/15. + */ +public interface IBarDataSet extends IBarLineScatterCandleBubbleDataSet { + + /** + * Returns true if this DataSet is stacked (stacksize > 1) or not. + * + * @return + */ + boolean isStacked(); + + /** + * Returns the maximum number of bars that can be stacked upon another in + * this DataSet. This should return 1 for non stacked bars, and > 1 for stacked bars. + * + * @return + */ + int getStackSize(); + + /** + * Returns the color used for drawing the bar-shadows. The bar shadows is a + * surface behind the bar that indicates the maximum value. + * + * @return + */ + int getBarShadowColor(); + + /** + * Returns the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. + * + * @return + */ + float getBarBorderWidth(); + + /** + * Returns the color drawing borders around the bars. + * + * @return + */ + int getBarBorderColor(); + + /** + * Returns the alpha value (transparency) that is used for drawing the + * highlight indicator. + * + * @return + */ + int getHighLightAlpha(); + + + /** + * Returns the labels used for the different value-stacks in the legend. + * This is only relevant for stacked bar entries. + * + * @return + */ + String[] getStackLabels(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java new file mode 100644 index 0000000000..0f9454b2cd --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java @@ -0,0 +1,16 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import com.github.mikephil.charting.data.Entry; + +/** + * Created by philipp on 21/10/15. + */ +public interface IBarLineScatterCandleBubbleDataSet extends IDataSet { + + /** + * Returns the color that is used for drawing the highlight indicators. + * + * @return + */ + int getHighLightColor(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java new file mode 100644 index 0000000000..e284aac209 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java @@ -0,0 +1,27 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import com.github.mikephil.charting.data.BubbleEntry; + +/** + * Created by philipp on 21/10/15. + */ +public interface IBubbleDataSet extends IBarLineScatterCandleBubbleDataSet { + + /** + * Sets the width of the circle that surrounds the bubble when highlighted, + * in dp. + * + * @param width + */ + void setHighlightCircleWidth(float width); + + float getMaxSize(); + + boolean isNormalizeSizeEnabled(); + + /** + * Returns the width of the highlight-circle that surrounds the bubble + * @return + */ + float getHighlightCircleWidth(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java new file mode 100644 index 0000000000..1d004ed959 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java @@ -0,0 +1,85 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import android.graphics.Paint; + +import com.github.mikephil.charting.data.CandleEntry; + +/** + * Created by philipp on 21/10/15. + */ +public interface ICandleDataSet extends ILineScatterCandleRadarDataSet { + + /** + * Returns the space that is left out on the left and right side of each + * candle. + * + * @return + */ + float getBarSpace(); + + /** + * Returns whether the candle bars should show? + * When false, only "ticks" will show + * + * - default: true + * + * @return + */ + boolean getShowCandleBar(); + + /** + * Returns the width of the candle-shadow-line in pixels. + * + * @return + */ + float getShadowWidth(); + + /** + * Returns shadow color for all entries + * + * @return + */ + int getShadowColor(); + + /** + * Returns the neutral color (for open == close) + * + * @return + */ + int getNeutralColor(); + + /** + * Returns the increasing color (for open < close). + * + * @return + */ + int getIncreasingColor(); + + /** + * Returns the decreasing color (for open > close). + * + * @return + */ + int getDecreasingColor(); + + /** + * Returns paint style when open < close + * + * @return + */ + Paint.Style getIncreasingPaintStyle(); + + /** + * Returns paint style when open > close + * + * @return + */ + Paint.Style getDecreasingPaintStyle(); + + /** + * Is the shadow color same as the candle color? + * + * @return + */ + boolean getShadowColorSameAsCandle(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java new file mode 100644 index 0000000000..fd8af7064b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -0,0 +1,486 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import android.graphics.DashPathEffect; +import android.graphics.PointF; +import android.graphics.Typeface; + +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.MPPointF; + +import java.util.List; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public interface IDataSet { + + /** ###### ###### DATA RELATED METHODS ###### ###### */ + + /** + * returns the minimum y-value this DataSet holds + * + * @return + */ + float getYMin(); + + /** + * returns the maximum y-value this DataSet holds + * + * @return + */ + float getYMax(); + + /** + * returns the minimum x-value this DataSet holds + * + * @return + */ + float getXMin(); + + /** + * returns the maximum x-value this DataSet holds + * + * @return + */ + float getXMax(); + + /** + * Returns the number of y-values this DataSet represents -> the size of the y-values array + * -> yvals.size() + * + * @return + */ + int getEntryCount(); + + /** + * Calculates the minimum and maximum x and y values (mXMin, mXMax, mYMin, mYMax). + */ + void calcMinMax(); + + /** + * Calculates the min and max y-values from the Entry closest to the given fromX to the Entry closest to the given toX value. + * This is only needed for the autoScaleMinMax feature. + * + * @param fromX + * @param toX + */ + void calcMinMaxY(float fromX, float toX); + + /** + * Returns the first Entry object found at the given x-value with binary + * search. + * If the no Entry at the specified x-value is found, this method + * returns the Entry at the closest x-value according to the rounding. + * INFORMATION: This method does calculations at runtime. Do + * not over-use in performance critical situations. + * + * @param xValue the x-value + * @param closestToY If there are multiple y-values for the specified x-value, + * @param rounding determine whether to round up/down/closest + * if there is no Entry matching the provided x-value + * @return + * + * + */ + T getEntryForXValue(float xValue, float closestToY, DataSet.Rounding rounding); + + /** + * Returns the first Entry object found at the given x-value with binary + * search. + * If the no Entry at the specified x-value is found, this method + * returns the Entry at the closest x-value. + * INFORMATION: This method does calculations at runtime. Do + * not over-use in performance critical situations. + * + * + * @param xValue the x-value + * @param closestToY If there are multiple y-values for the specified x-value, + * @return + */ + T getEntryForXValue(float xValue, float closestToY); + + /** + * Returns all Entry objects found at the given x-value with binary + * search. An empty array if no Entry object at that x-value. + * INFORMATION: This method does calculations at runtime. Do + * not over-use in performance critical situations. + * + * @param xValue + * @return + */ + List getEntriesForXValue(float xValue); + + /** + * Returns the Entry object found at the given index (NOT xIndex) in the values array. + * + * @param index + * @return + */ + T getEntryForIndex(int index); + + /** + * Returns the first Entry index found at the given x-value with binary + * search. + * If the no Entry at the specified x-value is found, this method + * returns the Entry at the closest x-value according to the rounding. + * INFORMATION: This method does calculations at runtime. Do + * not over-use in performance critical situations. + * + * @param xValue the x-value + * @param closestToY If there are multiple y-values for the specified x-value, + * @param rounding determine whether to round up/down/closest + * if there is no Entry matching the provided x-value + * @return + */ + int getEntryIndex(float xValue, float closestToY, DataSet.Rounding rounding); + + /** + * Returns the position of the provided entry in the DataSets Entry array. + * Returns -1 if doesn't exist. + * + * @param e + * @return + */ + int getEntryIndex(T e); + + + /** + * This method returns the actual + * index in the Entry array of the DataSet for a given xIndex. IMPORTANT: This method does + * calculations at runtime, do not over-use in performance critical + * situations. + * + * @param xIndex + * @return + */ + int getIndexInEntries(int xIndex); + + /** + * Adds an Entry to the DataSet dynamically. + * Entries are added to the end of the list. + * This will also recalculate the current minimum and maximum + * values of the DataSet and the value-sum. + * + * @param e + */ + boolean addEntry(T e); + + + /** + * Adds an Entry to the DataSet dynamically. + * Entries are added to their appropriate index in the values array respective to their x-position. + * This will also recalculate the current minimum and maximum + * values of the DataSet and the value-sum. + * + * @param e + */ + void addEntryOrdered(T e); + + /** + * Removes the first Entry (at index 0) of this DataSet from the entries array. + * Returns true if successful, false if not. + * + * @return + */ + boolean removeFirst(); + + /** + * Removes the last Entry (at index size-1) of this DataSet from the entries array. + * Returns true if successful, false if not. + * + * @return + */ + boolean removeLast(); + + /** + * Removes an Entry from the DataSets entries array. This will also + * recalculate the current minimum and maximum values of the DataSet and the + * value-sum. Returns true if an Entry was removed, false if no Entry could + * be removed. + * + * @param e + */ + boolean removeEntry(T e); + + /** + * Removes the Entry object closest to the given x-value from the DataSet. + * Returns true if an Entry was removed, false if no Entry could be removed. + * + * @param xValue + */ + boolean removeEntryByXValue(float xValue); + + /** + * Removes the Entry object at the given index in the values array from the DataSet. + * Returns true if an Entry was removed, false if no Entry could be removed. + * + * @param index + * @return + */ + boolean removeEntry(int index); + + /** + * Checks if this DataSet contains the specified Entry. Returns true if so, + * false if not. NOTE: Performance is pretty bad on this one, do not + * over-use in performance critical situations. + * + * @param entry + * @return + */ + boolean contains(T entry); + + /** + * Removes all values from this DataSet and does all necessary recalculations. + */ + void clear(); + + + /** ###### ###### STYLING RELATED (& OTHER) METHODS ###### ###### */ + + /** + * Returns the label string that describes the DataSet. + * + * @return + */ + String getLabel(); + + /** + * Sets the label string that describes the DataSet. + * + * @param label + */ + void setLabel(String label); + + /** + * Returns the axis this DataSet should be plotted against. + * + * @return + */ + YAxis.AxisDependency getAxisDependency(); + + /** + * Set the y-axis this DataSet should be plotted against (either LEFT or + * RIGHT). Default: LEFT + * + * @param dependency + */ + void setAxisDependency(YAxis.AxisDependency dependency); + + /** + * returns all the colors that are set for this DataSet + * + * @return + */ + List getColors(); + + /** + * Returns the first color (index 0) of the colors-array this DataSet + * contains. This is only used for performance reasons when only one color is in the colors array (size == 1) + * + * @return + */ + int getColor(); + + /** + * Returns the color at the given index of the DataSet's color array. + * Performs a IndexOutOfBounds check by modulus. + * + * @param index + * @return + */ + int getColor(int index); + + /** + * returns true if highlighting of values is enabled, false if not + * + * @return + */ + boolean isHighlightEnabled(); + + /** + * If set to true, value highlighting is enabled which means that values can + * be highlighted programmatically or by touch gesture. + * + * @param enabled + */ + void setHighlightEnabled(boolean enabled); + + /** + * Sets the formatter to be used for drawing the values inside the chart. If + * no formatter is set, the chart will automatically determine a reasonable + * formatting (concerning decimals) for all the values that are drawn inside + * the chart. Use chart.getDefaultValueFormatter() to use the formatter + * calculated by the chart. + * + * @param f + */ + void setValueFormatter(IValueFormatter f); + + /** + * Returns the formatter used for drawing the values inside the chart. + * + * @return + */ + IValueFormatter getValueFormatter(); + + /** + * Returns true if the valueFormatter object of this DataSet is null. + * + * @return + */ + boolean needsFormatter(); + + /** + * Sets the color the value-labels of this DataSet should have. + * + * @param color + */ + void setValueTextColor(int color); + + /** + * Sets a list of colors to be used as the colors for the drawn values. + * + * @param colors + */ + void setValueTextColors(List colors); + + /** + * Sets a Typeface for the value-labels of this DataSet. + * + * @param tf + */ + void setValueTypeface(Typeface tf); + + /** + * Sets the text-size of the value-labels of this DataSet in dp. + * + * @param size + */ + void setValueTextSize(float size); + + /** + * Returns only the first color of all colors that are set to be used for the values. + * + * @return + */ + int getValueTextColor(); + + /** + * Returns the color at the specified index that is used for drawing the values inside the chart. + * Uses modulus internally. + * + * @param index + * @return + */ + int getValueTextColor(int index); + + /** + * Returns the typeface that is used for drawing the values inside the chart + * + * @return + */ + Typeface getValueTypeface(); + + /** + * Returns the text size that is used for drawing the values inside the chart + * + * @return + */ + float getValueTextSize(); + + /** + * The form to draw for this dataset in the legend. + *

+ * Return `DEFAULT` to use the default legend form. + */ + Legend.LegendForm getForm(); + + /** + * The form size to draw for this dataset in the legend. + *

+ * Return `Float.NaN` to use the default legend form size. + */ + float getFormSize(); + + /** + * The line width for drawing the form of this dataset in the legend + *

+ * Return `Float.NaN` to use the default legend form line width. + */ + float getFormLineWidth(); + + /** + * The line dash path effect used for shapes that consist of lines. + *

+ * Return `null` to use the default legend form line dash effect. + */ + DashPathEffect getFormLineDashEffect(); + + /** + * set this to true to draw y-values on the chart. + * + * NOTE (for bar and line charts): if `maxVisibleCount` is reached, no values will be drawn even + * if this is enabled + * @param enabled + */ + void setDrawValues(boolean enabled); + + /** + * Returns true if y-value drawing is enabled, false if not + * + * @return + */ + boolean isDrawValuesEnabled(); + + /** + * Set this to true to draw y-icons on the chart. + * + * NOTE (for bar and line charts): if `maxVisibleCount` is reached, no icons will be drawn even + * if this is enabled + * + * @param enabled + */ + void setDrawIcons(boolean enabled); + + /** + * Returns true if y-icon drawing is enabled, false if not + * + * @return + */ + boolean isDrawIconsEnabled(); + + /** + * Offset of icons drawn on the chart. + * + * For all charts except Pie and Radar it will be ordinary (x offset,y offset). + * + * For Pie and Radar chart it will be (y offset, distance from center offset); so if you want icon to be rendered under value, you should increase X component of CGPoint, and if you want icon to be rendered closet to center, you should decrease height component of CGPoint. + * @param offset + */ + void setIconsOffset(MPPointF offset); + + /** + * Get the offset for drawing icons. + */ + MPPointF getIconsOffset(); + + /** + * Set the visibility of this DataSet. If not visible, the DataSet will not + * be drawn to the chart upon refreshing it. + * + * @param visible + */ + void setVisible(boolean visible); + + /** + * Returns true if this DataSet is visible inside the chart, or false if it + * is currently hidden. + * + * @return + */ + boolean isVisible(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java new file mode 100644 index 0000000000..3f534fe848 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java @@ -0,0 +1,103 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import android.graphics.DashPathEffect; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IFillFormatter; + +/** + * Created by Philpp Jahoda on 21/10/15. + */ +public interface ILineDataSet extends ILineRadarDataSet { + + /** + * Returns the drawing mode for this line dataset + * + * @return + */ + LineDataSet.Mode getMode(); + + /** + * Returns the intensity of the cubic lines (the effect intensity). + * Max = 1f = very cubic, Min = 0.05f = low cubic effect, Default: 0.2f + * + * @return + */ + float getCubicIntensity(); + + @Deprecated + boolean isDrawCubicEnabled(); + + @Deprecated + boolean isDrawSteppedEnabled(); + + /** + * Returns the size of the drawn circles. + */ + float getCircleRadius(); + + /** + * Returns the hole radius of the drawn circles. + */ + float getCircleHoleRadius(); + + /** + * Returns the color at the given index of the DataSet's circle-color array. + * Performs a IndexOutOfBounds check by modulus. + * + * @param index + * @return + */ + int getCircleColor(int index); + + /** + * Returns the number of colors in this DataSet's circle-color array. + * + * @return + */ + int getCircleColorCount(); + + /** + * Returns true if drawing circles for this DataSet is enabled, false if not + * + * @return + */ + boolean isDrawCirclesEnabled(); + + /** + * Returns the color of the inner circle (the circle-hole). + * + * @return + */ + int getCircleHoleColor(); + + /** + * Returns true if drawing the circle-holes is enabled, false if not. + * + * @return + */ + boolean isDrawCircleHoleEnabled(); + + /** + * Returns the DashPathEffect that is used for drawing the lines. + * + * @return + */ + DashPathEffect getDashPathEffect(); + + /** + * Returns true if the dashed-line effect is enabled, false if not. + * If the DashPathEffect object is null, also return false here. + * + * @return + */ + boolean isDashedLineEnabled(); + + /** + * Returns the IFillFormatter that is set for this DataSet. + * + * @return + */ + IFillFormatter getFillFormatter(); +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java new file mode 100644 index 0000000000..ce89822716 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java @@ -0,0 +1,58 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.data.Entry; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public interface ILineRadarDataSet extends ILineScatterCandleRadarDataSet { + + /** + * Returns the color that is used for filling the line surface area. + * + * @return + */ + int getFillColor(); + + /** + * Returns the drawable used for filling the area below the line. + * + * @return + */ + Drawable getFillDrawable(); + + /** + * Returns the alpha value that is used for filling the line surface, + * default: 85 + * + * @return + */ + int getFillAlpha(); + + /** + * Returns the stroke-width of the drawn line + * + * @return + */ + float getLineWidth(); + + /** + * Returns true if filled drawing is enabled, false if not + * + * @return + */ + boolean isDrawFilledEnabled(); + + /** + * Set to true if the DataSet should be drawn filled (surface), and not just + * as a line, disabling this will give great performance boost. Please note that this method + * uses the canvas.clipPath(...) method for drawing the filled area. + * For devices with API level < 18 (Android 4.3), hardware acceleration of the chart should + * be turned off. Default: false + * + * @param enabled + */ + void setDrawFilled(boolean enabled); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java new file mode 100644 index 0000000000..9ab6802556 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java @@ -0,0 +1,35 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import android.graphics.DashPathEffect; + +import com.github.mikephil.charting.data.Entry; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public interface ILineScatterCandleRadarDataSet extends IBarLineScatterCandleBubbleDataSet { + + /** + * Returns true if vertical highlight indicator lines are enabled (drawn) + * @return + */ + boolean isVerticalHighlightIndicatorEnabled(); + + /** + * Returns true if vertical highlight indicator lines are enabled (drawn) + * @return + */ + boolean isHorizontalHighlightIndicatorEnabled(); + + /** + * Returns the line-width in which highlight lines are to be drawn. + * @return + */ + float getHighlightLineWidth(); + + /** + * Returns the DashPathEffect that is used for highlighting. + * @return + */ + DashPathEffect getDashPathEffectHighlight(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java new file mode 100644 index 0000000000..a53a9645af --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java @@ -0,0 +1,70 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; + +/** + * Created by Philipp Jahoda on 03/11/15. + */ +public interface IPieDataSet extends IDataSet { + + /** + * Returns the space that is set to be between the piechart-slices of this + * DataSet, in pixels. + * + * @return + */ + float getSliceSpace(); + + /** + * When enabled, slice spacing will be 0.0 when the smallest value is going to be + * smaller than the slice spacing itself. + * + * @return + */ + boolean isAutomaticallyDisableSliceSpacingEnabled(); + + /** + * Returns the distance a highlighted piechart slice is "shifted" away from + * the chart-center in dp. + * + * @return + */ + float getSelectionShift(); + + PieDataSet.ValuePosition getXValuePosition(); + PieDataSet.ValuePosition getYValuePosition(); + + /** + * When valuePosition is OutsideSlice, indicates line color + * */ + int getValueLineColor(); + + /** + * When valuePosition is OutsideSlice, indicates line width + * */ + float getValueLineWidth(); + + /** + * When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size + * */ + float getValueLinePart1OffsetPercentage(); + + /** + * When valuePosition is OutsideSlice, indicates length of first half of the line + * */ + float getValueLinePart1Length(); + + /** + * When valuePosition is OutsideSlice, indicates length of second half of the line + * */ + float getValueLinePart2Length(); + + /** + * When valuePosition is OutsideSlice, this allows variable line length + * */ + boolean isValueLineVariableLength(); + +} + diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java new file mode 100644 index 0000000000..8af00d5376 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java @@ -0,0 +1,30 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import com.github.mikephil.charting.data.RadarEntry; + +/** + * Created by Philipp Jahoda on 03/11/15. + */ +public interface IRadarDataSet extends ILineRadarDataSet { + + /// flag indicating whether highlight circle should be drawn or not + boolean isDrawHighlightCircleEnabled(); + + /// Sets whether highlight circle should be drawn or not + void setDrawHighlightCircleEnabled(boolean enabled); + + int getHighlightCircleFillColor(); + + /// The stroke color for highlight circle. + /// If Utils.COLOR_NONE, the color of the dataset is taken. + int getHighlightCircleStrokeColor(); + + int getHighlightCircleStrokeAlpha(); + + float getHighlightCircleInnerRadius(); + + float getHighlightCircleOuterRadius(); + + float getHighlightCircleStrokeWidth(); + +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java new file mode 100644 index 0000000000..ac6122742a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java @@ -0,0 +1,38 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; + +/** + * Created by philipp on 21/10/15. + */ +public interface IScatterDataSet extends ILineScatterCandleRadarDataSet { + + /** + * Returns the currently set scatter shape size + * + * @return + */ + float getScatterShapeSize(); + + /** + * Returns radius of the hole in the shape + * + * @return + */ + float getScatterShapeHoleRadius(); + + /** + * Returns the color for the hole in the shape + * + * @return + */ + int getScatterShapeHoleColor(); + + /** + * Returns the IShapeRenderer responsible for rendering this DataSet. + * + * @return + */ + IShapeRenderer getShapeRenderer(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java new file mode 100644 index 0000000000..8f953a06aa --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java @@ -0,0 +1,65 @@ +package com.github.mikephil.charting.jobs; + +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.view.View; + +import com.github.mikephil.charting.utils.ObjectPool; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 19/02/16. + */ +@SuppressLint("NewApi") +public class AnimatedMoveViewJob extends AnimatedViewPortJob { + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(4, new AnimatedMoveViewJob(null,0,0,null,null,0,0,0)); + pool.setReplenishPercentage(0.5f); + } + + public static AnimatedMoveViewJob getInstance(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration){ + AnimatedMoveViewJob result = pool.get(); + result.mViewPortHandler = viewPortHandler; + result.xValue = xValue; + result.yValue = yValue; + result.mTrans = trans; + result.view = v; + result.xOrigin = xOrigin; + result.yOrigin = yOrigin; + //result.resetAnimator(); + result.animator.setDuration(duration); + return result; + } + + public static void recycleInstance(AnimatedMoveViewJob instance){ + pool.recycle(instance); + } + + + public AnimatedMoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration) { + super(viewPortHandler, xValue, yValue, trans, v, xOrigin, yOrigin, duration); + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + + pts[0] = xOrigin + (xValue - xOrigin) * phase; + pts[1] = yOrigin + (yValue - yOrigin) * phase; + + mTrans.pointValuesToPixel(pts); + mViewPortHandler.centerViewPort(pts, view); + } + + public void recycleSelf(){ + recycleInstance(this); + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new AnimatedMoveViewJob(null,0,0,null,null,0,0,0); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java new file mode 100644 index 0000000000..f8b520a419 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java @@ -0,0 +1,99 @@ +package com.github.mikephil.charting.jobs; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.view.View; + +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 19/02/16. + */ +@SuppressLint("NewApi") +public abstract class AnimatedViewPortJob extends ViewPortJob implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { + + protected ObjectAnimator animator; + + protected float phase; + + protected float xOrigin; + protected float yOrigin; + + public AnimatedViewPortJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration) { + super(viewPortHandler, xValue, yValue, trans, v); + this.xOrigin = xOrigin; + this.yOrigin = yOrigin; + animator = ObjectAnimator.ofFloat(this, "phase", 0f, 1f); + animator.setDuration(duration); + animator.addUpdateListener(this); + animator.addListener(this); + } + + @SuppressLint("NewApi") + @Override + public void run() { + animator.start(); + } + + public float getPhase() { + return phase; + } + + public void setPhase(float phase) { + this.phase = phase; + } + + public float getXOrigin() { + return xOrigin; + } + + public float getYOrigin() { + return yOrigin; + } + + public abstract void recycleSelf(); + + protected void resetAnimator(){ + animator.removeAllListeners(); + animator.removeAllUpdateListeners(); + animator.reverse(); + animator.addUpdateListener(this); + animator.addListener(this); + } + + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + try{ + recycleSelf(); + }catch (IllegalArgumentException e){ + // don't worry about it. + } + } + + @Override + public void onAnimationCancel(Animator animation) { + try{ + recycleSelf(); + }catch (IllegalArgumentException e){ + // don't worry about it. + } + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java new file mode 100644 index 0000000000..0157e8fa76 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java @@ -0,0 +1,117 @@ +package com.github.mikephil.charting.jobs; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.graphics.Matrix; +import android.view.View; + +import com.github.mikephil.charting.charts.BarLineChartBase; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.utils.ObjectPool; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 19/02/16. + */ +@SuppressLint("NewApi") +public class AnimatedZoomJob extends AnimatedViewPortJob implements Animator.AnimatorListener { + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(8, new AnimatedZoomJob(null,null,null,null,0,0,0,0,0,0,0,0,0,0)); + } + + public static AnimatedZoomJob getInstance(ViewPortHandler viewPortHandler, View v, Transformer trans, YAxis axis, float xAxisRange, float scaleX, float scaleY, float xOrigin, float yOrigin, float zoomCenterX, float zoomCenterY, float zoomOriginX, float zoomOriginY, long duration) { + AnimatedZoomJob result = pool.get(); + result.mViewPortHandler = viewPortHandler; + result.xValue = scaleX; + result.yValue = scaleY; + result.mTrans = trans; + result.view = v; + result.xOrigin = xOrigin; + result.yOrigin = yOrigin; + result.resetAnimator(); + result.animator.setDuration(duration); + return result; + } + + protected float zoomOriginX; + protected float zoomOriginY; + + protected float zoomCenterX; + protected float zoomCenterY; + + protected YAxis yAxis; + + protected float xAxisRange; + + @SuppressLint("NewApi") + public AnimatedZoomJob(ViewPortHandler viewPortHandler, View v, Transformer trans, YAxis axis, float xAxisRange, float scaleX, float scaleY, float xOrigin, float yOrigin, float zoomCenterX, float zoomCenterY, float zoomOriginX, float zoomOriginY, long duration) { + super(viewPortHandler, scaleX, scaleY, trans, v, xOrigin, yOrigin, duration); + + this.zoomCenterX = zoomCenterX; + this.zoomCenterY = zoomCenterY; + this.zoomOriginX = zoomOriginX; + this.zoomOriginY = zoomOriginY; + this.animator.addListener(this); + this.yAxis = axis; + this.xAxisRange = xAxisRange; + } + + protected Matrix mOnAnimationUpdateMatrixBuffer = new Matrix(); + @Override + public void onAnimationUpdate(ValueAnimator animation) { + + float scaleX = xOrigin + (xValue - xOrigin) * phase; + float scaleY = yOrigin + (yValue - yOrigin) * phase; + + Matrix save = mOnAnimationUpdateMatrixBuffer; + mViewPortHandler.setZoom(scaleX, scaleY, save); + mViewPortHandler.refresh(save, view, false); + + float valsInView = yAxis.mAxisRange / mViewPortHandler.getScaleY(); + float xsInView = xAxisRange / mViewPortHandler.getScaleX(); + + pts[0] = zoomOriginX + ((zoomCenterX - xsInView / 2f) - zoomOriginX) * phase; + pts[1] = zoomOriginY + ((zoomCenterY + valsInView / 2f) - zoomOriginY) * phase; + + mTrans.pointValuesToPixel(pts); + + mViewPortHandler.translate(pts, save); + mViewPortHandler.refresh(save, view, true); + } + + @Override + public void onAnimationEnd(Animator animation) { + ((BarLineChartBase) view).calculateOffsets(); + view.postInvalidate(); + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + + @Override + public void recycleSelf() { + + } + + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new AnimatedZoomJob(null,null,null,null,0,0,0,0,0,0,0,0,0,0); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java new file mode 100644 index 0000000000..46b56b1347 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java @@ -0,0 +1,56 @@ + +package com.github.mikephil.charting.jobs; + +import android.view.View; + +import com.github.mikephil.charting.utils.ObjectPool; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 19/02/16. + */ +public class MoveViewJob extends ViewPortJob { + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(2, new MoveViewJob(null,0,0,null,null)); + pool.setReplenishPercentage(0.5f); + } + + public static MoveViewJob getInstance(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v){ + MoveViewJob result = pool.get(); + result.mViewPortHandler = viewPortHandler; + result.xValue = xValue; + result.yValue = yValue; + result.mTrans = trans; + result.view = v; + return result; + } + + public static void recycleInstance(MoveViewJob instance){ + pool.recycle(instance); + } + + public MoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v) { + super(viewPortHandler, xValue, yValue, trans, v); + } + + @Override + public void run() { + + pts[0] = xValue; + pts[1] = yValue; + + mTrans.pointValuesToPixel(pts); + mViewPortHandler.centerViewPort(pts, view); + + this.recycleInstance(this); + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new MoveViewJob(mViewPortHandler, xValue, yValue, mTrans, view); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java new file mode 100644 index 0000000000..c424e4b87a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java @@ -0,0 +1,47 @@ + +package com.github.mikephil.charting.jobs; + +import android.view.View; + +import com.github.mikephil.charting.utils.ObjectPool; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Runnable that is used for viewport modifications since they cannot be + * executed at any time. This can be used to delay the execution of viewport + * modifications until the onSizeChanged(...) method of the chart-view is called. + * This is especially important if viewport modifying methods are called on the chart + * directly after initialization. + * + * @author Philipp Jahoda + */ +public abstract class ViewPortJob extends ObjectPool.Poolable implements Runnable { + + protected float[] pts = new float[2]; + + protected ViewPortHandler mViewPortHandler; + protected float xValue = 0f; + protected float yValue = 0f; + protected Transformer mTrans; + protected View view; + + public ViewPortJob(ViewPortHandler viewPortHandler, float xValue, float yValue, + Transformer trans, View v) { + + this.mViewPortHandler = viewPortHandler; + this.xValue = xValue; + this.yValue = yValue; + this.mTrans = trans; + this.view = v; + + } + + public float getXValue() { + return xValue; + } + + public float getYValue() { + return yValue; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java new file mode 100644 index 0000000000..c39586ca87 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java @@ -0,0 +1,87 @@ + +package com.github.mikephil.charting.jobs; + +import android.graphics.Matrix; +import android.view.View; + +import com.github.mikephil.charting.charts.BarLineChartBase; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.utils.ObjectPool; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 19/02/16. + */ +public class ZoomJob extends ViewPortJob { + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(1, new ZoomJob(null, 0, 0, 0, 0, null, null, null)); + pool.setReplenishPercentage(0.5f); + } + + public static ZoomJob getInstance(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, + Transformer trans, YAxis.AxisDependency axis, View v) { + ZoomJob result = pool.get(); + result.xValue = xValue; + result.yValue = yValue; + result.scaleX = scaleX; + result.scaleY = scaleY; + result.mViewPortHandler = viewPortHandler; + result.mTrans = trans; + result.axisDependency = axis; + result.view = v; + return result; + } + + public static void recycleInstance(ZoomJob instance) { + pool.recycle(instance); + } + + protected float scaleX; + protected float scaleY; + + protected YAxis.AxisDependency axisDependency; + + public ZoomJob(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, Transformer trans, + YAxis.AxisDependency axis, View v) { + super(viewPortHandler, xValue, yValue, trans, v); + + this.scaleX = scaleX; + this.scaleY = scaleY; + this.axisDependency = axis; + } + + protected Matrix mRunMatrixBuffer = new Matrix(); + + @Override + public void run() { + + Matrix save = mRunMatrixBuffer; + mViewPortHandler.zoom(scaleX, scaleY, save); + mViewPortHandler.refresh(save, view, false); + + float yValsInView = ((BarLineChartBase) view).getAxis(axisDependency).mAxisRange / mViewPortHandler.getScaleY(); + float xValsInView = ((BarLineChartBase) view).getXAxis().mAxisRange / mViewPortHandler.getScaleX(); + + pts[0] = xValue - xValsInView / 2f; + pts[1] = yValue + yValsInView / 2f; + + mTrans.pointValuesToPixel(pts); + + mViewPortHandler.translate(pts, save); + mViewPortHandler.refresh(save, view, false); + + ((BarLineChartBase) view).calculateOffsets(); + view.postInvalidate(); + + recycleInstance(this); + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new ZoomJob(null, 0, 0, 0, 0, null, null, null); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java new file mode 100644 index 0000000000..e041e5ad97 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java @@ -0,0 +1,673 @@ +package com.github.mikephil.charting.listener; + +import android.annotation.SuppressLint; +import android.graphics.Matrix; +import android.graphics.PointF; +import android.util.Log; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.animation.AnimationUtils; + +import com.github.mikephil.charting.charts.BarLineChartBase; +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * TouchListener for Bar-, Line-, Scatter- and CandleStickChart with handles all + * touch interaction. Longpress == Zoom out. Double-Tap == Zoom in. + * + * @author Philipp Jahoda + */ +public class BarLineChartTouchListener extends ChartTouchListener>>> { + + /** + * the original touch-matrix from the chart + */ + private Matrix mMatrix = new Matrix(); + + /** + * matrix for saving the original matrix state + */ + private Matrix mSavedMatrix = new Matrix(); + + /** + * point where the touch action started + */ + private MPPointF mTouchStartPoint = MPPointF.getInstance(0,0); + + /** + * center between two pointers (fingers on the display) + */ + private MPPointF mTouchPointCenter = MPPointF.getInstance(0,0); + + private float mSavedXDist = 1f; + private float mSavedYDist = 1f; + private float mSavedDist = 1f; + + private IDataSet mClosestDataSetToTouch; + + /** + * used for tracking velocity of dragging + */ + private VelocityTracker mVelocityTracker; + + private long mDecelerationLastTime = 0; + private MPPointF mDecelerationCurrentPoint = MPPointF.getInstance(0,0); + private MPPointF mDecelerationVelocity = MPPointF.getInstance(0,0); + + /** + * the distance of movement that will be counted as a drag + */ + private float mDragTriggerDist; + + /** + * the minimum distance between the pointers that will trigger a zoom gesture + */ + private float mMinScalePointerDistance; + + /** + * Constructor with initialization parameters. + * + * @param chart instance of the chart + * @param touchMatrix the touch-matrix of the chart + * @param dragTriggerDistance the minimum movement distance that will be interpreted as a "drag" gesture in dp (3dp equals + * to about 9 pixels on a 5.5" FHD screen) + */ + public BarLineChartTouchListener(BarLineChartBase>> chart, Matrix touchMatrix, float dragTriggerDistance) { + super(chart); + this.mMatrix = touchMatrix; + + this.mDragTriggerDist = Utils.convertDpToPixel(dragTriggerDistance); + + this.mMinScalePointerDistance = Utils.convertDpToPixel(3.5f); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent event) { + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(event); + + if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + + if (mTouchMode == NONE) { + mGestureDetector.onTouchEvent(event); + } + + if (!mChart.isDragEnabled() && (!mChart.isScaleXEnabled() && !mChart.isScaleYEnabled())) + return true; + + // Handle touch events here... + switch (event.getAction() & MotionEvent.ACTION_MASK) { + + case MotionEvent.ACTION_DOWN: + + startAction(event); + + stopDeceleration(); + + saveTouchStart(event); + + break; + case MotionEvent.ACTION_POINTER_DOWN: + + if (event.getPointerCount() >= 2) { + + mChart.disableScroll(); + + saveTouchStart(event); + + // get the distance between the pointers on the x-axis + mSavedXDist = getXDist(event); + + // get the distance between the pointers on the y-axis + mSavedYDist = getYDist(event); + + // get the total distance between the pointers + mSavedDist = spacing(event); + + if (mSavedDist > 10f) { + + if (mChart.isPinchZoomEnabled()) { + mTouchMode = PINCH_ZOOM; + } else { + if (mChart.isScaleXEnabled() != mChart.isScaleYEnabled()) { + mTouchMode = mChart.isScaleXEnabled() ? X_ZOOM : Y_ZOOM; + } else { + mTouchMode = mSavedXDist > mSavedYDist ? X_ZOOM : Y_ZOOM; + } + } + } + + // determine the touch-pointer center + midPoint(mTouchPointCenter, event); + } + break; + case MotionEvent.ACTION_MOVE: + + if (mTouchMode == DRAG) { + + mChart.disableScroll(); + performDrag(event); + + } else if (mTouchMode == X_ZOOM || mTouchMode == Y_ZOOM || mTouchMode == PINCH_ZOOM) { + + mChart.disableScroll(); + + if (mChart.isScaleXEnabled() || mChart.isScaleYEnabled()) + performZoom(event); + + } else if (mTouchMode == NONE + && Math.abs(distance(event.getX(), mTouchStartPoint.x, event.getY(), + mTouchStartPoint.y)) > mDragTriggerDist) { + + if (mChart.hasNoDragOffset()) { + + if (!mChart.isFullyZoomedOut() && mChart.isDragEnabled()) { + mTouchMode = DRAG; + } else { + + mLastGesture = ChartGesture.DRAG; + + if (mChart.isHighlightPerDragEnabled()) + performHighlightDrag(event); + } + + } else if (mChart.isDragEnabled()) { + mLastGesture = ChartGesture.DRAG; + mTouchMode = DRAG; + } + } + break; + + case MotionEvent.ACTION_UP: + + final VelocityTracker velocityTracker = mVelocityTracker; + final int pointerId = event.getPointerId(0); + velocityTracker.computeCurrentVelocity(1000, Utils.getMaximumFlingVelocity()); + final float velocityY = velocityTracker.getYVelocity(pointerId); + final float velocityX = velocityTracker.getXVelocity(pointerId); + + if (Math.abs(velocityX) > Utils.getMinimumFlingVelocity() || + Math.abs(velocityY) > Utils.getMinimumFlingVelocity()) { + + if (mTouchMode == DRAG && mChart.isDragDecelerationEnabled()) { + + stopDeceleration(); + + mDecelerationLastTime = AnimationUtils.currentAnimationTimeMillis(); + + mDecelerationCurrentPoint.x = event.getX(); + mDecelerationCurrentPoint.y = event.getY(); + + mDecelerationVelocity.x = velocityX; + mDecelerationVelocity.y = velocityY; + + Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by + // Google + } + } + + if (mTouchMode == X_ZOOM || + mTouchMode == Y_ZOOM || + mTouchMode == PINCH_ZOOM || + mTouchMode == POST_ZOOM) { + + // Range might have changed, which means that Y-axis labels + // could have changed in size, affecting Y-axis size. + // So we need to recalculate offsets. + mChart.calculateOffsets(); + mChart.postInvalidate(); + } + + mTouchMode = NONE; + mChart.enableScroll(); + + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + + endAction(event); + + break; + case MotionEvent.ACTION_POINTER_UP: + Utils.velocityTrackerPointerUpCleanUpIfNecessary(event, mVelocityTracker); + + mTouchMode = POST_ZOOM; + break; + + case MotionEvent.ACTION_CANCEL: + + mTouchMode = NONE; + endAction(event); + break; + } + + // perform the transformation, update the chart + mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, true); + + return true; // indicate event was handled + } + + /** + * ################ ################ ################ ################ + */ + /** BELOW CODE PERFORMS THE ACTUAL TOUCH ACTIONS */ + + /** + * Saves the current Matrix state and the touch-start point. + * + * @param event + */ + private void saveTouchStart(MotionEvent event) { + + mSavedMatrix.set(mMatrix); + mTouchStartPoint.x = event.getX(); + mTouchStartPoint.y = event.getY(); + + mClosestDataSetToTouch = mChart.getDataSetByTouchPoint(event.getX(), event.getY()); + } + + /** + * Performs all necessary operations needed for dragging. + * + * @param event + */ + private void performDrag(MotionEvent event) { + + mLastGesture = ChartGesture.DRAG; + + mMatrix.set(mSavedMatrix); + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + float dX, dY; + + // check if axis is inverted + if (inverted()) { + + // if there is an inverted horizontalbarchart + if (mChart instanceof HorizontalBarChart) { + dX = -(event.getX() - mTouchStartPoint.x); + dY = event.getY() - mTouchStartPoint.y; + } else { + dX = event.getX() - mTouchStartPoint.x; + dY = -(event.getY() - mTouchStartPoint.y); + } + } else { + dX = event.getX() - mTouchStartPoint.x; + dY = event.getY() - mTouchStartPoint.y; + } + + mMatrix.postTranslate(dX, dY); + + if (l != null) + l.onChartTranslate(event, dX, dY); + } + + /** + * Performs the all operations necessary for pinch and axis zoom. + * + * @param event + */ + private void performZoom(MotionEvent event) { + + if (event.getPointerCount() >= 2) { // two finger zoom + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + // get the distance between the pointers of the touch event + float totalDist = spacing(event); + + if (totalDist > mMinScalePointerDistance) { + + // get the translation + MPPointF t = getTrans(mTouchPointCenter.x, mTouchPointCenter.y); + ViewPortHandler h = mChart.getViewPortHandler(); + + // take actions depending on the activated touch mode + if (mTouchMode == PINCH_ZOOM) { + + mLastGesture = ChartGesture.PINCH_ZOOM; + + float scale = totalDist / mSavedDist; // total scale + + boolean isZoomingOut = (scale < 1); + + boolean canZoomMoreX = isZoomingOut ? + h.canZoomOutMoreX() : + h.canZoomInMoreX(); + + boolean canZoomMoreY = isZoomingOut ? + h.canZoomOutMoreY() : + h.canZoomInMoreY(); + + float scaleX = (mChart.isScaleXEnabled()) ? scale : 1f; + float scaleY = (mChart.isScaleYEnabled()) ? scale : 1f; + + if (canZoomMoreY || canZoomMoreX) { + + mMatrix.set(mSavedMatrix); + mMatrix.postScale(scaleX, scaleY, t.x, t.y); + + if (l != null) + l.onChartScale(event, scaleX, scaleY); + } + + } else if (mTouchMode == X_ZOOM && mChart.isScaleXEnabled()) { + + mLastGesture = ChartGesture.X_ZOOM; + + float xDist = getXDist(event); + float scaleX = xDist / mSavedXDist; // x-axis scale + + boolean isZoomingOut = (scaleX < 1); + boolean canZoomMoreX = isZoomingOut ? + h.canZoomOutMoreX() : + h.canZoomInMoreX(); + + if (canZoomMoreX) { + + mMatrix.set(mSavedMatrix); + mMatrix.postScale(scaleX, 1f, t.x, t.y); + + if (l != null) + l.onChartScale(event, scaleX, 1f); + } + + } else if (mTouchMode == Y_ZOOM && mChart.isScaleYEnabled()) { + + mLastGesture = ChartGesture.Y_ZOOM; + + float yDist = getYDist(event); + float scaleY = yDist / mSavedYDist; // y-axis scale + + boolean isZoomingOut = (scaleY < 1); + boolean canZoomMoreY = isZoomingOut ? + h.canZoomOutMoreY() : + h.canZoomInMoreY(); + + if (canZoomMoreY) { + + mMatrix.set(mSavedMatrix); + mMatrix.postScale(1f, scaleY, t.x, t.y); + + if (l != null) + l.onChartScale(event, 1f, scaleY); + } + } + + MPPointF.recycleInstance(t); + } + } + } + + /** + * Highlights upon dragging, generates callbacks for the selection-listener. + * + * @param e + */ + private void performHighlightDrag(MotionEvent e) { + + Highlight h = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); + + if (h != null && !h.equalTo(mLastHighlighted)) { + mLastHighlighted = h; + mChart.highlightValue(h, true); + } + } + + /** + * ################ ################ ################ ################ + */ + /** DOING THE MATH BELOW ;-) */ + + + /** + * Determines the center point between two pointer touch points. + * + * @param point + * @param event + */ + private static void midPoint(MPPointF point, MotionEvent event) { + float x = event.getX(0) + event.getX(1); + float y = event.getY(0) + event.getY(1); + point.x = (x / 2f); + point.y = (y / 2f); + } + + /** + * returns the distance between two pointer touch points + * + * @param event + * @return + */ + private static float spacing(MotionEvent event) { + float x = event.getX(0) - event.getX(1); + float y = event.getY(0) - event.getY(1); + return (float) Math.sqrt(x * x + y * y); + } + + /** + * calculates the distance on the x-axis between two pointers (fingers on + * the display) + * + * @param e + * @return + */ + private static float getXDist(MotionEvent e) { + float x = Math.abs(e.getX(0) - e.getX(1)); + return x; + } + + /** + * calculates the distance on the y-axis between two pointers (fingers on + * the display) + * + * @param e + * @return + */ + private static float getYDist(MotionEvent e) { + float y = Math.abs(e.getY(0) - e.getY(1)); + return y; + } + + /** + * Returns a recyclable MPPointF instance. + * returns the correct translation depending on the provided x and y touch + * points + * + * @param x + * @param y + * @return + */ + public MPPointF getTrans(float x, float y) { + + ViewPortHandler vph = mChart.getViewPortHandler(); + + float xTrans = x - vph.offsetLeft(); + float yTrans = 0f; + + // check if axis is inverted + if (inverted()) { + yTrans = -(y - vph.offsetTop()); + } else { + yTrans = -(mChart.getMeasuredHeight() - y - vph.offsetBottom()); + } + + return MPPointF.getInstance(xTrans, yTrans); + } + + /** + * Returns true if the current touch situation should be interpreted as inverted, false if not. + * + * @return + */ + private boolean inverted() { + return (mClosestDataSetToTouch == null && mChart.isAnyAxisInverted()) || (mClosestDataSetToTouch != null + && mChart.isInverted(mClosestDataSetToTouch.getAxisDependency())); + } + + /** + * ################ ################ ################ ################ + */ + /** GETTERS AND GESTURE RECOGNITION BELOW */ + + /** + * returns the matrix object the listener holds + * + * @return + */ + public Matrix getMatrix() { + return mMatrix; + } + + /** + * Sets the minimum distance that will be interpreted as a "drag" by the chart in dp. + * Default: 3dp + * + * @param dragTriggerDistance + */ + public void setDragTriggerDist(float dragTriggerDistance) { + this.mDragTriggerDist = Utils.convertDpToPixel(dragTriggerDistance); + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + + mLastGesture = ChartGesture.DOUBLE_TAP; + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + if (l != null) { + l.onChartDoubleTapped(e); + } + + // check if double-tap zooming is enabled + if (mChart.isDoubleTapToZoomEnabled() && mChart.getData().getEntryCount() > 0) { + + MPPointF trans = getTrans(e.getX(), e.getY()); + + mChart.zoom(mChart.isScaleXEnabled() ? 1.4f : 1f, mChart.isScaleYEnabled() ? 1.4f : 1f, trans.x, trans.y); + + if (mChart.isLogEnabled()) + Log.i("BarlineChartTouch", "Double-Tap, Zooming In, x: " + trans.x + ", y: " + + trans.y); + + MPPointF.recycleInstance(trans); + } + + return super.onDoubleTap(e); + } + + @Override + public void onLongPress(MotionEvent e) { + + mLastGesture = ChartGesture.LONG_PRESS; + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + if (l != null) { + + l.onChartLongPressed(e); + } + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + + mLastGesture = ChartGesture.SINGLE_TAP; + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + if (l != null) { + l.onChartSingleTapped(e); + } + + if (!mChart.isHighlightPerTapEnabled()) { + return false; + } + + Highlight h = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); + performHighlight(h, e); + + return super.onSingleTapUp(e); + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + + mLastGesture = ChartGesture.FLING; + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + if (l != null) { + l.onChartFling(e1, e2, velocityX, velocityY); + } + + return super.onFling(e1, e2, velocityX, velocityY); + } + + public void stopDeceleration() { + mDecelerationVelocity.x = 0; + mDecelerationVelocity.y = 0; + } + + public void computeScroll() { + + if (mDecelerationVelocity.x == 0.f && mDecelerationVelocity.y == 0.f) + return; // There's no deceleration in progress + + final long currentTime = AnimationUtils.currentAnimationTimeMillis(); + + mDecelerationVelocity.x *= mChart.getDragDecelerationFrictionCoef(); + mDecelerationVelocity.y *= mChart.getDragDecelerationFrictionCoef(); + + final float timeInterval = (float) (currentTime - mDecelerationLastTime) / 1000.f; + + float distanceX = mDecelerationVelocity.x * timeInterval; + float distanceY = mDecelerationVelocity.y * timeInterval; + + mDecelerationCurrentPoint.x += distanceX; + mDecelerationCurrentPoint.y += distanceY; + + MotionEvent event = MotionEvent.obtain(currentTime, currentTime, MotionEvent.ACTION_MOVE, mDecelerationCurrentPoint.x, + mDecelerationCurrentPoint.y, 0); + performDrag(event); + event.recycle(); + mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, false); + + mDecelerationLastTime = currentTime; + + if (Math.abs(mDecelerationVelocity.x) >= 0.01 || Math.abs(mDecelerationVelocity.y) >= 0.01) + Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by Google + else { + // Range might have changed, which means that Y-axis labels + // could have changed in size, affecting Y-axis size. + // So we need to recalculate offsets. + mChart.calculateOffsets(); + mChart.postInvalidate(); + + stopDeceleration(); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java new file mode 100644 index 0000000000..75c8e864b4 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java @@ -0,0 +1,143 @@ +package com.github.mikephil.charting.listener; + +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; + +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.highlight.Highlight; + +/** + * Created by philipp on 12/06/15. + */ +public abstract class ChartTouchListener> extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener { + + public enum ChartGesture { + NONE, DRAG, X_ZOOM, Y_ZOOM, PINCH_ZOOM, ROTATE, SINGLE_TAP, DOUBLE_TAP, LONG_PRESS, FLING + } + + /** + * the last touch gesture that has been performed + **/ + protected ChartGesture mLastGesture = ChartGesture.NONE; + + // states + protected static final int NONE = 0; + protected static final int DRAG = 1; + protected static final int X_ZOOM = 2; + protected static final int Y_ZOOM = 3; + protected static final int PINCH_ZOOM = 4; + protected static final int POST_ZOOM = 5; + protected static final int ROTATE = 6; + + /** + * integer field that holds the current touch-state + */ + protected int mTouchMode = NONE; + + /** + * the last highlighted object (via touch) + */ + protected Highlight mLastHighlighted; + + /** + * the gesturedetector used for detecting taps and longpresses, ... + */ + protected GestureDetector mGestureDetector; + + /** + * the chart the listener represents + */ + protected T mChart; + + public ChartTouchListener(T chart) { + this.mChart = chart; + + mGestureDetector = new GestureDetector(chart.getContext(), this); + } + + /** + * Calls the OnChartGestureListener to do the start callback + * + * @param me + */ + public void startAction(MotionEvent me) { + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + if (l != null) + l.onChartGestureStart(me, mLastGesture); + } + + /** + * Calls the OnChartGestureListener to do the end callback + * + * @param me + */ + public void endAction(MotionEvent me) { + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + if (l != null) + l.onChartGestureEnd(me, mLastGesture); + } + + /** + * Sets the last value that was highlighted via touch. + * + * @param high + */ + public void setLastHighlighted(Highlight high) { + mLastHighlighted = high; + } + + /** + * returns the touch mode the listener is currently in + * + * @return + */ + public int getTouchMode() { + return mTouchMode; + } + + /** + * Returns the last gesture that has been performed on the chart. + * + * @return + */ + public ChartGesture getLastGesture() { + return mLastGesture; + } + + + /** + * Perform a highlight operation. + * + * @param e + */ + protected void performHighlight(Highlight h, MotionEvent e) { + + if (h == null || h.equalTo(mLastHighlighted)) { + mChart.highlightValue(null, true); + mLastHighlighted = null; + } else { + mChart.highlightValue(h, true); + mLastHighlighted = h; + } + } + + /** + * returns the distance between two points + * + * @param eventX + * @param startX + * @param eventY + * @param startY + * @return + */ + protected static float distance(float eventX, float startX, float eventY, float startY) { + float dx = eventX - startX; + float dy = eventY - startY; + return (float) Math.sqrt(dx * dx + dy * dy); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java new file mode 100644 index 0000000000..a17cdde941 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java @@ -0,0 +1,76 @@ +package com.github.mikephil.charting.listener; + +import android.view.MotionEvent; + +/** + * Listener for callbacks when doing gestures on the chart. + * + * @author Philipp Jahoda + */ +public interface OnChartGestureListener { + + /** + * Callbacks when a touch-gesture has started on the chart (ACTION_DOWN) + * + * @param me + * @param lastPerformedGesture + */ + void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture); + + /** + * Callbacks when a touch-gesture has ended on the chart (ACTION_UP, ACTION_CANCEL) + * + * @param me + * @param lastPerformedGesture + */ + void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture); + + /** + * Callbacks when the chart is longpressed. + * + * @param me + */ + void onChartLongPressed(MotionEvent me); + + /** + * Callbacks when the chart is double-tapped. + * + * @param me + */ + void onChartDoubleTapped(MotionEvent me); + + /** + * Callbacks when the chart is single-tapped. + * + * @param me + */ + void onChartSingleTapped(MotionEvent me); + + /** + * Callbacks then a fling gesture is made on the chart. + * + * @param me1 + * @param me2 + * @param velocityX + * @param velocityY + */ + void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY); + + /** + * Callbacks when the chart is scaled / zoomed via pinch zoom gesture. + * + * @param me + * @param scaleX scalefactor on the x-axis + * @param scaleY scalefactor on the y-axis + */ + void onChartScale(MotionEvent me, float scaleX, float scaleY); + + /** + * Callbacks when the chart is moved / translated via drag gesture. + * + * @param me + * @param dX translation distance on the x-axis + * @param dY translation distance on the y-axis + */ + void onChartTranslate(MotionEvent me, float dX, float dY); +} diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java similarity index 50% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java index 798d19f34f..7f50232b7e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java @@ -1,30 +1,27 @@ - package com.github.mikephil.charting.listener; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.utils.Highlight; +import com.github.mikephil.charting.highlight.Highlight; /** * Listener for callbacks when selecting values inside the chart by * touch-gesture. - * + * * @author Philipp Jahoda */ public interface OnChartValueSelectedListener { /** * Called when a value has been selected inside the chart. - * - * @param e The selected Entry. - * @param dataSetIndex The index in the datasets array of the data object - * the Entrys DataSet is in. - * @param h the corresponding highlight object that contains information - * about the highlighted position + * + * @param e The selected Entry + * @param h The corresponding highlight object that contains information + * about the highlighted position such as dataSetIndex, ... */ - public void onValueSelected(Entry e, int dataSetIndex, Highlight h); + void onValueSelected(Entry e, Highlight h); /** * Called when nothing has been selected or an "un-select" has been made. */ - public void onNothingSelected(); + void onNothingSelected(); } diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnDrawListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawListener.java similarity index 85% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnDrawListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawListener.java index 58ed4bc69a..5890350bcd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/OnDrawListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawListener.java @@ -18,14 +18,14 @@ public interface OnDrawListener { * @param entry * the last drawn entry */ - public void onEntryAdded(Entry entry); + void onEntryAdded(Entry entry); /** * Called whenever an entry is moved by the user after beeing highlighted * * @param entry */ - public void onEntryMoved(Entry entry); + void onEntryMoved(Entry entry); /** * Called when drawing finger is lifted and the draw is finished. @@ -33,6 +33,6 @@ public interface OnDrawListener { * @param dataSet * the last drawn DataSet */ - public void onDrawFinished(DataSet dataSet); + void onDrawFinished(DataSet dataSet); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java new file mode 100644 index 0000000000..527617472a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java @@ -0,0 +1,287 @@ + +package com.github.mikephil.charting.listener; + +import android.annotation.SuppressLint; +import android.graphics.PointF; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.AnimationUtils; + +import com.github.mikephil.charting.charts.PieRadarChartBase; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; + +import java.util.ArrayList; + +/** + * Touchlistener for the PieChart. + * + * @author Philipp Jahoda + */ +public class PieRadarChartTouchListener extends ChartTouchListener> { + + private MPPointF mTouchStartPoint = MPPointF.getInstance(0,0); + + /** + * the angle where the dragging started + */ + private float mStartAngle = 0f; + + private ArrayList _velocitySamples = new ArrayList(); + + private long mDecelerationLastTime = 0; + private float mDecelerationAngularVelocity = 0.f; + + public PieRadarChartTouchListener(PieRadarChartBase chart) { + super(chart); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent event) { + + if (mGestureDetector.onTouchEvent(event)) + return true; + + // if rotation by touch is enabled + if (mChart.isRotationEnabled()) { + + float x = event.getX(); + float y = event.getY(); + + switch (event.getAction()) { + + case MotionEvent.ACTION_DOWN: + + startAction(event); + + stopDeceleration(); + + resetVelocity(); + + if (mChart.isDragDecelerationEnabled()) + sampleVelocity(x, y); + + setGestureStartAngle(x, y); + mTouchStartPoint.x = x; + mTouchStartPoint.y = y; + + break; + case MotionEvent.ACTION_MOVE: + + if (mChart.isDragDecelerationEnabled()) + sampleVelocity(x, y); + + if (mTouchMode == NONE + && distance(x, mTouchStartPoint.x, y, mTouchStartPoint.y) + > Utils.convertDpToPixel(8f)) { + mLastGesture = ChartGesture.ROTATE; + mTouchMode = ROTATE; + mChart.disableScroll(); + } else if (mTouchMode == ROTATE) { + updateGestureRotation(x, y); + mChart.invalidate(); + } + + endAction(event); + + break; + case MotionEvent.ACTION_UP: + + if (mChart.isDragDecelerationEnabled()) { + + stopDeceleration(); + + sampleVelocity(x, y); + + mDecelerationAngularVelocity = calculateVelocity(); + + if (mDecelerationAngularVelocity != 0.f) { + mDecelerationLastTime = AnimationUtils.currentAnimationTimeMillis(); + + Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by Google + } + } + + mChart.enableScroll(); + mTouchMode = NONE; + + endAction(event); + + break; + } + } + + return true; + } + + @Override + public void onLongPress(MotionEvent me) { + + mLastGesture = ChartGesture.LONG_PRESS; + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + if (l != null) { + l.onChartLongPressed(me); + } + } + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + return true; + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + + mLastGesture = ChartGesture.SINGLE_TAP; + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + if (l != null) { + l.onChartSingleTapped(e); + } + + if(!mChart.isHighlightPerTapEnabled()) { + return false; + } + + Highlight high = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); + performHighlight(high, e); + + return true; + } + + private void resetVelocity() { + _velocitySamples.clear(); + } + + private void sampleVelocity(float touchLocationX, float touchLocationY) { + + long currentTime = AnimationUtils.currentAnimationTimeMillis(); + + _velocitySamples.add(new AngularVelocitySample(currentTime, mChart.getAngleForPoint(touchLocationX, touchLocationY))); + + // Remove samples older than our sample time - 1 seconds + for (int i = 0, count = _velocitySamples.size(); i < count - 2; i++) { + if (currentTime - _velocitySamples.get(i).time > 1000) { + _velocitySamples.remove(0); + i--; + count--; + } else { + break; + } + } + } + + private float calculateVelocity() { + + if (_velocitySamples.isEmpty()) + return 0.f; + + AngularVelocitySample firstSample = _velocitySamples.get(0); + AngularVelocitySample lastSample = _velocitySamples.get(_velocitySamples.size() - 1); + + // Look for a sample that's closest to the latest sample, but not the same, so we can deduce the direction + AngularVelocitySample beforeLastSample = firstSample; + for (int i = _velocitySamples.size() - 1; i >= 0; i--) { + beforeLastSample = _velocitySamples.get(i); + if (beforeLastSample.angle != lastSample.angle) { + break; + } + } + + // Calculate the sampling time + float timeDelta = (lastSample.time - firstSample.time) / 1000.f; + if (timeDelta == 0.f) { + timeDelta = 0.1f; + } + + // Calculate clockwise/ccw by choosing two values that should be closest to each other, + // so if the angles are two far from each other we know they are inverted "for sure" + boolean clockwise = lastSample.angle >= beforeLastSample.angle; + if (Math.abs(lastSample.angle - beforeLastSample.angle) > 270.0) { + clockwise = !clockwise; + } + + // Now if the "gesture" is over a too big of an angle - then we know the angles are inverted, and we need to move them closer to each other from both sides of the 360.0 wrapping point + if (lastSample.angle - firstSample.angle > 180.0) { + firstSample.angle += 360.0; + } else if (firstSample.angle - lastSample.angle > 180.0) { + lastSample.angle += 360.0; + } + + // The velocity + float velocity = Math.abs((lastSample.angle - firstSample.angle) / timeDelta); + + // Direction? + if (!clockwise) { + velocity = -velocity; + } + + return velocity; + } + + /** + * sets the starting angle of the rotation, this is only used by the touch + * listener, x and y is the touch position + * + * @param x + * @param y + */ + public void setGestureStartAngle(float x, float y) { + mStartAngle = mChart.getAngleForPoint(x, y) - mChart.getRawRotationAngle(); + } + + /** + * updates the view rotation depending on the given touch position, also + * takes the starting angle into consideration + * + * @param x + * @param y + */ + public void updateGestureRotation(float x, float y) { + mChart.setRotationAngle(mChart.getAngleForPoint(x, y) - mStartAngle); + } + + /** + * Sets the deceleration-angular-velocity to 0f + */ + public void stopDeceleration() { + mDecelerationAngularVelocity = 0.f; + } + + public void computeScroll() { + + if (mDecelerationAngularVelocity == 0.f) + return; // There's no deceleration in progress + + final long currentTime = AnimationUtils.currentAnimationTimeMillis(); + + mDecelerationAngularVelocity *= mChart.getDragDecelerationFrictionCoef(); + + final float timeInterval = (float) (currentTime - mDecelerationLastTime) / 1000.f; + + mChart.setRotationAngle(mChart.getRotationAngle() + mDecelerationAngularVelocity * timeInterval); + + mDecelerationLastTime = currentTime; + + if (Math.abs(mDecelerationAngularVelocity) >= 0.001) + Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by Google + else + stopDeceleration(); + } + + private class AngularVelocitySample { + + public long time; + public float angle; + + public AngularVelocitySample(long time, float angle) { + this.time = time; + this.angle = angle; + } + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/matrix/Vector3.java b/MPChartLib/src/main/java/com/github/mikephil/charting/matrix/Vector3.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/matrix/Vector3.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/matrix/Vector3.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java new file mode 100644 index 0000000000..90528a1359 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -0,0 +1,287 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Baseclass of all axis renderers. + * + * @author Philipp Jahoda + */ +public abstract class AxisRenderer extends Renderer { + + /** base axis this axis renderer works with */ + protected AxisBase mAxis; + + /** transformer to transform values to screen pixels and return */ + protected Transformer mTrans; + + /** + * paint object for the grid lines + */ + protected Paint mGridPaint; + + /** + * paint for the x-label values + */ + protected Paint mAxisLabelPaint; + + /** + * paint for the line surrounding the chart + */ + protected Paint mAxisLinePaint; + + /** + * paint used for the limit lines + */ + protected Paint mLimitLinePaint; + + public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase axis) { + super(viewPortHandler); + + this.mTrans = trans; + this.mAxis = axis; + + if(mViewPortHandler != null) { + + mAxisLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + mGridPaint = new Paint(); + mGridPaint.setColor(Color.GRAY); + mGridPaint.setStrokeWidth(1f); + mGridPaint.setStyle(Style.STROKE); + mGridPaint.setAlpha(90); + + mAxisLinePaint = new Paint(); + mAxisLinePaint.setColor(Color.BLACK); + mAxisLinePaint.setStrokeWidth(1f); + mAxisLinePaint.setStyle(Style.STROKE); + + mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mLimitLinePaint.setStyle(Paint.Style.STROKE); + } + } + + /** + * Returns the Paint object used for drawing the axis (labels). + * + * @return + */ + public Paint getPaintAxisLabels() { + return mAxisLabelPaint; + } + + /** + * Returns the Paint object that is used for drawing the grid-lines of the + * axis. + * + * @return + */ + public Paint getPaintGrid() { + return mGridPaint; + } + + /** + * Returns the Paint object that is used for drawing the axis-line that goes + * alongside the axis. + * + * @return + */ + public Paint getPaintAxisLine() { + return mAxisLinePaint; + } + + /** + * Returns the Transformer object used for transforming the axis values. + * + * @return + */ + public Transformer getTransformer() { + return mTrans; + } + + /** + * Computes the axis values. + * + * @param min - the minimum value in the data object for this axis + * @param max - the maximum value in the data object for this axis + */ + public void computeAxis(float min, float max, boolean inverted) { + + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler != null && mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { + + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); + + if (!inverted) { + + min = (float) p2.y; + max = (float) p1.y; + } else { + + min = (float) p1.y; + max = (float) p2.y; + } + + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); + } + + computeAxisValues(min, max); + } + + /** + * Sets up the axis values. Computes the desired number of labels between the two given extremes. + * + * @return + */ + protected void computeAxisValues(float min, float max) { + + float yMin = min; + float yMax = max; + + int labelCount = mAxis.getLabelCount(); + double range = Math.abs(yMax - yMin); + + if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { + mAxis.mEntries = new float[]{}; + mAxis.mCenteredEntries = new float[]{}; + mAxis.mEntryCount = 0; + return; + } + + // Find out how much spacing (in y value space) between axis values + double rawInterval = range / labelCount; + double interval = Utils.roundToNextSignificant(rawInterval); + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if (mAxis.isGranularityEnabled()) + interval = interval < mAxis.getGranularity() ? mAxis.getGranularity() : interval; + + // Normalize interval + double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); + int intervalSigDigit = (int) (interval / intervalMagnitude); + if (intervalSigDigit > 5) { + // Use one order of magnitude higher, to avoid intervals like 0.9 or + // 90 + interval = Math.floor(10 * intervalMagnitude); + } + + int n = mAxis.isCenterAxisLabelsEnabled() ? 1 : 0; + + // force label count + if (mAxis.isForceLabelsEnabled()) { + + interval = (float) range / (float) (labelCount - 1); + mAxis.mEntryCount = labelCount; + + if (mAxis.mEntries.length < labelCount) { + // Ensure stops contains at least numStops elements. + mAxis.mEntries = new float[labelCount]; + } + + float v = min; + + for (int i = 0; i < labelCount; i++) { + mAxis.mEntries[i] = v; + v += interval; + } + + n = labelCount; + + // no forced count + } else { + + double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; + if(mAxis.isCenterAxisLabelsEnabled()) { + first -= interval; + } + + double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); + + double f; + int i; + + if (interval != 0.0) { + for (f = first; f <= last; f += interval) { + ++n; + } + } + + mAxis.mEntryCount = n; + + if (mAxis.mEntries.length < n) { + // Ensure stops contains at least numStops elements. + mAxis.mEntries = new float[n]; + } + + for (f = first, i = 0; i < n; f += interval, ++i) { + + if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) + f = 0.0; + + mAxis.mEntries[i] = (float) f; + } + } + + // set decimals + if (interval < 1) { + mAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); + } else { + mAxis.mDecimals = 0; + } + + if (mAxis.isCenterAxisLabelsEnabled()) { + + if (mAxis.mCenteredEntries.length < n) { + mAxis.mCenteredEntries = new float[n]; + } + + float offset = (float)interval / 2f; + + for (int i = 0; i < n; i++) { + mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; + } + } + } + + /** + * Draws the axis labels to the screen. + * + * @param c + */ + public abstract void renderAxisLabels(Canvas c); + + /** + * Draws the grid lines belonging to the axis. + * + * @param c + */ + public abstract void renderGridLines(Canvas c); + + /** + * Draws the line that goes alongside the axis. + * + * @param c + */ + public abstract void renderAxisLine(Canvas c); + + /** + * Draws the LimitLines associated with this axis to the screen. + * + * @param c + */ + public abstract void renderLimitLines(Canvas c); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java new file mode 100644 index 0000000000..f17761234e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -0,0 +1,483 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.buffer.BarBuffer; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.Range; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class BarChartRenderer extends BarLineScatterCandleBubbleRenderer { + + protected BarDataProvider mChart; + + /** + * the rect object that is used for drawing the bars + */ + protected RectF mBarRect = new RectF(); + + protected BarBuffer[] mBarBuffers; + + protected Paint mShadowPaint; + protected Paint mBarBorderPaint; + + public BarChartRenderer(BarDataProvider chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + this.mChart = chart; + + mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mHighlightPaint.setStyle(Paint.Style.FILL); + mHighlightPaint.setColor(Color.rgb(0, 0, 0)); + // set alpha after color + mHighlightPaint.setAlpha(120); + + mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mShadowPaint.setStyle(Paint.Style.FILL); + + mBarBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mBarBorderPaint.setStyle(Paint.Style.STROKE); + } + + @Override + public void initBuffers() { + + BarData barData = mChart.getBarData(); + mBarBuffers = new BarBuffer[barData.getDataSetCount()]; + + for (int i = 0; i < mBarBuffers.length; i++) { + IBarDataSet set = barData.getDataSetByIndex(i); + mBarBuffers[i] = new BarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1), + barData.getDataSetCount(), set.isStacked()); + } + } + + @Override + public void drawData(Canvas c) { + + BarData barData = mChart.getBarData(); + + for (int i = 0; i < barData.getDataSetCount(); i++) { + + IBarDataSet set = barData.getDataSetByIndex(i); + + if (set.isVisible()) { + drawDataSet(c, set, i); + } + } + } + + private RectF mBarShadowRectBuffer = new RectF(); + + protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mBarBorderPaint.setColor(dataSet.getBarBorderColor()); + mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); + + final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f; + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + // draw the bar shadow before the values + if (mChart.isDrawBarShadowEnabled()) { + mShadowPaint.setColor(dataSet.getBarShadowColor()); + + BarData barData = mChart.getBarData(); + + final float barWidth = barData.getBarWidth(); + final float barWidthHalf = barWidth / 2.0f; + float x; + + for (int i = 0, count = Math.min((int)(Math.ceil((float)(dataSet.getEntryCount()) * phaseX)), dataSet.getEntryCount()); + i < count; + i++) { + + BarEntry e = dataSet.getEntryForIndex(i); + + x = e.getX(); + + mBarShadowRectBuffer.left = x - barWidthHalf; + mBarShadowRectBuffer.right = x + barWidthHalf; + + trans.rectValueToPixel(mBarShadowRectBuffer); + + if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right)) + continue; + + if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left)) + break; + + mBarShadowRectBuffer.top = mViewPortHandler.contentTop(); + mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom(); + + c.drawRect(mBarShadowRectBuffer, mShadowPaint); + } + } + + // initialize the buffer + BarBuffer buffer = mBarBuffers[index]; + buffer.setPhases(phaseX, phaseY); + buffer.setDataSet(index); + buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); + buffer.setBarWidth(mChart.getBarData().getBarWidth()); + + buffer.feed(dataSet); + + trans.pointValuesToPixel(buffer.buffer); + + final boolean isSingleColor = dataSet.getColors().size() == 1; + + if (isSingleColor) { + mRenderPaint.setColor(dataSet.getColor()); + } + + for (int j = 0; j < buffer.size(); j += 4) { + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) + continue; + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) + break; + + if (!isSingleColor) { + // Set the color for the currently drawn value. If the index + // is out of bounds, reuse colors. + mRenderPaint.setColor(dataSet.getColor(j / 4)); + } + + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + + if (drawBorder) { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mBarBorderPaint); + } + } + } + + protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) { + + float left = x - barWidthHalf; + float right = x + barWidthHalf; + float top = y1; + float bottom = y2; + + mBarRect.set(left, top, right, bottom); + + trans.rectToPixelPhase(mBarRect, mAnimator.getPhaseY()); + } + + @Override + public void drawValues(Canvas c) { + + // if values are drawn + if (isDrawingValuesAllowed(mChart)) { + + List dataSets = mChart.getBarData().getDataSets(); + + final float valueOffsetPlus = Utils.convertDpToPixel(4.5f); + float posOffset = 0f; + float negOffset = 0f; + boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); + + for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { + + IBarDataSet dataSet = dataSets.get(i); + + if (!shouldDrawValues(dataSet)) + continue; + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); + + // calculate the correct offset depending on the draw position of + // the value + float valueTextHeight = Utils.calcTextHeight(mValuePaint, "8"); + posOffset = (drawValueAboveBar ? -valueOffsetPlus : valueTextHeight + valueOffsetPlus); + negOffset = (drawValueAboveBar ? valueTextHeight + valueOffsetPlus : -valueOffsetPlus); + + if (isInverted) { + posOffset = -posOffset - valueTextHeight; + negOffset = -negOffset - valueTextHeight; + } + + // get the buffer + BarBuffer buffer = mBarBuffers[i]; + + final float phaseY = mAnimator.getPhaseY(); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + // if only single values are drawn (sum) + if (!dataSet.isStacked()) { + + for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) { + + float x = (buffer.buffer[j] + buffer.buffer[j + 2]) / 2f; + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if (!mViewPortHandler.isInBoundsY(buffer.buffer[j + 1]) + || !mViewPortHandler.isInBoundsLeft(x)) + continue; + + BarEntry entry = dataSet.getEntryForIndex(j / 4); + float val = entry.getY(); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), val, entry, i, x, + val >= 0 ? + (buffer.buffer[j + 1] + posOffset) : + (buffer.buffer[j + 3] + negOffset), + dataSet.getValueTextColor(j / 4)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = x; + float py = val >= 0 ? + (buffer.buffer[j + 1] + posOffset) : + (buffer.buffer[j + 3] + negOffset); + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + // if we have stacks + } else { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + int bufferIndex = 0; + int index = 0; + + while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) { + + BarEntry entry = dataSet.getEntryForIndex(index); + + float[] vals = entry.getYVals(); + float x = (buffer.buffer[bufferIndex] + buffer.buffer[bufferIndex + 2]) / 2f; + + int color = dataSet.getValueTextColor(index); + + // we still draw stacked bars, but there is one + // non-stacked + // in between + if (vals == null) { + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if (!mViewPortHandler.isInBoundsY(buffer.buffer[bufferIndex + 1]) + || !mViewPortHandler.isInBoundsLeft(x)) + continue; + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, + buffer.buffer[bufferIndex + 1] + + (entry.getY() >= 0 ? posOffset : negOffset), + color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = x; + float py = buffer.buffer[bufferIndex + 1] + + (entry.getY() >= 0 ? posOffset : negOffset); + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + + // draw stack values + } else { + + float[] transformed = new float[vals.length * 2]; + + float posY = 0f; + float negY = -entry.getNegativeSum(); + + for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) { + + float value = vals[idx]; + float y; + + if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) { + // Take care of the situation of a 0.0 value, which overlaps a non-zero bar + y = value; + } else if (value >= 0.0f) { + posY += value; + y = posY; + } else { + y = negY; + negY -= value; + } + + transformed[k + 1] = y * phaseY; + } + + trans.pointValuesToPixel(transformed); + + for (int k = 0; k < transformed.length; k += 2) { + + final float val = vals[k / 2]; + final boolean drawBelow = + (val == 0.0f && negY == 0.0f && posY > 0.0f) || + val < 0.0f; + float y = transformed[k + 1] + + (drawBelow ? negOffset : posOffset); + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if (!mViewPortHandler.isInBoundsY(y) + || !mViewPortHandler.isInBoundsLeft(x)) + continue; + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + vals[k / 2], + entry, + i, + x, + y, + color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + } + + bufferIndex = vals == null ? bufferIndex + 4 : bufferIndex + 4 * vals.length; + index++; + } + } + + MPPointF.recycleInstance(iconsOffset); + } + } + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + BarData barData = mChart.getBarData(); + + for (Highlight high : indices) { + + IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + BarEntry e = set.getEntryForXValue(high.getX(), high.getY()); + + if (!isInBoundsX(e, set)) + continue; + + Transformer trans = mChart.getTransformer(set.getAxisDependency()); + + mHighlightPaint.setColor(set.getHighLightColor()); + mHighlightPaint.setAlpha(set.getHighLightAlpha()); + + boolean isStack = (high.getStackIndex() >= 0 && e.isStacked()) ? true : false; + + final float y1; + final float y2; + + if (isStack) { + + if(mChart.isHighlightFullBarEnabled()) { + + y1 = e.getPositiveSum(); + y2 = -e.getNegativeSum(); + + } else { + + Range range = e.getRanges()[high.getStackIndex()]; + + y1 = range.from; + y2 = range.to; + } + + } else { + y1 = e.getY(); + y2 = 0.f; + } + + prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans); + + setHighlightDrawPos(high, mBarRect); + + c.drawRect(mBarRect, mHighlightPaint); + } + } + + /** + * Sets the drawing position of the highlight object based on the riven bar-rect. + * @param high + */ + protected void setHighlightDrawPos(Highlight high, RectF bar) { + high.setDraw(bar.centerX(), bar.top); + } + + @Override + public void drawExtras(Canvas c) { + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java new file mode 100644 index 0000000000..06599187d3 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java @@ -0,0 +1,96 @@ +package com.github.mikephil.charting.renderer; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 09/06/16. + */ +public abstract class BarLineScatterCandleBubbleRenderer extends DataRenderer { + + /** + * buffer for storing the current minimum and maximum visible x + */ + protected XBounds mXBounds = new XBounds(); + + public BarLineScatterCandleBubbleRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + } + + /** + * Returns true if the DataSet values should be drawn, false if not. + * + * @param set + * @return + */ + protected boolean shouldDrawValues(IDataSet set) { + return set.isVisible() && (set.isDrawValuesEnabled() || set.isDrawIconsEnabled()); + } + + /** + * Checks if the provided entry object is in bounds for drawing considering the current animation phase. + * + * @param e + * @param set + * @return + */ + protected boolean isInBoundsX(Entry e, IBarLineScatterCandleBubbleDataSet set) { + + if (e == null) + return false; + + float entryIndex = set.getEntryIndex(e); + + if (e == null || entryIndex >= set.getEntryCount() * mAnimator.getPhaseX()) { + return false; + } else { + return true; + } + } + + /** + * Class representing the bounds of the current viewport in terms of indices in the values array of a DataSet. + */ + protected class XBounds { + + /** + * minimum visible entry index + */ + public int min; + + /** + * maximum visible entry index + */ + public int max; + + /** + * range of visible entry indices + */ + public int range; + + /** + * Calculates the minimum and maximum x values as well as the range between them. + * + * @param chart + * @param dataSet + */ + public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) { + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); + + float low = chart.getLowestVisibleX(); + float high = chart.getHighestVisibleX(); + + Entry entryFrom = dataSet.getEntryForXValue(low, Float.NaN, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXValue(high, Float.NaN, DataSet.Rounding.UP); + + min = entryFrom == null ? 0 : dataSet.getEntryIndex(entryFrom); + max = entryTo == null ? 0 : dataSet.getEntryIndex(entryTo); + range = (int) ((max - min) * phaseX); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java new file mode 100644 index 0000000000..17bba048b9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -0,0 +1,271 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint.Style; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.data.BubbleData; +import com.github.mikephil.charting.data.BubbleEntry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +/** + * Bubble chart implementation: Copyright 2015 Pierre-Marc Airoldi Licensed + * under Apache License 2.0 Ported by Daniel Cohen Gindi + */ +public class BubbleChartRenderer extends BarLineScatterCandleBubbleRenderer { + + protected BubbleDataProvider mChart; + + public BubbleChartRenderer(BubbleDataProvider chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + + mRenderPaint.setStyle(Style.FILL); + + mHighlightPaint.setStyle(Style.STROKE); + mHighlightPaint.setStrokeWidth(Utils.convertDpToPixel(1.5f)); + } + + @Override + public void initBuffers() { + + } + + @Override + public void drawData(Canvas c) { + + BubbleData bubbleData = mChart.getBubbleData(); + + for (IBubbleDataSet set : bubbleData.getDataSets()) { + + if (set.isVisible()) + drawDataSet(c, set); + } + } + + private float[] sizeBuffer = new float[4]; + private float[] pointBuffer = new float[2]; + + protected float getShapeSize(float entrySize, float maxSize, float reference, boolean normalizeSize) { + final float factor = normalizeSize ? ((maxSize == 0f) ? 1f : (float) Math.sqrt(entrySize / maxSize)) : + entrySize; + final float shapeSize = reference * factor; + return shapeSize; + } + + protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + float phaseY = mAnimator.getPhaseY(); + + mXBounds.set(mChart, dataSet); + + sizeBuffer[0] = 0f; + sizeBuffer[2] = 1f; + + trans.pointValuesToPixel(sizeBuffer); + + boolean normalizeSize = dataSet.isNormalizeSizeEnabled(); + + // calcualte the full width of 1 step on the x-axis + final float maxBubbleWidth = Math.abs(sizeBuffer[2] - sizeBuffer[0]); + final float maxBubbleHeight = Math.abs(mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); + final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); + + for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) { + + final BubbleEntry entry = dataSet.getEntryForIndex(j); + + pointBuffer[0] = entry.getX(); + pointBuffer[1] = (entry.getY()) * phaseY; + trans.pointValuesToPixel(pointBuffer); + + float shapeHalf = getShapeSize(entry.getSize(), dataSet.getMaxSize(), referenceSize, normalizeSize) / 2f; + + if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) + || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) + continue; + + if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) + continue; + + if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) + break; + + final int color = dataSet.getColor((int) entry.getX()); + + mRenderPaint.setColor(color); + c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mRenderPaint); + } + } + + @Override + public void drawValues(Canvas c) { + + BubbleData bubbleData = mChart.getBubbleData(); + + if (bubbleData == null) + return; + + // if values are drawn + if (isDrawingValuesAllowed(mChart)) { + + final List dataSets = bubbleData.getDataSets(); + + float lineHeight = Utils.calcTextHeight(mValuePaint, "1"); + + for (int i = 0; i < dataSets.size(); i++) { + + IBubbleDataSet dataSet = dataSets.get(i); + + if (!shouldDrawValues(dataSet)) + continue; + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + final float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); + final float phaseY = mAnimator.getPhaseY(); + + mXBounds.set(mChart, dataSet); + + final float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) + .generateTransformedValuesBubble(dataSet, phaseY, mXBounds.min, mXBounds.max); + + final float alpha = phaseX == 1 ? phaseY : phaseX; + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + for (int j = 0; j < positions.length; j += 2) { + + int valueTextColor = dataSet.getValueTextColor(j / 2 + mXBounds.min); + valueTextColor = Color.argb(Math.round(255.f * alpha), Color.red(valueTextColor), + Color.green(valueTextColor), Color.blue(valueTextColor)); + + float x = positions[j]; + float y = positions[j + 1]; + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if ((!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y))) + continue; + + BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), entry.getSize(), entry, i, x, + y + (0.5f * lineHeight), valueTextColor); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + MPPointF.recycleInstance(iconsOffset); + } + } + } + + @Override + public void drawExtras(Canvas c) { + } + + private float[] _hsvBuffer = new float[3]; + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + BubbleData bubbleData = mChart.getBubbleData(); + + float phaseY = mAnimator.getPhaseY(); + + for (Highlight high : indices) { + + IBubbleDataSet set = bubbleData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + final BubbleEntry entry = set.getEntryForXValue(high.getX(), high.getY()); + + if (entry.getY() != high.getY()) + continue; + + if (!isInBoundsX(entry, set)) + continue; + + Transformer trans = mChart.getTransformer(set.getAxisDependency()); + + sizeBuffer[0] = 0f; + sizeBuffer[2] = 1f; + + trans.pointValuesToPixel(sizeBuffer); + + boolean normalizeSize = set.isNormalizeSizeEnabled(); + + // calcualte the full width of 1 step on the x-axis + final float maxBubbleWidth = Math.abs(sizeBuffer[2] - sizeBuffer[0]); + final float maxBubbleHeight = Math.abs( + mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); + final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); + + pointBuffer[0] = entry.getX(); + pointBuffer[1] = (entry.getY()) * phaseY; + trans.pointValuesToPixel(pointBuffer); + + high.setDraw(pointBuffer[0], pointBuffer[1]); + + float shapeHalf = getShapeSize(entry.getSize(), + set.getMaxSize(), + referenceSize, + normalizeSize) / 2f; + + if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) + || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) + continue; + + if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) + continue; + + if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) + break; + + final int originalColor = set.getColor((int) entry.getX()); + + Color.RGBToHSV(Color.red(originalColor), Color.green(originalColor), + Color.blue(originalColor), _hsvBuffer); + _hsvBuffer[2] *= 0.5f; + final int color = Color.HSVToColor(Color.alpha(originalColor), _hsvBuffer); + + mHighlightPaint.setColor(color); + mHighlightPaint.setStrokeWidth(set.getHighlightCircleWidth()); + c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mHighlightPaint); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java new file mode 100644 index 0000000000..e4c06fe46c --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -0,0 +1,363 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.data.CandleData; +import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class CandleStickChartRenderer extends LineScatterCandleRadarRenderer { + + protected CandleDataProvider mChart; + + private float[] mShadowBuffers = new float[8]; + private float[] mBodyBuffers = new float[4]; + private float[] mRangeBuffers = new float[4]; + private float[] mOpenBuffers = new float[4]; + private float[] mCloseBuffers = new float[4]; + + public CandleStickChartRenderer(CandleDataProvider chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + } + + @Override + public void initBuffers() { + + } + + @Override + public void drawData(Canvas c) { + + CandleData candleData = mChart.getCandleData(); + + for (ICandleDataSet set : candleData.getDataSets()) { + + if (set.isVisible()) + drawDataSet(c, set); + } + } + + @SuppressWarnings("ResourceAsColor") + protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + float phaseY = mAnimator.getPhaseY(); + float barSpace = dataSet.getBarSpace(); + boolean showCandleBar = dataSet.getShowCandleBar(); + + mXBounds.set(mChart, dataSet); + + mRenderPaint.setStrokeWidth(dataSet.getShadowWidth()); + + // draw the body + for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) { + + // get the entry + CandleEntry e = dataSet.getEntryForIndex(j); + + if (e == null) + continue; + + final float xPos = e.getX(); + + final float open = e.getOpen(); + final float close = e.getClose(); + final float high = e.getHigh(); + final float low = e.getLow(); + + if (showCandleBar) { + // calculate the shadow + + mShadowBuffers[0] = xPos; + mShadowBuffers[2] = xPos; + mShadowBuffers[4] = xPos; + mShadowBuffers[6] = xPos; + + if (open > close) { + mShadowBuffers[1] = high * phaseY; + mShadowBuffers[3] = open * phaseY; + mShadowBuffers[5] = low * phaseY; + mShadowBuffers[7] = close * phaseY; + } else if (open < close) { + mShadowBuffers[1] = high * phaseY; + mShadowBuffers[3] = close * phaseY; + mShadowBuffers[5] = low * phaseY; + mShadowBuffers[7] = open * phaseY; + } else { + mShadowBuffers[1] = high * phaseY; + mShadowBuffers[3] = open * phaseY; + mShadowBuffers[5] = low * phaseY; + mShadowBuffers[7] = mShadowBuffers[3]; + } + + trans.pointValuesToPixel(mShadowBuffers); + + // draw the shadows + + if (dataSet.getShadowColorSameAsCandle()) { + + if (open > close) + mRenderPaint.setColor( + dataSet.getDecreasingColor() == ColorTemplate.COLOR_NONE ? + dataSet.getColor(j) : + dataSet.getDecreasingColor() + ); + + else if (open < close) + mRenderPaint.setColor( + dataSet.getIncreasingColor() == ColorTemplate.COLOR_NONE ? + dataSet.getColor(j) : + dataSet.getIncreasingColor() + ); + + else + mRenderPaint.setColor( + dataSet.getNeutralColor() == ColorTemplate.COLOR_NONE ? + dataSet.getColor(j) : + dataSet.getNeutralColor() + ); + + } else { + mRenderPaint.setColor( + dataSet.getShadowColor() == ColorTemplate.COLOR_NONE ? + dataSet.getColor(j) : + dataSet.getShadowColor() + ); + } + + mRenderPaint.setStyle(Paint.Style.STROKE); + + c.drawLines(mShadowBuffers, mRenderPaint); + + // calculate the body + + mBodyBuffers[0] = xPos - 0.5f + barSpace; + mBodyBuffers[1] = close * phaseY; + mBodyBuffers[2] = (xPos + 0.5f - barSpace); + mBodyBuffers[3] = open * phaseY; + + trans.pointValuesToPixel(mBodyBuffers); + + // draw body differently for increasing and decreasing entry + if (open > close) { // decreasing + + if (dataSet.getDecreasingColor() == ColorTemplate.COLOR_NONE) { + mRenderPaint.setColor(dataSet.getColor(j)); + } else { + mRenderPaint.setColor(dataSet.getDecreasingColor()); + } + + mRenderPaint.setStyle(dataSet.getDecreasingPaintStyle()); + + c.drawRect( + mBodyBuffers[0], mBodyBuffers[3], + mBodyBuffers[2], mBodyBuffers[1], + mRenderPaint); + + } else if (open < close) { + + if (dataSet.getIncreasingColor() == ColorTemplate.COLOR_NONE) { + mRenderPaint.setColor(dataSet.getColor(j)); + } else { + mRenderPaint.setColor(dataSet.getIncreasingColor()); + } + + mRenderPaint.setStyle(dataSet.getIncreasingPaintStyle()); + + c.drawRect( + mBodyBuffers[0], mBodyBuffers[1], + mBodyBuffers[2], mBodyBuffers[3], + mRenderPaint); + } else { // equal values + + if (dataSet.getNeutralColor() == ColorTemplate.COLOR_NONE) { + mRenderPaint.setColor(dataSet.getColor(j)); + } else { + mRenderPaint.setColor(dataSet.getNeutralColor()); + } + + c.drawLine( + mBodyBuffers[0], mBodyBuffers[1], + mBodyBuffers[2], mBodyBuffers[3], + mRenderPaint); + } + } else { + + mRangeBuffers[0] = xPos; + mRangeBuffers[1] = high * phaseY; + mRangeBuffers[2] = xPos; + mRangeBuffers[3] = low * phaseY; + + mOpenBuffers[0] = xPos - 0.5f + barSpace; + mOpenBuffers[1] = open * phaseY; + mOpenBuffers[2] = xPos; + mOpenBuffers[3] = open * phaseY; + + mCloseBuffers[0] = xPos + 0.5f - barSpace; + mCloseBuffers[1] = close * phaseY; + mCloseBuffers[2] = xPos; + mCloseBuffers[3] = close * phaseY; + + trans.pointValuesToPixel(mRangeBuffers); + trans.pointValuesToPixel(mOpenBuffers); + trans.pointValuesToPixel(mCloseBuffers); + + // draw the ranges + int barColor; + + if (open > close) + barColor = dataSet.getDecreasingColor() == ColorTemplate.COLOR_NONE + ? dataSet.getColor(j) + : dataSet.getDecreasingColor(); + else if (open < close) + barColor = dataSet.getIncreasingColor() == ColorTemplate.COLOR_NONE + ? dataSet.getColor(j) + : dataSet.getIncreasingColor(); + else + barColor = dataSet.getNeutralColor() == ColorTemplate.COLOR_NONE + ? dataSet.getColor(j) + : dataSet.getNeutralColor(); + + mRenderPaint.setColor(barColor); + c.drawLine( + mRangeBuffers[0], mRangeBuffers[1], + mRangeBuffers[2], mRangeBuffers[3], + mRenderPaint); + c.drawLine( + mOpenBuffers[0], mOpenBuffers[1], + mOpenBuffers[2], mOpenBuffers[3], + mRenderPaint); + c.drawLine( + mCloseBuffers[0], mCloseBuffers[1], + mCloseBuffers[2], mCloseBuffers[3], + mRenderPaint); + } + } + } + + @Override + public void drawValues(Canvas c) { + + // if values are drawn + if (isDrawingValuesAllowed(mChart)) { + + List dataSets = mChart.getCandleData().getDataSets(); + + for (int i = 0; i < dataSets.size(); i++) { + + ICandleDataSet dataSet = dataSets.get(i); + + if (!shouldDrawValues(dataSet)) + continue; + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mXBounds.set(mChart, dataSet); + + float[] positions = trans.generateTransformedValuesCandle( + dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), mXBounds.min, mXBounds.max); + + float yOffset = Utils.convertDpToPixel(5f); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + for (int j = 0; j < positions.length; j += 2) { + + float x = positions[j]; + float y = positions[j + 1]; + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) + continue; + + CandleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + entry.getHigh(), + entry, + i, + x, + y - yOffset, + dataSet + .getValueTextColor(j / 2)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + MPPointF.recycleInstance(iconsOffset); + } + } + } + + @Override + public void drawExtras(Canvas c) { + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + CandleData candleData = mChart.getCandleData(); + + for (Highlight high : indices) { + + ICandleDataSet set = candleData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + CandleEntry e = set.getEntryForXValue(high.getX(), high.getY()); + + if (!isInBoundsX(e, set)) + continue; + + float lowValue = e.getLow() * mAnimator.getPhaseY(); + float highValue = e.getHigh() * mAnimator.getPhaseY(); + float y = (lowValue + highValue) / 2f; + + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), y); + + high.setDraw((float) pix.x, (float) pix.y); + + // draw the lines + drawHighlightLines(c, (float) pix.x, (float) pix.y, set); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java new file mode 100644 index 0000000000..6d0d4d3da0 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java @@ -0,0 +1,167 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.charts.CombinedChart; +import com.github.mikephil.charting.charts.CombinedChart.DrawOrder; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.data.CombinedData; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +/** + * Renderer class that is responsible for rendering multiple different data-types. + */ +public class CombinedChartRenderer extends DataRenderer { + + /** + * all rederers for the different kinds of data this combined-renderer can draw + */ + protected List mRenderers = new ArrayList(5); + + protected WeakReference mChart; + + public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = new WeakReference(chart); + createRenderers(); + } + + /** + * Creates the renderers needed for this combined-renderer in the required order. Also takes the DrawOrder into + * consideration. + */ + public void createRenderers() { + + mRenderers.clear(); + + CombinedChart chart = (CombinedChart)mChart.get(); + if (chart == null) + return; + + DrawOrder[] orders = chart.getDrawOrder(); + + for (DrawOrder order : orders) { + + switch (order) { + case BAR: + if (chart.getBarData() != null) + mRenderers.add(new BarChartRenderer(chart, mAnimator, mViewPortHandler)); + break; + case BUBBLE: + if (chart.getBubbleData() != null) + mRenderers.add(new BubbleChartRenderer(chart, mAnimator, mViewPortHandler)); + break; + case LINE: + if (chart.getLineData() != null) + mRenderers.add(new LineChartRenderer(chart, mAnimator, mViewPortHandler)); + break; + case CANDLE: + if (chart.getCandleData() != null) + mRenderers.add(new CandleStickChartRenderer(chart, mAnimator, mViewPortHandler)); + break; + case SCATTER: + if (chart.getScatterData() != null) + mRenderers.add(new ScatterChartRenderer(chart, mAnimator, mViewPortHandler)); + break; + } + } + } + + @Override + public void initBuffers() { + + for (DataRenderer renderer : mRenderers) + renderer.initBuffers(); + } + + @Override + public void drawData(Canvas c) { + + for (DataRenderer renderer : mRenderers) + renderer.drawData(c); + } + + @Override + public void drawValues(Canvas c) { + + for (DataRenderer renderer : mRenderers) + renderer.drawValues(c); + } + + @Override + public void drawExtras(Canvas c) { + + for (DataRenderer renderer : mRenderers) + renderer.drawExtras(c); + } + + protected List mHighlightBuffer = new ArrayList(); + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + Chart chart = mChart.get(); + if (chart == null) return; + + for (DataRenderer renderer : mRenderers) { + ChartData data = null; + + if (renderer instanceof BarChartRenderer) + data = ((BarChartRenderer)renderer).mChart.getBarData(); + else if (renderer instanceof LineChartRenderer) + data = ((LineChartRenderer)renderer).mChart.getLineData(); + else if (renderer instanceof CandleStickChartRenderer) + data = ((CandleStickChartRenderer)renderer).mChart.getCandleData(); + else if (renderer instanceof ScatterChartRenderer) + data = ((ScatterChartRenderer)renderer).mChart.getScatterData(); + else if (renderer instanceof BubbleChartRenderer) + data = ((BubbleChartRenderer)renderer).mChart.getBubbleData(); + + int dataIndex = data == null ? -1 + : ((CombinedData)chart.getData()).getAllData().indexOf(data); + + mHighlightBuffer.clear(); + + for (Highlight h : indices) { + if (h.getDataIndex() == dataIndex || h.getDataIndex() == -1) + mHighlightBuffer.add(h); + } + + renderer.drawHighlighted(c, mHighlightBuffer.toArray(new Highlight[mHighlightBuffer.size()])); + } + } + + /** + * Returns the sub-renderer object at the specified index. + * + * @param index + * @return + */ + public DataRenderer getSubRenderer(int index) { + if (index >= mRenderers.size() || index < 0) + return null; + else + return mRenderers.get(index); + } + + /** + * Returns all sub-renderers. + * + * @return + */ + public List getSubRenderers() { + return mRenderers; + } + + public void setSubRenderers(List renderers) { + this.mRenderers = renderers; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java new file mode 100644 index 0000000000..e8e5446f4d --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java @@ -0,0 +1,169 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Superclass of all render classes for the different data types (line, bar, ...). + * + * @author Philipp Jahoda + */ +public abstract class DataRenderer extends Renderer { + + /** + * the animator object used to perform animations on the chart data + */ + protected ChartAnimator mAnimator; + + /** + * main paint object used for rendering + */ + protected Paint mRenderPaint; + + /** + * paint used for highlighting values + */ + protected Paint mHighlightPaint; + + protected Paint mDrawPaint; + + /** + * paint object for drawing values (text representing values of chart + * entries) + */ + protected Paint mValuePaint; + + public DataRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(viewPortHandler); + this.mAnimator = animator; + + mRenderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mRenderPaint.setStyle(Style.FILL); + + mDrawPaint = new Paint(Paint.DITHER_FLAG); + + mValuePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mValuePaint.setColor(Color.rgb(63, 63, 63)); + mValuePaint.setTextAlign(Align.CENTER); + mValuePaint.setTextSize(Utils.convertDpToPixel(9f)); + + mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mHighlightPaint.setStyle(Paint.Style.STROKE); + mHighlightPaint.setStrokeWidth(2f); + mHighlightPaint.setColor(Color.rgb(255, 187, 115)); + } + + protected boolean isDrawingValuesAllowed(ChartInterface chart) { + return chart.getData().getEntryCount() < chart.getMaxVisibleCount() + * mViewPortHandler.getScaleX(); + } + + /** + * Returns the Paint object this renderer uses for drawing the values + * (value-text). + * + * @return + */ + public Paint getPaintValues() { + return mValuePaint; + } + + /** + * Returns the Paint object this renderer uses for drawing highlight + * indicators. + * + * @return + */ + public Paint getPaintHighlight() { + return mHighlightPaint; + } + + /** + * Returns the Paint object used for rendering. + * + * @return + */ + public Paint getPaintRender() { + return mRenderPaint; + } + + /** + * Applies the required styling (provided by the DataSet) to the value-paint + * object. + * + * @param set + */ + protected void applyValueTextStyle(IDataSet set) { + + mValuePaint.setTypeface(set.getValueTypeface()); + mValuePaint.setTextSize(set.getValueTextSize()); + } + + /** + * Initializes the buffers used for rendering with a new size. Since this + * method performs memory allocations, it should only be called if + * necessary. + */ + public abstract void initBuffers(); + + /** + * Draws the actual data in form of lines, bars, ... depending on Renderer subclass. + * + * @param c + */ + public abstract void drawData(Canvas c); + + /** + * Loops over all Entrys and draws their values. + * + * @param c + */ + public abstract void drawValues(Canvas c); + + /** + * Draws the value of the given entry by using the provided IValueFormatter. + * + * @param c canvas + * @param formatter formatter for custom value-formatting + * @param value the value to be drawn + * @param entry the entry the value belongs to + * @param dataSetIndex the index of the DataSet the drawn Entry belongs to + * @param x position + * @param y position + * @param color + */ + public void drawValue(Canvas c, IValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler), x, y, mValuePaint); + } + + /** + * Draws any kind of additional information (e.g. line-circles). + * + * @param c + */ + public abstract void drawExtras(Canvas c); + + /** + * Draws all highlight indicators for the values that are currently highlighted. + * + * @param c + * @param indices the highlighted values + */ + public abstract void drawHighlighted(Canvas c, Highlight[] indices); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java new file mode 100644 index 0000000000..a1e1650865 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -0,0 +1,427 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Paint.Align; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.buffer.BarBuffer; +import com.github.mikephil.charting.buffer.HorizontalBarBuffer; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +/** + * Renderer for the HorizontalBarChart. + * + * @author Philipp Jahoda + */ +public class HorizontalBarChartRenderer extends BarChartRenderer { + + public HorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(chart, animator, viewPortHandler); + + mValuePaint.setTextAlign(Align.LEFT); + } + + @Override + public void initBuffers() { + + BarData barData = mChart.getBarData(); + mBarBuffers = new HorizontalBarBuffer[barData.getDataSetCount()]; + + for (int i = 0; i < mBarBuffers.length; i++) { + IBarDataSet set = barData.getDataSetByIndex(i); + mBarBuffers[i] = new HorizontalBarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1), + barData.getDataSetCount(), set.isStacked()); + } + } + + private RectF mBarShadowRectBuffer = new RectF(); + + @Override + protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mBarBorderPaint.setColor(dataSet.getBarBorderColor()); + mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); + + final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f; + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + // draw the bar shadow before the values + if (mChart.isDrawBarShadowEnabled()) { + mShadowPaint.setColor(dataSet.getBarShadowColor()); + + BarData barData = mChart.getBarData(); + + final float barWidth = barData.getBarWidth(); + final float barWidthHalf = barWidth / 2.0f; + float x; + + for (int i = 0, count = Math.min((int)(Math.ceil((float)(dataSet.getEntryCount()) * phaseX)), dataSet.getEntryCount()); + i < count; + i++) { + + BarEntry e = dataSet.getEntryForIndex(i); + + x = e.getX(); + + mBarShadowRectBuffer.top = x - barWidthHalf; + mBarShadowRectBuffer.bottom = x + barWidthHalf; + + trans.rectValueToPixel(mBarShadowRectBuffer); + + if (!mViewPortHandler.isInBoundsTop(mBarShadowRectBuffer.bottom)) + continue; + + if (!mViewPortHandler.isInBoundsBottom(mBarShadowRectBuffer.top)) + break; + + mBarShadowRectBuffer.left = mViewPortHandler.contentLeft(); + mBarShadowRectBuffer.right = mViewPortHandler.contentRight(); + + c.drawRect(mBarShadowRectBuffer, mShadowPaint); + } + } + + // initialize the buffer + BarBuffer buffer = mBarBuffers[index]; + buffer.setPhases(phaseX, phaseY); + buffer.setDataSet(index); + buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); + buffer.setBarWidth(mChart.getBarData().getBarWidth()); + + buffer.feed(dataSet); + + trans.pointValuesToPixel(buffer.buffer); + + final boolean isSingleColor = dataSet.getColors().size() == 1; + + if (isSingleColor) { + mRenderPaint.setColor(dataSet.getColor()); + } + + for (int j = 0; j < buffer.size(); j += 4) { + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) + break; + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) + continue; + + if (!isSingleColor) { + // Set the color for the currently drawn value. If the index + // is out of bounds, reuse colors. + mRenderPaint.setColor(dataSet.getColor(j / 4)); + } + + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + + if (drawBorder) { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mBarBorderPaint); + } + } + } + + @Override + public void drawValues(Canvas c) { + // if values are drawn + if (isDrawingValuesAllowed(mChart)) { + + List dataSets = mChart.getBarData().getDataSets(); + + final float valueOffsetPlus = Utils.convertDpToPixel(5f); + float posOffset = 0f; + float negOffset = 0f; + final boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); + + for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { + + IBarDataSet dataSet = dataSets.get(i); + + if (!shouldDrawValues(dataSet)) + continue; + + boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + final float halfTextHeight = Utils.calcTextHeight(mValuePaint, "10") / 2f; + + IValueFormatter formatter = dataSet.getValueFormatter(); + + // get the buffer + BarBuffer buffer = mBarBuffers[i]; + + final float phaseY = mAnimator.getPhaseY(); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + // if only single values are drawn (sum) + if (!dataSet.isStacked()) { + + for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) { + + float y = (buffer.buffer[j + 1] + buffer.buffer[j + 3]) / 2f; + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1])) + break; + + if (!mViewPortHandler.isInBoundsX(buffer.buffer[j])) + continue; + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) + continue; + + BarEntry entry = dataSet.getEntryForIndex(j / 4); + float val = entry.getY(); + String formattedValue = formatter.getFormattedValue(val, entry, i, mViewPortHandler); + + // calculate the correct offset depending on the draw position of the value + float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); + posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); + negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); + + if (isInverted) { + posOffset = -posOffset - valueTextWidth; + negOffset = -negOffset - valueTextWidth; + } + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + formattedValue, + buffer.buffer[j + 2] + (val >= 0 ? posOffset : negOffset), + y + halfTextHeight, + dataSet.getValueTextColor(j / 2)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = buffer.buffer[j + 2] + (val >= 0 ? posOffset : negOffset); + float py = y; + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + // if each value of a potential stack should be drawn + } else { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + int bufferIndex = 0; + int index = 0; + + while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) { + + BarEntry entry = dataSet.getEntryForIndex(index); + + int color = dataSet.getValueTextColor(index); + float[] vals = entry.getYVals(); + + // we still draw stacked bars, but there is one + // non-stacked + // in between + if (vals == null) { + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[bufferIndex + 1])) + break; + + if (!mViewPortHandler.isInBoundsX(buffer.buffer[bufferIndex])) + continue; + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[bufferIndex + 1])) + continue; + + float val = entry.getY(); + String formattedValue = formatter.getFormattedValue(val, + entry, i, mViewPortHandler); + + // calculate the correct offset depending on the draw position of the value + float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); + posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); + negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); + + if (isInverted) { + posOffset = -posOffset - valueTextWidth; + negOffset = -negOffset - valueTextWidth; + } + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, formattedValue, + buffer.buffer[bufferIndex + 2] + + (entry.getY() >= 0 ? posOffset : negOffset), + buffer.buffer[bufferIndex + 1] + halfTextHeight, color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = buffer.buffer[bufferIndex + 2] + + (entry.getY() >= 0 ? posOffset : negOffset); + float py = buffer.buffer[bufferIndex + 1]; + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + + } else { + + float[] transformed = new float[vals.length * 2]; + + float posY = 0f; + float negY = -entry.getNegativeSum(); + + for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) { + + float value = vals[idx]; + float y; + + if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) { + // Take care of the situation of a 0.0 value, which overlaps a non-zero bar + y = value; + } else if (value >= 0.0f) { + posY += value; + y = posY; + } else { + y = negY; + negY -= value; + } + + transformed[k] = y * phaseY; + } + + trans.pointValuesToPixel(transformed); + + for (int k = 0; k < transformed.length; k += 2) { + + final float val = vals[k / 2]; + String formattedValue = formatter.getFormattedValue(val, + entry, i, mViewPortHandler); + + // calculate the correct offset depending on the draw position of the value + float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); + posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); + negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); + + if (isInverted) { + posOffset = -posOffset - valueTextWidth; + negOffset = -negOffset - valueTextWidth; + } + + final boolean drawBelow = + (val == 0.0f && negY == 0.0f && posY > 0.0f) || + val < 0.0f; + + float x = transformed[k] + + (drawBelow ? negOffset : posOffset); + float y = (buffer.buffer[bufferIndex + 1] + buffer.buffer[bufferIndex + 3]) / 2f; + + if (!mViewPortHandler.isInBoundsTop(y)) + break; + + if (!mViewPortHandler.isInBoundsX(x)) + continue; + + if (!mViewPortHandler.isInBoundsBottom(y)) + continue; + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, formattedValue, x, y + halfTextHeight, color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + } + + bufferIndex = vals == null ? bufferIndex + 4 : bufferIndex + 4 * vals.length; + index++; + } + } + + MPPointF.recycleInstance(iconsOffset); + } + } + } + + protected void drawValue(Canvas c, String valueText, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(valueText, x, y, mValuePaint); + } + + @Override + protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) { + + float top = x - barWidthHalf; + float bottom = x + barWidthHalf; + float left = y1; + float right = y2; + + mBarRect.set(left, top, right, bottom); + + trans.rectToPixelPhaseHorizontal(mBarRect, mAnimator.getPhaseY()); + } + + @Override + protected void setHighlightDrawPos(Highlight high, RectF bar) { + high.setDraw(bar.centerY(), bar.right); + } + + @Override + protected boolean isDrawingValuesAllowed(ChartInterface chart) { + return chart.getData().getEntryCount() < chart.getMaxVisibleCount() + * mViewPortHandler.getScaleY(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java new file mode 100644 index 0000000000..85597db6a1 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -0,0 +1,560 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Path; +import android.graphics.Typeface; + +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.LegendEntry; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class LegendRenderer extends Renderer { + + /** + * paint for the legend labels + */ + protected Paint mLegendLabelPaint; + + /** + * paint used for the legend forms + */ + protected Paint mLegendFormPaint; + + /** + * the legend object this renderer renders + */ + protected Legend mLegend; + + public LegendRenderer(ViewPortHandler viewPortHandler, Legend legend) { + super(viewPortHandler); + + this.mLegend = legend; + + mLegendLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mLegendLabelPaint.setTextSize(Utils.convertDpToPixel(9f)); + mLegendLabelPaint.setTextAlign(Align.LEFT); + + mLegendFormPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mLegendFormPaint.setStyle(Paint.Style.FILL); + } + + /** + * Returns the Paint object used for drawing the Legend labels. + * + * @return + */ + public Paint getLabelPaint() { + return mLegendLabelPaint; + } + + /** + * Returns the Paint object used for drawing the Legend forms. + * + * @return + */ + public Paint getFormPaint() { + return mLegendFormPaint; + } + + + protected List computedEntries = new ArrayList<>(16); + + /** + * Prepares the legend and calculates all needed forms, labels and colors. + * + * @param data + */ + public void computeLegend(ChartData data) { + + if (!mLegend.isLegendCustom()) { + + computedEntries.clear(); + + // loop for building up the colors and labels used in the legend + for (int i = 0; i < data.getDataSetCount(); i++) { + + IDataSet dataSet = data.getDataSetByIndex(i); + + List clrs = dataSet.getColors(); + int entryCount = dataSet.getEntryCount(); + + // if we have a barchart with stacked bars + if (dataSet instanceof IBarDataSet && ((IBarDataSet) dataSet).isStacked()) { + + IBarDataSet bds = (IBarDataSet) dataSet; + String[] sLabels = bds.getStackLabels(); + + for (int j = 0; j < clrs.size() && j < bds.getStackSize(); j++) { + + computedEntries.add(new LegendEntry( + sLabels[j % sLabels.length], + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), + clrs.get(j) + )); + } + + if (bds.getLabel() != null) { + // add the legend description label + computedEntries.add(new LegendEntry( + dataSet.getLabel(), + Legend.LegendForm.NONE, + Float.NaN, + Float.NaN, + null, + ColorTemplate.COLOR_NONE + )); + } + + } else if (dataSet instanceof IPieDataSet) { + + IPieDataSet pds = (IPieDataSet) dataSet; + + for (int j = 0; j < clrs.size() && j < entryCount; j++) { + + computedEntries.add(new LegendEntry( + pds.getEntryForIndex(j).getLabel(), + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), + clrs.get(j) + )); + } + + if (pds.getLabel() != null) { + // add the legend description label + computedEntries.add(new LegendEntry( + dataSet.getLabel(), + Legend.LegendForm.NONE, + Float.NaN, + Float.NaN, + null, + ColorTemplate.COLOR_NONE + )); + } + + } else if (dataSet instanceof ICandleDataSet && ((ICandleDataSet) dataSet).getDecreasingColor() != + ColorTemplate.COLOR_NONE) { + + int decreasingColor = ((ICandleDataSet) dataSet).getDecreasingColor(); + int increasingColor = ((ICandleDataSet) dataSet).getIncreasingColor(); + + computedEntries.add(new LegendEntry( + null, + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), + decreasingColor + )); + + computedEntries.add(new LegendEntry( + dataSet.getLabel(), + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), + increasingColor + )); + + } else { // all others + + for (int j = 0; j < clrs.size() && j < entryCount; j++) { + + String label; + + // if multiple colors are set for a DataSet, group them + if (j < clrs.size() - 1 && j < entryCount - 1) { + label = null; + } else { // add label to the last entry + label = data.getDataSetByIndex(i).getLabel(); + } + + computedEntries.add(new LegendEntry( + label, + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), + clrs.get(j) + )); + } + } + } + + if (mLegend.getExtraEntries() != null) { + Collections.addAll(computedEntries, mLegend.getExtraEntries()); + } + + mLegend.setEntries(computedEntries); + } + + Typeface tf = mLegend.getTypeface(); + + if (tf != null) + mLegendLabelPaint.setTypeface(tf); + + mLegendLabelPaint.setTextSize(mLegend.getTextSize()); + mLegendLabelPaint.setColor(mLegend.getTextColor()); + + // calculate all dimensions of the mLegend + mLegend.calculateDimensions(mLegendLabelPaint, mViewPortHandler); + } + + protected Paint.FontMetrics legendFontMetrics = new Paint.FontMetrics(); + + public void renderLegend(Canvas c) { + + if (!mLegend.isEnabled()) + return; + + Typeface tf = mLegend.getTypeface(); + + if (tf != null) + mLegendLabelPaint.setTypeface(tf); + + mLegendLabelPaint.setTextSize(mLegend.getTextSize()); + mLegendLabelPaint.setColor(mLegend.getTextColor()); + + float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint, legendFontMetrics); + float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint, legendFontMetrics) + + Utils.convertDpToPixel(mLegend.getYEntrySpace()); + float formYOffset = labelLineHeight - Utils.calcTextHeight(mLegendLabelPaint, "ABC") / 2.f; + + LegendEntry[] entries = mLegend.getEntries(); + + float formToTextSpace = Utils.convertDpToPixel(mLegend.getFormToTextSpace()); + float xEntrySpace = Utils.convertDpToPixel(mLegend.getXEntrySpace()); + Legend.LegendOrientation orientation = mLegend.getOrientation(); + Legend.LegendHorizontalAlignment horizontalAlignment = mLegend.getHorizontalAlignment(); + Legend.LegendVerticalAlignment verticalAlignment = mLegend.getVerticalAlignment(); + Legend.LegendDirection direction = mLegend.getDirection(); + float defaultFormSize = Utils.convertDpToPixel(mLegend.getFormSize()); + + // space between the entries + float stackSpace = Utils.convertDpToPixel(mLegend.getStackSpace()); + + float yoffset = mLegend.getYOffset(); + float xoffset = mLegend.getXOffset(); + float originPosX = 0.f; + + switch (horizontalAlignment) { + case LEFT: + + if (orientation == Legend.LegendOrientation.VERTICAL) + originPosX = xoffset; + else + originPosX = mViewPortHandler.contentLeft() + xoffset; + + if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) + originPosX += mLegend.mNeededWidth; + + break; + + case RIGHT: + + if (orientation == Legend.LegendOrientation.VERTICAL) + originPosX = mViewPortHandler.getChartWidth() - xoffset; + else + originPosX = mViewPortHandler.contentRight() - xoffset; + + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + originPosX -= mLegend.mNeededWidth; + + break; + + case CENTER: + + if (orientation == Legend.LegendOrientation.VERTICAL) + originPosX = mViewPortHandler.getChartWidth() / 2.f; + else + originPosX = mViewPortHandler.contentLeft() + + mViewPortHandler.contentWidth() / 2.f; + + originPosX += (direction == Legend.LegendDirection.LEFT_TO_RIGHT + ? +xoffset + : -xoffset); + + // Horizontally layed out legends do the center offset on a line basis, + // So here we offset the vertical ones only. + if (orientation == Legend.LegendOrientation.VERTICAL) { + originPosX += (direction == Legend.LegendDirection.LEFT_TO_RIGHT + ? -mLegend.mNeededWidth / 2.0 + xoffset + : mLegend.mNeededWidth / 2.0 - xoffset); + } + + break; + } + + switch (orientation) { + case HORIZONTAL: { + + List calculatedLineSizes = mLegend.getCalculatedLineSizes(); + List calculatedLabelSizes = mLegend.getCalculatedLabelSizes(); + List calculatedLabelBreakPoints = mLegend.getCalculatedLabelBreakPoints(); + + float posX = originPosX; + float posY = 0.f; + + switch (verticalAlignment) { + case TOP: + posY = yoffset; + break; + + case BOTTOM: + posY = mViewPortHandler.getChartHeight() - yoffset - mLegend.mNeededHeight; + break; + + case CENTER: + posY = (mViewPortHandler.getChartHeight() - mLegend.mNeededHeight) / 2.f + yoffset; + break; + } + + int lineIndex = 0; + + for (int i = 0, count = entries.length; i < count; i++) { + + LegendEntry e = entries[i]; + boolean drawingForm = e.form != Legend.LegendForm.NONE; + float formSize = Float.isNaN(e.formSize) ? defaultFormSize : Utils.convertDpToPixel(e.formSize); + + if (i < calculatedLabelBreakPoints.size() && calculatedLabelBreakPoints.get(i)) { + posX = originPosX; + posY += labelLineHeight + labelLineSpacing; + } + + if (posX == originPosX && + horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER && + lineIndex < calculatedLineSizes.size()) { + posX += (direction == Legend.LegendDirection.RIGHT_TO_LEFT + ? calculatedLineSizes.get(lineIndex).width + : -calculatedLineSizes.get(lineIndex).width) / 2.f; + lineIndex++; + } + + boolean isStacked = e.label == null; // grouped forms have null labels + + if (drawingForm) { + if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) + posX -= formSize; + + drawForm(c, posX, posY + formYOffset, e, mLegend); + + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + posX += formSize; + } + + if (!isStacked) { + if (drawingForm) + posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -formToTextSpace : + formToTextSpace; + + if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) + posX -= calculatedLabelSizes.get(i).width; + + drawLabel(c, posX, posY + labelLineHeight, e.label); + + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + posX += calculatedLabelSizes.get(i).width; + + posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -xEntrySpace : xEntrySpace; + } else + posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -stackSpace : stackSpace; + } + + break; + } + + case VERTICAL: { + // contains the stacked legend size in pixels + float stack = 0f; + boolean wasStacked = false; + float posY = 0.f; + + switch (verticalAlignment) { + case TOP: + posY = (horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER + ? 0.f + : mViewPortHandler.contentTop()); + posY += yoffset; + break; + + case BOTTOM: + posY = (horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER + ? mViewPortHandler.getChartHeight() + : mViewPortHandler.contentBottom()); + posY -= mLegend.mNeededHeight + yoffset; + break; + + case CENTER: + posY = mViewPortHandler.getChartHeight() / 2.f + - mLegend.mNeededHeight / 2.f + + mLegend.getYOffset(); + break; + } + + for (int i = 0; i < entries.length; i++) { + + LegendEntry e = entries[i]; + boolean drawingForm = e.form != Legend.LegendForm.NONE; + float formSize = Float.isNaN(e.formSize) ? defaultFormSize : Utils.convertDpToPixel(e.formSize); + + float posX = originPosX; + + if (drawingForm) { + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + posX += stack; + else + posX -= formSize - stack; + + drawForm(c, posX, posY + formYOffset, e, mLegend); + + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + posX += formSize; + } + + if (e.label != null) { + + if (drawingForm && !wasStacked) + posX += direction == Legend.LegendDirection.LEFT_TO_RIGHT ? formToTextSpace + : -formToTextSpace; + else if (wasStacked) + posX = originPosX; + + if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) + posX -= Utils.calcTextWidth(mLegendLabelPaint, e.label); + + if (!wasStacked) { + drawLabel(c, posX, posY + labelLineHeight, e.label); + } else { + posY += labelLineHeight + labelLineSpacing; + drawLabel(c, posX, posY + labelLineHeight, e.label); + } + + // make a step down + posY += labelLineHeight + labelLineSpacing; + stack = 0f; + } else { + stack += formSize + stackSpace; + wasStacked = true; + } + } + + break; + + } + } + } + + private Path mLineFormPath = new Path(); + + /** + * Draws the Legend-form at the given position with the color at the given + * index. + * + * @param c canvas to draw with + * @param x position + * @param y position + * @param entry the entry to render + * @param legend the legend context + */ + protected void drawForm( + Canvas c, + float x, float y, + LegendEntry entry, + Legend legend) { + + if (entry.formColor == ColorTemplate.COLOR_SKIP || + entry.formColor == ColorTemplate.COLOR_NONE || + entry.formColor == 0) + return; + + int restoreCount = c.save(); + + Legend.LegendForm form = entry.form; + if (form == Legend.LegendForm.DEFAULT) + form = legend.getForm(); + + mLegendFormPaint.setColor(entry.formColor); + + final float formSize = Utils.convertDpToPixel( + Float.isNaN(entry.formSize) + ? legend.getFormSize() + : entry.formSize); + final float half = formSize / 2f; + + switch (form) { + case NONE: + // Do nothing + break; + + case EMPTY: + // Do not draw, but keep space for the form + break; + + case DEFAULT: + case CIRCLE: + mLegendFormPaint.setStyle(Paint.Style.FILL); + c.drawCircle(x + half, y, half, mLegendFormPaint); + break; + + case SQUARE: + mLegendFormPaint.setStyle(Paint.Style.FILL); + c.drawRect(x, y - half, x + formSize, y + half, mLegendFormPaint); + break; + + case LINE: + { + final float formLineWidth = Utils.convertDpToPixel( + Float.isNaN(entry.formLineWidth) + ? legend.getFormLineWidth() + : entry.formLineWidth); + final DashPathEffect formLineDashEffect = entry.formLineDashEffect == null + ? legend.getFormLineDashEffect() + : entry.formLineDashEffect; + mLegendFormPaint.setStyle(Paint.Style.STROKE); + mLegendFormPaint.setStrokeWidth(formLineWidth); + mLegendFormPaint.setPathEffect(formLineDashEffect); + + mLineFormPath.reset(); + mLineFormPath.moveTo(x, y); + mLineFormPath.lineTo(x + formSize, y); + c.drawPath(mLineFormPath, mLegendFormPaint); + } + break; + } + + c.restoreToCount(restoreCount); + } + + /** + * Draws the provided label at the given position. + * + * @param c canvas to draw with + * @param x + * @param y + * @param label the label to draw + */ + protected void drawLabel(Canvas c, float x, float y, String label) { + c.drawText(label, x, y, mLegendLabelPaint); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java new file mode 100644 index 0000000000..9921cb1ba6 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -0,0 +1,845 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.List; + +public class LineChartRenderer extends LineRadarRenderer { + + protected LineDataProvider mChart; + + /** + * paint for the inner circle of the value indicators + */ + protected Paint mCirclePaintInner; + + /** + * Bitmap object used for drawing the paths (otherwise they are too long if + * rendered directly on the canvas) + */ + protected WeakReference mDrawBitmap; + + /** + * on this canvas, the paths are rendered, it is initialized with the + * pathBitmap + */ + protected Canvas mBitmapCanvas; + + /** + * the bitmap configuration to be used + */ + protected Bitmap.Config mBitmapConfig = Bitmap.Config.ARGB_8888; + + protected Path cubicPath = new Path(); + protected Path cubicFillPath = new Path(); + + public LineChartRenderer(LineDataProvider chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + + mCirclePaintInner = new Paint(Paint.ANTI_ALIAS_FLAG); + mCirclePaintInner.setStyle(Paint.Style.FILL); + mCirclePaintInner.setColor(Color.WHITE); + } + + @Override + public void initBuffers() { + } + + @Override + public void drawData(Canvas c) { + + int width = (int) mViewPortHandler.getChartWidth(); + int height = (int) mViewPortHandler.getChartHeight(); + + if (mDrawBitmap == null + || (mDrawBitmap.get().getWidth() != width) + || (mDrawBitmap.get().getHeight() != height)) { + + if (width > 0 && height > 0) { + + mDrawBitmap = new WeakReference(Bitmap.createBitmap(width, height, mBitmapConfig)); + mBitmapCanvas = new Canvas(mDrawBitmap.get()); + } else + return; + } + + mDrawBitmap.get().eraseColor(Color.TRANSPARENT); + + LineData lineData = mChart.getLineData(); + + for (ILineDataSet set : lineData.getDataSets()) { + + if (set.isVisible()) + drawDataSet(c, set); + } + + c.drawBitmap(mDrawBitmap.get(), 0, 0, mRenderPaint); + } + + protected void drawDataSet(Canvas c, ILineDataSet dataSet) { + + if (dataSet.getEntryCount() < 1) + return; + + mRenderPaint.setStrokeWidth(dataSet.getLineWidth()); + mRenderPaint.setPathEffect(dataSet.getDashPathEffect()); + + switch (dataSet.getMode()) { + default: + case LINEAR: + case STEPPED: + drawLinear(c, dataSet); + break; + + case CUBIC_BEZIER: + drawCubicBezier(dataSet); + break; + + case HORIZONTAL_BEZIER: + drawHorizontalBezier(dataSet); + break; + } + + mRenderPaint.setPathEffect(null); + } + + protected void drawHorizontalBezier(ILineDataSet dataSet) { + + float phaseY = mAnimator.getPhaseY(); + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mXBounds.set(mChart, dataSet); + + cubicPath.reset(); + + if (mXBounds.range >= 1) { + + Entry prev = dataSet.getEntryForIndex(mXBounds.min); + Entry cur = prev; + + // let the spline start + cubicPath.moveTo(cur.getX(), cur.getY() * phaseY); + + for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) { + + prev = cur; + cur = dataSet.getEntryForIndex(j); + + final float cpx = (prev.getX()) + + (cur.getX() - prev.getX()) / 2.0f; + + cubicPath.cubicTo( + cpx, prev.getY() * phaseY, + cpx, cur.getY() * phaseY, + cur.getX(), cur.getY() * phaseY); + } + } + + // if filled is enabled, close the path + if (dataSet.isDrawFilledEnabled()) { + + cubicFillPath.reset(); + cubicFillPath.addPath(cubicPath); + // create a new path, this is bad for performance + drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds); + } + + mRenderPaint.setColor(dataSet.getColor()); + + mRenderPaint.setStyle(Paint.Style.STROKE); + + trans.pathValueToPixel(cubicPath); + + mBitmapCanvas.drawPath(cubicPath, mRenderPaint); + + mRenderPaint.setPathEffect(null); + } + + protected void drawCubicBezier(ILineDataSet dataSet) { + + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); + float phaseY = mAnimator.getPhaseY(); + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mXBounds.set(mChart, dataSet); + + float intensity = dataSet.getCubicIntensity(); + + cubicPath.reset(); + + if (mXBounds.range >= 1) { + + float prevDx = 0f; + float prevDy = 0f; + float curDx = 0f; + float curDy = 0f; + + // Take an extra point from the left, and an extra from the right. + // That's because we need 4 points for a cubic bezier (cubic=4), otherwise we get lines moving and doing weird stuff on the edges of the chart. + // So in the starting `prev` and `cur`, go -2, -1 + // And in the `lastIndex`, add +1 + + final int firstIndex = mXBounds.min + 1; + final int lastIndex = mXBounds.min + mXBounds.range; + + Entry prevPrev; + Entry prev = dataSet.getEntryForIndex(Math.max(firstIndex - 2, 0)); + Entry cur = dataSet.getEntryForIndex(Math.max(firstIndex - 1, 0)); + Entry next = cur; + int nextIndex = -1; + + if (cur == null) return; + + // let the spline start + cubicPath.moveTo(cur.getX(), cur.getY() * phaseY); + + for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) { + + prevPrev = prev; + prev = cur; + cur = nextIndex == j ? next : dataSet.getEntryForIndex(j); + + nextIndex = j + 1 < dataSet.getEntryCount() ? j + 1 : j; + next = dataSet.getEntryForIndex(nextIndex); + + prevDx = (cur.getX() - prevPrev.getX()) * intensity; + prevDy = (cur.getY() - prevPrev.getY()) * intensity; + curDx = (next.getX() - prev.getX()) * intensity; + curDy = (next.getY() - prev.getY()) * intensity; + + cubicPath.cubicTo(prev.getX() + prevDx, (prev.getY() + prevDy) * phaseY, + cur.getX() - curDx, + (cur.getY() - curDy) * phaseY, cur.getX(), cur.getY() * phaseY); + } + } + + // if filled is enabled, close the path + if (dataSet.isDrawFilledEnabled()) { + + cubicFillPath.reset(); + cubicFillPath.addPath(cubicPath); + + drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds); + } + + mRenderPaint.setColor(dataSet.getColor()); + + mRenderPaint.setStyle(Paint.Style.STROKE); + + trans.pathValueToPixel(cubicPath); + + mBitmapCanvas.drawPath(cubicPath, mRenderPaint); + + mRenderPaint.setPathEffect(null); + } + + protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transformer trans, XBounds bounds) { + + float fillMin = dataSet.getFillFormatter() + .getFillLinePosition(dataSet, mChart); + + spline.lineTo(dataSet.getEntryForIndex(bounds.min + bounds.range).getX(), fillMin); + spline.lineTo(dataSet.getEntryForIndex(bounds.min).getX(), fillMin); + spline.close(); + + trans.pathValueToPixel(spline); + + final Drawable drawable = dataSet.getFillDrawable(); + if (drawable != null) { + + drawFilledPath(c, spline, drawable); + } else { + + drawFilledPath(c, spline, dataSet.getFillColor(), dataSet.getFillAlpha()); + } + } + + private float[] mLineBuffer = new float[4]; + + /** + * Draws a normal line. + * + * @param c + * @param dataSet + */ + protected void drawLinear(Canvas c, ILineDataSet dataSet) { + + int entryCount = dataSet.getEntryCount(); + + final boolean isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled(); + final int pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2; + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + float phaseY = mAnimator.getPhaseY(); + + mRenderPaint.setStyle(Paint.Style.STROKE); + + Canvas canvas = null; + + // if the data-set is dashed, draw on bitmap-canvas + if (dataSet.isDashedLineEnabled()) { + canvas = mBitmapCanvas; + } else { + canvas = c; + } + + mXBounds.set(mChart, dataSet); + + // if drawing filled is enabled + if (dataSet.isDrawFilledEnabled() && entryCount > 0) { + drawLinearFill(c, dataSet, trans, mXBounds); + } + + // more than 1 color + if (dataSet.getColors().size() > 1) { + + if (mLineBuffer.length <= pointsPerEntryPair * 2) + mLineBuffer = new float[pointsPerEntryPair * 4]; + + for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) { + + Entry e = dataSet.getEntryForIndex(j); + if (e == null) continue; + + mLineBuffer[0] = e.getX(); + mLineBuffer[1] = e.getY() * phaseY; + + if (j < mXBounds.max) { + + e = dataSet.getEntryForIndex(j + 1); + + if (e == null) break; + + if (isDrawSteppedEnabled) { + mLineBuffer[2] = e.getX(); + mLineBuffer[3] = mLineBuffer[1]; + mLineBuffer[4] = mLineBuffer[2]; + mLineBuffer[5] = mLineBuffer[3]; + mLineBuffer[6] = e.getX(); + mLineBuffer[7] = e.getY() * phaseY; + } else { + mLineBuffer[2] = e.getX(); + mLineBuffer[3] = e.getY() * phaseY; + } + + } else { + mLineBuffer[2] = mLineBuffer[0]; + mLineBuffer[3] = mLineBuffer[1]; + } + + trans.pointValuesToPixel(mLineBuffer); + + if (!mViewPortHandler.isInBoundsRight(mLineBuffer[0])) + break; + + // make sure the lines don't do shitty things outside + // bounds + if (!mViewPortHandler.isInBoundsLeft(mLineBuffer[2]) + || (!mViewPortHandler.isInBoundsTop(mLineBuffer[1]) && !mViewPortHandler + .isInBoundsBottom(mLineBuffer[3]))) + continue; + + // get the color that is set for this line-segment + mRenderPaint.setColor(dataSet.getColor(j)); + + canvas.drawLines(mLineBuffer, 0, pointsPerEntryPair * 2, mRenderPaint); + } + + } else { // only one color per dataset + + if (mLineBuffer.length < Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2) + mLineBuffer = new float[Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 4]; + + Entry e1, e2; + + e1 = dataSet.getEntryForIndex(mXBounds.min); + + if (e1 != null) { + + int j = 0; + for (int x = mXBounds.min; x <= mXBounds.range + mXBounds.min; x++) { + + e1 = dataSet.getEntryForIndex(x == 0 ? 0 : (x - 1)); + e2 = dataSet.getEntryForIndex(x); + + if (e1 == null || e2 == null) continue; + + mLineBuffer[j++] = e1.getX(); + mLineBuffer[j++] = e1.getY() * phaseY; + + if (isDrawSteppedEnabled) { + mLineBuffer[j++] = e2.getX(); + mLineBuffer[j++] = e1.getY() * phaseY; + mLineBuffer[j++] = e2.getX(); + mLineBuffer[j++] = e1.getY() * phaseY; + } + + mLineBuffer[j++] = e2.getX(); + mLineBuffer[j++] = e2.getY() * phaseY; + } + + if (j > 0) { + trans.pointValuesToPixel(mLineBuffer); + + final int size = Math.max((mXBounds.range + 1) * pointsPerEntryPair, pointsPerEntryPair) * 2; + + mRenderPaint.setColor(dataSet.getColor()); + + canvas.drawLines(mLineBuffer, 0, size, mRenderPaint); + } + } + } + + mRenderPaint.setPathEffect(null); + } + + protected Path mGenerateFilledPathBuffer = new Path(); + + /** + * Draws a filled linear path on the canvas. + * + * @param c + * @param dataSet + * @param trans + * @param bounds + */ + protected void drawLinearFill(Canvas c, ILineDataSet dataSet, Transformer trans, XBounds bounds) { + + final Path filled = mGenerateFilledPathBuffer; + + final int startingIndex = bounds.min; + final int endingIndex = bounds.range + bounds.min; + final int indexInterval = 128; + + int currentStartIndex = 0; + int currentEndIndex = indexInterval; + int iterations = 0; + + // Doing this iteratively in order to avoid OutOfMemory errors that can happen on large bounds sets. + do { + currentStartIndex = startingIndex + (iterations * indexInterval); + currentEndIndex = currentStartIndex + indexInterval; + currentEndIndex = currentEndIndex > endingIndex ? endingIndex : currentEndIndex; + + if (currentStartIndex <= currentEndIndex) { + generateFilledPath(dataSet, currentStartIndex, currentEndIndex, filled); + + trans.pathValueToPixel(filled); + + final Drawable drawable = dataSet.getFillDrawable(); + if (drawable != null) { + + drawFilledPath(c, filled, drawable); + } else { + + drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha()); + } + } + + iterations++; + + } while (currentStartIndex <= currentEndIndex); + + } + + /** + * Generates a path that is used for filled drawing. + * + * @param dataSet The dataset from which to read the entries. + * @param startIndex The index from which to start reading the dataset + * @param endIndex The index from which to stop reading the dataset + * @param outputPath The path object that will be assigned the chart data. + * @return + */ + private void generateFilledPath(final ILineDataSet dataSet, final int startIndex, final int endIndex, final Path outputPath) { + + final float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); + final float phaseY = mAnimator.getPhaseY(); + final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED; + + final Path filled = outputPath; + filled.reset(); + + final Entry entry = dataSet.getEntryForIndex(startIndex); + + filled.moveTo(entry.getX(), fillMin); + filled.lineTo(entry.getX(), entry.getY() * phaseY); + + // create a new path + Entry currentEntry = null; + Entry previousEntry = null; + for (int x = startIndex + 1; x <= endIndex; x++) { + + currentEntry = dataSet.getEntryForIndex(x); + + if (isDrawSteppedEnabled && previousEntry != null) { + filled.lineTo(currentEntry.getX(), previousEntry.getY() * phaseY); + } + + filled.lineTo(currentEntry.getX(), currentEntry.getY() * phaseY); + + previousEntry = currentEntry; + } + + // close up + if (currentEntry != null) { + filled.lineTo(currentEntry.getX(), fillMin); + } + + filled.close(); + } + + @Override + public void drawValues(Canvas c) { + + if (isDrawingValuesAllowed(mChart)) { + + List dataSets = mChart.getLineData().getDataSets(); + + for (int i = 0; i < dataSets.size(); i++) { + + ILineDataSet dataSet = dataSets.get(i); + + if (!shouldDrawValues(dataSet)) + continue; + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + // make sure the values do not interfear with the circles + int valOffset = (int) (dataSet.getCircleRadius() * 1.75f); + + if (!dataSet.isDrawCirclesEnabled()) + valOffset = valOffset / 2; + + mXBounds.set(mChart, dataSet); + + float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator + .getPhaseY(), mXBounds.min, mXBounds.max); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + for (int j = 0; j < positions.length; j += 2) { + + float x = positions[j]; + float y = positions[j + 1]; + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) + continue; + + Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, + y - valOffset, dataSet.getValueTextColor(j / 2)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + MPPointF.recycleInstance(iconsOffset); + } + } + } + + @Override + public void drawExtras(Canvas c) { + drawCircles(c); + } + + /** + * cache for the circle bitmaps of all datasets + */ + private HashMap mImageCaches = new HashMap<>(); + + /** + * buffer for drawing the circles + */ + private float[] mCirclesBuffer = new float[2]; + + protected void drawCircles(Canvas c) { + + mRenderPaint.setStyle(Paint.Style.FILL); + + float phaseY = mAnimator.getPhaseY(); + + mCirclesBuffer[0] = 0; + mCirclesBuffer[1] = 0; + + List dataSets = mChart.getLineData().getDataSets(); + + for (int i = 0; i < dataSets.size(); i++) { + + ILineDataSet dataSet = dataSets.get(i); + + if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() || + dataSet.getEntryCount() == 0) + continue; + + mCirclePaintInner.setColor(dataSet.getCircleHoleColor()); + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mXBounds.set(mChart, dataSet); + + float circleRadius = dataSet.getCircleRadius(); + float circleHoleRadius = dataSet.getCircleHoleRadius(); + boolean drawCircleHole = dataSet.isDrawCircleHoleEnabled() && + circleHoleRadius < circleRadius && + circleHoleRadius > 0.f; + boolean drawTransparentCircleHole = drawCircleHole && + dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE; + + DataSetImageCache imageCache; + + if (mImageCaches.containsKey(dataSet)) { + imageCache = mImageCaches.get(dataSet); + } else { + imageCache = new DataSetImageCache(); + mImageCaches.put(dataSet, imageCache); + } + + boolean changeRequired = imageCache.init(dataSet); + + // only fill the cache with new bitmaps if a change is required + if (changeRequired) { + imageCache.fill(dataSet, drawCircleHole, drawTransparentCircleHole); + } + + int boundsRangeCount = mXBounds.range + mXBounds.min; + + for (int j = mXBounds.min; j <= boundsRangeCount; j++) { + + Entry e = dataSet.getEntryForIndex(j); + + if (e == null) break; + + mCirclesBuffer[0] = e.getX(); + mCirclesBuffer[1] = e.getY() * phaseY; + + trans.pointValuesToPixel(mCirclesBuffer); + + if (!mViewPortHandler.isInBoundsRight(mCirclesBuffer[0])) + break; + + if (!mViewPortHandler.isInBoundsLeft(mCirclesBuffer[0]) || + !mViewPortHandler.isInBoundsY(mCirclesBuffer[1])) + continue; + + Bitmap circleBitmap = imageCache.getBitmap(j); + + if (circleBitmap != null) { + c.drawBitmap(circleBitmap, mCirclesBuffer[0] - circleRadius, mCirclesBuffer[1] - circleRadius, null); + } + } + } + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + LineData lineData = mChart.getLineData(); + + for (Highlight high : indices) { + + ILineDataSet set = lineData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + Entry e = set.getEntryForXValue(high.getX(), high.getY()); + + if (!isInBoundsX(e, set)) + continue; + + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY() * mAnimator + .getPhaseY()); + + high.setDraw((float) pix.x, (float) pix.y); + + // draw the lines + drawHighlightLines(c, (float) pix.x, (float) pix.y, set); + } + } + + /** + * Sets the Bitmap.Config to be used by this renderer. + * Default: Bitmap.Config.ARGB_8888 + * Use Bitmap.Config.ARGB_4444 to consume less memory. + * + * @param config + */ + public void setBitmapConfig(Bitmap.Config config) { + mBitmapConfig = config; + releaseBitmap(); + } + + /** + * Returns the Bitmap.Config that is used by this renderer. + * + * @return + */ + public Bitmap.Config getBitmapConfig() { + return mBitmapConfig; + } + + /** + * Releases the drawing bitmap. This should be called when {@link LineChart#onDetachedFromWindow()}. + */ + public void releaseBitmap() { + if (mBitmapCanvas != null) { + mBitmapCanvas.setBitmap(null); + mBitmapCanvas = null; + } + if (mDrawBitmap != null) { + mDrawBitmap.get().recycle(); + mDrawBitmap.clear(); + mDrawBitmap = null; + } + } + + private class DataSetImageCache { + + private Path mCirclePathBuffer = new Path(); + + private Bitmap[] circleBitmaps; + + /** + * Sets up the cache, returns true if a change of cache was required. + * + * @param set + * @return + */ + protected boolean init(ILineDataSet set) { + + int size = set.getCircleColorCount(); + boolean changeRequired = false; + + if (circleBitmaps == null) { + circleBitmaps = new Bitmap[size]; + changeRequired = true; + } else if (circleBitmaps.length != size) { + circleBitmaps = new Bitmap[size]; + changeRequired = true; + } + + return changeRequired; + } + + /** + * Fills the cache with bitmaps for the given dataset. + * + * @param set + * @param drawCircleHole + * @param drawTransparentCircleHole + */ + protected void fill(ILineDataSet set, boolean drawCircleHole, boolean drawTransparentCircleHole) { + + int colorCount = set.getCircleColorCount(); + float circleRadius = set.getCircleRadius(); + float circleHoleRadius = set.getCircleHoleRadius(); + + for (int i = 0; i < colorCount; i++) { + + Bitmap.Config conf = Bitmap.Config.ARGB_4444; + Bitmap circleBitmap = Bitmap.createBitmap((int) (circleRadius * 2.1), (int) (circleRadius * 2.1), conf); + + Canvas canvas = new Canvas(circleBitmap); + circleBitmaps[i] = circleBitmap; + mRenderPaint.setColor(set.getCircleColor(i)); + + if (drawTransparentCircleHole) { + // Begin path for circle with hole + mCirclePathBuffer.reset(); + + mCirclePathBuffer.addCircle( + circleRadius, + circleRadius, + circleRadius, + Path.Direction.CW); + + // Cut hole in path + mCirclePathBuffer.addCircle( + circleRadius, + circleRadius, + circleHoleRadius, + Path.Direction.CCW); + + // Fill in-between + canvas.drawPath(mCirclePathBuffer, mRenderPaint); + } else { + + canvas.drawCircle( + circleRadius, + circleRadius, + circleRadius, + mRenderPaint); + + if (drawCircleHole) { + canvas.drawCircle( + circleRadius, + circleRadius, + circleHoleRadius, + mCirclePaintInner); + } + } + } + } + + /** + * Returns the cached Bitmap at the given index. + * + * @param index + * @return + */ + protected Bitmap getBitmap(int index) { + return circleBitmaps[index % circleBitmaps.length]; + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java new file mode 100644 index 0000000000..288eff64bf --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java @@ -0,0 +1,95 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 25/01/16. + */ +public abstract class LineRadarRenderer extends LineScatterCandleRadarRenderer { + + public LineRadarRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + } + + /** + * Draws the provided path in filled mode with the provided drawable. + * + * @param c + * @param filledPath + * @param drawable + */ + protected void drawFilledPath(Canvas c, Path filledPath, Drawable drawable) { + + if (clipPathSupported()) { + + int save = c.save(); + c.clipPath(filledPath); + + drawable.setBounds((int) mViewPortHandler.contentLeft(), + (int) mViewPortHandler.contentTop(), + (int) mViewPortHandler.contentRight(), + (int) mViewPortHandler.contentBottom()); + drawable.draw(c); + + c.restoreToCount(save); + } else { + throw new RuntimeException("Fill-drawables not (yet) supported below API level 18, " + + "this code was run on API level " + Utils.getSDKInt() + "."); + } + } + + /** + * Draws the provided path in filled mode with the provided color and alpha. + * Special thanks to Angelo Suzuki (https://github.com/tinsukE) for this. + * + * @param c + * @param filledPath + * @param fillColor + * @param fillAlpha + */ + protected void drawFilledPath(Canvas c, Path filledPath, int fillColor, int fillAlpha) { + + int color = (fillAlpha << 24) | (fillColor & 0xffffff); + + if (clipPathSupported()) { + + int save = c.save(); + + c.clipPath(filledPath); + + c.drawColor(color); + c.restoreToCount(save); + } else { + + // save + Paint.Style previous = mRenderPaint.getStyle(); + int previousColor = mRenderPaint.getColor(); + + // set + mRenderPaint.setStyle(Paint.Style.FILL); + mRenderPaint.setColor(color); + + c.drawPath(filledPath, mRenderPaint); + + // restore + mRenderPaint.setColor(previousColor); + mRenderPaint.setStyle(previous); + } + } + + /** + * Clip path with hardware acceleration only working properly on API level 18 and above. + * + * @return + */ + private boolean clipPathSupported() { + return Utils.getSDKInt() >= 18; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java new file mode 100644 index 0000000000..53f54cbfd9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java @@ -0,0 +1,63 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Path; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.interfaces.datasets.ILineScatterCandleRadarDataSet; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 11/07/15. + */ +public abstract class LineScatterCandleRadarRenderer extends BarLineScatterCandleBubbleRenderer { + + /** + * path that is used for drawing highlight-lines (drawLines(...) cannot be used because of dashes) + */ + private Path mHighlightLinePath = new Path(); + + public LineScatterCandleRadarRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + } + + /** + * Draws vertical & horizontal highlight-lines if enabled. + * + * @param c + * @param x x-position of the highlight line intersection + * @param y y-position of the highlight line intersection + * @param set the currently drawn dataset + */ + protected void drawHighlightLines(Canvas c, float x, float y, ILineScatterCandleRadarDataSet set) { + + // set color and stroke-width + mHighlightPaint.setColor(set.getHighLightColor()); + mHighlightPaint.setStrokeWidth(set.getHighlightLineWidth()); + + // draw highlighted lines (if enabled) + mHighlightPaint.setPathEffect(set.getDashPathEffectHighlight()); + + // draw vertical highlight lines + if (set.isVerticalHighlightIndicatorEnabled()) { + + // create vertical path + mHighlightLinePath.reset(); + mHighlightLinePath.moveTo(x, mViewPortHandler.contentTop()); + mHighlightLinePath.lineTo(x, mViewPortHandler.contentBottom()); + + c.drawPath(mHighlightLinePath, mHighlightPaint); + } + + // draw horizontal highlight lines + if (set.isHorizontalHighlightIndicatorEnabled()) { + + // create horizontal path + mHighlightLinePath.reset(); + mHighlightLinePath.moveTo(mViewPortHandler.contentLeft(), y); + mHighlightLinePath.lineTo(mViewPortHandler.contentRight(), y); + + c.drawPath(mHighlightLinePath, mHighlightPaint); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java new file mode 100644 index 0000000000..be72a0834f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -0,0 +1,1018 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.lang.ref.WeakReference; +import java.util.List; + +public class PieChartRenderer extends DataRenderer { + + protected PieChart mChart; + + /** + * paint for the hole in the center of the pie chart and the transparent + * circle + */ + protected Paint mHolePaint; + protected Paint mTransparentCirclePaint; + protected Paint mValueLinePaint; + + /** + * paint object for the text that can be displayed in the center of the + * chart + */ + private TextPaint mCenterTextPaint; + + /** + * paint object used for drwing the slice-text + */ + private Paint mEntryLabelsPaint; + + private StaticLayout mCenterTextLayout; + private CharSequence mCenterTextLastValue; + private RectF mCenterTextLastBounds = new RectF(); + private RectF[] mRectBuffer = {new RectF(), new RectF(), new RectF()}; + + /** + * Bitmap for drawing the center hole + */ + protected WeakReference mDrawBitmap; + + protected Canvas mBitmapCanvas; + + public PieChartRenderer(PieChart chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + + mHolePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mHolePaint.setColor(Color.WHITE); + mHolePaint.setStyle(Style.FILL); + + mTransparentCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mTransparentCirclePaint.setColor(Color.WHITE); + mTransparentCirclePaint.setStyle(Style.FILL); + mTransparentCirclePaint.setAlpha(105); + + mCenterTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + mCenterTextPaint.setColor(Color.BLACK); + mCenterTextPaint.setTextSize(Utils.convertDpToPixel(12f)); + + mValuePaint.setTextSize(Utils.convertDpToPixel(13f)); + mValuePaint.setColor(Color.WHITE); + mValuePaint.setTextAlign(Align.CENTER); + + mEntryLabelsPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mEntryLabelsPaint.setColor(Color.WHITE); + mEntryLabelsPaint.setTextAlign(Align.CENTER); + mEntryLabelsPaint.setTextSize(Utils.convertDpToPixel(13f)); + + mValueLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mValueLinePaint.setStyle(Style.STROKE); + } + + public Paint getPaintHole() { + return mHolePaint; + } + + public Paint getPaintTransparentCircle() { + return mTransparentCirclePaint; + } + + public TextPaint getPaintCenterText() { + return mCenterTextPaint; + } + + public Paint getPaintEntryLabels() { + return mEntryLabelsPaint; + } + + @Override + public void initBuffers() { + // TODO Auto-generated method stub + } + + @Override + public void drawData(Canvas c) { + + int width = (int) mViewPortHandler.getChartWidth(); + int height = (int) mViewPortHandler.getChartHeight(); + + if (mDrawBitmap == null + || (mDrawBitmap.get().getWidth() != width) + || (mDrawBitmap.get().getHeight() != height)) { + + if (width > 0 && height > 0) { + + mDrawBitmap = new WeakReference(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444)); + mBitmapCanvas = new Canvas(mDrawBitmap.get()); + } else + return; + } + + mDrawBitmap.get().eraseColor(Color.TRANSPARENT); + + PieData pieData = mChart.getData(); + + for (IPieDataSet set : pieData.getDataSets()) { + + if (set.isVisible() && set.getEntryCount() > 0) + drawDataSet(c, set); + } + } + + private Path mPathBuffer = new Path(); + private RectF mInnerRectBuffer = new RectF(); + + protected float calculateMinimumRadiusForSpacedSlice( + MPPointF center, + float radius, + float angle, + float arcStartPointX, + float arcStartPointY, + float startAngle, + float sweepAngle) { + final float angleMiddle = startAngle + sweepAngle / 2.f; + + // Other point of the arc + float arcEndPointX = center.x + radius * (float) Math.cos((startAngle + sweepAngle) * Utils.FDEG2RAD); + float arcEndPointY = center.y + radius * (float) Math.sin((startAngle + sweepAngle) * Utils.FDEG2RAD); + + // Middle point on the arc + float arcMidPointX = center.x + radius * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); + float arcMidPointY = center.y + radius * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); + + // This is the base of the contained triangle + double basePointsDistance = Math.sqrt( + Math.pow(arcEndPointX - arcStartPointX, 2) + + Math.pow(arcEndPointY - arcStartPointY, 2)); + + // After reducing space from both sides of the "slice", + // the angle of the contained triangle should stay the same. + // So let's find out the height of that triangle. + float containedTriangleHeight = (float) (basePointsDistance / 2.0 * + Math.tan((180.0 - angle) / 2.0 * Utils.DEG2RAD)); + + // Now we subtract that from the radius + float spacedRadius = radius - containedTriangleHeight; + + // And now subtract the height of the arc that's between the triangle and the outer circle + spacedRadius -= Math.sqrt( + Math.pow(arcMidPointX - (arcEndPointX + arcStartPointX) / 2.f, 2) + + Math.pow(arcMidPointY - (arcEndPointY + arcStartPointY) / 2.f, 2)); + + return spacedRadius; + } + + /** + * Calculates the sliceSpace to use based on visible values and their size compared to the set sliceSpace. + * + * @param dataSet + * @return + */ + protected float getSliceSpace(IPieDataSet dataSet) { + + if (!dataSet.isAutomaticallyDisableSliceSpacingEnabled()) + return dataSet.getSliceSpace(); + + float spaceSizeRatio = dataSet.getSliceSpace() / mViewPortHandler.getSmallestContentExtension(); + float minValueRatio = dataSet.getYMin() / mChart.getData().getYValueSum() * 2; + + float sliceSpace = spaceSizeRatio > minValueRatio ? 0f : dataSet.getSliceSpace(); + + return sliceSpace; + } + + protected void drawDataSet(Canvas c, IPieDataSet dataSet) { + + float angle = 0; + float rotationAngle = mChart.getRotationAngle(); + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + final RectF circleBox = mChart.getCircleBox(); + + final int entryCount = dataSet.getEntryCount(); + final float[] drawAngles = mChart.getDrawAngles(); + final MPPointF center = mChart.getCenterCircleBox(); + final float radius = mChart.getRadius(); + final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); + final float userInnerRadius = drawInnerArc + ? radius * (mChart.getHoleRadius() / 100.f) + : 0.f; + + int visibleAngleCount = 0; + for (int j = 0; j < entryCount; j++) { + // draw only if the value is greater than zero + if ((Math.abs(dataSet.getEntryForIndex(j).getY()) > Utils.FLOAT_EPSILON)) { + visibleAngleCount++; + } + } + + final float sliceSpace = visibleAngleCount <= 1 ? 0.f : getSliceSpace(dataSet); + + for (int j = 0; j < entryCount; j++) { + + float sliceAngle = drawAngles[j]; + float innerRadius = userInnerRadius; + + Entry e = dataSet.getEntryForIndex(j); + + // draw only if the value is greater than zero + if ((Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) { + + if (!mChart.needsHighlight(j)) { + + final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; + + mRenderPaint.setColor(dataSet.getColor(j)); + + final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * radius); + final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; + float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; + if (sweepAngleOuter < 0.f) { + sweepAngleOuter = 0.f; + } + + mPathBuffer.reset(); + + float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD); + float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD); + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); + } else { + + mPathBuffer.moveTo(arcStartPointX, arcStartPointY); + + mPathBuffer.arcTo( + circleBox, + startAngleOuter, + sweepAngleOuter + ); + } + + // API < 21 does not receive floats in addArc, but a RectF + mInnerRectBuffer.set( + center.x - innerRadius, + center.y - innerRadius, + center.x + innerRadius, + center.y + innerRadius); + + if (drawInnerArc && + (innerRadius > 0.f || accountForSliceSpacing)) { + + if (accountForSliceSpacing) { + float minSpacedRadius = + calculateMinimumRadiusForSpacedSlice( + center, radius, + sliceAngle * phaseY, + arcStartPointX, arcStartPointY, + startAngleOuter, + sweepAngleOuter); + + if (minSpacedRadius < 0.f) + minSpacedRadius = -minSpacedRadius; + + innerRadius = Math.max(innerRadius, minSpacedRadius); + } + + final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * innerRadius); + final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; + float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; + if (sweepAngleInner < 0.f) { + sweepAngleInner = 0.f; + } + final float endAngleInner = startAngleInner + sweepAngleInner; + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); + } else { + + mPathBuffer.lineTo( + center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD), + center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD)); + + mPathBuffer.arcTo( + mInnerRectBuffer, + endAngleInner, + -sweepAngleInner + ); + } + } else { + + if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) { + if (accountForSliceSpacing) { + + float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; + + float sliceSpaceOffset = + calculateMinimumRadiusForSpacedSlice( + center, + radius, + sliceAngle * phaseY, + arcStartPointX, + arcStartPointY, + startAngleOuter, + sweepAngleOuter); + + float arcEndPointX = center.x + + sliceSpaceOffset * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); + float arcEndPointY = center.y + + sliceSpaceOffset * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); + + mPathBuffer.lineTo( + arcEndPointX, + arcEndPointY); + + } else { + mPathBuffer.lineTo( + center.x, + center.y); + } + } + + } + + mPathBuffer.close(); + + mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); + } + } + + angle += sliceAngle * phaseX; + } + + MPPointF.recycleInstance(center); + } + + @Override + public void drawValues(Canvas c) { + + MPPointF center = mChart.getCenterCircleBox(); + + // get whole the radius + float radius = mChart.getRadius(); + float rotationAngle = mChart.getRotationAngle(); + float[] drawAngles = mChart.getDrawAngles(); + float[] absoluteAngles = mChart.getAbsoluteAngles(); + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + final float holeRadiusPercent = mChart.getHoleRadius() / 100.f; + float labelRadiusOffset = radius / 10f * 3.6f; + + if (mChart.isDrawHoleEnabled()) { + labelRadiusOffset = (radius - (radius * holeRadiusPercent)) / 2f; + } + + final float labelRadius = radius - labelRadiusOffset; + + PieData data = mChart.getData(); + List dataSets = data.getDataSets(); + + float yValueSum = data.getYValueSum(); + + boolean drawEntryLabels = mChart.isDrawEntryLabelsEnabled(); + + float angle; + int xIndex = 0; + + c.save(); + + float offset = Utils.convertDpToPixel(5.f); + + for (int i = 0; i < dataSets.size(); i++) { + + IPieDataSet dataSet = dataSets.get(i); + + final boolean drawValues = dataSet.isDrawValuesEnabled(); + + if (!drawValues && !drawEntryLabels) + continue; + + final PieDataSet.ValuePosition xValuePosition = dataSet.getXValuePosition(); + final PieDataSet.ValuePosition yValuePosition = dataSet.getYValuePosition(); + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + float lineHeight = Utils.calcTextHeight(mValuePaint, "Q") + + Utils.convertDpToPixel(4f); + + IValueFormatter formatter = dataSet.getValueFormatter(); + + int entryCount = dataSet.getEntryCount(); + + mValueLinePaint.setColor(dataSet.getValueLineColor()); + mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth())); + + final float sliceSpace = getSliceSpace(dataSet); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + for (int j = 0; j < entryCount; j++) { + + PieEntry entry = dataSet.getEntryForIndex(j); + + if (xIndex == 0) + angle = 0.f; + else + angle = absoluteAngles[xIndex - 1] * phaseX; + + final float sliceAngle = drawAngles[xIndex]; + final float sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius); + + // offset needed to center the drawn text in the slice + final float angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2.f) / 2.f; + + angle = angle + angleOffset; + + final float transformedAngle = rotationAngle + angle * phaseY; + + float value = mChart.isUsePercentValuesEnabled() ? entry.getY() + / yValueSum * 100f : entry.getY(); + + final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD); + final float sliceYBase = (float) Math.sin(transformedAngle * Utils.FDEG2RAD); + + final boolean drawXOutside = drawEntryLabels && + xValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE; + final boolean drawYOutside = drawValues && + yValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE; + final boolean drawXInside = drawEntryLabels && + xValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE; + final boolean drawYInside = drawValues && + yValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE; + + if (drawXOutside || drawYOutside) { + + final float valueLineLength1 = dataSet.getValueLinePart1Length(); + final float valueLineLength2 = dataSet.getValueLinePart2Length(); + final float valueLinePart1OffsetPercentage = dataSet.getValueLinePart1OffsetPercentage() / 100.f; + + float pt2x, pt2y; + float labelPtx, labelPty; + + float line1Radius; + + if (mChart.isDrawHoleEnabled()) + line1Radius = (radius - (radius * holeRadiusPercent)) + * valueLinePart1OffsetPercentage + + (radius * holeRadiusPercent); + else + line1Radius = radius * valueLinePart1OffsetPercentage; + + final float polyline2Width = dataSet.isValueLineVariableLength() + ? labelRadius * valueLineLength2 * (float) Math.abs(Math.sin( + transformedAngle * Utils.FDEG2RAD)) + : labelRadius * valueLineLength2; + + final float pt0x = line1Radius * sliceXBase + center.x; + final float pt0y = line1Radius * sliceYBase + center.y; + + final float pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x; + final float pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y; + + if (transformedAngle % 360.0 >= 90.0 && transformedAngle % 360.0 <= 270.0) { + pt2x = pt1x - polyline2Width; + pt2y = pt1y; + + mValuePaint.setTextAlign(Align.RIGHT); + + if(drawXOutside) + mEntryLabelsPaint.setTextAlign(Align.RIGHT); + + labelPtx = pt2x - offset; + labelPty = pt2y; + } else { + pt2x = pt1x + polyline2Width; + pt2y = pt1y; + mValuePaint.setTextAlign(Align.LEFT); + + if(drawXOutside) + mEntryLabelsPaint.setTextAlign(Align.LEFT); + + labelPtx = pt2x + offset; + labelPty = pt2y; + } + + if (dataSet.getValueLineColor() != ColorTemplate.COLOR_NONE) { + c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint); + c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint); + } + + // draw everything, depending on settings + if (drawXOutside && drawYOutside) { + + drawValue(c, + formatter, + value, + entry, + 0, + labelPtx, + labelPty, + dataSet.getValueTextColor(j)); + + if (j < data.getEntryCount() && entry.getLabel() != null) { + drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight); + } + + } else if (drawXOutside) { + if (j < data.getEntryCount() && entry.getLabel() != null) { + drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight / 2.f); + } + } else if (drawYOutside) { + + drawValue(c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2.f, dataSet + .getValueTextColor(j)); + } + } + + if (drawXInside || drawYInside) { + // calculate the text position + float x = labelRadius * sliceXBase + center.x; + float y = labelRadius * sliceYBase + center.y; + + mValuePaint.setTextAlign(Align.CENTER); + + // draw everything, depending on settings + if (drawXInside && drawYInside) { + + drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); + + if (j < data.getEntryCount() && entry.getLabel() != null) { + drawEntryLabel(c, entry.getLabel(), x, y + lineHeight); + } + + } else if (drawXInside) { + if (j < data.getEntryCount() && entry.getLabel() != null) { + drawEntryLabel(c, entry.getLabel(), x, y + lineHeight / 2f); + } + } else if (drawYInside) { + + drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); + } + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float x = (labelRadius + iconsOffset.y) * sliceXBase + center.x; + float y = (labelRadius + iconsOffset.y) * sliceYBase + center.y; + y += iconsOffset.x; + + Utils.drawImage( + c, + icon, + (int)x, + (int)y, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + + xIndex++; + } + + MPPointF.recycleInstance(iconsOffset); + } + MPPointF.recycleInstance(center); + c.restore(); + } + + /** + * Draws an entry label at the specified position. + * + * @param c + * @param label + * @param x + * @param y + */ + protected void drawEntryLabel(Canvas c, String label, float x, float y) { + c.drawText(label, x, y, mEntryLabelsPaint); + } + + @Override + public void drawExtras(Canvas c) { + // drawCircles(c); + drawHole(c); + c.drawBitmap(mDrawBitmap.get(), 0, 0, null); + drawCenterText(c); + } + + private Path mHoleCirclePath = new Path(); + + /** + * draws the hole in the center of the chart and the transparent circle / + * hole + */ + protected void drawHole(Canvas c) { + + if (mChart.isDrawHoleEnabled() && mBitmapCanvas != null) { + + float radius = mChart.getRadius(); + float holeRadius = radius * (mChart.getHoleRadius() / 100); + MPPointF center = mChart.getCenterCircleBox(); + + if (Color.alpha(mHolePaint.getColor()) > 0) { + // draw the hole-circle + mBitmapCanvas.drawCircle( + center.x, center.y, + holeRadius, mHolePaint); + } + + // only draw the circle if it can be seen (not covered by the hole) + if (Color.alpha(mTransparentCirclePaint.getColor()) > 0 && + mChart.getTransparentCircleRadius() > mChart.getHoleRadius()) { + + int alpha = mTransparentCirclePaint.getAlpha(); + float secondHoleRadius = radius * (mChart.getTransparentCircleRadius() / 100); + + mTransparentCirclePaint.setAlpha((int) ((float) alpha * mAnimator.getPhaseX() * mAnimator.getPhaseY())); + + // draw the transparent-circle + mHoleCirclePath.reset(); + mHoleCirclePath.addCircle(center.x, center.y, secondHoleRadius, Path.Direction.CW); + mHoleCirclePath.addCircle(center.x, center.y, holeRadius, Path.Direction.CCW); + mBitmapCanvas.drawPath(mHoleCirclePath, mTransparentCirclePaint); + + // reset alpha + mTransparentCirclePaint.setAlpha(alpha); + } + MPPointF.recycleInstance(center); + } + } + + protected Path mDrawCenterTextPathBuffer = new Path(); + /** + * draws the description text in the center of the pie chart makes most + * sense when center-hole is enabled + */ + protected void drawCenterText(Canvas c) { + + CharSequence centerText = mChart.getCenterText(); + + if (mChart.isDrawCenterTextEnabled() && centerText != null) { + + MPPointF center = mChart.getCenterCircleBox(); + MPPointF offset = mChart.getCenterTextOffset(); + + float x = center.x + offset.x; + float y = center.y + offset.y; + + float innerRadius = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled() + ? mChart.getRadius() * (mChart.getHoleRadius() / 100f) + : mChart.getRadius(); + + RectF holeRect = mRectBuffer[0]; + holeRect.left = x - innerRadius; + holeRect.top = y - innerRadius; + holeRect.right = x + innerRadius; + holeRect.bottom = y + innerRadius; + RectF boundingRect = mRectBuffer[1]; + boundingRect.set(holeRect); + + float radiusPercent = mChart.getCenterTextRadiusPercent() / 100f; + if (radiusPercent > 0.0) { + boundingRect.inset( + (boundingRect.width() - boundingRect.width() * radiusPercent) / 2.f, + (boundingRect.height() - boundingRect.height() * radiusPercent) / 2.f + ); + } + + if (!centerText.equals(mCenterTextLastValue) || !boundingRect.equals(mCenterTextLastBounds)) { + + // Next time we won't recalculate StaticLayout... + mCenterTextLastBounds.set(boundingRect); + mCenterTextLastValue = centerText; + + float width = mCenterTextLastBounds.width(); + + // If width is 0, it will crash. Always have a minimum of 1 + mCenterTextLayout = new StaticLayout(centerText, 0, centerText.length(), + mCenterTextPaint, + (int) Math.max(Math.ceil(width), 1.f), + Layout.Alignment.ALIGN_CENTER, 1.f, 0.f, false); + } + + //float layoutWidth = Utils.getStaticLayoutMaxWidth(mCenterTextLayout); + float layoutHeight = mCenterTextLayout.getHeight(); + + c.save(); + if (Build.VERSION.SDK_INT >= 18) { + Path path = mDrawCenterTextPathBuffer; + path.reset(); + path.addOval(holeRect, Path.Direction.CW); + c.clipPath(path); + } + + c.translate(boundingRect.left, boundingRect.top + (boundingRect.height() - layoutHeight) / 2.f); + mCenterTextLayout.draw(c); + + c.restore(); + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(offset); + } + } + + protected RectF mDrawHighlightedRectF = new RectF(); + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + float angle; + float rotationAngle = mChart.getRotationAngle(); + + float[] drawAngles = mChart.getDrawAngles(); + float[] absoluteAngles = mChart.getAbsoluteAngles(); + final MPPointF center = mChart.getCenterCircleBox(); + final float radius = mChart.getRadius(); + final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); + final float userInnerRadius = drawInnerArc + ? radius * (mChart.getHoleRadius() / 100.f) + : 0.f; + + final RectF highlightedCircleBox = mDrawHighlightedRectF; + highlightedCircleBox.set(0,0,0,0); + + for (int i = 0; i < indices.length; i++) { + + // get the index to highlight + int index = (int) indices[i].getX(); + + if (index >= drawAngles.length) + continue; + + IPieDataSet set = mChart.getData() + .getDataSetByIndex(indices[i] + .getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + final int entryCount = set.getEntryCount(); + int visibleAngleCount = 0; + for (int j = 0; j < entryCount; j++) { + // draw only if the value is greater than zero + if ((Math.abs(set.getEntryForIndex(j).getY()) > Utils.FLOAT_EPSILON)) { + visibleAngleCount++; + } + } + + if (index == 0) + angle = 0.f; + else + angle = absoluteAngles[index - 1] * phaseX; + + final float sliceSpace = visibleAngleCount <= 1 ? 0.f : set.getSliceSpace(); + + float sliceAngle = drawAngles[index]; + float innerRadius = userInnerRadius; + + float shift = set.getSelectionShift(); + final float highlightedRadius = radius + shift; + highlightedCircleBox.set(mChart.getCircleBox()); + highlightedCircleBox.inset(-shift, -shift); + + final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; + + mRenderPaint.setColor(set.getColor(index)); + + final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * radius); + + final float sliceSpaceAngleShifted = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * highlightedRadius); + + final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; + float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; + if (sweepAngleOuter < 0.f) { + sweepAngleOuter = 0.f; + } + + final float startAngleShifted = rotationAngle + (angle + sliceSpaceAngleShifted / 2.f) * phaseY; + float sweepAngleShifted = (sliceAngle - sliceSpaceAngleShifted) * phaseY; + if (sweepAngleShifted < 0.f) { + sweepAngleShifted = 0.f; + } + + mPathBuffer.reset(); + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, highlightedRadius, Path.Direction.CW); + } else { + + mPathBuffer.moveTo( + center.x + highlightedRadius * (float) Math.cos(startAngleShifted * Utils.FDEG2RAD), + center.y + highlightedRadius * (float) Math.sin(startAngleShifted * Utils.FDEG2RAD)); + + mPathBuffer.arcTo( + highlightedCircleBox, + startAngleShifted, + sweepAngleShifted + ); + } + + float sliceSpaceRadius = 0.f; + if (accountForSliceSpacing) { + sliceSpaceRadius = + calculateMinimumRadiusForSpacedSlice( + center, radius, + sliceAngle * phaseY, + center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD), + center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD), + startAngleOuter, + sweepAngleOuter); + } + + // API < 21 does not receive floats in addArc, but a RectF + mInnerRectBuffer.set( + center.x - innerRadius, + center.y - innerRadius, + center.x + innerRadius, + center.y + innerRadius); + + if (drawInnerArc && + (innerRadius > 0.f || accountForSliceSpacing)) { + + if (accountForSliceSpacing) { + float minSpacedRadius = sliceSpaceRadius; + + if (minSpacedRadius < 0.f) + minSpacedRadius = -minSpacedRadius; + + innerRadius = Math.max(innerRadius, minSpacedRadius); + } + + final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * innerRadius); + final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; + float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; + if (sweepAngleInner < 0.f) { + sweepAngleInner = 0.f; + } + final float endAngleInner = startAngleInner + sweepAngleInner; + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); + } else { + + mPathBuffer.lineTo( + center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD), + center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD)); + + mPathBuffer.arcTo( + mInnerRectBuffer, + endAngleInner, + -sweepAngleInner + ); + } + } else { + + if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) { + + if (accountForSliceSpacing) { + final float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; + + final float arcEndPointX = center.x + + sliceSpaceRadius * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); + final float arcEndPointY = center.y + + sliceSpaceRadius * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); + + mPathBuffer.lineTo( + arcEndPointX, + arcEndPointY); + + } else { + + mPathBuffer.lineTo( + center.x, + center.y); + } + + } + + } + + mPathBuffer.close(); + + mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); + } + + MPPointF.recycleInstance(center); + } + + /** + * This gives all pie-slices a rounded edge. + * + * @param c + */ + protected void drawRoundedSlices(Canvas c) { + + if (!mChart.isDrawRoundedSlicesEnabled()) + return; + + IPieDataSet dataSet = mChart.getData().getDataSet(); + + if (!dataSet.isVisible()) + return; + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + MPPointF center = mChart.getCenterCircleBox(); + float r = mChart.getRadius(); + + // calculate the radius of the "slice-circle" + float circleRadius = (r - (r * mChart.getHoleRadius() / 100f)) / 2f; + + float[] drawAngles = mChart.getDrawAngles(); + float angle = mChart.getRotationAngle(); + + for (int j = 0; j < dataSet.getEntryCount(); j++) { + + float sliceAngle = drawAngles[j]; + + Entry e = dataSet.getEntryForIndex(j); + + // draw only if the value is greater than zero + if ((Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) { + + float x = (float) ((r - circleRadius) + * Math.cos(Math.toRadians((angle + sliceAngle) + * phaseY)) + center.x); + float y = (float) ((r - circleRadius) + * Math.sin(Math.toRadians((angle + sliceAngle) + * phaseY)) + center.y); + + mRenderPaint.setColor(dataSet.getColor(j)); + mBitmapCanvas.drawCircle(x, y, circleRadius, mRenderPaint); + } + + angle += sliceAngle * phaseX; + } + MPPointF.recycleInstance(center); + } + + /** + * Releases the drawing bitmap. This should be called when {@link LineChart#onDetachedFromWindow()}. + */ + public void releaseBitmap() { + if (mBitmapCanvas != null) { + mBitmapCanvas.setBitmap(null); + mBitmapCanvas = null; + } + if (mDrawBitmap != null) { + mDrawBitmap.get().recycle(); + mDrawBitmap.clear(); + mDrawBitmap = null; + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java new file mode 100644 index 0000000000..dbf0e8f807 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -0,0 +1,398 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.data.RadarData; +import com.github.mikephil.charting.data.RadarEntry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +public class RadarChartRenderer extends LineRadarRenderer { + + protected RadarChart mChart; + + /** + * paint for drawing the web + */ + protected Paint mWebPaint; + protected Paint mHighlightCirclePaint; + + public RadarChartRenderer(RadarChart chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + + mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mHighlightPaint.setStyle(Paint.Style.STROKE); + mHighlightPaint.setStrokeWidth(2f); + mHighlightPaint.setColor(Color.rgb(255, 187, 115)); + + mWebPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mWebPaint.setStyle(Paint.Style.STROKE); + + mHighlightCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + } + + public Paint getWebPaint() { + return mWebPaint; + } + + @Override + public void initBuffers() { + // TODO Auto-generated method stub + + } + + @Override + public void drawData(Canvas c) { + + RadarData radarData = mChart.getData(); + + int mostEntries = radarData.getMaxEntryCountSet().getEntryCount(); + + for (IRadarDataSet set : radarData.getDataSets()) { + + if (set.isVisible()) { + drawDataSet(c, set, mostEntries); + } + } + } + + protected Path mDrawDataSetSurfacePathBuffer = new Path(); + /** + * Draws the RadarDataSet + * + * @param c + * @param dataSet + * @param mostEntries the entry count of the dataset with the most entries + */ + protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + Path surface = mDrawDataSetSurfacePathBuffer; + surface.reset(); + + boolean hasMovedToPoint = false; + + for (int j = 0; j < dataSet.getEntryCount(); j++) { + + mRenderPaint.setColor(dataSet.getColor(j)); + + RadarEntry e = dataSet.getEntryForIndex(j); + + Utils.getPosition( + center, + (e.getY() - mChart.getYChartMin()) * factor * phaseY, + sliceangle * j * phaseX + mChart.getRotationAngle(), pOut); + + if (Float.isNaN(pOut.x)) + continue; + + if (!hasMovedToPoint) { + surface.moveTo(pOut.x, pOut.y); + hasMovedToPoint = true; + } else + surface.lineTo(pOut.x, pOut.y); + } + + if (dataSet.getEntryCount() > mostEntries) { + // if this is not the largest set, draw a line to the center before closing + surface.lineTo(center.x, center.y); + } + + surface.close(); + + if (dataSet.isDrawFilledEnabled()) { + + final Drawable drawable = dataSet.getFillDrawable(); + if (drawable != null) { + + drawFilledPath(c, surface, drawable); + } else { + + drawFilledPath(c, surface, dataSet.getFillColor(), dataSet.getFillAlpha()); + } + } + + mRenderPaint.setStrokeWidth(dataSet.getLineWidth()); + mRenderPaint.setStyle(Paint.Style.STROKE); + + // draw the line (only if filled is disabled or alpha is below 255) + if (!dataSet.isDrawFilledEnabled() || dataSet.getFillAlpha() < 255) + c.drawPath(surface, mRenderPaint); + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + } + + @Override + public void drawValues(Canvas c) { + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + MPPointF pIcon = MPPointF.getInstance(0,0); + + float yoffset = Utils.convertDpToPixel(5f); + + for (int i = 0; i < mChart.getData().getDataSetCount(); i++) { + + IRadarDataSet dataSet = mChart.getData().getDataSetByIndex(i); + + if (!shouldDrawValues(dataSet)) + continue; + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + for (int j = 0; j < dataSet.getEntryCount(); j++) { + + RadarEntry entry = dataSet.getEntryForIndex(j); + + Utils.getPosition( + center, + (entry.getY() - mChart.getYChartMin()) * factor * phaseY, + sliceangle * j * phaseX + mChart.getRotationAngle(), + pOut); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + entry.getY(), + entry, + i, + pOut.x, + pOut.y - yoffset, + dataSet.getValueTextColor + (j)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.getPosition( + center, + (entry.getY()) * factor * phaseY + iconsOffset.y, + sliceangle * j * phaseX + mChart.getRotationAngle(), + pIcon); + + //noinspection SuspiciousNameCombination + pIcon.y += iconsOffset.x; + + Utils.drawImage( + c, + icon, + (int)pIcon.x, + (int)pIcon.y, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + MPPointF.recycleInstance(iconsOffset); + } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + MPPointF.recycleInstance(pIcon); + } + + @Override + public void drawExtras(Canvas c) { + drawWeb(c); + } + + protected void drawWeb(Canvas c) { + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + float rotationangle = mChart.getRotationAngle(); + + MPPointF center = mChart.getCenterOffsets(); + + // draw the web lines that come from the center + mWebPaint.setStrokeWidth(mChart.getWebLineWidth()); + mWebPaint.setColor(mChart.getWebColor()); + mWebPaint.setAlpha(mChart.getWebAlpha()); + + final int xIncrements = 1 + mChart.getSkipWebLineCount(); + int maxEntryCount = mChart.getData().getMaxEntryCountSet().getEntryCount(); + + MPPointF p = MPPointF.getInstance(0,0); + for (int i = 0; i < maxEntryCount; i += xIncrements) { + + Utils.getPosition( + center, + mChart.getYRange() * factor, + sliceangle * i + rotationangle, + p); + + c.drawLine(center.x, center.y, p.x, p.y, mWebPaint); + } + MPPointF.recycleInstance(p); + + // draw the inner-web + mWebPaint.setStrokeWidth(mChart.getWebLineWidthInner()); + mWebPaint.setColor(mChart.getWebColorInner()); + mWebPaint.setAlpha(mChart.getWebAlpha()); + + int labelCount = mChart.getYAxis().mEntryCount; + + MPPointF p1out = MPPointF.getInstance(0,0); + MPPointF p2out = MPPointF.getInstance(0,0); + for (int j = 0; j < labelCount; j++) { + + for (int i = 0; i < mChart.getData().getEntryCount(); i++) { + + float r = (mChart.getYAxis().mEntries[j] - mChart.getYChartMin()) * factor; + + Utils.getPosition(center, r, sliceangle * i + rotationangle, p1out); + Utils.getPosition(center, r, sliceangle * (i + 1) + rotationangle, p2out); + + c.drawLine(p1out.x, p1out.y, p2out.x, p2out.y, mWebPaint); + + + } + } + MPPointF.recycleInstance(p1out); + MPPointF.recycleInstance(p2out); + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + + RadarData radarData = mChart.getData(); + + for (Highlight high : indices) { + + IRadarDataSet set = radarData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + RadarEntry e = set.getEntryForIndex((int) high.getX()); + + if (!isInBoundsX(e, set)) + continue; + + float y = (e.getY() - mChart.getYChartMin()); + + Utils.getPosition(center, + y * factor * mAnimator.getPhaseY(), + sliceangle * high.getX() * mAnimator.getPhaseX() + mChart.getRotationAngle(), + pOut); + + high.setDraw(pOut.x, pOut.y); + + // draw the lines + drawHighlightLines(c, pOut.x, pOut.y, set); + + if (set.isDrawHighlightCircleEnabled()) { + + if (!Float.isNaN(pOut.x) && !Float.isNaN(pOut.y)) { + + int strokeColor = set.getHighlightCircleStrokeColor(); + if (strokeColor == ColorTemplate.COLOR_NONE) { + strokeColor = set.getColor(0); + } + + if (set.getHighlightCircleStrokeAlpha() < 255) { + strokeColor = ColorTemplate.colorWithAlpha(strokeColor, set.getHighlightCircleStrokeAlpha()); + } + + drawHighlightCircle(c, + pOut, + set.getHighlightCircleInnerRadius(), + set.getHighlightCircleOuterRadius(), + set.getHighlightCircleFillColor(), + strokeColor, + set.getHighlightCircleStrokeWidth()); + } + } + } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + } + + protected Path mDrawHighlightCirclePathBuffer = new Path(); + public void drawHighlightCircle(Canvas c, + MPPointF point, + float innerRadius, + float outerRadius, + int fillColor, + int strokeColor, + float strokeWidth) { + c.save(); + + outerRadius = Utils.convertDpToPixel(outerRadius); + innerRadius = Utils.convertDpToPixel(innerRadius); + + if (fillColor != ColorTemplate.COLOR_NONE) { + Path p = mDrawHighlightCirclePathBuffer; + p.reset(); + p.addCircle(point.x, point.y, outerRadius, Path.Direction.CW); + if (innerRadius > 0.f) { + p.addCircle(point.x, point.y, innerRadius, Path.Direction.CCW); + } + mHighlightCirclePaint.setColor(fillColor); + mHighlightCirclePaint.setStyle(Paint.Style.FILL); + c.drawPath(p, mHighlightCirclePaint); + } + + if (strokeColor != ColorTemplate.COLOR_NONE) { + mHighlightCirclePaint.setColor(strokeColor); + mHighlightCirclePaint.setStyle(Paint.Style.STROKE); + mHighlightCirclePaint.setStrokeWidth(Utils.convertDpToPixel(strokeWidth)); + c.drawCircle(point.x, point.y, outerRadius, mHighlightCirclePaint); + } + + c.restore(); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/Renderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java similarity index 87% rename from MPChartLib/src/com/github/mikephil/charting/renderer/Renderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java index a76bea0811..90f49683ae 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/Renderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java @@ -1,6 +1,8 @@ package com.github.mikephil.charting.renderer; +import com.github.mikephil.charting.utils.ViewPortHandler; + /** * Abstract baseclass of all Renderers. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java new file mode 100644 index 0000000000..36a38a53c5 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -0,0 +1,194 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.Log; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class ScatterChartRenderer extends LineScatterCandleRadarRenderer { + + protected ScatterDataProvider mChart; + + public ScatterChartRenderer(ScatterDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + } + + @Override + public void initBuffers() { + } + + @Override + public void drawData(Canvas c) { + + ScatterData scatterData = mChart.getScatterData(); + + for (IScatterDataSet set : scatterData.getDataSets()) { + + if (set.isVisible()) + drawDataSet(c, set); + } + } + + float[] mPixelBuffer = new float[2]; + + protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { + + ViewPortHandler viewPortHandler = mViewPortHandler; + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + float phaseY = mAnimator.getPhaseY(); + + IShapeRenderer renderer = dataSet.getShapeRenderer(); + if (renderer == null) { + Log.i("MISSING", "There's no IShapeRenderer specified for ScatterDataSet"); + return; + } + + int max = (int)(Math.min( + Math.ceil((float)dataSet.getEntryCount() * mAnimator.getPhaseX()), + (float)dataSet.getEntryCount())); + + for (int i = 0; i < max; i++) { + + Entry e = dataSet.getEntryForIndex(i); + + mPixelBuffer[0] = e.getX(); + mPixelBuffer[1] = e.getY() * phaseY; + + trans.pointValuesToPixel(mPixelBuffer); + + if (!viewPortHandler.isInBoundsRight(mPixelBuffer[0])) + break; + + if (!viewPortHandler.isInBoundsLeft(mPixelBuffer[0]) + || !viewPortHandler.isInBoundsY(mPixelBuffer[1])) + continue; + + mRenderPaint.setColor(dataSet.getColor(i / 2)); + renderer.renderShape( + c, dataSet, mViewPortHandler, + mPixelBuffer[0], mPixelBuffer[1], + mRenderPaint); + } + } + + @Override + public void drawValues(Canvas c) { + + // if values are drawn + if (isDrawingValuesAllowed(mChart)) { + + List dataSets = mChart.getScatterData().getDataSets(); + + for (int i = 0; i < mChart.getScatterData().getDataSetCount(); i++) { + + IScatterDataSet dataSet = dataSets.get(i); + + if (!shouldDrawValues(dataSet)) + continue; + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + mXBounds.set(mChart, dataSet); + + float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) + .generateTransformedValuesScatter(dataSet, + mAnimator.getPhaseX(), mAnimator.getPhaseY(), mXBounds.min, mXBounds.max); + + float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + for (int j = 0; j < positions.length; j += 2) { + + if (!mViewPortHandler.isInBoundsRight(positions[j])) + break; + + // make sure the lines don't do shitty things outside bounds + if ((!mViewPortHandler.isInBoundsLeft(positions[j]) + || !mViewPortHandler.isInBoundsY(positions[j + 1]))) + continue; + + Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + entry.getY(), + entry, + i, + positions[j], + positions[j + 1] - shapeSize, + dataSet.getValueTextColor(j / 2 + mXBounds.min)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(positions[j] + iconsOffset.x), + (int)(positions[j + 1] + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + MPPointF.recycleInstance(iconsOffset); + } + } + } + + @Override + public void drawExtras(Canvas c) { + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + ScatterData scatterData = mChart.getScatterData(); + + for (Highlight high : indices) { + + IScatterDataSet set = scatterData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + final Entry e = set.getEntryForXValue(high.getX(), high.getY()); + + if (!isInBoundsX(e, set)) + continue; + + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY() * mAnimator + .getPhaseY()); + + high.setDraw((float) pix.x, (float) pix.y); + + // draw the lines + drawHighlightLines(c, (float) pix.x, (float) pix.y, set); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java new file mode 100644 index 0000000000..2c305796df --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -0,0 +1,401 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Path; +import android.graphics.RectF; + +import com.github.mikephil.charting.components.LimitLine; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class XAxisRenderer extends AxisRenderer { + + protected XAxis mXAxis; + + public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans) { + super(viewPortHandler, trans, xAxis); + + this.mXAxis = xAxis; + + mAxisLabelPaint.setColor(Color.BLACK); + mAxisLabelPaint.setTextAlign(Align.CENTER); + mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); + } + + protected void setupGridPaint() { + mGridPaint.setColor(mXAxis.getGridColor()); + mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); + mGridPaint.setPathEffect(mXAxis.getGridDashPathEffect()); + } + + @Override + public void computeAxis(float min, float max, boolean inverted) { + + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { + + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), mViewPortHandler.contentTop()); + + if (inverted) { + + min = (float) p2.x; + max = (float) p1.x; + } else { + + min = (float) p1.x; + max = (float) p2.x; + } + + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); + } + + computeAxisValues(min, max); + } + + @Override + protected void computeAxisValues(float min, float max) { + super.computeAxisValues(min, max); + + computeSize(); + } + + protected void computeSize() { + + String longest = mXAxis.getLongestLabel(); + + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + + final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, longest); + + final float labelWidth = labelSize.width; + final float labelHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q"); + + final FSize labelRotatedSize = Utils.getSizeOfRotatedRectangleByDegrees( + labelWidth, + labelHeight, + mXAxis.getLabelRotationAngle()); + + + mXAxis.mLabelWidth = Math.round(labelWidth); + mXAxis.mLabelHeight = Math.round(labelHeight); + mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width); + mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); + + FSize.recycleInstance(labelRotatedSize); + FSize.recycleInstance(labelSize); + } + + @Override + public void renderAxisLabels(Canvas c) { + + if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) + return; + + float yoffset = mXAxis.getYOffset(); + + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + mAxisLabelPaint.setColor(mXAxis.getTextColor()); + + MPPointF pointF = MPPointF.getInstance(0,0); + if (mXAxis.getPosition() == XAxisPosition.TOP) { + pointF.x = 0.5f; + pointF.y = 1.0f; + drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { + pointF.x = 0.5f; + pointF.y = 1.0f; + drawLabels(c, mViewPortHandler.contentTop() + yoffset + mXAxis.mLabelRotatedHeight, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() - yoffset - mXAxis.mLabelRotatedHeight, pointF); + + } else { // BOTH SIDED + pointF.x = 0.5f; + pointF.y = 1.0f; + drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF); + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF); + } + MPPointF.recycleInstance(pointF); + } + + @Override + public void renderAxisLine(Canvas c) { + + if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled()) + return; + + mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); + mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); + mAxisLinePaint.setPathEffect(mXAxis.getAxisLineDashPathEffect()); + + if (mXAxis.getPosition() == XAxisPosition.TOP + || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE + || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { + c.drawLine(mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), + mViewPortHandler.contentTop(), mAxisLinePaint); + } + + if (mXAxis.getPosition() == XAxisPosition.BOTTOM + || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE + || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { + c.drawLine(mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } + } + + /** + * draws the x-labels on the specified y-position + * + * @param pos + */ + protected void drawLabels(Canvas c, float pos, MPPointF anchor) { + + final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); + boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); + + float[] positions = new float[mXAxis.mEntryCount * 2]; + + for (int i = 0; i < positions.length; i += 2) { + + // only fill x values + if (centeringEnabled) { + positions[i] = mXAxis.mCenteredEntries[i / 2]; + } else { + positions[i] = mXAxis.mEntries[i / 2]; + } + } + + mTrans.pointValuesToPixel(positions); + + for (int i = 0; i < positions.length; i += 2) { + + float x = positions[i]; + + if (mViewPortHandler.isInBoundsX(x)) { + + String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); + + if (mXAxis.isAvoidFirstLastClippingEnabled()) { + + // avoid clipping of the last + if (i == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) { + float width = Utils.calcTextWidth(mAxisLabelPaint, label); + + if (width > mViewPortHandler.offsetRight() * 2 + && x + width > mViewPortHandler.getChartWidth()) + x -= width / 2; + + // avoid clipping of the first + } else if (i == 0) { + + float width = Utils.calcTextWidth(mAxisLabelPaint, label); + x += width / 2; + } + } + + drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees); + } + } + } + + protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) { + Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); + } + protected Path mRenderGridLinesPath = new Path(); + protected float[] mRenderGridLinesBuffer = new float[2]; + @Override + public void renderGridLines(Canvas c) { + + if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) + return; + + int clipRestoreCount = c.save(); + c.clipRect(getGridClippingRect()); + + if(mRenderGridLinesBuffer.length != mAxis.mEntryCount * 2){ + mRenderGridLinesBuffer = new float[mXAxis.mEntryCount * 2]; + } + float[] positions = mRenderGridLinesBuffer; + + for (int i = 0; i < positions.length; i += 2) { + positions[i] = mXAxis.mEntries[i / 2]; + positions[i + 1] = mXAxis.mEntries[i / 2]; + } + + mTrans.pointValuesToPixel(positions); + + setupGridPaint(); + + Path gridLinePath = mRenderGridLinesPath; + gridLinePath.reset(); + + for (int i = 0; i < positions.length; i += 2) { + + drawGridLine(c, positions[i], positions[i + 1], gridLinePath); + } + + c.restoreToCount(clipRestoreCount); + } + + protected RectF mGridClippingRect = new RectF(); + + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f); + return mGridClippingRect; + } + + /** + * Draws the grid line at the specified position using the provided path. + * + * @param c + * @param x + * @param y + * @param gridLinePath + */ + protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { + + gridLinePath.moveTo(x, mViewPortHandler.contentBottom()); + gridLinePath.lineTo(x, mViewPortHandler.contentTop()); + + // draw a path because lines don't support dashing on lower android versions + c.drawPath(gridLinePath, mGridPaint); + + gridLinePath.reset(); + } + + protected float[] mRenderLimitLinesBuffer = new float[2]; + protected RectF mLimitLineClippingRect = new RectF(); + + /** + * Draws the LimitLines associated with this axis to the screen. + * + * @param c + */ + @Override + public void renderLimitLines(Canvas c) { + + List limitLines = mXAxis.getLimitLines(); + + if (limitLines == null || limitLines.size() <= 0) + return; + + float[] position = mRenderLimitLinesBuffer; + position[0] = 0; + position[1] = 0; + + for (int i = 0; i < limitLines.size(); i++) { + + LimitLine l = limitLines.get(i); + + if (!l.isEnabled()) + continue; + + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(-l.getLineWidth(), 0.f); + c.clipRect(mLimitLineClippingRect); + + position[0] = l.getLimit(); + position[1] = 0.f; + + mTrans.pointValuesToPixel(position); + + renderLimitLineLine(c, l, position); + renderLimitLineLabel(c, l, position, 2.f + l.getYOffset()); + + c.restoreToCount(clipRestoreCount); + } + } + + float[] mLimitLineSegmentsBuffer = new float[4]; + private Path mLimitLinePath = new Path(); + + public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) { + mLimitLineSegmentsBuffer[0] = position[0]; + mLimitLineSegmentsBuffer[1] = mViewPortHandler.contentTop(); + mLimitLineSegmentsBuffer[2] = position[0]; + mLimitLineSegmentsBuffer[3] = mViewPortHandler.contentBottom(); + + mLimitLinePath.reset(); + mLimitLinePath.moveTo(mLimitLineSegmentsBuffer[0], mLimitLineSegmentsBuffer[1]); + mLimitLinePath.lineTo(mLimitLineSegmentsBuffer[2], mLimitLineSegmentsBuffer[3]); + + mLimitLinePaint.setStyle(Paint.Style.STROKE); + mLimitLinePaint.setColor(limitLine.getLineColor()); + mLimitLinePaint.setStrokeWidth(limitLine.getLineWidth()); + mLimitLinePaint.setPathEffect(limitLine.getDashPathEffect()); + + c.drawPath(mLimitLinePath, mLimitLinePaint); + } + + public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset) { + String label = limitLine.getLabel(); + + // if drawing the limit-value label is enabled + if (label != null && !label.equals("")) { + + mLimitLinePaint.setStyle(limitLine.getTextStyle()); + mLimitLinePaint.setPathEffect(null); + mLimitLinePaint.setColor(limitLine.getTextColor()); + mLimitLinePaint.setStrokeWidth(0.5f); + mLimitLinePaint.setTextSize(limitLine.getTextSize()); + + + float xOffset = limitLine.getLineWidth() + limitLine.getXOffset(); + + final LimitLine.LimitLabelPosition labelPosition = limitLine.getLabelPosition(); + + if (labelPosition == LimitLine.LimitLabelPosition.RIGHT_TOP) { + + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, position[0] + xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, + mLimitLinePaint); + } else if (labelPosition == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, position[0] + xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint); + } else if (labelPosition == LimitLine.LimitLabelPosition.LEFT_TOP) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + c.drawText(label, position[0] - xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, + mLimitLinePaint); + } else { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, position[0] - xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint); + } + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java new file mode 100644 index 0000000000..86047cf1b8 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -0,0 +1,310 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Path; +import android.graphics.RectF; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.LimitLine; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class XAxisRendererHorizontalBarChart extends XAxisRenderer { + + protected BarChart mChart; + + public XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, + Transformer trans, BarChart chart) { + super(viewPortHandler, xAxis, trans); + + this.mChart = chart; + } + + @Override + public void computeAxis(float min, float max, boolean inverted) { + + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { + + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + + if (inverted) { + + min = (float) p2.y; + max = (float) p1.y; + } else { + + min = (float) p1.y; + max = (float) p2.y; + } + + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); + } + + computeAxisValues(min, max); + } + + @Override + protected void computeSize() { + + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + + String longest = mXAxis.getLongestLabel(); + + final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, longest); + + final float labelWidth = (int)(labelSize.width + mXAxis.getXOffset() * 3.5f); + final float labelHeight = labelSize.height; + + final FSize labelRotatedSize = Utils.getSizeOfRotatedRectangleByDegrees( + labelSize.width, + labelHeight, + mXAxis.getLabelRotationAngle()); + + mXAxis.mLabelWidth = Math.round(labelWidth); + mXAxis.mLabelHeight = Math.round(labelHeight); + mXAxis.mLabelRotatedWidth = (int)(labelRotatedSize.width + mXAxis.getXOffset() * 3.5f); + mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); + + FSize.recycleInstance(labelRotatedSize); + } + + @Override + public void renderAxisLabels(Canvas c) { + + if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) + return; + + float xoffset = mXAxis.getXOffset(); + + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + mAxisLabelPaint.setColor(mXAxis.getTextColor()); + + MPPointF pointF = MPPointF.getInstance(0,0); + + if (mXAxis.getPosition() == XAxisPosition.TOP) { + pointF.x = 0.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentRight() + xoffset, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentRight() - xoffset, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() + xoffset, pointF); + + } else { // BOTH SIDED + pointF.x = 0.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentRight() + xoffset, pointF); + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF); + } + + MPPointF.recycleInstance(pointF); + } + + @Override + protected void drawLabels(Canvas c, float pos, MPPointF anchor) { + + final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); + boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); + + float[] positions = new float[mXAxis.mEntryCount * 2]; + + for (int i = 0; i < positions.length; i += 2) { + + // only fill x values + if (centeringEnabled) { + positions[i + 1] = mXAxis.mCenteredEntries[i / 2]; + } else { + positions[i + 1] = mXAxis.mEntries[i / 2]; + } + } + + mTrans.pointValuesToPixel(positions); + + for (int i = 0; i < positions.length; i += 2) { + + float y = positions[i + 1]; + + if (mViewPortHandler.isInBoundsY(y)) { + + String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); + drawLabel(c, label, pos, y, anchor, labelRotationAngleDegrees); + } + } + } + + @Override + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth()); + return mGridClippingRect; + } + + @Override + protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { + + gridLinePath.moveTo(mViewPortHandler.contentRight(), y); + gridLinePath.lineTo(mViewPortHandler.contentLeft(), y); + + // draw a path because lines don't support dashing on lower android versions + c.drawPath(gridLinePath, mGridPaint); + + gridLinePath.reset(); + } + + @Override + public void renderAxisLine(Canvas c) { + + if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled()) + return; + + mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); + mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); + + if (mXAxis.getPosition() == XAxisPosition.TOP + || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE + || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { + c.drawLine(mViewPortHandler.contentRight(), + mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } + + if (mXAxis.getPosition() == XAxisPosition.BOTTOM + || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE + || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { + c.drawLine(mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop(), mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } + } + + protected Path mRenderLimitLinesPathBuffer = new Path(); + /** + * Draws the LimitLines associated with this axis to the screen. + * This is the standard YAxis renderer using the XAxis limit lines. + * + * @param c + */ + @Override + public void renderLimitLines(Canvas c) { + + List limitLines = mXAxis.getLimitLines(); + + if (limitLines == null || limitLines.size() <= 0) + return; + + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; + + Path limitLinePath = mRenderLimitLinesPathBuffer; + limitLinePath.reset(); + + for (int i = 0; i < limitLines.size(); i++) { + + LimitLine l = limitLines.get(i); + + if(!l.isEnabled()) + continue; + + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(0.f, -l.getLineWidth()); + c.clipRect(mLimitLineClippingRect); + + mLimitLinePaint.setStyle(Paint.Style.STROKE); + mLimitLinePaint.setColor(l.getLineColor()); + mLimitLinePaint.setStrokeWidth(l.getLineWidth()); + mLimitLinePaint.setPathEffect(l.getDashPathEffect()); + + pts[1] = l.getLimit(); + + mTrans.pointValuesToPixel(pts); + + limitLinePath.moveTo(mViewPortHandler.contentLeft(), pts[1]); + limitLinePath.lineTo(mViewPortHandler.contentRight(), pts[1]); + + c.drawPath(limitLinePath, mLimitLinePaint); + limitLinePath.reset(); + // c.drawLines(pts, mLimitLinePaint); + + String label = l.getLabel(); + + // if drawing the limit-value label is enabled + if (label != null && !label.equals("")) { + + mLimitLinePaint.setStyle(l.getTextStyle()); + mLimitLinePaint.setPathEffect(null); + mLimitLinePaint.setColor(l.getTextColor()); + mLimitLinePaint.setStrokeWidth(0.5f); + mLimitLinePaint.setTextSize(l.getTextSize()); + + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); + float yOffset = l.getLineWidth() + labelLineHeight + l.getYOffset(); + + final LimitLine.LimitLabelPosition position = l.getLabelPosition(); + + if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, + mViewPortHandler.contentRight() - xOffset, + pts[1] - yOffset + labelLineHeight, mLimitLinePaint); + + } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, + mViewPortHandler.contentRight() - xOffset, + pts[1] + yOffset, mLimitLinePaint); + + } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, + mViewPortHandler.contentLeft() + xOffset, + pts[1] - yOffset + labelLineHeight, mLimitLinePaint); + + } else { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, + mViewPortHandler.offsetLeft() + xOffset, + pts[1] + yOffset, mLimitLinePaint); + } + } + + c.restoreToCount(clipRestoreCount); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java new file mode 100644 index 0000000000..956e8c7d5c --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java @@ -0,0 +1,71 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.PointF; + +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +public class XAxisRendererRadarChart extends XAxisRenderer { + + private RadarChart mChart; + + public XAxisRendererRadarChart(ViewPortHandler viewPortHandler, XAxis xAxis, RadarChart chart) { + super(viewPortHandler, xAxis, null); + + mChart = chart; + } + + @Override + public void renderAxisLabels(Canvas c) { + + if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) + return; + + final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); + final MPPointF drawLabelAnchor = MPPointF.getInstance(0.5f, 0.25f); + + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + mAxisLabelPaint.setColor(mXAxis.getTextColor()); + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + for (int i = 0; i < mChart.getData().getMaxEntryCountSet().getEntryCount(); i++) { + + String label = mXAxis.getValueFormatter().getFormattedValue(i, mXAxis); + + float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; + + Utils.getPosition(center, mChart.getYRange() * factor + + mXAxis.mLabelRotatedWidth / 2f, angle, pOut); + + drawLabel(c, label, pOut.x, pOut.y - mXAxis.mLabelRotatedHeight / 2.f, + drawLabelAnchor, labelRotationAngleDegrees); + } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + MPPointF.recycleInstance(drawLabelAnchor); + } + + /** + * XAxis LimitLines on RadarChart not yet supported. + * + * @param c + */ + @Override + public void renderLimitLines(Canvas c) { + // this space intentionally left blank + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java new file mode 100644 index 0000000000..a2bf679777 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -0,0 +1,347 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Path; +import android.graphics.RectF; + +import com.github.mikephil.charting.components.LimitLine; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class YAxisRenderer extends AxisRenderer { + + protected YAxis mYAxis; + + protected Paint mZeroLinePaint; + + public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer trans) { + super(viewPortHandler, trans, yAxis); + + this.mYAxis = yAxis; + + if(mViewPortHandler != null) { + + mAxisLabelPaint.setColor(Color.BLACK); + mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); + + mZeroLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mZeroLinePaint.setColor(Color.GRAY); + mZeroLinePaint.setStrokeWidth(1f); + mZeroLinePaint.setStyle(Paint.Style.STROKE); + } + } + + /** + * draws the y-axis labels to the screen + */ + @Override + public void renderAxisLabels(Canvas c) { + + if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) + return; + + float[] positions = getTransformedPositions(); + + mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); + mAxisLabelPaint.setColor(mYAxis.getTextColor()); + + float xoffset = mYAxis.getXOffset(); + float yoffset = Utils.calcTextHeight(mAxisLabelPaint, "A") / 2.5f + mYAxis.getYOffset(); + + AxisDependency dependency = mYAxis.getAxisDependency(); + YAxisLabelPosition labelPosition = mYAxis.getLabelPosition(); + + float xPos = 0f; + + if (dependency == AxisDependency.LEFT) { + + if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { + mAxisLabelPaint.setTextAlign(Align.RIGHT); + xPos = mViewPortHandler.offsetLeft() - xoffset; + } else { + mAxisLabelPaint.setTextAlign(Align.LEFT); + xPos = mViewPortHandler.offsetLeft() + xoffset; + } + + } else { + + if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { + mAxisLabelPaint.setTextAlign(Align.LEFT); + xPos = mViewPortHandler.contentRight() + xoffset; + } else { + mAxisLabelPaint.setTextAlign(Align.RIGHT); + xPos = mViewPortHandler.contentRight() - xoffset; + } + } + + drawYLabels(c, xPos, positions, yoffset); + } + + @Override + public void renderAxisLine(Canvas c) { + + if (!mYAxis.isEnabled() || !mYAxis.isDrawAxisLineEnabled()) + return; + + mAxisLinePaint.setColor(mYAxis.getAxisLineColor()); + mAxisLinePaint.setStrokeWidth(mYAxis.getAxisLineWidth()); + + if (mYAxis.getAxisDependency() == AxisDependency.LEFT) { + c.drawLine(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } else { + c.drawLine(mViewPortHandler.contentRight(), mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } + } + + /** + * draws the y-labels on the specified x-position + * + * @param fixedPosition + * @param positions + */ + protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) { + + final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; + final int to = mYAxis.isDrawTopYLabelEntryEnabled() + ? mYAxis.mEntryCount + : (mYAxis.mEntryCount - 1); + + // draw + for (int i = from; i < to; i++) { + + String text = mYAxis.getFormattedLabel(i); + + c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisLabelPaint); + } + } + + protected Path mRenderGridLinesPath = new Path(); + @Override + public void renderGridLines(Canvas c) { + + if (!mYAxis.isEnabled()) + return; + + if (mYAxis.isDrawGridLinesEnabled()) { + + int clipRestoreCount = c.save(); + c.clipRect(getGridClippingRect()); + + float[] positions = getTransformedPositions(); + + mGridPaint.setColor(mYAxis.getGridColor()); + mGridPaint.setStrokeWidth(mYAxis.getGridLineWidth()); + mGridPaint.setPathEffect(mYAxis.getGridDashPathEffect()); + + Path gridLinePath = mRenderGridLinesPath; + gridLinePath.reset(); + + // draw the grid + for (int i = 0; i < positions.length; i += 2) { + + // draw a path because lines don't support dashing on lower android versions + c.drawPath(linePath(gridLinePath, i, positions), mGridPaint); + gridLinePath.reset(); + } + + c.restoreToCount(clipRestoreCount); + } + + if (mYAxis.isDrawZeroLineEnabled()) { + drawZeroLine(c); + } + } + + protected RectF mGridClippingRect = new RectF(); + + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth()); + return mGridClippingRect; + } + + /** + * Calculates the path for a grid line. + * + * @param p + * @param i + * @param positions + * @return + */ + protected Path linePath(Path p, int i, float[] positions) { + + p.moveTo(mViewPortHandler.offsetLeft(), positions[i + 1]); + p.lineTo(mViewPortHandler.contentRight(), positions[i + 1]); + + return p; + } + + protected float[] mGetTransformedPositionsBuffer = new float[2]; + /** + * Transforms the values contained in the axis entries to screen pixels and returns them in form of a float array + * of x- and y-coordinates. + * + * @return + */ + protected float[] getTransformedPositions() { + + if(mGetTransformedPositionsBuffer.length != mYAxis.mEntryCount * 2){ + mGetTransformedPositionsBuffer = new float[mYAxis.mEntryCount * 2]; + } + float[] positions = mGetTransformedPositionsBuffer; + + for (int i = 0; i < positions.length; i += 2) { + // only fill y values, x values are not needed for y-labels + positions[i + 1] = mYAxis.mEntries[i / 2]; + } + + mTrans.pointValuesToPixel(positions); + return positions; + } + + protected Path mDrawZeroLinePath = new Path(); + protected RectF mZeroLineClippingRect = new RectF(); + + /** + * Draws the zero line. + */ + protected void drawZeroLine(Canvas c) { + + int clipRestoreCount = c.save(); + mZeroLineClippingRect.set(mViewPortHandler.getContentRect()); + mZeroLineClippingRect.inset(0.f, -mYAxis.getZeroLineWidth()); + c.clipRect(mZeroLineClippingRect); + + // draw zero line + MPPointD pos = mTrans.getPixelForValues(0f, 0f); + + mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); + mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); + + Path zeroLinePath = mDrawZeroLinePath; + zeroLinePath.reset(); + + zeroLinePath.moveTo(mViewPortHandler.contentLeft(), (float) pos.y); + zeroLinePath.lineTo(mViewPortHandler.contentRight(), (float) pos.y); + + // draw a path because lines don't support dashing on lower android versions + c.drawPath(zeroLinePath, mZeroLinePaint); + + c.restoreToCount(clipRestoreCount); + } + + protected Path mRenderLimitLines = new Path(); + protected float[] mRenderLimitLinesBuffer = new float[2]; + protected RectF mLimitLineClippingRect = new RectF(); + /** + * Draws the LimitLines associated with this axis to the screen. + * + * @param c + */ + @Override + public void renderLimitLines(Canvas c) { + + List limitLines = mYAxis.getLimitLines(); + + if (limitLines == null || limitLines.size() <= 0) + return; + + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; + Path limitLinePath = mRenderLimitLines; + limitLinePath.reset(); + + for (int i = 0; i < limitLines.size(); i++) { + + LimitLine l = limitLines.get(i); + + if (!l.isEnabled()) + continue; + + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(0.f, -l.getLineWidth()); + c.clipRect(mLimitLineClippingRect); + + mLimitLinePaint.setStyle(Paint.Style.STROKE); + mLimitLinePaint.setColor(l.getLineColor()); + mLimitLinePaint.setStrokeWidth(l.getLineWidth()); + mLimitLinePaint.setPathEffect(l.getDashPathEffect()); + + pts[1] = l.getLimit(); + + mTrans.pointValuesToPixel(pts); + + limitLinePath.moveTo(mViewPortHandler.contentLeft(), pts[1]); + limitLinePath.lineTo(mViewPortHandler.contentRight(), pts[1]); + + c.drawPath(limitLinePath, mLimitLinePaint); + limitLinePath.reset(); + // c.drawLines(pts, mLimitLinePaint); + + String label = l.getLabel(); + + // if drawing the limit-value label is enabled + if (label != null && !label.equals("")) { + + mLimitLinePaint.setStyle(l.getTextStyle()); + mLimitLinePaint.setPathEffect(null); + mLimitLinePaint.setColor(l.getTextColor()); + mLimitLinePaint.setTypeface(l.getTypeface()); + mLimitLinePaint.setStrokeWidth(0.5f); + mLimitLinePaint.setTextSize(l.getTextSize()); + + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); + float yOffset = l.getLineWidth() + labelLineHeight + l.getYOffset(); + + final LimitLine.LimitLabelPosition position = l.getLabelPosition(); + + if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, + mViewPortHandler.contentRight() - xOffset, + pts[1] - yOffset + labelLineHeight, mLimitLinePaint); + + } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, + mViewPortHandler.contentRight() - xOffset, + pts[1] + yOffset, mLimitLinePaint); + + } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, + mViewPortHandler.contentLeft() + xOffset, + pts[1] - yOffset + labelLineHeight, mLimitLinePaint); + + } else { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, + mViewPortHandler.offsetLeft() + xOffset, + pts[1] + yOffset, mLimitLinePaint); + } + } + + c.restoreToCount(clipRestoreCount); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java new file mode 100644 index 0000000000..71275b03c3 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -0,0 +1,310 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Path; +import android.graphics.RectF; + +import com.github.mikephil.charting.components.LimitLine; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class YAxisRendererHorizontalBarChart extends YAxisRenderer { + + public YAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, YAxis yAxis, + Transformer trans) { + super(viewPortHandler, yAxis, trans); + + mLimitLinePaint.setTextAlign(Align.LEFT); + } + + /** + * Computes the axis values. + * + * @param yMin - the minimum y-value in the data object for this axis + * @param yMax - the maximum y-value in the data object for this axis + */ + @Override + public void computeAxis(float yMin, float yMax, boolean inverted) { + + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler.contentHeight() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { + + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), + mViewPortHandler.contentTop()); + + if (!inverted) { + yMin = (float) p1.x; + yMax = (float) p2.x; + } else { + yMin = (float) p2.x; + yMax = (float) p1.x; + } + + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); + } + + computeAxisValues(yMin, yMax); + } + + /** + * draws the y-axis labels to the screen + */ + @Override + public void renderAxisLabels(Canvas c) { + + if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) + return; + + float[] positions = getTransformedPositions(); + + mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); + mAxisLabelPaint.setColor(mYAxis.getTextColor()); + mAxisLabelPaint.setTextAlign(Align.CENTER); + + float baseYOffset = Utils.convertDpToPixel(2.5f); + float textHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q"); + + AxisDependency dependency = mYAxis.getAxisDependency(); + YAxisLabelPosition labelPosition = mYAxis.getLabelPosition(); + + float yPos = 0f; + + if (dependency == AxisDependency.LEFT) { + + if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { + yPos = mViewPortHandler.contentTop() - baseYOffset; + } else { + yPos = mViewPortHandler.contentTop() - baseYOffset; + } + + } else { + + if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { + yPos = mViewPortHandler.contentBottom() + textHeight + baseYOffset; + } else { + yPos = mViewPortHandler.contentBottom() + textHeight + baseYOffset; + } + } + + drawYLabels(c, yPos, positions, mYAxis.getYOffset()); + } + + @Override + public void renderAxisLine(Canvas c) { + + if (!mYAxis.isEnabled() || !mYAxis.isDrawAxisLineEnabled()) + return; + + mAxisLinePaint.setColor(mYAxis.getAxisLineColor()); + mAxisLinePaint.setStrokeWidth(mYAxis.getAxisLineWidth()); + + if (mYAxis.getAxisDependency() == AxisDependency.LEFT) { + c.drawLine(mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), + mViewPortHandler.contentTop(), mAxisLinePaint); + } else { + c.drawLine(mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } + } + + /** + * draws the y-labels on the specified x-position + * + * @param fixedPosition + * @param positions + */ + @Override + protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) { + + mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); + mAxisLabelPaint.setColor(mYAxis.getTextColor()); + + final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; + final int to = mYAxis.isDrawTopYLabelEntryEnabled() + ? mYAxis.mEntryCount + : (mYAxis.mEntryCount - 1); + + for (int i = from; i < to; i++) { + + String text = mYAxis.getFormattedLabel(i); + + c.drawText(text, positions[i * 2], fixedPosition - offset, mAxisLabelPaint); + } + } + + @Override + protected float[] getTransformedPositions() { + + if(mGetTransformedPositionsBuffer.length != mYAxis.mEntryCount * 2) { + mGetTransformedPositionsBuffer = new float[mYAxis.mEntryCount * 2]; + } + float[] positions = mGetTransformedPositionsBuffer; + + for (int i = 0; i < positions.length; i += 2) { + // only fill x values, y values are not needed for x-labels + positions[i] = mYAxis.mEntries[i / 2]; + } + + mTrans.pointValuesToPixel(positions); + return positions; + } + + @Override + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f); + return mGridClippingRect; + } + + @Override + protected Path linePath(Path p, int i, float[] positions) { + + p.moveTo(positions[i], mViewPortHandler.contentTop()); + p.lineTo(positions[i], mViewPortHandler.contentBottom()); + + return p; + } + + protected Path mDrawZeroLinePathBuffer = new Path(); + + @Override + protected void drawZeroLine(Canvas c) { + + int clipRestoreCount = c.save(); + mZeroLineClippingRect.set(mViewPortHandler.getContentRect()); + mZeroLineClippingRect.inset(-mYAxis.getZeroLineWidth(), 0.f); + c.clipRect(mLimitLineClippingRect); + + // draw zero line + MPPointD pos = mTrans.getPixelForValues(0f, 0f); + + mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); + mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); + + Path zeroLinePath = mDrawZeroLinePathBuffer; + zeroLinePath.reset(); + + zeroLinePath.moveTo((float) pos.x - 1, mViewPortHandler.contentTop()); + zeroLinePath.lineTo((float) pos.x - 1, mViewPortHandler.contentBottom()); + + // draw a path because lines don't support dashing on lower android versions + c.drawPath(zeroLinePath, mZeroLinePaint); + + c.restoreToCount(clipRestoreCount); + } + + protected Path mRenderLimitLinesPathBuffer = new Path(); + protected float[] mRenderLimitLinesBuffer = new float[4]; + /** + * Draws the LimitLines associated with this axis to the screen. + * This is the standard XAxis renderer using the YAxis limit lines. + * + * @param c + */ + @Override + public void renderLimitLines(Canvas c) { + + List limitLines = mYAxis.getLimitLines(); + + if (limitLines == null || limitLines.size() <= 0) + return; + + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; + pts[2] = 0; + pts[3] = 0; + Path limitLinePath = mRenderLimitLinesPathBuffer; + limitLinePath.reset(); + + for (int i = 0; i < limitLines.size(); i++) { + + LimitLine l = limitLines.get(i); + + if (!l.isEnabled()) + continue; + + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(-l.getLineWidth(), 0.f); + c.clipRect(mLimitLineClippingRect); + + pts[0] = l.getLimit(); + pts[2] = l.getLimit(); + + mTrans.pointValuesToPixel(pts); + + pts[1] = mViewPortHandler.contentTop(); + pts[3] = mViewPortHandler.contentBottom(); + + limitLinePath.moveTo(pts[0], pts[1]); + limitLinePath.lineTo(pts[2], pts[3]); + + mLimitLinePaint.setStyle(Paint.Style.STROKE); + mLimitLinePaint.setColor(l.getLineColor()); + mLimitLinePaint.setPathEffect(l.getDashPathEffect()); + mLimitLinePaint.setStrokeWidth(l.getLineWidth()); + + c.drawPath(limitLinePath, mLimitLinePaint); + limitLinePath.reset(); + + String label = l.getLabel(); + + // if drawing the limit-value label is enabled + if (label != null && !label.equals("")) { + + mLimitLinePaint.setStyle(l.getTextStyle()); + mLimitLinePaint.setPathEffect(null); + mLimitLinePaint.setColor(l.getTextColor()); + mLimitLinePaint.setTypeface(l.getTypeface()); + mLimitLinePaint.setStrokeWidth(0.5f); + mLimitLinePaint.setTextSize(l.getTextSize()); + + float xOffset = l.getLineWidth() + l.getXOffset(); + float yOffset = Utils.convertDpToPixel(2f) + l.getYOffset(); + + final LimitLine.LimitLabelPosition position = l.getLabelPosition(); + + if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { + + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, pts[0] + xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, mLimitLinePaint); + } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, pts[0] + xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint); + } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + c.drawText(label, pts[0] - xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, mLimitLinePaint); + } else { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, pts[0] - xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint); + } + } + + c.restoreToCount(clipRestoreCount); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java new file mode 100644 index 0000000000..ee7392e928 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -0,0 +1,228 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.PointF; + +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.components.LimitLine; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class YAxisRendererRadarChart extends YAxisRenderer { + + private RadarChart mChart; + + public YAxisRendererRadarChart(ViewPortHandler viewPortHandler, YAxis yAxis, RadarChart chart) { + super(viewPortHandler, yAxis, null); + + this.mChart = chart; + } + + @Override + protected void computeAxisValues(float min, float max) { + + float yMin = min; + float yMax = max; + + int labelCount = mAxis.getLabelCount(); + double range = Math.abs(yMax - yMin); + + if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { + mAxis.mEntries = new float[]{}; + mAxis.mCenteredEntries = new float[]{}; + mAxis.mEntryCount = 0; + return; + } + + // Find out how much spacing (in y value space) between axis values + double rawInterval = range / labelCount; + double interval = Utils.roundToNextSignificant(rawInterval); + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if (mAxis.isGranularityEnabled()) + interval = interval < mAxis.getGranularity() ? mAxis.getGranularity() : interval; + + // Normalize interval + double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); + int intervalSigDigit = (int) (interval / intervalMagnitude); + if (intervalSigDigit > 5) { + // Use one order of magnitude higher, to avoid intervals like 0.9 or + // 90 + interval = Math.floor(10 * intervalMagnitude); + } + + boolean centeringEnabled = mAxis.isCenterAxisLabelsEnabled(); + int n = centeringEnabled ? 1 : 0; + + // force label count + if (mAxis.isForceLabelsEnabled()) { + + float step = (float) range / (float) (labelCount - 1); + mAxis.mEntryCount = labelCount; + + if (mAxis.mEntries.length < labelCount) { + // Ensure stops contains at least numStops elements. + mAxis.mEntries = new float[labelCount]; + } + + float v = min; + + for (int i = 0; i < labelCount; i++) { + mAxis.mEntries[i] = v; + v += step; + } + + n = labelCount; + + // no forced count + } else { + + double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; + if (centeringEnabled) { + first -= interval; + } + + double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); + + double f; + int i; + + if (interval != 0.0) { + for (f = first; f <= last; f += interval) { + ++n; + } + } + + n++; + + mAxis.mEntryCount = n; + + if (mAxis.mEntries.length < n) { + // Ensure stops contains at least numStops elements. + mAxis.mEntries = new float[n]; + } + + for (f = first, i = 0; i < n; f += interval, ++i) { + + if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) + f = 0.0; + + mAxis.mEntries[i] = (float) f; + } + } + + // set decimals + if (interval < 1) { + mAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); + } else { + mAxis.mDecimals = 0; + } + + if (centeringEnabled) { + + if (mAxis.mCenteredEntries.length < n) { + mAxis.mCenteredEntries = new float[n]; + } + + float offset = (mAxis.mEntries[1] - mAxis.mEntries[0]) / 2f; + + for (int i = 0; i < n; i++) { + mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; + } + } + + mAxis.mAxisMinimum = mAxis.mEntries[0]; + mAxis.mAxisMaximum = mAxis.mEntries[n-1]; + mAxis.mAxisRange = Math.abs(mAxis.mAxisMaximum - mAxis.mAxisMinimum); + } + + @Override + public void renderAxisLabels(Canvas c) { + + if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) + return; + + mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); + mAxisLabelPaint.setColor(mYAxis.getTextColor()); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + float factor = mChart.getFactor(); + + final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; + final int to = mYAxis.isDrawTopYLabelEntryEnabled() + ? mYAxis.mEntryCount + : (mYAxis.mEntryCount - 1); + + for (int j = from; j < to; j++) { + + float r = (mYAxis.mEntries[j] - mYAxis.mAxisMinimum) * factor; + + Utils.getPosition(center, r, mChart.getRotationAngle(), pOut); + + String label = mYAxis.getFormattedLabel(j); + + c.drawText(label, pOut.x + 10, pOut.y, mAxisLabelPaint); + } + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + } + + private Path mRenderLimitLinesPathBuffer = new Path(); + @Override + public void renderLimitLines(Canvas c) { + + List limitLines = mYAxis.getLimitLines(); + + if (limitLines == null) + return; + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + for (int i = 0; i < limitLines.size(); i++) { + + LimitLine l = limitLines.get(i); + + if (!l.isEnabled()) + continue; + + mLimitLinePaint.setColor(l.getLineColor()); + mLimitLinePaint.setPathEffect(l.getDashPathEffect()); + mLimitLinePaint.setStrokeWidth(l.getLineWidth()); + + float r = (l.getLimit() - mChart.getYChartMin()) * factor; + + Path limitPath = mRenderLimitLinesPathBuffer; + limitPath.reset(); + + + for (int j = 0; j < mChart.getData().getMaxEntryCountSet().getEntryCount(); j++) { + + Utils.getPosition(center, r, sliceangle * j + mChart.getRotationAngle(), pOut); + + if (j == 0) + limitPath.moveTo(pOut.x, pOut.y); + else + limitPath.lineTo(pOut.x, pOut.y); + } + limitPath.close(); + + c.drawPath(limitPath, mLimitLinePaint); + } + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java new file mode 100644 index 0000000000..9328b276c4 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java @@ -0,0 +1,41 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class ChevronDownShapeRenderer implements IShapeRenderer +{ + + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; + + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + c.drawLine( + posX, + posY + (2 * shapeHalf), + posX + (2 * shapeHalf), + posY, + renderPaint); + + c.drawLine( + posX, + posY + (2 * shapeHalf), + posX - (2 * shapeHalf), + posY, + renderPaint); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java new file mode 100644 index 0000000000..6dea0abf7b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java @@ -0,0 +1,42 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class ChevronUpShapeRenderer implements IShapeRenderer +{ + + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; + + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + c.drawLine( + posX, + posY - (2 * shapeHalf), + posX + (2 * shapeHalf), + posY, + renderPaint); + + c.drawLine( + posX, + posY - (2 * shapeHalf), + posX - (2 * shapeHalf), + posY, + renderPaint); + + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java new file mode 100644 index 0000000000..ac7abb92de --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java @@ -0,0 +1,63 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class CircleShapeRenderer implements IShapeRenderer +{ + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeSize = dataSet.getScatterShapeSize(); + final float shapeHalf = shapeSize / 2f; + final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); + final float shapeHoleSize = shapeHoleSizeHalf * 2.f; + final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; + final float shapeStrokeSizeHalf = shapeStrokeSize / 2.f; + + final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); + + if (shapeSize > 0.0) { + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(shapeStrokeSize); + + c.drawCircle( + posX, + posY, + shapeHoleSizeHalf + shapeStrokeSizeHalf, + renderPaint); + + if (shapeHoleColor != ColorTemplate.COLOR_NONE) { + renderPaint.setStyle(Paint.Style.FILL); + + renderPaint.setColor(shapeHoleColor); + c.drawCircle( + posX, + posY, + shapeHoleSizeHalf, + renderPaint); + } + } else { + renderPaint.setStyle(Paint.Style.FILL); + + c.drawCircle( + posX, + posY, + shapeHalf, + renderPaint); + } + + } + +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java new file mode 100644 index 0000000000..202670d6ba --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java @@ -0,0 +1,41 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class CrossShapeRenderer implements IShapeRenderer +{ + + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; + + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + c.drawLine( + posX - shapeHalf, + posY, + posX + shapeHalf, + posY, + renderPaint); + c.drawLine( + posX, + posY - shapeHalf, + posX, + posY + shapeHalf, + renderPaint); + + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java new file mode 100644 index 0000000000..20b57a900d --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java @@ -0,0 +1,28 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:07 + */ +public interface IShapeRenderer +{ + + /** + * Renders the provided ScatterDataSet with a shape. + * + * @param c Canvas object for drawing the shape + * @param dataSet The DataSet to be drawn + * @param viewPortHandler Contains information about the current state of the view + * @param posX Position to draw the shape at + * @param posY Position to draw the shape at + * @param renderPaint Paint object used for styling and drawing + */ + void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java new file mode 100644 index 0000000000..ac98679233 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java @@ -0,0 +1,63 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class SquareShapeRenderer implements IShapeRenderer +{ + + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeSize = dataSet.getScatterShapeSize(); + final float shapeHalf = shapeSize / 2f; + final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); + final float shapeHoleSize = shapeHoleSizeHalf * 2.f; + final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; + final float shapeStrokeSizeHalf = shapeStrokeSize / 2.f; + + final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); + + if (shapeSize > 0.0) { + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(shapeStrokeSize); + + c.drawRect(posX - shapeHoleSizeHalf - shapeStrokeSizeHalf, + posY - shapeHoleSizeHalf - shapeStrokeSizeHalf, + posX + shapeHoleSizeHalf + shapeStrokeSizeHalf, + posY + shapeHoleSizeHalf + shapeStrokeSizeHalf, + renderPaint); + + if (shapeHoleColor != ColorTemplate.COLOR_NONE) { + renderPaint.setStyle(Paint.Style.FILL); + + renderPaint.setColor(shapeHoleColor); + c.drawRect(posX - shapeHoleSizeHalf, + posY - shapeHoleSizeHalf, + posX + shapeHoleSizeHalf, + posY + shapeHoleSizeHalf, + renderPaint); + } + + } else { + renderPaint.setStyle(Paint.Style.FILL); + + c.drawRect(posX - shapeHalf, + posY - shapeHalf, + posX + shapeHalf, + posY + shapeHalf, + renderPaint); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java new file mode 100644 index 0000000000..5343454bbb --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java @@ -0,0 +1,80 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class TriangleShapeRenderer implements IShapeRenderer +{ + + protected Path mTrianglePathBuffer = new Path(); + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeSize = dataSet.getScatterShapeSize(); + final float shapeHalf = shapeSize / 2f; + final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); + final float shapeHoleSize = shapeHoleSizeHalf * 2.f; + final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; + + final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); + + renderPaint.setStyle(Paint.Style.FILL); + + // create a triangle path + Path tri = mTrianglePathBuffer; + tri.reset(); + + tri.moveTo(posX, posY - shapeHalf); + tri.lineTo(posX + shapeHalf, posY + shapeHalf); + tri.lineTo(posX - shapeHalf, posY + shapeHalf); + + if (shapeSize > 0.0) { + tri.lineTo(posX, posY - shapeHalf); + + tri.moveTo(posX - shapeHalf + shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.lineTo(posX + shapeHalf - shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.lineTo(posX, + posY - shapeHalf + shapeStrokeSize); + tri.lineTo(posX - shapeHalf + shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + } + + tri.close(); + + c.drawPath(tri, renderPaint); + tri.reset(); + + if (shapeSize > 0.0 && + shapeHoleColor != ColorTemplate.COLOR_NONE) { + + renderPaint.setColor(shapeHoleColor); + + tri.moveTo(posX, + posY - shapeHalf + shapeStrokeSize); + tri.lineTo(posX + shapeHalf - shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.lineTo(posX - shapeHalf + shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.close(); + + c.drawPath(tri, renderPaint); + tri.reset(); + } + + } + +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java new file mode 100644 index 0000000000..225640ec8e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java @@ -0,0 +1,42 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class XShapeRenderer implements IShapeRenderer +{ + + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; + + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + c.drawLine( + posX - shapeHalf, + posY - shapeHalf, + posX + shapeHalf, + posY + shapeHalf, + renderPaint); + c.drawLine( + posX + shapeHalf, + posY - shapeHalf, + posX - shapeHalf, + posY + shapeHalf, + renderPaint); + + } + +} \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java similarity index 62% rename from MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java index 4ddec02668..4d9c1de790 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java @@ -5,16 +5,28 @@ import android.graphics.Color; import java.util.ArrayList; +import java.util.List; /** * Class that holds predefined color integer arrays (e.g. * ColorTemplate.VORDIPLOM_COLORS) and convenience methods for loading colors * from resources. - * + * * @author Philipp Jahoda */ public class ColorTemplate { + /** + * an "invalid" color that indicates that no color is set + */ + public static final int COLOR_NONE = 0x00112233; + + /** + * this "color" is used for the Legend creation and indicates that the next + * form should be skipped + */ + public static final int COLOR_SKIP = 0x00112234; + /** * THE COLOR THEMES ARE PREDEFINED (predefined color integer arrays), FEEL * FREE TO CREATE YOUR OWN WITH AS MANY DIFFERENT COLORS AS YOU WANT @@ -39,40 +51,55 @@ public class ColorTemplate { Color.rgb(192, 255, 140), Color.rgb(255, 247, 140), Color.rgb(255, 208, 140), Color.rgb(140, 234, 255), Color.rgb(255, 140, 157) }; + public static final int[] MATERIAL_COLORS = { + rgb("#2ecc71"), rgb("#f1c40f"), rgb("#e74c3c"), rgb("#3498db") + }; - // public static final int[] FRESH_COLORS = { - // R.color.fresh_1, R.color.fresh_2, R.color.fresh_3, R.color.fresh_4, - // R.color.fresh_5 - // }; - // public static final int[] MONO_COLORS = { - // R.color.mono_1, R.color.mono_2, R.color.mono_3, R.color.mono_4, - // R.color.mono_5 - // }; - // public static final int[] GREEN_COLORS = { - // R.color.greens_1, R.color.greens_2, R.color.greens_3, R.color.greens_4, - // R.color.greens_5 - // }; + /** + * Converts the given hex-color-string to rgb. + * + * @param hex + * @return + */ + public static int rgb(String hex) { + int color = (int) Long.parseLong(hex.replace("#", ""), 16); + int r = (color >> 16) & 0xFF; + int g = (color >> 8) & 0xFF; + int b = (color >> 0) & 0xFF; + return Color.rgb(r, g, b); + } /** * Returns the Android ICS holo blue light color. - * + * * @return */ public static int getHoloBlue() { return Color.rgb(51, 181, 229); } + /** + * Sets the alpha component of the given color. + * + * @param color + * @param alpha 0 - 255 + * @return + */ + public static int colorWithAlpha(int color, int alpha) { + return (color & 0xffffff) | ((alpha & 0xff) << 24); + } + /** * turn an array of resource-colors (contains resource-id integers) into an * array list of actual color integers - * + * * @param r * @param colors an integer array of resource id's of colors * @return */ - public static ArrayList createColors(Resources r, int[] colors) { + public static List createColors(Resources r, int[] colors) { - ArrayList result = new ArrayList(); + List result = new ArrayList(); for (int i : colors) { result.add(r.getColor(i)); @@ -84,13 +111,13 @@ public static ArrayList createColors(Resources r, int[] colors) { /** * Turns an array of colors (integer color values) into an ArrayList of * colors. - * + * * @param colors * @return */ - public static ArrayList createColors(int[] colors) { + public static List createColors(int[] colors) { - ArrayList result = new ArrayList(); + List result = new ArrayList(); for (int i : colors) { result.add(i); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXComparator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXComparator.java new file mode 100644 index 0000000000..8f59c12d07 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXComparator.java @@ -0,0 +1,22 @@ +package com.github.mikephil.charting.utils; + +import com.github.mikephil.charting.data.Entry; + +import java.util.Comparator; + +/** + * Comparator for comparing Entry-objects by their x-value. + * Created by philipp on 17/06/15. + */ +public class EntryXComparator implements Comparator { + @Override + public int compare(Entry entry1, Entry entry2) { + float diff = entry1.getX() - entry2.getX(); + + if (diff == 0f) return 0; + else { + if (diff > 0f) return 1; + else return -1; + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java new file mode 100644 index 0000000000..a12bc45918 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java @@ -0,0 +1,79 @@ + +package com.github.mikephil.charting.utils; + +import java.util.List; + +/** + * Class for describing width and height dimensions in some arbitrary + * unit. Replacement for the android.Util.SizeF which is available only on API >= 21. + */ +public final class FSize extends ObjectPool.Poolable{ + + // TODO : Encapsulate width & height + + public float width; + public float height; + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(256, new FSize(0,0)); + pool.setReplenishPercentage(0.5f); + } + + + protected ObjectPool.Poolable instantiate(){ + return new FSize(0,0); + } + + public static FSize getInstance(final float width, final float height){ + FSize result = pool.get(); + result.width = width; + result.height = height; + return result; + } + + public static void recycleInstance(FSize instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + public FSize() { + } + + public FSize(final float width, final float height) { + this.width = width; + this.height = height; + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj instanceof FSize) { + final FSize other = (FSize) obj; + return width == other.width && height == other.height; + } + return false; + } + + @Override + public String toString() { + return width + "x" + height; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Float.floatToIntBits(width) ^ Float.floatToIntBits(height); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java similarity index 65% rename from MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java index d5958b0d5c..5aff51ff84 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java @@ -16,6 +16,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.List; /** * Utilities class for interacting with the assets and the devices storage to @@ -33,14 +34,14 @@ public class FileUtils { * @param path the name of the file on the sd-card (+ path if needed) * @return */ - public static ArrayList loadEntriesFromFile(String path) { + public static List loadEntriesFromFile(String path) { File sdcard = Environment.getExternalStorageDirectory(); // Get the text file File file = new File(sdcard, path); - ArrayList entries = new ArrayList(); + List entries = new ArrayList(); try { @SuppressWarnings("resource") @@ -49,18 +50,18 @@ public static ArrayList loadEntriesFromFile(String path) { while ((line = br.readLine()) != null) { String[] split = line.split("#"); - - if(split.length <= 2) { + + if (split.length <= 2) { entries.add(new Entry(Float.parseFloat(split[0]), Integer.parseInt(split[1]))); } else { - + float[] vals = new float[split.length - 1]; - - for(int i = 0; i < vals.length; i++) { + + for (int i = 0; i < vals.length; i++) { vals[i] = Float.parseFloat(split[i]); - } - - entries.add(new BarEntry(vals, Integer.parseInt(split[split.length - 1]))); + } + + entries.add(new BarEntry(Integer.parseInt(split[split.length - 1]), vals)); } } } catch (IOException e) { @@ -74,7 +75,7 @@ public static ArrayList loadEntriesFromFile(String path) { // // Get the text file // File file = new File(sdcard, path); // - // ArrayList entries = new ArrayList(); + // List entries = new ArrayList(); // String label = ""; // // try { @@ -105,9 +106,9 @@ public static ArrayList loadEntriesFromFile(String path) { * @param path the name of the file in the assets folder (+ path if needed) * @return */ - public static ArrayList loadEntriesFromAssets(AssetManager am, String path) { + public static List loadEntriesFromAssets(AssetManager am, String path) { - ArrayList entries = new ArrayList(); + List entries = new ArrayList(); BufferedReader reader = null; try { @@ -119,18 +120,18 @@ public static ArrayList loadEntriesFromAssets(AssetManager am, String pat while (line != null) { // process line String[] split = line.split("#"); - - if(split.length <= 2) { - entries.add(new Entry(Float.parseFloat(split[0]), Integer.parseInt(split[1]))); + + if (split.length <= 2) { + entries.add(new Entry(Float.parseFloat(split[1]), Float.parseFloat(split[0]))); } else { - + float[] vals = new float[split.length - 1]; - - for(int i = 0; i < vals.length; i++) { + + for (int i = 0; i < vals.length; i++) { vals[i] = Float.parseFloat(split[i]); - } - - entries.add(new BarEntry(vals, Integer.parseInt(split[split.length - 1]))); + } + + entries.add(new BarEntry(Integer.parseInt(split[split.length - 1]), vals)); } line = reader.readLine(); } @@ -151,7 +152,7 @@ public static ArrayList loadEntriesFromAssets(AssetManager am, String pat return entries; // String label = null; - // ArrayList entries = new ArrayList(); + // List entries = new ArrayList(); // // BufferedReader reader = null; // try { @@ -190,10 +191,10 @@ public static ArrayList loadEntriesFromAssets(AssetManager am, String pat /** * Saves an Array of Entries to the specified location on the sdcard * - * @param ds + * @param entries * @param path */ - public static void saveToSdCard(ArrayList entries, String path) { + public static void saveToSdCard(List entries, String path) { File sdcard = Environment.getExternalStorageDirectory(); @@ -215,7 +216,7 @@ public static void saveToSdCard(ArrayList entries, String path) { for (Entry e : entries) { - buf.append(e.getVal() + "#" + e.getXIndex()); + buf.append(e.getY() + "#" + e.getX()); buf.newLine(); } @@ -225,4 +226,76 @@ public static void saveToSdCard(ArrayList entries, String path) { Log.e(LOG, e.toString()); } } + + public static List loadBarEntriesFromAssets(AssetManager am, String path) { + + List entries = new ArrayList(); + + BufferedReader reader = null; + try { + reader = new BufferedReader( + new InputStreamReader(am.open(path), "UTF-8")); + + String line = reader.readLine(); + + while (line != null) { + // process line + String[] split = line.split("#"); + + entries.add(new BarEntry(Float.parseFloat(split[1]), Float.parseFloat(split[0]))); + + line = reader.readLine(); + } + } catch (IOException e) { + Log.e(LOG, e.toString()); + + } finally { + + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + Log.e(LOG, e.toString()); + } + } + } + + return entries; + + // String label = null; + // ArrayList entries = new ArrayList(); + // + // BufferedReader reader = null; + // try { + // reader = new BufferedReader( + // new InputStreamReader(am.open(path), "UTF-8")); + // + // // do reading, usually loop until end of file reading + // label = reader.readLine(); + // String line = reader.readLine(); + // + // while (line != null) { + // // process line + // String[] split = line.split("#"); + // entries.add(new Entry(Float.parseFloat(split[0]), + // Integer.parseInt(split[1]))); + // line = reader.readLine(); + // } + // } catch (IOException e) { + // Log.e(LOG, e.toString()); + // + // } finally { + // + // if (reader != null) { + // try { + // reader.close(); + // } catch (IOException e) { + // Log.e(LOG, e.toString()); + // } + // } + // } + // + // DataSet ds = new DataSet(entries, label); + // return ds; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/HorizontalViewPortHandler.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/HorizontalViewPortHandler.java new file mode 100644 index 0000000000..5a415b0477 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/HorizontalViewPortHandler.java @@ -0,0 +1,29 @@ + +package com.github.mikephil.charting.utils; + +/** + * ViewPortHandler for HorizontalBarChart. + */ +public class HorizontalViewPortHandler extends ViewPortHandler { + + +// @Override +// public void setMinimumScaleX(float xScale) { +// setMinimumScaleY(xScale); +// } +// +// @Override +// public void setMinimumScaleY(float yScale) { +// setMinimumScaleX(yScale); +// } +// +// @Override +// public void setMinMaxScaleX(float minScaleX, float maxScaleX) { +// setMinMaxScaleY(minScaleX, maxScaleX); +// } +// +// @Override +// public void setMinMaxScaleY(float minScaleY, float maxScaleY) { +// setMinMaxScaleX(minScaleY, maxScaleY); +// } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointD.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointD.java new file mode 100644 index 0000000000..f6220a72e9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointD.java @@ -0,0 +1,53 @@ + +package com.github.mikephil.charting.utils; + +import java.util.List; + +/** + * Point encapsulating two double values. + * + * @author Philipp Jahoda + */ +public class MPPointD extends ObjectPool.Poolable { + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(64, new MPPointD(0,0)); + pool.setReplenishPercentage(0.5f); + } + + public static MPPointD getInstance(double x, double y){ + MPPointD result = pool.get(); + result.x = x; + result.y = y; + return result; + } + + public static void recycleInstance(MPPointD instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + public double x; + public double y; + + protected ObjectPool.Poolable instantiate(){ + return new MPPointD(0,0); + } + + private MPPointD(double x, double y) { + this.x = x; + this.y = y; + } + + /** + * returns a string representation of the object + */ + public String toString() { + return "MPPointD, x: " + x + ", y: " + y; + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java new file mode 100644 index 0000000000..fb5a00f508 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java @@ -0,0 +1,99 @@ +package com.github.mikephil.charting.utils; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * Created by Tony Patino on 6/24/16. + */ +public class MPPointF extends ObjectPool.Poolable { + + private static ObjectPool pool; + + public float x; + public float y; + + static { + pool = ObjectPool.create(32, new MPPointF(0,0)); + pool.setReplenishPercentage(0.5f); + } + + public MPPointF() { + } + + public MPPointF(float x, float y) { + this.x = x; + this.y = y; + } + + public static MPPointF getInstance(float x, float y) { + MPPointF result = pool.get(); + result.x = x; + result.y = y; + return result; + } + + public static MPPointF getInstance() { + return pool.get(); + } + + public static MPPointF getInstance(MPPointF copy) { + MPPointF result = pool.get(); + result.x = copy.x; + result.y = copy.y; + return result; + } + + public static void recycleInstance(MPPointF instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + /** + * Return a new point from the data in the specified parcel. + */ + public MPPointF createFromParcel(Parcel in) { + MPPointF r = new MPPointF(0,0); + r.my_readFromParcel(in); + return r; + } + + /** + * Return an array of rectangles of the specified size. + */ + public MPPointF[] newArray(int size) { + return new MPPointF[size]; + } + }; + + /** + * Set the point's coordinates from the data stored in the specified + * parcel. To write a point to a parcel, call writeToParcel(). + * Provided to support older Android devices. + * + * @param in The parcel to read the point's coordinates from + */ + public void my_readFromParcel(Parcel in) { + x = in.readFloat(); + y = in.readFloat(); + } + + public float getX(){ + return this.x; + } + + public float getY(){ + return this.y; + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new MPPointF(0,0); + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ObjectPool.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ObjectPool.java new file mode 100644 index 0000000000..d1d54371f9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ObjectPool.java @@ -0,0 +1,218 @@ +package com.github.mikephil.charting.utils; + +import java.util.List; + +/** + * An object pool for recycling of object instances extending Poolable. + * + * + * Cost/Benefit : + * Cost - The pool can only contain objects extending Poolable. + * Benefit - The pool can very quickly determine if an object is elligable for storage without iteration. + * Benefit - The pool can also know if an instance of Poolable is already stored in a different pool instance. + * Benefit - The pool can grow as needed, if it is empty + * Cost - However, refilling the pool when it is empty might incur a time cost with sufficiently large capacity. Set the replenishPercentage to a lower number if this is a concern. + * + * Created by Tony Patino on 6/20/16. + */ +public class ObjectPool { + + private static int ids = 0; + + private int poolId; + private int desiredCapacity; + private Object[] objects; + private int objectsPointer; + private T modelObject; + private float replenishPercentage; + + + /** + * Returns the id of the given pool instance. + * + * @return an integer ID belonging to this pool instance. + */ + public int getPoolId(){ + return poolId; + } + + /** + * Returns an ObjectPool instance, of a given starting capacity, that recycles instances of a given Poolable object. + * + * @param withCapacity A positive integer value. + * @param object An instance of the object that the pool should recycle. + * @return + */ + public static synchronized ObjectPool create(int withCapacity, Poolable object){ + ObjectPool result = new ObjectPool(withCapacity, object); + result.poolId = ids; + ids++; + + return result; + } + + private ObjectPool(int withCapacity, T object){ + if(withCapacity <= 0){ + throw new IllegalArgumentException("Object Pool must be instantiated with a capacity greater than 0!"); + } + this.desiredCapacity = withCapacity; + this.objects = new Object[this.desiredCapacity]; + this.objectsPointer = 0; + this.modelObject = object; + this.replenishPercentage = 1.0f; + this.refillPool(); + } + + /** + * Set the percentage of the pool to replenish on empty. Valid values are between + * 0.00f and 1.00f + * + * @param percentage a value between 0 and 1, representing the percentage of the pool to replenish. + */ + public void setReplenishPercentage(float percentage){ + float p = percentage; + if(p > 1){ + p = 1; + } + else if(p < 0f){ + p = 0f; + } + this.replenishPercentage = p; + } + + public float getReplenishPercentage(){ + return replenishPercentage; + } + + private void refillPool(){ + this.refillPool(this.replenishPercentage); + } + + private void refillPool(float percentage){ + int portionOfCapacity = (int) (desiredCapacity * percentage); + + if(portionOfCapacity < 1){ + portionOfCapacity = 1; + }else if(portionOfCapacity > desiredCapacity){ + portionOfCapacity = desiredCapacity; + } + + for(int i = 0 ; i < portionOfCapacity ; i++){ + this.objects[i] = modelObject.instantiate(); + } + objectsPointer = portionOfCapacity - 1; + } + + /** + * Returns an instance of Poolable. If get() is called with an empty pool, the pool will be + * replenished. If the pool capacity is sufficiently large, this could come at a performance + * cost. + * + * @return An instance of Poolable object T + */ + public synchronized T get(){ + + if(this.objectsPointer == -1 && this.replenishPercentage > 0.0f){ + this.refillPool(); + } + + T result = (T)objects[this.objectsPointer]; + result.currentOwnerId = Poolable.NO_OWNER; + this.objectsPointer--; + + return result; + } + + /** + * Recycle an instance of Poolable that this pool is capable of generating. + * The T instance passed must not already exist inside this or any other ObjectPool instance. + * + * @param object An object of type T to recycle + */ + public synchronized void recycle(T object){ + if(object.currentOwnerId != Poolable.NO_OWNER){ + if(object.currentOwnerId == this.poolId){ + throw new IllegalArgumentException("The object passed is already stored in this pool!"); + }else { + throw new IllegalArgumentException("The object to recycle already belongs to poolId " + object.currentOwnerId + ". Object cannot belong to two different pool instances simultaneously!"); + } + } + + this.objectsPointer++; + if(this.objectsPointer >= objects.length){ + this.resizePool(); + } + + object.currentOwnerId = this.poolId; + objects[this.objectsPointer] = object; + + } + + /** + * Recycle a List of Poolables that this pool is capable of generating. + * The T instances passed must not already exist inside this or any other ObjectPool instance. + * + * @param objects A list of objects of type T to recycle + */ + public synchronized void recycle(List objects){ + while(objects.size() + this.objectsPointer + 1 > this.desiredCapacity){ + this.resizePool(); + } + final int objectsListSize = objects.size(); + + // Not relying on recycle(T object) because this is more performant. + for(int i = 0 ; i < objectsListSize ; i++){ + T object = objects.get(i); + if(object.currentOwnerId != Poolable.NO_OWNER){ + if(object.currentOwnerId == this.poolId){ + throw new IllegalArgumentException("The object passed is already stored in this pool!"); + }else { + throw new IllegalArgumentException("The object to recycle already belongs to poolId " + object.currentOwnerId + ". Object cannot belong to two different pool instances simultaneously!"); + } + } + object.currentOwnerId = this.poolId; + this.objects[this.objectsPointer + 1 + i] = object; + } + this.objectsPointer += objectsListSize; + } + + private void resizePool() { + final int oldCapacity = this.desiredCapacity; + this.desiredCapacity *= 2; + Object[] temp = new Object[this.desiredCapacity]; + for(int i = 0 ; i < oldCapacity ; i++){ + temp[i] = this.objects[i]; + } + this.objects = temp; + } + + /** + * Returns the capacity of this object pool. Note : The pool will automatically resize + * to contain additional objects if the user tries to add more objects than the pool's + * capacity allows, but this comes at a performance cost. + * + * @return The capacity of the pool. + */ + public int getPoolCapacity(){ + return this.objects.length; + } + + /** + * Returns the number of objects remaining in the pool, for diagnostic purposes. + * + * @return The number of objects remaining in the pool. + */ + public int getPoolCount(){ + return this.objectsPointer + 1; + } + + + public static abstract class Poolable{ + + public static int NO_OWNER = -1; + int currentOwnerId = NO_OWNER; + + protected abstract Poolable instantiate(); + + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java new file mode 100644 index 0000000000..0496bd0673 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java @@ -0,0 +1,459 @@ + +package com.github.mikephil.charting.utils; + +import android.graphics.Matrix; +import android.graphics.Path; +import android.graphics.RectF; + +import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; + +import java.util.List; + +/** + * Transformer class that contains all matrices and is responsible for + * transforming values into pixels on the screen and backwards. + * + * @author Philipp Jahoda + */ +public class Transformer { + + /** + * matrix to map the values to the screen pixels + */ + protected Matrix mMatrixValueToPx = new Matrix(); + + /** + * matrix for handling the different offsets of the chart + */ + protected Matrix mMatrixOffset = new Matrix(); + + protected ViewPortHandler mViewPortHandler; + + public Transformer(ViewPortHandler viewPortHandler) { + this.mViewPortHandler = viewPortHandler; + } + + /** + * Prepares the matrix that transforms values to pixels. Calculates the + * scale factors from the charts size and offsets. + * + * @param xChartMin + * @param deltaX + * @param deltaY + * @param yChartMin + */ + public void prepareMatrixValuePx(float xChartMin, float deltaX, float deltaY, float yChartMin) { + + float scaleX = (float) ((mViewPortHandler.contentWidth()) / deltaX); + float scaleY = (float) ((mViewPortHandler.contentHeight()) / deltaY); + + if (Float.isInfinite(scaleX)) { + scaleX = 0; + } + if (Float.isInfinite(scaleY)) { + scaleY = 0; + } + + // setup all matrices + mMatrixValueToPx.reset(); + mMatrixValueToPx.postTranslate(-xChartMin, -yChartMin); + mMatrixValueToPx.postScale(scaleX, -scaleY); + } + + /** + * Prepares the matrix that contains all offsets. + * + * @param inverted + */ + public void prepareMatrixOffset(boolean inverted) { + + mMatrixOffset.reset(); + + // offset.postTranslate(mOffsetLeft, getHeight() - mOffsetBottom); + + if (!inverted) + mMatrixOffset.postTranslate(mViewPortHandler.offsetLeft(), + mViewPortHandler.getChartHeight() - mViewPortHandler.offsetBottom()); + else { + mMatrixOffset + .setTranslate(mViewPortHandler.offsetLeft(), -mViewPortHandler.offsetTop()); + mMatrixOffset.postScale(1.0f, -1.0f); + } + } + + protected float[] valuePointsForGenerateTransformedValuesScatter = new float[1]; + + /** + * Transforms an List of Entry into a float array containing the x and + * y values transformed with all matrices for the SCATTERCHART. + * + * @param data + * @return + */ + public float[] generateTransformedValuesScatter(IScatterDataSet data, float phaseX, + float phaseY, int from, int to) { + + final int count = (int) ((to - from) * phaseX + 1) * 2; + + if (valuePointsForGenerateTransformedValuesScatter.length != count) { + valuePointsForGenerateTransformedValuesScatter = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesScatter; + + for (int j = 0; j < count; j += 2) { + + Entry e = data.getEntryForIndex(j / 2 + from); + + if (e != null) { + valuePoints[j] = e.getX(); + valuePoints[j + 1] = e.getY() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; + } + } + + getValueToPixelMatrix().mapPoints(valuePoints); + + return valuePoints; + } + + protected float[] valuePointsForGenerateTransformedValuesBubble = new float[1]; + + /** + * Transforms an List of Entry into a float array containing the x and + * y values transformed with all matrices for the BUBBLECHART. + * + * @param data + * @return + */ + public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY, int from, int to) { + + final int count = (to - from + 1) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; + + if (valuePointsForGenerateTransformedValuesBubble.length != count) { + valuePointsForGenerateTransformedValuesBubble = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesBubble; + + for (int j = 0; j < count; j += 2) { + + Entry e = data.getEntryForIndex(j / 2 + from); + + if (e != null) { + valuePoints[j] = e.getX(); + valuePoints[j + 1] = e.getY() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; + } + } + + getValueToPixelMatrix().mapPoints(valuePoints); + + return valuePoints; + } + + protected float[] valuePointsForGenerateTransformedValuesLine = new float[1]; + + /** + * Transforms an List of Entry into a float array containing the x and + * y values transformed with all matrices for the LINECHART. + * + * @param data + * @return + */ + public float[] generateTransformedValuesLine(ILineDataSet data, + float phaseX, float phaseY, + int min, int max) { + + final int count = ((int) ((max - min) * phaseX) + 1) * 2; + + if (valuePointsForGenerateTransformedValuesLine.length != count) { + valuePointsForGenerateTransformedValuesLine = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesLine; + + for (int j = 0; j < count; j += 2) { + + Entry e = data.getEntryForIndex(j / 2 + min); + + if (e != null) { + valuePoints[j] = e.getX(); + valuePoints[j + 1] = e.getY() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; + } + } + + getValueToPixelMatrix().mapPoints(valuePoints); + + return valuePoints; + } + + protected float[] valuePointsForGenerateTransformedValuesCandle = new float[1]; + + /** + * Transforms an List of Entry into a float array containing the x and + * y values transformed with all matrices for the CANDLESTICKCHART. + * + * @param data + * @return + */ + public float[] generateTransformedValuesCandle(ICandleDataSet data, + float phaseX, float phaseY, int from, int to) { + + final int count = (int) ((to - from) * phaseX + 1) * 2; + + if (valuePointsForGenerateTransformedValuesCandle.length != count) { + valuePointsForGenerateTransformedValuesCandle = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesCandle; + + for (int j = 0; j < count; j += 2) { + + CandleEntry e = data.getEntryForIndex(j / 2 + from); + + if (e != null) { + valuePoints[j] = e.getX(); + valuePoints[j + 1] = e.getHigh() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; + } + } + + getValueToPixelMatrix().mapPoints(valuePoints); + + return valuePoints; + } + + /** + * transform a path with all the given matrices VERY IMPORTANT: keep order + * to value-touch-offset + * + * @param path + */ + public void pathValueToPixel(Path path) { + + path.transform(mMatrixValueToPx); + path.transform(mViewPortHandler.getMatrixTouch()); + path.transform(mMatrixOffset); + } + + /** + * Transforms multiple paths will all matrices. + * + * @param paths + */ + public void pathValuesToPixel(List paths) { + + for (int i = 0; i < paths.size(); i++) { + pathValueToPixel(paths.get(i)); + } + } + + /** + * Transform an array of points with all matrices. VERY IMPORTANT: Keep + * matrix order "value-touch-offset" when transforming. + * + * @param pts + */ + public void pointValuesToPixel(float[] pts) { + + mMatrixValueToPx.mapPoints(pts); + mViewPortHandler.getMatrixTouch().mapPoints(pts); + mMatrixOffset.mapPoints(pts); + } + + /** + * Transform a rectangle with all matrices. + * + * @param r + */ + public void rectValueToPixel(RectF r) { + + mMatrixValueToPx.mapRect(r); + mViewPortHandler.getMatrixTouch().mapRect(r); + mMatrixOffset.mapRect(r); + } + + /** + * Transform a rectangle with all matrices with potential animation phases. + * + * @param r + * @param phaseY + */ + public void rectToPixelPhase(RectF r, float phaseY) { + + // multiply the height of the rect with the phase + r.top *= phaseY; + r.bottom *= phaseY; + + mMatrixValueToPx.mapRect(r); + mViewPortHandler.getMatrixTouch().mapRect(r); + mMatrixOffset.mapRect(r); + } + + public void rectToPixelPhaseHorizontal(RectF r, float phaseY) { + + // multiply the height of the rect with the phase + r.left *= phaseY; + r.right *= phaseY; + + mMatrixValueToPx.mapRect(r); + mViewPortHandler.getMatrixTouch().mapRect(r); + mMatrixOffset.mapRect(r); + } + + /** + * Transform a rectangle with all matrices with potential animation phases. + * + * @param r + */ + public void rectValueToPixelHorizontal(RectF r) { + + mMatrixValueToPx.mapRect(r); + mViewPortHandler.getMatrixTouch().mapRect(r); + mMatrixOffset.mapRect(r); + } + + /** + * Transform a rectangle with all matrices with potential animation phases. + * + * @param r + * @param phaseY + */ + public void rectValueToPixelHorizontal(RectF r, float phaseY) { + + // multiply the height of the rect with the phase + r.left *= phaseY; + r.right *= phaseY; + + mMatrixValueToPx.mapRect(r); + mViewPortHandler.getMatrixTouch().mapRect(r); + mMatrixOffset.mapRect(r); + } + + /** + * transforms multiple rects with all matrices + * + * @param rects + */ + public void rectValuesToPixel(List rects) { + + Matrix m = getValueToPixelMatrix(); + + for (int i = 0; i < rects.size(); i++) + m.mapRect(rects.get(i)); + } + + protected Matrix mPixelToValueMatrixBuffer = new Matrix(); + + /** + * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) + * into values on the chart. + * + * @param pixels + */ + public void pixelsToValue(float[] pixels) { + + Matrix tmp = mPixelToValueMatrixBuffer; + tmp.reset(); + + // invert all matrixes to convert back to the original value + mMatrixOffset.invert(tmp); + tmp.mapPoints(pixels); + + mViewPortHandler.getMatrixTouch().invert(tmp); + tmp.mapPoints(pixels); + + mMatrixValueToPx.invert(tmp); + tmp.mapPoints(pixels); + } + + /** + * buffer for performance + */ + float[] ptsBuffer = new float[2]; + + /** + * Returns a recyclable MPPointD instance. + * returns the x and y values in the chart at the given touch point + * (encapsulated in a MPPointD). This method transforms pixel coordinates to + * coordinates / values in the chart. This is the opposite method to + * getPixelForValues(...). + * + * @param x + * @param y + * @return + */ + public MPPointD getValuesByTouchPoint(float x, float y) { + + MPPointD result = MPPointD.getInstance(0, 0); + getValuesByTouchPoint(x, y, result); + return result; + } + + public void getValuesByTouchPoint(float x, float y, MPPointD outputPoint) { + + ptsBuffer[0] = x; + ptsBuffer[1] = y; + + pixelsToValue(ptsBuffer); + + outputPoint.x = ptsBuffer[0]; + outputPoint.y = ptsBuffer[1]; + } + + /** + * Returns a recyclable MPPointD instance. + * Returns the x and y coordinates (pixels) for a given x and y value in the chart. + * + * @param x + * @param y + * @return + */ + public MPPointD getPixelForValues(float x, float y) { + + ptsBuffer[0] = x; + ptsBuffer[1] = y; + + pointValuesToPixel(ptsBuffer); + + double xPx = ptsBuffer[0]; + double yPx = ptsBuffer[1]; + + return MPPointD.getInstance(xPx, yPx); + } + + public Matrix getValueMatrix() { + return mMatrixValueToPx; + } + + public Matrix getOffsetMatrix() { + return mMatrixOffset; + } + + private Matrix mMBuffer1 = new Matrix(); + + public Matrix getValueToPixelMatrix() { + mMBuffer1.set(mMatrixValueToPx); + mMBuffer1.postConcat(mViewPortHandler.mMatrixTouch); + mMBuffer1.postConcat(mMatrixOffset); + return mMBuffer1; + } + + private Matrix mMBuffer2 = new Matrix(); + + public Matrix getPixelToValueMatrix() { + getValueToPixelMatrix().invert(mMBuffer2); + return mMBuffer2; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java new file mode 100644 index 0000000000..05fa82ae29 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java @@ -0,0 +1,44 @@ + +package com.github.mikephil.charting.utils; + +/** + * Transformer class for the HorizontalBarChart. + * + * @author Philipp Jahoda + */ +public class TransformerHorizontalBarChart extends Transformer { + + public TransformerHorizontalBarChart(ViewPortHandler viewPortHandler) { + super(viewPortHandler); + } + + /** + * Prepares the matrix that contains all offsets. + * + * @param inverted + */ + public void prepareMatrixOffset(boolean inverted) { + + mMatrixOffset.reset(); + + // offset.postTranslate(mOffsetLeft, getHeight() - mOffsetBottom); + + if (!inverted) + mMatrixOffset.postTranslate(mViewPortHandler.offsetLeft(), + mViewPortHandler.getChartHeight() - mViewPortHandler.offsetBottom()); + else { + mMatrixOffset + .setTranslate( + -(mViewPortHandler.getChartWidth() - mViewPortHandler.offsetRight()), + mViewPortHandler.getChartHeight() - mViewPortHandler.offsetBottom()); + mMatrixOffset.postScale(-1.0f, 1.0f); + } + + // mMatrixOffset.set(offset); + + // mMatrixOffset.reset(); + // + // mMatrixOffset.postTranslate(mOffsetLeft, getHeight() - + // mOffsetBottom); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java new file mode 100644 index 0000000000..c302673919 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java @@ -0,0 +1,779 @@ + +package com.github.mikephil.charting.utils; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.SizeF; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; + +import com.github.mikephil.charting.formatter.DefaultValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; + +import java.util.List; + +/** + * Utilities class that has some helper methods. Needs to be initialized by + * calling Utils.init(...) before usage. Inside the Chart.init() method, this is + * done, if the Utils are used before that, Utils.init(...) needs to be called + * manually. + * + * @author Philipp Jahoda + */ +public abstract class Utils { + + private static DisplayMetrics mMetrics; + private static int mMinimumFlingVelocity = 50; + private static int mMaximumFlingVelocity = 8000; + public final static double DEG2RAD = (Math.PI / 180.0); + public final static float FDEG2RAD = ((float) Math.PI / 180.f); + + @SuppressWarnings("unused") + public final static double DOUBLE_EPSILON = Double.longBitsToDouble(1); + + @SuppressWarnings("unused") + public final static float FLOAT_EPSILON = Float.intBitsToFloat(1); + + /** + * initialize method, called inside the Chart.init() method. + * + * @param context + */ + @SuppressWarnings("deprecation") + public static void init(Context context) { + + if (context == null) { + // noinspection deprecation + mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); + // noinspection deprecation + mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity(); + + Log.e("MPChartLib-Utils" + , "Utils.init(...) PROVIDED CONTEXT OBJECT IS NULL"); + + } else { + ViewConfiguration viewConfiguration = ViewConfiguration.get(context); + mMinimumFlingVelocity = viewConfiguration.getScaledMinimumFlingVelocity(); + mMaximumFlingVelocity = viewConfiguration.getScaledMaximumFlingVelocity(); + + Resources res = context.getResources(); + mMetrics = res.getDisplayMetrics(); + } + } + + /** + * initialize method, called inside the Chart.init() method. backwards + * compatibility - to not break existing code + * + * @param res + */ + @Deprecated + public static void init(Resources res) { + + mMetrics = res.getDisplayMetrics(); + + // noinspection deprecation + mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); + // noinspection deprecation + mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity(); + } + + /** + * This method converts dp unit to equivalent pixels, depending on device + * density. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. + * + * @param dp A value in dp (density independent pixels) unit. Which we need + * to convert into pixels + * @return A float value to represent px equivalent to dp depending on + * device density + */ + public static float convertDpToPixel(float dp) { + + if (mMetrics == null) { + + Log.e("MPChartLib-Utils", + "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before" + + " calling Utils.convertDpToPixel(...). Otherwise conversion does not " + + "take place."); + return dp; + } + + return dp * mMetrics.density; + } + + /** + * This method converts device specific pixels to density independent + * pixels. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. + * + * @param px A value in px (pixels) unit. Which we need to convert into db + * @return A float value to represent dp equivalent to px value + */ + public static float convertPixelsToDp(float px) { + + if (mMetrics == null) { + + Log.e("MPChartLib-Utils", + "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before" + + " calling Utils.convertPixelsToDp(...). Otherwise conversion does not" + + " take place."); + return px; + } + + return px / mMetrics.density; + } + + /** + * calculates the approximate width of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + * + * @param paint + * @param demoText + * @return + */ + public static int calcTextWidth(Paint paint, String demoText) { + return (int) paint.measureText(demoText); + } + + private static Rect mCalcTextHeightRect = new Rect(); + /** + * calculates the approximate height of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + * + * @param paint + * @param demoText + * @return + */ + public static int calcTextHeight(Paint paint, String demoText) { + + Rect r = mCalcTextHeightRect; + r.set(0,0,0,0); + paint.getTextBounds(demoText, 0, demoText.length(), r); + return r.height(); + } + + private static Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); + + public static float getLineHeight(Paint paint) { + return getLineHeight(paint, mFontMetrics); + } + + public static float getLineHeight(Paint paint, Paint.FontMetrics fontMetrics){ + paint.getFontMetrics(fontMetrics); + return fontMetrics.descent - fontMetrics.ascent; + } + + public static float getLineSpacing(Paint paint) { + return getLineSpacing(paint, mFontMetrics); + } + + public static float getLineSpacing(Paint paint, Paint.FontMetrics fontMetrics){ + paint.getFontMetrics(fontMetrics); + return fontMetrics.ascent - fontMetrics.top + fontMetrics.bottom; + } + + /** + * Returns a recyclable FSize instance. + * calculates the approximate size of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + * + * @param paint + * @param demoText + * @return A Recyclable FSize instance + */ + public static FSize calcTextSize(Paint paint, String demoText) { + + FSize result = FSize.getInstance(0,0); + calcTextSize(paint, demoText, result); + return result; + } + + private static Rect mCalcTextSizeRect = new Rect(); + /** + * calculates the approximate size of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + * + * @param paint + * @param demoText + * @param outputFSize An output variable, modified by the function. + */ + public static void calcTextSize(Paint paint, String demoText, FSize outputFSize) { + + Rect r = mCalcTextSizeRect; + r.set(0,0,0,0); + paint.getTextBounds(demoText, 0, demoText.length(), r); + outputFSize.width = r.width(); + outputFSize.height = r.height(); + + } + + + /** + * Math.pow(...) is very expensive, so avoid calling it and create it + * yourself. + */ + private static final int POW_10[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 + }; + + private static IValueFormatter mDefaultValueFormatter = generateDefaultValueFormatter(); + + private static IValueFormatter generateDefaultValueFormatter() { + final DefaultValueFormatter formatter = new DefaultValueFormatter(1); + return formatter; + } + + /// - returns: The default value formatter used for all chart components that needs a default + public static IValueFormatter getDefaultValueFormatter() + { + return mDefaultValueFormatter; + } + + /** + * Formats the given number to the given number of decimals, and returns the + * number as a string, maximum 35 characters. If thousands are separated, the separating + * character is a dot ("."). + * + * @param number + * @param digitCount + * @param separateThousands set this to true to separate thousands values + * @return + */ + public static String formatNumber(float number, int digitCount, boolean separateThousands) { + return formatNumber(number, digitCount, separateThousands, '.'); + } + + /** + * Formats the given number to the given number of decimals, and returns the + * number as a string, maximum 35 characters. + * + * @param number + * @param digitCount + * @param separateThousands set this to true to separate thousands values + * @param separateChar a caracter to be paced between the "thousands" + * @return + */ + public static String formatNumber(float number, int digitCount, boolean separateThousands, + char separateChar) { + + char[] out = new char[35]; + + boolean neg = false; + if (number == 0) { + return "0"; + } + + boolean zero = false; + if (number < 1 && number > -1) { + zero = true; + } + + if (number < 0) { + neg = true; + number = -number; + } + + if (digitCount > POW_10.length) { + digitCount = POW_10.length - 1; + } + + number *= POW_10[digitCount]; + long lval = Math.round(number); + int ind = out.length - 1; + int charCount = 0; + boolean decimalPointAdded = false; + + while (lval != 0 || charCount < (digitCount + 1)) { + int digit = (int) (lval % 10); + lval = lval / 10; + out[ind--] = (char) (digit + '0'); + charCount++; + + // add decimal point + if (charCount == digitCount) { + out[ind--] = ','; + charCount++; + decimalPointAdded = true; + + // add thousand separators + } else if (separateThousands && lval != 0 && charCount > digitCount) { + + if (decimalPointAdded) { + + if ((charCount - digitCount) % 4 == 0) { + out[ind--] = separateChar; + charCount++; + } + + } else { + + if ((charCount - digitCount) % 4 == 3) { + out[ind--] = separateChar; + charCount++; + } + } + } + } + + // if number around zero (between 1 and -1) + if (zero) { + out[ind--] = '0'; + charCount += 1; + } + + // if the number is negative + if (neg) { + out[ind--] = '-'; + charCount += 1; + } + + int start = out.length - charCount; + + // use this instead of "new String(...)" because of issue < Android 4.0 + return String.valueOf(out, start, out.length - start); + } + + /** + * rounds the given number to the next significant number + * + * @param number + * @return + */ + public static float roundToNextSignificant(double number) { + if (Double.isInfinite(number) || + Double.isNaN(number) || + number == 0.0) + return 0; + + final float d = (float) Math.ceil((float) Math.log10(number < 0 ? -number : number)); + final int pw = 1 - (int) d; + final float magnitude = (float) Math.pow(10, pw); + final long shifted = Math.round(number * magnitude); + return shifted / magnitude; + } + + /** + * Returns the appropriate number of decimals to be used for the provided + * number. + * + * @param number + * @return + */ + public static int getDecimals(float number) { + + float i = roundToNextSignificant(number); + + if (Float.isInfinite(i)) + return 0; + + return (int) Math.ceil(-Math.log10(i)) + 2; + } + + /** + * Converts the provided Integer List to an int array. + * + * @param integers + * @return + */ + public static int[] convertIntegers(List integers) { + + int[] ret = new int[integers.size()]; + + copyIntegers(integers, ret); + + return ret; + } + + public static void copyIntegers(List from, int[] to){ + int count = to.length < from.size() ? to.length : from.size(); + for(int i = 0 ; i < count ; i++){ + to[i] = from.get(i); + } + } + + /** + * Converts the provided String List to a String array. + * + * @param strings + * @return + */ + public static String[] convertStrings(List strings) { + + String[] ret = new String[strings.size()]; + + for (int i = 0; i < ret.length; i++) { + ret[i] = strings.get(i); + } + + return ret; + } + + public static void copyStrings(List from, String[] to){ + int count = to.length < from.size() ? to.length : from.size(); + for(int i = 0 ; i < count ; i++){ + to[i] = from.get(i); + } + } + + /** + * Replacement for the Math.nextUp(...) method that is only available in + * HONEYCOMB and higher. Dat's some seeeeek sheeet. + * + * @param d + * @return + */ + public static double nextUp(double d) { + if (d == Double.POSITIVE_INFINITY) + return d; + else { + d += 0.0d; + return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + + ((d >= 0.0d) ? +1L : -1L)); + } + } + + /** + * Returns a recyclable MPPointF instance. + * Calculates the position around a center point, depending on the distance + * from the center, and the angle of the position around the center. + * + * @param center + * @param dist + * @param angle in degrees, converted to radians internally + * @return + */ + public static MPPointF getPosition(MPPointF center, float dist, float angle) { + + MPPointF p = MPPointF.getInstance(0,0); + getPosition(center, dist, angle, p); + return p; + } + + public static void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint){ + outputPoint.x = (float) (center.x + dist * Math.cos(Math.toRadians(angle))); + outputPoint.y = (float) (center.y + dist * Math.sin(Math.toRadians(angle))); + } + + public static void velocityTrackerPointerUpCleanUpIfNecessary(MotionEvent ev, + VelocityTracker tracker) { + + // Check the dot product of current velocities. + // If the pointer that left was opposing another velocity vector, clear. + tracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); + final int upIndex = ev.getActionIndex(); + final int id1 = ev.getPointerId(upIndex); + final float x1 = tracker.getXVelocity(id1); + final float y1 = tracker.getYVelocity(id1); + for (int i = 0, count = ev.getPointerCount(); i < count; i++) { + if (i == upIndex) + continue; + + final int id2 = ev.getPointerId(i); + final float x = x1 * tracker.getXVelocity(id2); + final float y = y1 * tracker.getYVelocity(id2); + + final float dot = x + y; + if (dot < 0) { + tracker.clear(); + break; + } + } + } + + /** + * Original method view.postInvalidateOnAnimation() only supportd in API >= + * 16, This is a replica of the code from ViewCompat. + * + * @param view + */ + @SuppressLint("NewApi") + public static void postInvalidateOnAnimation(View view) { + if (Build.VERSION.SDK_INT >= 16) + view.postInvalidateOnAnimation(); + else + view.postInvalidateDelayed(10); + } + + public static int getMinimumFlingVelocity() { + return mMinimumFlingVelocity; + } + + public static int getMaximumFlingVelocity() { + return mMaximumFlingVelocity; + } + + /** + * returns an angle between 0.f < 360.f (not less than zero, less than 360) + */ + public static float getNormalizedAngle(float angle) { + while (angle < 0.f) + angle += 360.f; + + return angle % 360.f; + } + + private static Rect mDrawableBoundsCache = new Rect(); + + public static void drawImage(Canvas canvas, + Drawable drawable, + int x, int y, + int width, int height) { + + MPPointF drawOffset = MPPointF.getInstance(); + drawOffset.x = x - (width / 2); + drawOffset.y = y - (height / 2); + + drawable.copyBounds(mDrawableBoundsCache); + drawable.setBounds( + mDrawableBoundsCache.left, + mDrawableBoundsCache.top, + mDrawableBoundsCache.left + width, + mDrawableBoundsCache.top + width); + + int saveId = canvas.save(); + // translate to the correct position and draw + canvas.translate(drawOffset.x, drawOffset.y); + drawable.draw(canvas); + canvas.restoreToCount(saveId); + } + + private static Rect mDrawTextRectBuffer = new Rect(); + private static Paint.FontMetrics mFontMetricsBuffer = new Paint.FontMetrics(); + + public static void drawXAxisValue(Canvas c, String text, float x, float y, + Paint paint, + MPPointF anchor, float angleDegrees) { + + float drawOffsetX = 0.f; + float drawOffsetY = 0.f; + + final float lineHeight = paint.getFontMetrics(mFontMetricsBuffer); + paint.getTextBounds(text, 0, text.length(), mDrawTextRectBuffer); + + // Android sometimes has pre-padding + drawOffsetX -= mDrawTextRectBuffer.left; + + // Android does not snap the bounds to line boundaries, + // and draws from bottom to top. + // And we want to normalize it. + drawOffsetY += -mFontMetricsBuffer.ascent; + + // To have a consistent point of reference, we always draw left-aligned + Paint.Align originalTextAlign = paint.getTextAlign(); + paint.setTextAlign(Paint.Align.LEFT); + + if (angleDegrees != 0.f) { + + // Move the text drawing rect in a way that it always rotates around its center + drawOffsetX -= mDrawTextRectBuffer.width() * 0.5f; + drawOffsetY -= lineHeight * 0.5f; + + float translateX = x; + float translateY = y; + + // Move the "outer" rect relative to the anchor, assuming its centered + if (anchor.x != 0.5f || anchor.y != 0.5f) { + final FSize rotatedSize = getSizeOfRotatedRectangleByDegrees( + mDrawTextRectBuffer.width(), + lineHeight, + angleDegrees); + + translateX -= rotatedSize.width * (anchor.x - 0.5f); + translateY -= rotatedSize.height * (anchor.y - 0.5f); + FSize.recycleInstance(rotatedSize); + } + + c.save(); + c.translate(translateX, translateY); + c.rotate(angleDegrees); + + c.drawText(text, drawOffsetX, drawOffsetY, paint); + + c.restore(); + } else { + if (anchor.x != 0.f || anchor.y != 0.f) { + + drawOffsetX -= mDrawTextRectBuffer.width() * anchor.x; + drawOffsetY -= lineHeight * anchor.y; + } + + drawOffsetX += x; + drawOffsetY += y; + + c.drawText(text, drawOffsetX, drawOffsetY, paint); + } + + paint.setTextAlign(originalTextAlign); + } + + public static void drawMultilineText(Canvas c, StaticLayout textLayout, + float x, float y, + TextPaint paint, + MPPointF anchor, float angleDegrees) { + + float drawOffsetX = 0.f; + float drawOffsetY = 0.f; + float drawWidth; + float drawHeight; + + final float lineHeight = paint.getFontMetrics(mFontMetricsBuffer); + + drawWidth = textLayout.getWidth(); + drawHeight = textLayout.getLineCount() * lineHeight; + + // Android sometimes has pre-padding + drawOffsetX -= mDrawTextRectBuffer.left; + + // Android does not snap the bounds to line boundaries, + // and draws from bottom to top. + // And we want to normalize it. + drawOffsetY += drawHeight; + + // To have a consistent point of reference, we always draw left-aligned + Paint.Align originalTextAlign = paint.getTextAlign(); + paint.setTextAlign(Paint.Align.LEFT); + + if (angleDegrees != 0.f) { + + // Move the text drawing rect in a way that it always rotates around its center + drawOffsetX -= drawWidth * 0.5f; + drawOffsetY -= drawHeight * 0.5f; + + float translateX = x; + float translateY = y; + + // Move the "outer" rect relative to the anchor, assuming its centered + if (anchor.x != 0.5f || anchor.y != 0.5f) { + final FSize rotatedSize = getSizeOfRotatedRectangleByDegrees( + drawWidth, + drawHeight, + angleDegrees); + + translateX -= rotatedSize.width * (anchor.x - 0.5f); + translateY -= rotatedSize.height * (anchor.y - 0.5f); + FSize.recycleInstance(rotatedSize); + } + + c.save(); + c.translate(translateX, translateY); + c.rotate(angleDegrees); + + c.translate(drawOffsetX, drawOffsetY); + textLayout.draw(c); + + c.restore(); + } else { + if (anchor.x != 0.f || anchor.y != 0.f) { + + drawOffsetX -= drawWidth * anchor.x; + drawOffsetY -= drawHeight * anchor.y; + } + + drawOffsetX += x; + drawOffsetY += y; + + c.save(); + + c.translate(drawOffsetX, drawOffsetY); + textLayout.draw(c); + + c.restore(); + } + + paint.setTextAlign(originalTextAlign); + } + + public static void drawMultilineText(Canvas c, String text, + float x, float y, + TextPaint paint, + FSize constrainedToSize, + MPPointF anchor, float angleDegrees) { + + StaticLayout textLayout = new StaticLayout( + text, 0, text.length(), + paint, + (int) Math.max(Math.ceil(constrainedToSize.width), 1.f), + Layout.Alignment.ALIGN_NORMAL, 1.f, 0.f, false); + + + drawMultilineText(c, textLayout, x, y, paint, anchor, angleDegrees); + } + + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by degrees. + * + * @param rectangleSize + * @param degrees + * @return A Recyclable FSize instance + */ + public static FSize getSizeOfRotatedRectangleByDegrees(FSize rectangleSize, float degrees) { + final float radians = degrees * FDEG2RAD; + return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, + radians); + } + + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by radians. + * + * @param rectangleSize + * @param radians + * @return A Recyclable FSize instance + */ + public static FSize getSizeOfRotatedRectangleByRadians(FSize rectangleSize, float radians) { + return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, + radians); + } + + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by degrees. + * + * @param rectangleWidth + * @param rectangleHeight + * @param degrees + * @return A Recyclable FSize instance + */ + public static FSize getSizeOfRotatedRectangleByDegrees(float rectangleWidth, float + rectangleHeight, float degrees) { + final float radians = degrees * FDEG2RAD; + return getSizeOfRotatedRectangleByRadians(rectangleWidth, rectangleHeight, radians); + } + + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by radians. + * + * @param rectangleWidth + * @param rectangleHeight + * @param radians + * @return A Recyclable FSize instance + */ + public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, float + rectangleHeight, float radians) { + return FSize.getInstance( + Math.abs(rectangleWidth * (float) Math.cos(radians)) + Math.abs(rectangleHeight * + (float) Math.sin(radians)), + Math.abs(rectangleWidth * (float) Math.sin(radians)) + Math.abs(rectangleHeight * + (float) Math.cos(radians)) + ); + } + + public static int getSDKInt() { + return android.os.Build.VERSION.SDK_INT; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java new file mode 100644 index 0000000000..71b4b9bf79 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java @@ -0,0 +1,759 @@ + +package com.github.mikephil.charting.utils; + +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.View; + +/** + * Class that contains information about the charts current viewport settings, including offsets, scale & translation + * levels, ... + * + * @author Philipp Jahoda + */ +public class ViewPortHandler { + + /** + * matrix used for touch events + */ + protected final Matrix mMatrixTouch = new Matrix(); + + /** + * this rectangle defines the area in which graph values can be drawn + */ + protected RectF mContentRect = new RectF(); + + protected float mChartWidth = 0f; + protected float mChartHeight = 0f; + + /** + * minimum scale value on the y-axis + */ + private float mMinScaleY = 1f; + + /** + * maximum scale value on the y-axis + */ + private float mMaxScaleY = Float.MAX_VALUE; + + /** + * minimum scale value on the x-axis + */ + private float mMinScaleX = 1f; + + /** + * maximum scale value on the x-axis + */ + private float mMaxScaleX = Float.MAX_VALUE; + + /** + * contains the current scale factor of the x-axis + */ + private float mScaleX = 1f; + + /** + * contains the current scale factor of the y-axis + */ + private float mScaleY = 1f; + + /** + * current translation (drag distance) on the x-axis + */ + private float mTransX = 0f; + + /** + * current translation (drag distance) on the y-axis + */ + private float mTransY = 0f; + + /** + * offset that allows the chart to be dragged over its bounds on the x-axis + */ + private float mTransOffsetX = 0f; + + /** + * offset that allows the chart to be dragged over its bounds on the x-axis + */ + private float mTransOffsetY = 0f; + + /** + * Constructor - don't forget calling setChartDimens(...) + */ + public ViewPortHandler() { + + } + + /** + * Sets the width and height of the chart. + * + * @param width + * @param height + */ + + public void setChartDimens(float width, float height) { + + float offsetLeft = this.offsetLeft(); + float offsetTop = this.offsetTop(); + float offsetRight = this.offsetRight(); + float offsetBottom = this.offsetBottom(); + + mChartHeight = height; + mChartWidth = width; + + restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom); + } + + public boolean hasChartDimens() { + if (mChartHeight > 0 && mChartWidth > 0) + return true; + else + return false; + } + + public void restrainViewPort(float offsetLeft, float offsetTop, float offsetRight, + float offsetBottom) { + mContentRect.set(offsetLeft, offsetTop, mChartWidth - offsetRight, mChartHeight + - offsetBottom); + } + + public float offsetLeft() { + return mContentRect.left; + } + + public float offsetRight() { + return mChartWidth - mContentRect.right; + } + + public float offsetTop() { + return mContentRect.top; + } + + public float offsetBottom() { + return mChartHeight - mContentRect.bottom; + } + + public float contentTop() { + return mContentRect.top; + } + + public float contentLeft() { + return mContentRect.left; + } + + public float contentRight() { + return mContentRect.right; + } + + public float contentBottom() { + return mContentRect.bottom; + } + + public float contentWidth() { + return mContentRect.width(); + } + + public float contentHeight() { + return mContentRect.height(); + } + + public RectF getContentRect() { + return mContentRect; + } + + public MPPointF getContentCenter() { + return MPPointF.getInstance(mContentRect.centerX(), mContentRect.centerY()); + } + + public float getChartHeight() { + return mChartHeight; + } + + public float getChartWidth() { + return mChartWidth; + } + + /** + * Returns the smallest extension of the content rect (width or height). + * + * @return + */ + public float getSmallestContentExtension() { + return Math.min(mContentRect.width(), mContentRect.height()); + } + + /** + * ################ ################ ################ ################ + */ + /** CODE BELOW THIS RELATED TO SCALING AND GESTURES */ + + /** + * Zooms in by 1.4f, x and y are the coordinates (in pixels) of the zoom + * center. + * + * @param x + * @param y + */ + public Matrix zoomIn(float x, float y) { + + Matrix save = new Matrix(); + zoomIn(x, y, save); + return save; + } + + public void zoomIn(float x, float y, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(1.4f, 1.4f, x, y); + } + + /** + * Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom + * center. + */ + public Matrix zoomOut(float x, float y) { + + Matrix save = new Matrix(); + zoomOut(x, y, save); + return save; + } + + public void zoomOut(float x, float y, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(0.7f, 0.7f, x, y); + } + + /** + * Zooms out to original size. + * @param outputMatrix + */ + public void resetZoom(Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(1.0f, 1.0f, 0.0f, 0.0f); + } + + /** + * Post-scales by the specified scale factors. + * + * @param scaleX + * @param scaleY + * @return + */ + public Matrix zoom(float scaleX, float scaleY) { + + Matrix save = new Matrix(); + zoom(scaleX, scaleY, save); + return save; + } + + public void zoom(float scaleX, float scaleY, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(scaleX, scaleY); + } + + /** + * Post-scales by the specified scale factors. x and y is pivot. + * + * @param scaleX + * @param scaleY + * @param x + * @param y + * @return + */ + public Matrix zoom(float scaleX, float scaleY, float x, float y) { + + Matrix save = new Matrix(); + zoom(scaleX, scaleY, x, y, save); + return save; + } + + public void zoom(float scaleX, float scaleY, float x, float y, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(scaleX, scaleY, x, y); + } + + /** + * Sets the scale factor to the specified values. + * + * @param scaleX + * @param scaleY + * @return + */ + public Matrix setZoom(float scaleX, float scaleY) { + + Matrix save = new Matrix(); + setZoom(scaleX, scaleY, save); + return save; + } + + public void setZoom(float scaleX, float scaleY, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.setScale(scaleX, scaleY); + } + + /** + * Sets the scale factor to the specified values. x and y is pivot. + * + * @param scaleX + * @param scaleY + * @param x + * @param y + * @return + */ + public Matrix setZoom(float scaleX, float scaleY, float x, float y) { + + Matrix save = new Matrix(); + save.set(mMatrixTouch); + + save.setScale(scaleX, scaleY, x, y); + + return save; + } + + protected float[] valsBufferForFitScreen = new float[9]; + + /** + * Resets all zooming and dragging and makes the chart fit exactly it's + * bounds. + */ + public Matrix fitScreen() { + + Matrix save = new Matrix(); + fitScreen(save); + return save; + } + + /** + * Resets all zooming and dragging and makes the chart fit exactly it's + * bounds. Output Matrix is available for those who wish to cache the object. + */ + public void fitScreen(Matrix outputMatrix) { + mMinScaleX = 1f; + mMinScaleY = 1f; + + outputMatrix.set(mMatrixTouch); + + float[] vals = valsBufferForFitScreen; + for (int i = 0; i < 9; i++) { + vals[i] = 0; + } + + outputMatrix.getValues(vals); + + // reset all translations and scaling + vals[Matrix.MTRANS_X] = 0f; + vals[Matrix.MTRANS_Y] = 0f; + vals[Matrix.MSCALE_X] = 1f; + vals[Matrix.MSCALE_Y] = 1f; + + outputMatrix.setValues(vals); + } + + /** + * Post-translates to the specified points. Less Performant. + * + * @param transformedPts + * @return + */ + public Matrix translate(final float[] transformedPts) { + + Matrix save = new Matrix(); + translate(transformedPts, save); + return save; + } + + /** + * Post-translates to the specified points. Output matrix allows for caching objects. + * + * @param transformedPts + * @return + */ + public void translate(final float[] transformedPts, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + final float x = transformedPts[0] - offsetLeft(); + final float y = transformedPts[1] - offsetTop(); + outputMatrix.postTranslate(-x, -y); + } + + protected Matrix mCenterViewPortMatrixBuffer = new Matrix(); + + /** + * Centers the viewport around the specified position (x-index and y-value) + * in the chart. Centering the viewport outside the bounds of the chart is + * not possible. Makes most sense in combination with the + * setScaleMinima(...) method. + * + * @param transformedPts the position to center view viewport to + * @param view + * @return save + */ + public void centerViewPort(final float[] transformedPts, final View view) { + + Matrix save = mCenterViewPortMatrixBuffer; + save.reset(); + save.set(mMatrixTouch); + + final float x = transformedPts[0] - offsetLeft(); + final float y = transformedPts[1] - offsetTop(); + + save.postTranslate(-x, -y); + + refresh(save, view, true); + } + + /** + * buffer for storing the 9 matrix values of a 3x3 matrix + */ + protected final float[] matrixBuffer = new float[9]; + + /** + * call this method to refresh the graph with a given matrix + * + * @param newMatrix + * @return + */ + public Matrix refresh(Matrix newMatrix, View chart, boolean invalidate) { + + mMatrixTouch.set(newMatrix); + + // make sure scale and translation are within their bounds + limitTransAndScale(mMatrixTouch, mContentRect); + + if (invalidate) + chart.invalidate(); + + newMatrix.set(mMatrixTouch); + return newMatrix; + } + + /** + * limits the maximum scale and X translation of the given matrix + * + * @param matrix + */ + public void limitTransAndScale(Matrix matrix, RectF content) { + + matrix.getValues(matrixBuffer); + + float curTransX = matrixBuffer[Matrix.MTRANS_X]; + float curScaleX = matrixBuffer[Matrix.MSCALE_X]; + + float curTransY = matrixBuffer[Matrix.MTRANS_Y]; + float curScaleY = matrixBuffer[Matrix.MSCALE_Y]; + + // min scale-x is 1f + mScaleX = Math.min(Math.max(mMinScaleX, curScaleX), mMaxScaleX); + + // min scale-y is 1f + mScaleY = Math.min(Math.max(mMinScaleY, curScaleY), mMaxScaleY); + + float width = 0f; + float height = 0f; + + if (content != null) { + width = content.width(); + height = content.height(); + } + + float maxTransX = -width * (mScaleX - 1f); + mTransX = Math.min(Math.max(curTransX, maxTransX - mTransOffsetX), mTransOffsetX); + + float maxTransY = height * (mScaleY - 1f); + mTransY = Math.max(Math.min(curTransY, maxTransY + mTransOffsetY), -mTransOffsetY); + + matrixBuffer[Matrix.MTRANS_X] = mTransX; + matrixBuffer[Matrix.MSCALE_X] = mScaleX; + + matrixBuffer[Matrix.MTRANS_Y] = mTransY; + matrixBuffer[Matrix.MSCALE_Y] = mScaleY; + + matrix.setValues(matrixBuffer); + } + + /** + * Sets the minimum scale factor for the x-axis + * + * @param xScale + */ + public void setMinimumScaleX(float xScale) { + + if (xScale < 1f) + xScale = 1f; + + mMinScaleX = xScale; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + + /** + * Sets the maximum scale factor for the x-axis + * + * @param xScale + */ + public void setMaximumScaleX(float xScale) { + + if (xScale == 0.f) + xScale = Float.MAX_VALUE; + + mMaxScaleX = xScale; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + + /** + * Sets the minimum and maximum scale factors for the x-axis + * + * @param minScaleX + * @param maxScaleX + */ + public void setMinMaxScaleX(float minScaleX, float maxScaleX) { + + if (minScaleX < 1f) + minScaleX = 1f; + + if (maxScaleX == 0.f) + maxScaleX = Float.MAX_VALUE; + + mMinScaleX = minScaleX; + mMaxScaleX = maxScaleX; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + + /** + * Sets the minimum scale factor for the y-axis + * + * @param yScale + */ + public void setMinimumScaleY(float yScale) { + + if (yScale < 1f) + yScale = 1f; + + mMinScaleY = yScale; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + + /** + * Sets the maximum scale factor for the y-axis + * + * @param yScale + */ + public void setMaximumScaleY(float yScale) { + + if (yScale == 0.f) + yScale = Float.MAX_VALUE; + + mMaxScaleY = yScale; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + + public void setMinMaxScaleY(float minScaleY, float maxScaleY) { + + if (minScaleY < 1f) + minScaleY = 1f; + + if (maxScaleY == 0.f) + maxScaleY = Float.MAX_VALUE; + + mMinScaleY = minScaleY; + mMaxScaleY = maxScaleY; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + + /** + * Returns the charts-touch matrix used for translation and scale on touch. + * + * @return + */ + public Matrix getMatrixTouch() { + return mMatrixTouch; + } + + /** + * ################ ################ ################ ################ + */ + /** + * BELOW METHODS FOR BOUNDS CHECK + */ + + public boolean isInBoundsX(float x) { + return isInBoundsLeft(x) && isInBoundsRight(x); + } + + public boolean isInBoundsY(float y) { + return isInBoundsTop(y) && isInBoundsBottom(y); + } + + public boolean isInBounds(float x, float y) { + return isInBoundsX(x) && isInBoundsY(y); + } + + public boolean isInBoundsLeft(float x) { + return mContentRect.left <= x + 1; + } + + public boolean isInBoundsRight(float x) { + x = (float) ((int) (x * 100.f)) / 100.f; + return mContentRect.right >= x - 1; + } + + public boolean isInBoundsTop(float y) { + return mContentRect.top <= y; + } + + public boolean isInBoundsBottom(float y) { + y = (float) ((int) (y * 100.f)) / 100.f; + return mContentRect.bottom >= y; + } + + /** + * returns the current x-scale factor + */ + public float getScaleX() { + return mScaleX; + } + + /** + * returns the current y-scale factor + */ + public float getScaleY() { + return mScaleY; + } + + public float getMinScaleX() { + return mMinScaleX; + } + + public float getMaxScaleX() { + return mMaxScaleX; + } + + public float getMinScaleY() { + return mMinScaleY; + } + + public float getMaxScaleY() { + return mMaxScaleY; + } + + /** + * Returns the translation (drag / pan) distance on the x-axis + * + * @return + */ + public float getTransX() { + return mTransX; + } + + /** + * Returns the translation (drag / pan) distance on the y-axis + * + * @return + */ + public float getTransY() { + return mTransY; + } + + /** + * if the chart is fully zoomed out, return true + * + * @return + */ + public boolean isFullyZoomedOut() { + + return isFullyZoomedOutX() && isFullyZoomedOutY(); + } + + /** + * Returns true if the chart is fully zoomed out on it's y-axis (vertical). + * + * @return + */ + public boolean isFullyZoomedOutY() { + return !(mScaleY > mMinScaleY || mMinScaleY > 1f); + } + + /** + * Returns true if the chart is fully zoomed out on it's x-axis + * (horizontal). + * + * @return + */ + public boolean isFullyZoomedOutX() { + return !(mScaleX > mMinScaleX || mMinScaleX > 1f); + } + + /** + * Set an offset in dp that allows the user to drag the chart over it's + * bounds on the x-axis. + * + * @param offset + */ + public void setDragOffsetX(float offset) { + mTransOffsetX = Utils.convertDpToPixel(offset); + } + + /** + * Set an offset in dp that allows the user to drag the chart over it's + * bounds on the y-axis. + * + * @param offset + */ + public void setDragOffsetY(float offset) { + mTransOffsetY = Utils.convertDpToPixel(offset); + } + + /** + * Returns true if both drag offsets (x and y) are zero or smaller. + * + * @return + */ + public boolean hasNoDragOffset() { + return mTransOffsetX <= 0 && mTransOffsetY <= 0; + } + + /** + * Returns true if the chart is not yet fully zoomed out on the x-axis + * + * @return + */ + public boolean canZoomOutMoreX() { + return mScaleX > mMinScaleX; + } + + /** + * Returns true if the chart is not yet fully zoomed in on the x-axis + * + * @return + */ + public boolean canZoomInMoreX() { + return mScaleX < mMaxScaleX; + } + + /** + * Returns true if the chart is not yet fully zoomed out on the y-axis + * + * @return + */ + public boolean canZoomOutMoreY() { + return mScaleY > mMinScaleY; + } + + /** + * Returns true if the chart is not yet fully zoomed in on the y-axis + * + * @return + */ + public boolean canZoomInMoreY() { + return mScaleY < mMaxScaleY; + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ApproximatorTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ApproximatorTest.java new file mode 100644 index 0000000000..784c290ceb --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ApproximatorTest.java @@ -0,0 +1,43 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.filter.Approximator; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 07/06/16. + */ +public class ApproximatorTest { + + @Test + public void testApproximation() { + + float[] points = new float[]{ + 10, 20, + 20, 30, + 25, 25, + 30, 28, + 31, 31, + 33, 33, + 40, 40, + 44, 40, + 48, 23, + 50, 20, + 55, 20, + 60, 25}; + + assertEquals(24, points.length); + + Approximator a = new Approximator(); + + float[] reduced = a.reduceWithDouglasPeucker(points, 2); + + assertEquals(18, reduced.length); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java new file mode 100644 index 0000000000..05cb2f3592 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java @@ -0,0 +1,105 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.renderer.AxisRenderer; +import com.github.mikephil.charting.renderer.YAxisRenderer; + +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 31/05/16. + */ +public class AxisRendererTest { + + + @Test + public void testComputeAxisValues() { + + YAxis yAxis = new YAxis(); + yAxis.setLabelCount(6); + AxisRenderer renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 100, false); + float[] entries = yAxis.mEntries; + + assertEquals(6, entries.length); + assertEquals(20, entries[1] - entries[0], 0.01); // interval 20 + assertEquals(0, entries[0], 0.01); + assertEquals(100, entries[entries.length - 1], 0.01); + + yAxis = new YAxis(); + yAxis.setLabelCount(6); + yAxis.setGranularity(50f); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 100, false); + entries = yAxis.mEntries; + + assertEquals(3, entries.length); + assertEquals(50, entries[1] - entries[0], 0.01); // interval 50 + assertEquals(0, entries[0], 0.01); + assertEquals(100, entries[entries.length - 1], 0.01); + + yAxis = new YAxis(); + yAxis.setLabelCount(5, true); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 100, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(25, entries[1] - entries[0], 0.01); // interval 25 + assertEquals(0, entries[0], 0.01); + assertEquals(100, entries[entries.length - 1], 0.01); + + yAxis = new YAxis(); + yAxis.setLabelCount(5, true); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 0.01f, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(0.0025, entries[1] - entries[0], 0.0001); + assertEquals(0, entries[0], 0.0001); + assertEquals(0.01, entries[entries.length - 1], 0.0001); + + yAxis = new YAxis(); + yAxis.setLabelCount(5, false); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 0.01f, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(0.0020, entries[1] - entries[0], 0.0001); + assertEquals(0, entries[0], 0.0001); + assertEquals(0.0080, entries[entries.length - 1], 0.0001); + + yAxis = new YAxis(); + yAxis.setLabelCount(6); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(-50, 50, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(-40, entries[0], 0.0001); + assertEquals(0, entries[2], 0.0001); + assertEquals(40, entries[entries.length - 1], 0.0001); + + yAxis = new YAxis(); + yAxis.setLabelCount(6); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(-50, 100, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(-30, entries[0], 0.0001); + assertEquals(30, entries[2], 0.0001); + assertEquals(90, entries[entries.length - 1], 0.0001); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/BarDataTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/BarDataTest.java new file mode 100644 index 0000000000..99954bce27 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/BarDataTest.java @@ -0,0 +1,72 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 06/06/16. + */ +public class BarDataTest { + + @Test + public void testGroupBars() { + + float groupSpace = 5f; + float barSpace = 1f; + + List values1 = new ArrayList<>(); + List values2 = new ArrayList<>(); + + for(int i = 0; i < 5; i++) { + values1.add(new BarEntry(i, 50)); + values2.add(new BarEntry(i, 60)); + } + + BarDataSet barDataSet1 = new BarDataSet(values1, "Set1"); + BarDataSet barDataSet2 = new BarDataSet(values2, "Set2"); + + BarData data = new BarData(barDataSet1, barDataSet2); + data.setBarWidth(10f); + + float groupWidth = data.getGroupWidth(groupSpace, barSpace); + assertEquals(27f, groupWidth, 0.01f); + + assertEquals(0f, values1.get(0).getX(), 0.01f); + assertEquals(1f, values1.get(1).getX(), 0.01f); + + data.groupBars(1000, groupSpace, barSpace); + + // 1000 + 2.5 + 0.5 + 5 + assertEquals(1008f, values1.get(0).getX(), 0.01f); + assertEquals(1019f, values2.get(0).getX(), 0.01f); + assertEquals(1035f, values1.get(1).getX(), 0.01f); + assertEquals(1046f, values2.get(1).getX(), 0.01f); + + data.groupBars(-1000, groupSpace, barSpace); + + assertEquals(-992f, values1.get(0).getX(), 0.01f); + assertEquals(-981f, values2.get(0).getX(), 0.01f); + assertEquals(-965f, values1.get(1).getX(), 0.01f); + assertEquals(-954f, values2.get(1).getX(), 0.01f); + + data.setBarWidth(20f); + groupWidth = data.getGroupWidth(groupSpace, barSpace); + assertEquals(47f, groupWidth, 0.01f); + + data.setBarWidth(10f); + data.groupBars(-20, groupSpace, barSpace); + + assertEquals(-12f, values1.get(0).getX(), 0.01f); + assertEquals(-1f, values2.get(0).getX(), 0.01f); + assertEquals(15f, values1.get(1).getX(), 0.01f); + assertEquals(26f, values2.get(1).getX(), 0.01f); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java new file mode 100644 index 0000000000..ae464eefd0 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java @@ -0,0 +1,211 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.data.ScatterDataSet; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +/** + * Created by philipp on 06/06/16. + */ +public class ChartDataTest { + + @Test + public void testDynamicChartData() { + + List entries1 = new ArrayList(); + entries1.add(new Entry(10, 10)); + entries1.add(new Entry(15, -2)); + entries1.add(new Entry(21, 50)); + + ScatterDataSet set1 = new ScatterDataSet(entries1, ""); + + List entries2 = new ArrayList(); + entries2.add(new Entry(-1, 10)); + entries2.add(new Entry(10, 2)); + entries2.add(new Entry(20, 5)); + + ScatterDataSet set2 = new ScatterDataSet(entries2, ""); + + ScatterData data = new ScatterData(set1, set2); + + assertEquals(-2, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(50f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(6, data.getEntryCount()); + + assertEquals(-1f, data.getXMin(), 0.01f); + assertEquals(21f, data.getXMax(), 0.01f); + + assertEquals(-2f, data.getYMin(), 0.01f); + assertEquals(50f, data.getYMax(), 0.01f); + + assertEquals(3, data.getMaxEntryCountSet().getEntryCount()); + + // now add and remove values + data.addEntry(new Entry(-10, -10), 0); + + assertEquals(set1, data.getMaxEntryCountSet()); + assertEquals(4, data.getMaxEntryCountSet().getEntryCount()); + + assertEquals(-10f, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(50f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-10f, data.getXMin(), 0.01f); + assertEquals(21f, data.getXMax(), 0.01f); + + assertEquals(-10f, data.getYMin(), 0.01f); + assertEquals(50f, data.getYMax(), 0.01f); + + data.addEntry(new Entry(-100, 100), 0); + data.addEntry(new Entry(0, -100), 0); + + assertEquals(-100f, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(100f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + // right axis will adapt left axis values if no right axis values are present + assertEquals(-100, data.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(100f, data.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + List entries3 = new ArrayList(); + entries3.add(new Entry(0, 200)); + entries3.add(new Entry(0, -50)); + + ScatterDataSet set3 = new ScatterDataSet(entries3, ""); + set3.setAxisDependency(YAxis.AxisDependency.RIGHT); + + data.addDataSet(set3); + + assertEquals(3, data.getDataSetCount()); + + assertEquals(-100f, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(100f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-50f, data.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(200f, data.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + LineData lineData = new LineData(); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertEquals(0, lineData.getDataSetCount()); + + List lineEntries1 = new ArrayList(); + lineEntries1.add(new Entry(10, 90)); + lineEntries1.add(new Entry(1000, 1000)); + + LineDataSet lineSet1 = new LineDataSet(lineEntries1, ""); + + lineData.addDataSet(lineSet1); + + assertEquals(1, lineData.getDataSetCount()); + assertEquals(2, lineSet1.getEntryCount()); + assertEquals(2, lineData.getEntryCount()); + + assertEquals(10, lineData.getXMin(), 0.01f); + assertEquals(1000f, lineData.getXMax(), 0.01f); + + assertEquals(90, lineData.getYMin(), 0.01f); + assertEquals(1000, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(1000, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + List lineEntries2 = new ArrayList(); + lineEntries2.add(new Entry(-1000, 2000)); + lineEntries2.add(new Entry(2000, -3000)); + + Entry e = new Entry(-1000, 2500); + lineEntries2.add(e); + + LineDataSet lineSet2 = new LineDataSet(lineEntries2, ""); + lineSet2.setAxisDependency(YAxis.AxisDependency.RIGHT); + + lineData.addDataSet(lineSet2); + + assertEquals(2, lineData.getDataSetCount()); + assertEquals(3, lineSet2.getEntryCount()); + assertEquals(5, lineData.getEntryCount()); + + assertEquals(-1000, lineData.getXMin(), 0.01f); + assertEquals(2000, lineData.getXMax(), 0.01f); + + assertEquals(-3000, lineData.getYMin(), 0.01f); + assertEquals(2500, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-3000, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(2500, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertTrue(lineData.removeEntry(e, 1)); + + assertEquals(-1000, lineData.getXMin(), 0.01f); + assertEquals(2000, lineData.getXMax(), 0.01f); + + assertEquals(-3000, lineData.getYMin(), 0.01f); + assertEquals(2000, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-3000, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(2000, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertEquals(2, lineData.getDataSetCount()); + assertTrue(lineData.removeDataSet(lineSet2)); + assertEquals(1, lineData.getDataSetCount()); + + assertEquals(10, lineData.getXMin(), 0.01f); + assertEquals(1000, lineData.getXMax(), 0.01f); + + assertEquals(90, lineData.getYMin(), 0.01f); + assertEquals(1000, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(1000, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertTrue(lineData.removeDataSet(lineSet1)); + assertEquals(0, lineData.getDataSetCount()); + + assertEquals(Float.MAX_VALUE, lineData.getXMin(), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getXMax(), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertFalse(lineData.removeDataSet(lineSet1)); + assertFalse(lineData.removeDataSet(lineSet2)); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java new file mode 100644 index 0000000000..3be28d2a57 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java @@ -0,0 +1,238 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.ScatterDataSet; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +/** + * Created by philipp on 31/05/16. + */ +public class DataSetTest { + + @Test + public void testCalcMinMax() { + + List entries = new ArrayList(); + entries.add(new Entry(10, 10)); + entries.add(new Entry(15, 2)); + entries.add(new Entry(21, 5)); + + ScatterDataSet set = new ScatterDataSet(entries, ""); + + assertEquals(10f, set.getXMin(), 0.01f); + assertEquals(21f, set.getXMax(), 0.01f); + + assertEquals(2f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + + assertEquals(3, set.getEntryCount()); + + set.addEntry(new Entry(25, 1)); + + assertEquals(10f, set.getXMin(), 0.01f); + assertEquals(25f, set.getXMax(), 0.01f); + + assertEquals(1f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + + assertEquals(4, set.getEntryCount()); + + set.removeEntry(3); + + assertEquals(10f, set.getXMin(), 0.01f); + assertEquals(21, set.getXMax(), 0.01f); + + assertEquals(2f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + } + + @Test + public void testAddRemoveEntry() { + + List entries = new ArrayList(); + entries.add(new Entry(10, 10)); + entries.add(new Entry(15, 2)); + entries.add(new Entry(21, 5)); + + ScatterDataSet set = new ScatterDataSet(entries, ""); + + assertEquals(3, set.getEntryCount()); + + set.addEntryOrdered(new Entry(5, 1)); + + assertEquals(4, set.getEntryCount()); + + assertEquals(5, set.getXMin(), 0.01f); + assertEquals(21, set.getXMax(), 0.01f); + + assertEquals(1f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + + assertEquals(5, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(1, set.getEntryForIndex(0).getY(), 0.01f); + + set.addEntryOrdered(new Entry(20, 50)); + + assertEquals(5, set.getEntryCount()); + + assertEquals(20, set.getEntryForIndex(3).getX(), 0.01f); + assertEquals(50, set.getEntryForIndex(3).getY(), 0.01f); + + assertTrue(set.removeEntry(3)); + + assertEquals(4, set.getEntryCount()); + + assertEquals(21, set.getEntryForIndex(3).getX(), 0.01f); + assertEquals(5, set.getEntryForIndex(3).getY(), 0.01f); + + assertEquals(5, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(1, set.getEntryForIndex(0).getY(), 0.01f); + + assertTrue(set.removeFirst()); + + assertEquals(3, set.getEntryCount()); + + assertEquals(10, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(10, set.getEntryForIndex(0).getY(), 0.01f); + + set.addEntryOrdered(new Entry(15, 3)); + + assertEquals(4, set.getEntryCount()); + + assertEquals(15, set.getEntryForIndex(1).getX(), 0.01f); + assertEquals(3, set.getEntryForIndex(1).getY(), 0.01f); + + assertEquals(21, set.getEntryForIndex(3).getX(), 0.01f); + assertEquals(5, set.getEntryForIndex(3).getY(), 0.01f); + + assertTrue(set.removeLast()); + + assertEquals(3, set.getEntryCount()); + + assertEquals(15, set.getEntryForIndex(2).getX(), 0.01f); + assertEquals(2, set.getEntryForIndex(2).getY(), 0.01f); + + assertTrue(set.removeLast()); + + assertEquals(2, set.getEntryCount()); + + assertTrue(set.removeLast()); + + assertEquals(1, set.getEntryCount()); + + assertEquals(10, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(10, set.getEntryForIndex(0).getY(), 0.01f); + + assertTrue(set.removeLast()); + + assertEquals(0, set.getEntryCount()); + + assertFalse(set.removeLast()); + assertFalse(set.removeFirst()); + } + + @Test + public void testGetEntryForXValue() { + + List entries = new ArrayList(); + entries.add(new Entry(10, 10)); + entries.add(new Entry(15, 5)); + entries.add(new Entry(21, 5)); + + ScatterDataSet set = new ScatterDataSet(entries, ""); + + Entry closest = set.getEntryForXValue(17, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(15, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(17, Float.NaN, DataSet.Rounding.DOWN); + assertEquals(15, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(15, Float.NaN, DataSet.Rounding.DOWN); + assertEquals(15, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(14, Float.NaN, DataSet.Rounding.DOWN); + assertEquals(10, closest.getX(), 0.01f); + assertEquals(10, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(17, Float.NaN, DataSet.Rounding.UP); + assertEquals(21, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(21, Float.NaN, DataSet.Rounding.UP); + assertEquals(21, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(21, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(21, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + } + + @Test + public void testGetEntryForXValueWithDuplicates() { + + // sorted list of values (by x position) + List values = new ArrayList(); + values.add(new Entry(0, 10)); + values.add(new Entry(1, 20)); + values.add(new Entry(2, 30)); + values.add(new Entry(3, 40)); + values.add(new Entry(3, 50)); // duplicate + values.add(new Entry(4, 60)); + values.add(new Entry(4, 70)); // duplicate + values.add(new Entry(5, 80)); + values.add(new Entry(6, 90)); + values.add(new Entry(7, 100)); + values.add(new Entry(8, 110)); + values.add(new Entry(8, 120)); // duplicate + + ScatterDataSet set = new ScatterDataSet(values, ""); + + Entry closest = set.getEntryForXValue(0, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(0, closest.getX(), 0.01f); + assertEquals(10, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(5, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(5, closest.getX(), 0.01f); + assertEquals(80, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(5.4f, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(5, closest.getX(), 0.01f); + assertEquals(80, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(4.6f, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(5, closest.getX(), 0.01f); + assertEquals(80, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(7, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(7, closest.getX(), 0.01f); + assertEquals(100, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(4f, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(4, closest.getX(), 0.01f); + assertEquals(60, closest.getY(), 0.01f); + + List entries = set.getEntriesForXValue(4f); + assertEquals(2, entries.size()); + assertEquals(60, entries.get(0).getY(), 0.01f); + assertEquals(70, entries.get(1).getY(), 0.01f); + + entries = set.getEntriesForXValue(3.5f); + assertEquals(0, entries.size()); + + entries = set.getEntriesForXValue(2f); + assertEquals(1, entries.size()); + assertEquals(30, entries.get(0).getY(), 0.01f); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java new file mode 100644 index 0000000000..f1e1e0279e --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java @@ -0,0 +1,95 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.formatter.LargeValueFormatter; + +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 06/06/16. + */ +public class LargeValueFormatterTest { + + @Test + public void test() { + + LargeValueFormatter formatter = new LargeValueFormatter(); + + String result = formatter.getFormattedValue(5f, null); + assertEquals("5", result); + + result = formatter.getFormattedValue(5.5f, null); + assertEquals("5.5", result); + + result = formatter.getFormattedValue(50f, null); + assertEquals("50", result); + + result = formatter.getFormattedValue(50.5f, null); + assertEquals("50.5", result); + + result = formatter.getFormattedValue(500f, null); + assertEquals("500", result); + + result = formatter.getFormattedValue(1100f, null); + assertEquals("1.1k", result); + + result = formatter.getFormattedValue(10000f, null); + assertEquals("10k", result); + + result = formatter.getFormattedValue(10500f, null); + assertEquals("10.5k", result); + + result = formatter.getFormattedValue(100000f, null); + assertEquals("100k", result); + + result = formatter.getFormattedValue(1000000f, null); + assertEquals("1m", result); + + result = formatter.getFormattedValue(1500000f, null); + assertEquals("1.5m", result); + + result = formatter.getFormattedValue(9500000f, null); + assertEquals("9.5m", result); + + result = formatter.getFormattedValue(22200000f, null); + assertEquals("22.2m", result); + + result = formatter.getFormattedValue(222000000f, null); + assertEquals("222m", result); + + result = formatter.getFormattedValue(1000000000f, null); + assertEquals("1b", result); + + result = formatter.getFormattedValue(9900000000f, null); + assertEquals("9.9b", result); + + result = formatter.getFormattedValue(99000000000f, null); + assertEquals("99b", result); + + result = formatter.getFormattedValue(99500000000f, null); + assertEquals("99.5b", result); + + result = formatter.getFormattedValue(999000000000f, null); + assertEquals("999b", result); + + result = formatter.getFormattedValue(1000000000000f, null); + assertEquals("1t", result); + + formatter.setSuffix(new String[]{"", "k", "m", "b", "t", "q"}); // quadrillion support + result = formatter.getFormattedValue(1000000000000000f, null); + assertEquals("1q", result); + + result = formatter.getFormattedValue(1100000000000000f, null); + assertEquals("1.1q", result); + + result = formatter.getFormattedValue(10000000000000000f, null); + assertEquals("10q", result); + + result = formatter.getFormattedValue(13300000000000000f, null); + assertEquals("13.3q", result); + + result = formatter.getFormattedValue(100000000000000000f, null); + assertEquals("100q", result); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java new file mode 100644 index 0000000000..e1dbe81be9 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java @@ -0,0 +1,240 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.utils.ObjectPool; + +import junit.framework.Assert; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by otheruser on 6/28/16. + */ +public class ObjectPoolTest { + + static class TestPoolable extends ObjectPool.Poolable{ + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(4, new TestPoolable(0,0)); + } + + public int foo = 0; + public int bar = 0; + + protected ObjectPool.Poolable instantiate(){ + return new TestPoolable(0,0); + } + + private TestPoolable(int foo, int bar){ + this.foo = foo; + this.bar = bar; + } + + public static TestPoolable getInstance(int foo, int bar){ + TestPoolable result = pool.get(); + result.foo = foo; + result.bar = bar; + return result; + } + + public static void recycleInstance(TestPoolable instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + public static ObjectPool getPool(){ + return pool; + } + + } + + @Test + public void testObjectPool(){ + + int poolCapacity = TestPoolable.getPool().getPoolCapacity(); + int poolCount = TestPoolable.getPool().getPoolCount(); + TestPoolable testPoolable; + ArrayList testPoolables = new ArrayList<>(); + + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + testPoolable = TestPoolable.getInstance(6,7); + Assert.assertEquals(6, testPoolable.foo); + Assert.assertEquals(7, testPoolable.bar); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(3, poolCount); + + TestPoolable.recycleInstance(testPoolable); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + + testPoolable = TestPoolable.getInstance(20,30); + Assert.assertEquals(20, testPoolable.foo); + Assert.assertEquals(30, testPoolable.bar); + + TestPoolable.recycleInstance(testPoolable); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(1,2)); + testPoolables.add(TestPoolable.getInstance(3,5)); + testPoolables.add(TestPoolable.getInstance(6,8)); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(0, poolCount); + + + TestPoolable.recycleInstances(testPoolables); + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + testPoolables.clear(); + + + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(1,2)); + testPoolables.add(TestPoolable.getInstance(3,5)); + testPoolables.add(TestPoolable.getInstance(6,8)); + testPoolables.add(TestPoolable.getInstance(8,9)); + Assert.assertEquals(12, testPoolables.get(0).foo); + Assert.assertEquals(24, testPoolables.get(0).bar); + Assert.assertEquals(1, testPoolables.get(1).foo); + Assert.assertEquals(2, testPoolables.get(1).bar); + Assert.assertEquals(3, testPoolables.get(2).foo); + Assert.assertEquals(5, testPoolables.get(2).bar); + Assert.assertEquals(6, testPoolables.get(3).foo); + Assert.assertEquals(8, testPoolables.get(3).bar); + Assert.assertEquals(8, testPoolables.get(4).foo); + Assert.assertEquals(9, testPoolables.get(4).bar); + + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(3, poolCount); + + TestPoolable.recycleInstances(testPoolables); + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(8, poolCapacity); + Assert.assertEquals(8, poolCount); + + testPoolables.clear(); + + + testPoolables.add(TestPoolable.getInstance(0,0)); + testPoolables.add(TestPoolable.getInstance(6,8)); + testPoolables.add(TestPoolable.getInstance(1,2)); + testPoolables.add(TestPoolable.getInstance(3,5)); + testPoolables.add(TestPoolable.getInstance(8,9)); + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(6,8)); + testPoolables.add(TestPoolable.getInstance(6,8)); + Assert.assertEquals(0, testPoolables.get(0).foo); + Assert.assertEquals(0, testPoolables.get(0).bar); + Assert.assertEquals(6, testPoolables.get(1).foo); + Assert.assertEquals(8, testPoolables.get(1).bar); + Assert.assertEquals(1, testPoolables.get(2).foo); + Assert.assertEquals(2, testPoolables.get(2).bar); + Assert.assertEquals(3, testPoolables.get(3).foo); + Assert.assertEquals(5, testPoolables.get(3).bar); + Assert.assertEquals(8, testPoolables.get(4).foo); + Assert.assertEquals(9, testPoolables.get(4).bar); + Assert.assertEquals(12, testPoolables.get(5).foo); + Assert.assertEquals(24, testPoolables.get(5).bar); + Assert.assertEquals(12, testPoolables.get(6).foo); + Assert.assertEquals(24, testPoolables.get(6).bar); + Assert.assertEquals(12, testPoolables.get(7).foo); + Assert.assertEquals(24, testPoolables.get(7).bar); + Assert.assertEquals(6, testPoolables.get(8).foo); + Assert.assertEquals(8, testPoolables.get(8).bar); + Assert.assertEquals(6, testPoolables.get(9).foo); + Assert.assertEquals(8, testPoolables.get(9).bar); + + for(TestPoolable p : testPoolables){ + TestPoolable.recycleInstance(p); + } + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(16, poolCount); + + testPoolable = TestPoolable.getInstance(9001,9001); + Assert.assertEquals(9001, testPoolable.foo); + Assert.assertEquals(9001, testPoolable.bar); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(15, poolCount); + + + TestPoolable.recycleInstance(testPoolable); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(16, poolCount); + + Exception e = null; + try{ + // expect an exception. + TestPoolable.recycleInstance(testPoolable); + }catch (Exception ex){ + e = ex; + }finally{ + Assert.assertEquals(e.getMessage(), true, e != null); + } + + testPoolables.clear(); + + TestPoolable.getPool().setReplenishPercentage(0.5f); + int i = 16; + while(i > 0){ + testPoolables.add(TestPoolable.getInstance(0,0)); + i--; + } + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(0, poolCount); + + testPoolables.add(TestPoolable.getInstance(0,0)); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(7, poolCount); + + + } + +} diff --git a/Projects_using_MPAndroidChart.txt b/Projects_using_MPAndroidChart.txt deleted file mode 100644 index d6adfc1da6..0000000000 --- a/Projects_using_MPAndroidChart.txt +++ /dev/null @@ -1,31 +0,0 @@ -References - -Blood Pressure Companion -https://play.google.com/store/apps/details?id=de.medando.bloodpressurecompanion - -Smoke Free - Stop smoking help -https://play.google.com/store/apps/details?id=com.portablepixels.smokefree - -Drivvo - Vehicle Management -https://play.google.com/store/apps/details?id=br.com.ctncardoso.ctncar - -Phone Addiction -https://play.google.com/store/apps/details?id=com.tutorialsface.phoneaddiction - -AS Sales Management -https://play.google.com/store/apps/details?id=com.armsoft.mtrade - -Notification Analyser -https://play.google.com/store/apps/details?id=com.tierep.notificationanalyser - -Bluetooth Terminal/Graphics -https://play.google.com/store/apps/details?id=com.emrctn.BluetoothGraphics - -HI - Health & Fitness Tracker - All-in-one Diet, Health & Fitness Tracker -https://play.google.com/store/apps/details?id=com.droidinfinity.healthplus - -БИТ.Лидер -https://play.google.com/store/apps/details?id=com.firstbit.bitlider - -My Expenses -https://play.google.com/store/apps/details?id=org.totschnig.myexpenses diff --git a/README.md b/README.md index bd8b9df4b5..30759eb357 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,57 @@ - [![Android Arsenal](http://img.shields.io/badge/Android%20Arsenal-MPAndroidChart-blue.svg?style=flat)](http://android-arsenal.com/details/1/741) [![Release](https://img.shields.io/github/release/PhilJay/MPAndroidChart.svg?label=maven central)](https://jitpack.io/#PhilJay/MPAndroidChart) [![API](https://img.shields.io/badge/API-8%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=8) +[![Twitter](https://img.shields.io/badge/Twitter-@PhilippJahoda-blue.svg?style=flat)](http://twitter.com/philippjahoda) +[![Twitter](https://img.shields.io/badge/Twitter-@mpandroidchart-blue.svg?style=flat)](http://twitter.com/mpandroidchart) +[![Android Arsenal](http://img.shields.io/badge/Android%20Arsenal-MPAndroidChart-orange.svg?style=flat)](http://android-arsenal.com/details/1/741) +[![Release](https://img.shields.io/github/release/PhilJay/MPAndroidChart.svg?label=maven central)](https://jitpack.io/#PhilJay/MPAndroidChart) [![API](https://img.shields.io/badge/API-8%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=8) -MPAndroidChart -======= +Remember: *It's all about the looks.* -A simple charting library for Android, supporting line-, bar-, scatter-, candlestick-, pie- and radarcharts (spider web), as well as scaling, dragging, selecting and animations. **Supporting Android 2.2 (API level 8)** and upwards. +![alt tag](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic.png) -Remember: *It's all about the looks.* +[**MPAndroidChart**](https://github.com/PhilJay/MPAndroidChart) :zap: is a powerful & easy to use chart library for Android. It runs on [API level 8](http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels) and upwards. -The **experimental** branch might contain new features that are still buggy. It is recommended to be safe and only make use of the code on the **master** branch. +As an additional feature, this library allows cross-platform development between Android and iOS as an iOS version of this library is also available: [**Charts**](https://github.com/danielgindi/Charts) :zap: -Forks, pull-requests or any other forms of contribution are **always welcome**. +Are you using this library? Let me know about it and I will add your project to the [**references**](https://github.com/PhilJay/MPAndroidChart/wiki/References). Donations ----- -If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated. +**This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated (and I love food, coffee and beer). Thank you! + +**PayPal** + + - [**Donate 5 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! + - [**Donate 10 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4C9TPE67F5PUQ): Wow, I am stunned. Let me take you to the movies! + - [**Donate 15 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! + - [**Donate 25 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H9JA4QX7UHXCY): That's some awesome stuff you did right there, dinner is on me! + - [**Donate 50 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZPQVJ2XRBSBYY): I really really want to support this project, great job! + - [**Donate 100 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KY7F59RYPCYCQ): You are the man! This project saved me hours (if not days) of struggle and hard work, simply awesome! + - Of course, you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! + +If you just want to be nice, you can check out and rate the new [**Telegram Chat Bot**]( https://storebot.me/bot/myalfred_bot) we created for scheduling meetings and other stuff. 😉 -PayPal -[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS) +## Xamarin + +Xamarin port (by [Flash3001](https://github.com/Flash3001)): *Android* - [GitHub](https://github.com/Flash3001/MPAndroidChart.Xamarin)/[NuGet](https://www.nuget.org/packages/MPAndroidChart/). *iOS* - [GitHub](https://github.com/Flash3001/iOSCharts.Xamarin)/[NuGet](https://www.nuget.org/packages/iOSCharts/). + +## Realm.io + +[MPAndroidChart-Realm](https://github.com/PhilJay/MPAndroidChart-Realm) allows to directly plot / draw data from [Realm.io](https://realm.io) mobile database. + +Spread the word +----- + +If you like this library, please tell others about it :two_hearts: :two_hearts: -Gittip + + + - - Support via Gittip - + - []()Follow me on **Twitter**: [**@PhilippJahoda**](https://twitter.com/PhilippJahoda) + - Contact me on **LinkedIn**: [**PhilippJahoda**](https://www.linkedin.com/in/philippjahoda/en) + - Look me up on **StackOverflow**: [**Philipp Jahoda**](http://stackoverflow.com/users/1590502/philipp-jahoda) -[![Gratipay](http://img.shields.io/gratipay/PhilJay.svg)](https://gratipay.com/PhilJay) Demo ----- @@ -38,35 +63,107 @@ For a brief overview of the most important features, please download the **PlayS Questions & Issues ----- -If you are having questions or problems, feel free to contact me. Since I would very much like that other users of this library **can also benefit** from your question, I am asking you to contact me via e-mail **only as a last option**. Instead, you should: +If you are having questions or problems, you should: - - Make sure you are using the latest version of the library. Check the [**release-section**](https://github.com/PhilJay/MPAndroidChart/releases). - - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) + - **Review your code**. Make absolutely sure that everything is correct on your side. + - Make sure you are using the **latest version** of the library. Check the [**release-section**](https://github.com/PhilJay/MPAndroidChart/releases). + - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.1/javadoc/) - Search or open questions on [**stackoverflow**](https://stackoverflow.com/search?q=mpandroidchart) with the `mpandroidchart` tag - Search [**known issues**](https://github.com/PhilJay/MPAndroidChart/issues) for your problem (open and closed) - - Create new issues (please **search known issues before**, do not create duplicate issues) - -You can let me know via e-mail that you have opened a stackoverflow question so that I might get to answering it more quickly. Thank you. + - Create new issues (please :fire: **search known issues before** :fire:, do not create duplicate issues) + - Check this: ["how not to contribute"](https://github.com/PhilJay/MPAndroidChart/wiki/How-not-to-contribute) + +Please do not expect answers to your questions if you have not considered all above mentioned approaches in advance. Features -======= +----- **Core features:** + - 8 different chart types - Scaling on both axes (with touch-gesture, axes separately or pinch-zoom) - Dragging / Panning (with touch-gesture) - Combined-Charts (line-, bar-, scatter-, candle-data) - - Finger drawing (draw values into the chart with touch-gesture) - - Highlighting values (with customizeable popup-views) - - Multiple / Separate Axes + - Dual (separate) Axes + - Customizable Axes (both x- and y-axis) + - Highlighting values (with customizable popup-views) - Save chart to SD-Card (as image, or as .txt file) - Predefined color templates - - Legends (generated automatically, customizeable) - - Customizeable Axes (both x- and y-axis) - - Animations (build up animations, on both x- and y-axis) + - Legends (generated automatically, customizable) + - Animations (build up animations, on both xPx- and yPx-axis) - Limit lines (providing additional information, maximums, ...) - - Fully customizeable (paints, typefaces, legends, colors, background, gestures, dashed lines, ...) + - Fully customizable (paints, typefaces, legends, colors, background, gestures, dashed lines, ...) + - Smooth zooming and scrolling for up to 30.000 data points in Line- and BarChart + - Gradle support + - Plotting data directly from [**Realm.io**](https://realm.io) mobile database: [**MPAndroidChart-Realm**](https://github.com/PhilJay/MPAndroidChart-Realm) :zap: + +Usage +----- + +In order to use the library, there are 4 different options: + +**1. Gradle dependency** (recommended) + + - Add the following to your project level `build.gradle`: + +```gradle +allprojects { + repositories { + maven { url "https://jitpack.io" } + } +} +``` + - Add this to your app `build.gradle`: -**Chart types:** +```gradle +dependencies { + compile 'com.github.PhilJay:MPAndroidChart:v3.0.1' +} +``` + +**2. Maven** +- Add the following to the `` section of your `pom.xml`: + + ```xml + + jitpack.io + https://jitpack.io + +``` +- Add the following to the `` section of your `pom.xml`: + + ```xml + + com.github.PhilJay + MPAndroidChart + v3.0.1 + +``` + +**3. jar file only** + - Download the [**latest .jar file**](https://github.com/PhilJay/MPAndroidChart/releases) from the releases section + - Copy the **mpandroidchartlibrary-version.jar** file into the `libs` folder of your Android application project + - Start using the library + +**4. clone whole repository** + - Open your **commandline-input** and navigate to the desired destination folder on your machine (where you want to place the library) + - Use the command `git clone https://github.com/PhilJay/MPAndroidChart.git` to download the full MPAndroidChart repository to your computer (this includes the folder of the library as well as the folder of the example project) + - Import the library folder (`MPChartLib`) into Android Studio (recommended) or your Eclipse workspace + - Add it as a reference to your project: + - [referencing library projects in Eclipse](http://developer.android.com/tools/projects/projects-eclipse.html#ReferencingLibraryProject) + - [managing projects from Android Studio](https://developer.android.com/sdk/installing/create-project.html) + + +Documentation +----- +For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.1/javadoc/). + +Furthermore, you can also rely on the [**MPChartExample**](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample) folder and check out the example code in that project. The corresponding application to the example project is also [**available in the Google PlayStore**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample). + +You have a problem that cannot be solved by having a look at the example project and documentation? +No problem, let's talk: [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/PhilJay/MPAndroidChart?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) + +Chart types +----- - **LineChart (with legend, simple design)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart4.png) @@ -76,8 +173,11 @@ Features - **LineChart (cubic lines)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/cubiclinechart.png) - - **LineChart (single DataSet)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/linechart.png) + - **LineChart (gradient fill)** +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/line_chart_gradient.png) + + - **Combined-Chart (bar- and linechart in this case)** +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/combined_chart.png) - **BarChart (with legend, simple design)** @@ -89,7 +189,7 @@ Features - **Horizontal-BarChart** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/horizontal_barchart.jpg) +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/horizontal_barchart.png) - **PieChart (with selection, ...)** @@ -104,68 +204,18 @@ Features ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/candlestickchart.png) - - **RadarChart** (spider web chart) - -![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/radarchart.png) - - - -Usage -======= + - **BubbleChart** (area covered by bubbles indicates the yValue) -In order to use the library, there are 4 options: +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/bubblechart.png) -**1. Gradle dependency** - - - Add the following to your `build.gradle`: - ```gradle -repositories { - maven { url "https://jitpack.io" } -} - -dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v2.0.7' -} -``` - -**2. Maven** -- Add the following to your `pom.xml`: - ```xml - - jitpack.io - https://jitpack.io - - - - com.github.PhilJay - MPAndroidChart - v2.0.7 - -``` - -**3. jar file only** - - Download the [**latest .jar file**](https://github.com/PhilJay/MPAndroidChart/releases) from the releases section - - Copy the **mpandroidchartlibrary-.jar** file into the `libs` folder of your Android application project - - Start using the library - -**4. clone whole repository** - - Open your **commandline-input** and navigate to your desired destination folder (where you want to put the library) - - Use the command `git clone https://github.com/PhilJay/MPAndroidChart.git` to download the full MPAndroidChart repository to your computer (this includes the folder of the library project as well as the example project) - - Import the library folder (`MPChartLib`) into your Eclipse workspace - - Add it as a reference to your project: [referencing library projects in Eclipse](http://developer.android.com/tools/projects/projects-eclipse.html#ReferencingLibraryProject) - -Documentation -======= -For a **detailed documentation**, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki). + - **RadarChart** (spider web chart) -Furthermore, you can also rely on the [**MPChartExample**](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample) folder and check out the example code in that project. The corresponding application to the example project is also [**available in the Google PlayStore**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample). +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/radarchart.png) -You have a problem that cannot be solved by having a look at the example project and documentation? -No problem, let's talk: [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/PhilJay/MPAndroidChart?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) License ======= -Copyright 2015 Philipp Jahoda +Copyright 2016 Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -179,4 +229,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -**Special thanks** to [mikegr](https://github.com/mikegr) and [ph1lb4](https://github.com/ph1lb4) for their contributions to this project. +**Special thanks** to [danielgindi](https://github.com/danielgindi), [mikegr](https://github.com/mikegr), [tony](https://github.com/tonypatino-monoclesociety) and [jitpack.io](https://github.com/jitpack-io) for their contributions to this project. diff --git a/build.gradle b/build.gradle index 8d6e95140e..5969598ccc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,15 @@ +task wrapper(type: Wrapper) { + gradleVersion = '2.9' +} + buildscript { repositories { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' - classpath 'com.github.dcendents:android-maven-plugin:1.2' + classpath "io.realm:realm-gradle-plugin:2.0.2" + classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } diff --git a/deploy.gradle b/deploy.gradle deleted file mode 100644 index b9382fe1f1..0000000000 --- a/deploy.gradle +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2013 Chris Banes - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -apply plugin:'maven' -apply plugin:'signing' - -def isReleaseBuild() { - return VERSION_NAME.contains("SNAPSHOT") == false -} - -def getReleaseRepositoryUrl() { - return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL - : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" -} - -def getSnapshotRepositoryUrl() { - return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL - : "https://oss.sonatype.org/content/repositories/snapshots/" -} - -def getRepositoryUsername() { - return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" -} - -def getRepositoryPassword() { - return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" -} - - -afterEvaluate { project -> - uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - pom.groupId = GROUP - pom.artifactId = POM_ARTIFACT_ID - pom.version = VERSION_NAME - - repository(url: getReleaseRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - snapshotRepository(url: getSnapshotRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - - pom.project { - name POM_NAME - packaging POM_PACKAGING - description hasProperty('POM_DESCRIPTION') ? POM_DESCRIPTION : "" - url hasProperty('POM_URL') ? POM_URL : "" - - scm { - url hasProperty('POM_SCM_URL') ? POM_SCM_URL : "" - connection hasProperty('POM_SCM_CONNECTION') ? POM_SCM_CONNECTION : "" - developerConnection hasProperty('POM_SCM_DEV_CONNECTION') ? POM_SCM_DEV_CONNECTION : "" - } - - licenses { - license { - name hasProperty('POM_LICENCE_NAME') ? POM_LICENCE_NAME : "" - url hasProperty('POM_LICENCE_URL') ? POM_LICENCE_URL : "" - distribution hasProperty('POM_LICENCE_DIST') ? POM_LICENCE_DIST : "" - } - } - - developers { - developer { - id POM_DEVELOPER_ID - name POM_DEVELOPER_NAME - } - } - } - } - } - } - - signing { - required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") && hasProperty('signing.keyId') } - sign configurations.archives - } - - task androidJavadocs(type: Javadoc) { - failOnError false - source = android.sourceSets.main.java.sourceFiles - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - } - - task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { - classifier = 'javadoc' - from androidJavadocs.destinationDir - } - - task androidSourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.sourceFiles - } - - artifacts { - archives androidSourcesJar - archives androidJavadocsJar - } -} - diff --git a/design/facebook_icon.png b/design/facebook_icon.png new file mode 100644 index 0000000000..73181685b1 Binary files /dev/null and b/design/facebook_icon.png differ diff --git a/design/feature_graphic.png b/design/feature_graphic.png new file mode 100644 index 0000000000..3497c2f75a Binary files /dev/null and b/design/feature_graphic.png differ diff --git a/design/feature_graphic.psd b/design/feature_graphic.psd new file mode 100644 index 0000000000..3088d5340a Binary files /dev/null and b/design/feature_graphic.psd differ diff --git a/design/googleplus_icon.png b/design/googleplus_icon.png new file mode 100644 index 0000000000..87f27e26b6 Binary files /dev/null and b/design/googleplus_icon.png differ diff --git a/design/twitter_icon.png b/design/twitter_icon.png new file mode 100644 index 0000000000..763cb133fc Binary files /dev/null and b/design/twitter_icon.png differ diff --git a/design/video_thumbnail.psd b/design/video_thumbnail.psd deleted file mode 100644 index 4695da6729..0000000000 Binary files a/design/video_thumbnail.psd and /dev/null differ diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000000..4a9594aeec --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx2048M \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 8c0fb64a86..941144813d 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 982af44537..aecf1e6c37 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Jan 11 01:47:43 CST 2015 +#Sat Sep 03 21:01:56 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/gradlew b/gradlew index 91a7e269e1..f6890acbdf 100755 --- a/gradlew +++ b/gradlew @@ -12,7 +12,7 @@ DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` -# Use the maximum available, or set MAX_FD != -1 to use that value. +# Use the maximum available, or set MAX_FD != -1 to use that yValue. MAX_FD="maximum" warn ( ) { @@ -42,11 +42,6 @@ case "`uname`" in ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" @@ -61,9 +56,9 @@ while [ -h "$PRG" ] ; do fi done SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- +cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" -cd "$SAVED" >&- +cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -114,6 +109,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` diff --git a/gradlew.bat b/gradlew.bat index aec99730b4..b411ee1fa1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -57,7 +57,7 @@ set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp -if "x%~1" == "x" goto execute +if "xPx%~1" == "xPx" goto execute set CMD_LINE_ARGS=%* goto execute diff --git a/performance.xlsx b/performance.xlsx deleted file mode 100644 index 041d4fea4e..0000000000 Binary files a/performance.xlsx and /dev/null differ diff --git a/screenshots/bubblechart.png b/screenshots/bubblechart.png new file mode 100644 index 0000000000..e8416be85b Binary files /dev/null and b/screenshots/bubblechart.png differ diff --git a/screenshots/candlestickchart.png b/screenshots/candlestickchart.png index e12f8c2893..0dce18ab3e 100644 Binary files a/screenshots/candlestickchart.png and b/screenshots/candlestickchart.png differ diff --git a/screenshots/candlestickchart_old.png b/screenshots/candlestickchart_old.png new file mode 100644 index 0000000000..e12f8c2893 Binary files /dev/null and b/screenshots/candlestickchart_old.png differ diff --git a/screenshots/combined_chart.png b/screenshots/combined_chart.png new file mode 100644 index 0000000000..0e251c11bb Binary files /dev/null and b/screenshots/combined_chart.png differ diff --git a/screenshots/grouped_barchart_wiki.png b/screenshots/grouped_barchart_wiki.png new file mode 100644 index 0000000000..c4d9e66836 Binary files /dev/null and b/screenshots/grouped_barchart_wiki.png differ diff --git a/screenshots/horizontal_barchart.jpg b/screenshots/horizontal_barchart.jpg deleted file mode 100644 index eee2f70afd..0000000000 Binary files a/screenshots/horizontal_barchart.jpg and /dev/null differ diff --git a/screenshots/horizontal_barchart.png b/screenshots/horizontal_barchart.png new file mode 100644 index 0000000000..b9d596007e Binary files /dev/null and b/screenshots/horizontal_barchart.png differ diff --git a/screenshots/line_chart_gradient.png b/screenshots/line_chart_gradient.png new file mode 100644 index 0000000000..2891dcec5f Binary files /dev/null and b/screenshots/line_chart_gradient.png differ diff --git a/screenshots/linechart_wiki.png b/screenshots/linechart_wiki.png new file mode 100644 index 0000000000..b81f5baf02 Binary files /dev/null and b/screenshots/linechart_wiki.png differ diff --git a/screenshots/normal_barchart_wiki.png b/screenshots/normal_barchart_wiki.png new file mode 100644 index 0000000000..93479eb37d Binary files /dev/null and b/screenshots/normal_barchart_wiki.png differ diff --git a/screenshots/piechart_wiki.png b/screenshots/piechart_wiki.png new file mode 100644 index 0000000000..deaed36137 Binary files /dev/null and b/screenshots/piechart_wiki.png differ diff --git a/screenshots/realm_wiki.png b/screenshots/realm_wiki.png new file mode 100644 index 0000000000..3387c2e1b4 Binary files /dev/null and b/screenshots/realm_wiki.png differ diff --git a/screenshots/simpledesign_barchart1.png b/screenshots/simpledesign_barchart1.png deleted file mode 100644 index e1c3f14155..0000000000 Binary files a/screenshots/simpledesign_barchart1.png and /dev/null differ diff --git a/screenshots/zero_line_example_barchart.png b/screenshots/zero_line_example_barchart.png new file mode 100644 index 0000000000..514238569f Binary files /dev/null and b/screenshots/zero_line_example_barchart.png differ diff --git a/settings.gradle b/settings.gradle index 0d770eb38c..32efe09de1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,7 @@ include 'MPChartLib' +//include 'MPAndroidChart-Realm' include 'MPChartExample' +//include ':MPChartLib-Realm' +//project(':MPChartLib-Realm').projectDir = new File('../MPAndroidChart-Realm/MPChartLib-Realm') +