From 12e3e7f3bf94a89f2b796b86dfa6586bba231b6b Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Fri, 1 May 2026 08:20:37 -0700 Subject: [PATCH 01/10] use rtemis.core checks --- demo_spectrogram.csv | 961 ++++++++++++++++++++++++++++++++++++++ r/R/axis.R | 51 +- r/R/components.R | 148 +++--- r/R/draw.R | 18 +- r/R/draw_spectrogram.R | 11 +- r/R/label.R | 10 +- r/R/option.R | 8 +- r/R/series.R | 86 ++-- r/R/styles.R | 57 +-- r/R/theme.R | 2 +- r/R/utils.R | 81 +--- r/man/Axis.Rd | 6 +- r/man/BoxplotSeries.Rd | 2 +- r/man/DataZoom.Rd | 4 +- r/man/ItemStyle.Rd | 6 +- r/man/Legend.Rd | 4 +- r/man/LineStyle.Rd | 6 +- r/man/PieSeries.Rd | 4 +- r/man/TextStyle.Rd | 12 +- r/man/Title.Rd | 8 +- r/man/Tooltip.Rd | 6 +- r/man/VisualMap.Rd | 2 +- r/man/draw_spectrogram.Rd | 2 +- 23 files changed, 1200 insertions(+), 295 deletions(-) create mode 100644 demo_spectrogram.csv diff --git a/demo_spectrogram.csv b/demo_spectrogram.csv new file mode 100644 index 0000000..afb1eac --- /dev/null +++ b/demo_spectrogram.csv @@ -0,0 +1,961 @@ +time,frequency,value +0.00390625,0,20.188491912499536 +0.25390625,0,26.930539965104806 +0.50390625,0,29.213644997299678 +0.75390625,0,21.094338054162133 +1.00390625,0,8.353774875283822 +1.25390625,0,-4.005011607169958 +1.50390625,0,-13.77959066496491 +1.75390625,0,-21.14652314177636 +2.00390625,0,-26.867152252956448 +2.25390625,0,-31.497345811068705 +2.50390625,0,-35.37640025376625 +2.75390625,0,-38.71414584427984 +3.00390625,0,-41.6463822815193 +3.25390625,0,-44.265018054892344 +3.50390625,0,-46.63471706675972 +0.00390625,2,-48.80250337336967 +0.25390625,2,-50.803553362068136 +0.50390625,2,-52.66482987374697 +0.75390625,2,-54.40744283119039 +1.00390625,2,-56.04823000427959 +1.25390625,2,-57.60084433869572 +1.50390625,2,-59.076519913196904 +1.75390625,2,-60.484623125389476 +2.00390625,2,-61.83305699254394 +2.25390625,2,-63.12856288117979 +2.50390625,2,-64.37694924519802 +2.75390625,2,-65.58326752171232 +3.00390625,2,-66.7519491637243 +3.25390625,2,-67.88691367348078 +3.50390625,2,-68.9916547031355 +0.00390625,4,-70.06930935668507 +0.25390625,4,-71.12271446745319 +0.50390625,4,-72.15445265930832 +0.75390625,4,-73.16689029924065 +1.00390625,4,-74.16220892923474 +1.25390625,4,-75.14243138825034 +1.50390625,4,-76.10944353481271 +1.75390625,4,-77.06501225833136 +2.00390625,4,-78.01080028113716 +2.25390625,4,-78.94837810034517 +2.50390625,4,-79.87923328491532 +2.75390625,4,-80.8047772109912 +3.00390625,4,-81.72634918122401 +3.25390625,4,-82.64521771702285 +3.50390625,4,-83.5625786166961 +0.00390625,6,-84.47954909229489 +0.25390625,6,-85.39715691261668 +0.50390625,6,-86.31632291456803 +0.75390625,6,-87.23783432851776 +1.00390625,6,-88.16230504535396 +1.25390625,6,-89.09011679567192 +1.50390625,6,-90.02133197361579 +1.75390625,6,-90.95556376308423 +2.00390625,6,-91.89178176129539 +2.25390625,6,-92.82802048240455 +2.50390625,6,-93.76094404764756 +2.75390625,6,-94.68520511636191 +3.00390625,6,-95.59252852456609 +3.25390625,6,-96.47047360206608 +3.50390625,6,-97.30093448173722 +0.00390625,8,-98.05870770840725 +0.25390625,8,-98.71095952239443 +0.50390625,8,-99.2190268812904 +0.75390625,8,-99.54402735563419 +1.00390625,8,-3.2588587211017 +1.25390625,8,12.331626529155539 +1.50390625,8,25.28777199931879 +1.75390625,8,29.635367127551014 +2.00390625,8,23.759431888567516 +2.25390625,8,10.697943061433746 +2.50390625,8,-2.885659415799144 +2.75390625,8,-14.169022938692784 +3.00390625,8,-23.01239076498019 +3.25390625,8,-30.100849071728497 +3.50390625,8,-35.951509965984656 +0.00390625,10,-40.83891222547181 +0.25390625,10,-44.89243203687703 +0.50390625,10,-48.18727977302498 +0.75390625,10,-50.81183222019191 +1.00390625,10,-52.89001438742542 +1.25390625,10,-54.55994626722247 +1.50390625,10,-55.94329886300379 +1.75390625,10,-57.13102442767702 +2.00390625,10,-58.18485438982121 +2.25390625,10,-59.14480449482635 +2.50390625,10,-60.03651330354225 +2.75390625,10,-60.87658549204761 +3.00390625,10,-61.67605103527886 +3.25390625,10,-62.442496118563696 +3.50390625,10,-63.181353916640084 +0.00390625,12,-63.89668703684051 +0.25390625,12,-64.59166695494932 +0.50390625,12,-65.26887283555034 +0.75390625,12,-65.9304818505011 +1.00390625,12,-66.5783935496462 +1.25390625,12,-67.2143136175334 +1.50390625,12,-67.83981231086675 +1.75390625,12,-68.45636696187633 +2.00390625,12,-69.06539442933217 +2.25390625,12,-69.66827728581804 +2.50390625,12,-70.26638626279205 +2.75390625,12,-70.86110071742266 +3.00390625,12,-71.45382844828868 +3.25390625,12,-72.04602594423625 +3.50390625,12,-72.63922006087363 +0.00390625,14,-73.23503214234678 +0.25390625,14,-73.83520572161741 +0.50390625,14,-74.44163917607655 +0.75390625,14,-75.0564250764844 +1.00390625,14,-75.681898526179907 +1.25390625,14,-76.32069759464257 +1.50390625,14,-76.97584015715408 +1.75390625,14,-77.65082324536466 +2.00390625,14,-78.34975373952997 +2.25390625,14,-79.07752349505023 +2.50390625,14,-79.84004874543372 +2.75390625,14,-80.64460476422838 +3.00390625,14,-81.50030558610277 +3.25390625,14,-82.41881187881661 +3.50390625,14,-83.41541128488024 +0.00390625,16,-84.5107346503511 +0.25390625,16,-85.7336178966063 +0.50390625,16,-87.12616962755449 +0.75390625,16,-88.75345828187989 +1.00390625,16,-90.72400369876436 +1.25390625,16,-93.23970067169648 +1.50390625,16,-96.74635897689794 +1.75390625,16,-102.5883633343375 +2.00390625,16,-9.965494113298414 +2.25390625,16,-3.196171106197826 +2.50390625,16,9.79401987164124 +2.75390625,16,22.878366500533932 +3.00390625,16,29.56598644996901 +3.25390625,16,25.93552264009869 +3.50390625,16,13.349993178743064 +0.00390625,18,-0.21455263307614578 +0.25390625,18,-11.351545131381592 +0.50390625,18,-19.678387345394096 +0.75390625,18,-25.985297771263536 +1.00390625,18,-30.96442029370618 +1.25390625,18,-35.04857198514932 +1.50390625,18,-38.502831995243234 +1.75390625,18,-41.49573036363595 +2.00390625,18,-44.13903982138435 +2.25390625,18,-46.50977644923023 +2.50390625,18,-48.662812119964315 +2.75390625,18,-50.63840103899718 +3.00390625,18,-52.46683982761211 +3.25390625,18,-54.17144957389503 +3.50390625,18,-55.77054134473249 +0.00390625,20,-57.27874661911613 +0.25390625,20,-58.70793973549027 +0.50390625,20,-60.06789149861312 +0.75390625,20,-61.366741475741186 +1.00390625,20,-62.61134537995849 +1.25390625,20,-63.807534689544134 +1.50390625,20,-64.9603134771881 +1.75390625,20,-66.07400954300797 +2.00390625,20,-67.15239176586506 +2.25390625,20,-68.19876208314868 +2.50390625,20,-69.21602814419839 +2.75390625,20,-70.20676100399358 +3.00390625,20,-71.17324106695833 +3.25390625,20,-72.11749462890903 +3.50390625,20,-73.04132277322294 +0.00390625,22,-73.94632387216478 +0.25390625,22,-74.83391064310698 +0.50390625,22,-75.70532236784584 +0.75390625,22,-76.56163270459724 +1.00390625,22,-77.40375328192832 +1.25390625,22,-78.23243309116415 +1.50390625,22,-79.04825353063767 +1.75390625,22,-79.85161872810339 +2.00390625,22,-80.64274064178142 +2.25390625,22,-81.42161816083082 +2.50390625,22,-82.18800930366987 +2.75390625,22,-82.94139534090327 +3.00390625,22,-83.68093562523059 +3.25390625,22,-84.40541183557423 +3.50390625,22,-85.11316068784984 +0.00390625,24,-85.80199487268722 +0.25390625,24,-86.46911364101942 +0.50390625,24,-87.11100709888278 +0.75390625,24,-87.72336297370018 +1.00390625,24,-88.3009909906668 +1.25390625,24,-88.83778849877503 +1.50390625,24,-89.3267800351004 +1.75390625,24,-89.7602697512443 +2.00390625,24,-90.13014400527176 +2.25390625,24,-90.42834417374999 +2.50390625,24,-90.64749333106113 +2.75390625,24,-90.78160717481347 +3.00390625,24,-23.907604928015473 +3.25390625,24,-15.571884306098735 +3.50390625,24,-5.069206747285514 +0.00390625,26,7.182487018929829 +0.25390625,26,20.09235135760465 +0.50390625,26,28.993023224655722 +0.75390625,26,27.621856908212195 +1.00390625,26,16.294125940954984 +1.25390625,26,3.209100735529464 +1.50390625,26,-7.846702819099029 +1.75390625,26,-16.108672968219516 +2.00390625,26,-22.32433420484012 +2.25390625,26,-27.228566388255643 +2.50390625,26,-31.268039179676283 +2.75390625,26,-34.70427108748397 +3.00390625,26,-37.69834267362515 +3.25390625,26,-40.35541848282378 +3.50390625,26,-42.747690351454224 +0.00390625,28,-44.9267934542105 +0.25390625,28,-46.9309195603767 +0.50390625,28,-48.789110355575 +0.75390625,28,-50.523968237148516 +1.00390625,28,-52.15343504385254 +1.25390625,28,-53.69199818350402 +1.50390625,28,-55.15153189831729 +1.75390625,28,-56.5418985509486 +2.00390625,28,-57.8713876492267 +2.25390625,28,-59.147042470645005 +2.50390625,28,-60.37490715280175 +2.75390625,28,-61.56021642761786 +3.00390625,28,-62.70754330000166 +3.25390625,28,-63.82091542200877 +3.50390625,28,-64.90390785022626 +0.00390625,30,-65.95971778160065 +0.25390625,30,-66.99122537446935 +0.50390625,30,-68.00104376287557 +0.75390625,30,-68.99156056415245 +1.00390625,30,-69.96497270618276 +1.25390625,30,-70.92331593686625 +1.50390625,30,-71.86849013494724 +1.75390625,30,-72.80228125564854 +2.00390625,30,-73.72638063387672 +2.25390625,30,-74.64240212292674 +2.50390625,30,-75.55189750153835 +2.75390625,30,-76.45637032348831 +3.00390625,30,-77.35728820163294 +3.25390625,30,-78.25609320487521 +3.50390625,30,-79.15420949796352 +0.00390625,32,-80.05304647712153 +0.25390625,32,-80.95399437478738 +0.50390625,32,-81.85840683697526 +0.75390625,32,-82.76756130864952 +1.00390625,32,-83.68258173529306 +1.25390625,32,-84.60429788247887 +1.50390625,32,-85.53299906762469 +1.75390625,32,-86.46801470640267 +2.00390625,32,-87.40701630605025 +2.25390625,32,-88.34488809772465 +2.50390625,32,-89.27197382298267 +2.75390625,32,-90.17154219302178 +3.00390625,32,-91.0165938663429 +3.25390625,32,-91.76697248272657 +3.50390625,32,-92.36936472623186 +0.00390625,34,-92.76430501041946 +0.25390625,34,-23.69416541546293 +0.50390625,34,-21.204266922769914 +0.75390625,34,-15.345277337584315 +1.00390625,34,-7.044932217292654 +1.25390625,34,4.0605802880899535 +1.50390625,34,17.017316679816005 +1.75390625,34,27.932675781835194 +2.00390625,34,28.796771228286282 +2.25390625,34,19.386977230548574 +2.50390625,34,6.491497010083346 +2.75390625,34,-5.2851840154660925 +3.00390625,34,-14.330372624244292 +3.25390625,34,-21.132986485916938 +3.50390625,34,-26.47368159795125 +0.00390625,36,-30.85889410432493 +0.25390625,36,-34.584468762081 +0.50390625,36,-37.83008359720366 +0.75390625,36,-40.71153512599121 +1.00390625,36,-43.30757226610511 +1.25390625,36,-45.6741977025687 +1.50390625,36,-47.85272702062102 +1.75390625,36,-49.87457982373806 +2.00390625,36,-51.76426567271841 +2.25390625,36,-53.54132245530715 +2.50390625,36,-55.221619800028776 +2.75390625,36,-56.81826273957233 +3.00390625,36,-58.342235256641374 +3.25390625,36,-59.80286961694965 +3.50390625,36,-61.208196074505665 +0.00390625,38,-62.565208598672996 +0.25390625,38,-63.880070545183116 +0.50390625,38,-65.15827668745503 +0.75390625,38,-66.4047831143065 +1.00390625,38,-67.62411326980009 +1.25390625,38,-68.82044615887796 +1.50390625,38,-69.99769126348001 +1.75390625,38,-71.15955361227263 +2.00390625,38,-72.30959177783748 +2.25390625,38,-73.45127100547282 +2.50390625,38,-74.58801339440735 +2.75390625,38,-75.72324683666889 +3.00390625,38,-76.86045426181192 +3.25390625,38,-78.00322485906985 +3.50390625,38,-79.15530880827052 +0.00390625,40,-80.32067739738119 +0.25390625,40,-81.50359048033289 +0.50390625,40,-82.70867351822771 +0.75390625,40,-83.94100673935375 +1.00390625,40,-85.20622923843467 +1.25390625,40,-86.51066060616154 +1.50390625,40,-87.86144243681477 +1.75390625,40,-89.26669968814932 +2.00390625,40,-90.73571687483948 +2.25390625,40,-92.27911231613193 +2.50390625,40,-93.90896700704387 +2.75390625,40,-95.63880656108904 +3.00390625,40,-97.48320782062169 +3.25390625,40,-99.45652286488664 +3.50390625,40,-101.56960624157813 +0.00390625,42,-103.82215319344954 +0.25390625,42,-106.18582623823029 +0.50390625,42,-108.5702004383165 +0.75390625,42,-110.76656574562092 +1.00390625,42,-112.40092544703364 +1.25390625,42,-34.01935460033141 +1.50390625,42,-31.1232085060962 +1.75390625,42,-25.61232258021395 +2.00390625,42,-18.98619630631855 +2.25390625,42,-10.578243643904667 +2.50390625,42,0.5366265762095584 +2.75390625,42,14.005952158828045 +3.00390625,42,26.36937356278915 +3.25390625,42,29.47591641828872 +3.50390625,42,22.276724974505797 +0.00390625,44,9.268714242016571 +0.25390625,44,-3.6719871604688015 +0.50390625,44,-14.087180346703654 +0.75390625,44,-22.047906514178223 +1.00390625,44,-28.30652617685185 +1.25390625,44,-33.428491392211825 +1.50390625,44,-37.76034055094695 +1.75390625,44,-41.517157122023775 +2.00390625,44,-44.83931507065405 +2.25390625,44,-47.82307378375983 +2.50390625,44,-50.53730175229873 +2.75390625,44,-53.033063191258314 +3.00390625,44,-55.349391668453464 +3.25390625,44,-57.5169262332269 +3.50390625,44,-59.56029336516974 +0.00390625,46,-61.49972267549816 +0.25390625,46,-63.352177665802934 +0.50390625,46,-65.13216992667488 +0.75390625,46,-66.85236072412005 +1.00390625,46,-68.52401542181349 +1.25390625,46,-70.15735190072513 +1.50390625,46,-71.76180708104786 +1.75390625,46,-73.34623153874054 +2.00390625,46,-74.91900709228932 +2.25390625,46,-76.48806102293177 +2.50390625,46,-78.06071637350064 +2.75390625,46,-79.64325727994344 +3.00390625,46,-81.23998160033108 +3.25390625,46,-82.85133158119315 +3.50390625,46,-84.47042663776423 +0.00390625,48,-86.07708840518856 +0.25390625,48,-87.62886006856284 +0.50390625,48,-89.05138777408551 +0.75390625,48,-90.23832164091917 +1.00390625,48,-91.07881363590222 +1.25390625,48,-91.51439695197863 +1.50390625,48,-91.57942772385854 +1.75390625,48,-91.37799918634815 +2.00390625,48,-91.02528892027851 +2.25390625,48,-90.60930010452195 +2.50390625,48,-90.18444598587328 +2.75390625,48,-89.7798508654297 +3.00390625,48,-89.40906957641509 +3.25390625,48,-89.07704017440098 +3.50390625,48,-88.78427426672202 +0.00390625,50,-88.52919180929396 +0.25390625,50,-88.30936831251654 +0.50390625,50,-88.1221799164946 +0.75390625,50,-87.96512540610976 +1.00390625,50,-87.83597733383624 +1.25390625,50,-87.73284509275194 +1.50390625,50,-87.65419423858118 +1.75390625,50,-87.5988454290033 +2.00390625,50,-87.56596605760068 +2.25390625,50,-71.99981436031449 +2.50390625,50,-44.473193632715784 +2.75390625,50,-36.62318127299181 +3.00390625,50,-29.9050082799489 +3.25390625,50,-22.575764196722204 +3.50390625,50,-13.668842198581231 +0.00390625,52,-2.32948240765774 +0.25390625,52,11.303037922239966 +0.50390625,52,24.29587305897773 +0.75390625,52,29.66922517057317 +1.00390625,52,24.74812733637865 +1.25390625,52,11.815049910540223 +1.50390625,52,-1.8676377169806373 +1.75390625,52,-13.214944454696347 +2.00390625,52,-21.95760541068949 +2.25390625,52,-28.792898437510686 +2.50390625,52,-34.29672582071058 +2.75390625,52,-38.82313680611104 +3.00390625,52,-42.59462016712752 +3.25390625,52,-45.7653700403887 +3.50390625,52,-48.45298945183992 +0.00390625,54,-50.75239791324087 +0.25390625,54,-52.741358715365436 +0.50390625,54,-54.48296705306784 +0.75390625,54,-56.02753713068601 +1.00390625,54,-57.41458450345515 +1.25390625,54,-58.674856166623485 +1.50390625,54,-59.83220886215286 +1.75390625,54,-60.905205598520666 +2.00390625,54,-61.908395498526346 +2.25390625,54,-62.853302265991545 +2.50390625,54,-63.749171111788826 +2.75390625,54,-64.60352708047877 +3.00390625,54,-65.42259112753919 +3.25390625,54,-66.21159109934815 +3.50390625,54,-66.97499578027382 +0.00390625,56,-67.7166930753271 +0.25390625,56,-68.44012762385229 +0.50390625,56,-69.14840910147375 +0.75390625,56,-69.84439944138049 +1.00390625,56,-70.53078508045992 +1.25390625,56,-71.21013890041606 +1.50390625,56,-71.88497545993815 +1.75390625,56,-72.5578025098561 +2.00390625,56,-73.2311713365306 +2.25390625,56,-73.90772837785997 +2.50390625,56,-74.59027041242972 +2.75390625,56,-75.28180594340932 +3.00390625,56,-75.98562557038387 +3.25390625,56,-76.70538460609005 +3.50390625,56,-77.4452016656839 +0.00390625,58,-78.20977715622705 +0.25390625,58,-79.00453536361314 +0.50390625,58,-79.83579124708741 +0.75390625,58,-80.71093615996699 +1.00390625,58,-81.63861696789569 +1.25390625,58,-82.62883415503465 +1.50390625,58,-83.69276126732095 +1.75390625,58,-84.84178256903374 +2.00390625,58,-86.08448650629201 +2.25390625,58,-87.41853731972972 +2.50390625,58,-88.81057251117683 +2.75390625,58,-90.15330229155224 +3.00390625,58,-91.20601217646433 +3.25390625,58,-43.424511110869155 +3.50390625,58,-41.626191721552416 +0.00390625,60,-37.80095418600909 +0.25390625,60,-33.26907668333019 +0.50390625,60,-28.09205862426667 +0.75390625,60,-21.916994079589216 +1.00390625,60,-14.145629809326401 +1.25390625,60,-3.982584963878828 +1.50390625,60,8.74479214546291 +1.75390625,60,21.73267267593225 +2.00390625,60,29.379519129515966 +2.25390625,60,26.72133945380612 +2.50390625,60,14.576338492720822 +2.75390625,60,1.206855927446748 +3.00390625,60,-9.82405221480236 +3.25390625,60,-18.03091941910652 +3.50390625,60,-24.218549317670654 +0.00390625,62,-29.103013999242037 +0.25390625,62,-33.12044545724447 +0.50390625,62,-36.530295578006246 +0.75390625,62,-39.49426899782277 +1.00390625,62,-42.11857991668455 +1.25390625,62,-44.4762855821628 +1.50390625,62,-46.61961634329317 +1.75390625,62,-48.587134427607275 +2.00390625,62,-50.4080883611046 +2.25390625,62,-52.10517233711915 +2.50390625,62,-53.69633818312035 +2.75390625,62,-55.196022538864895 +3.00390625,62,-56.61600057380205 +3.25390625,62,-57.965993897044626 +3.50390625,62,-59.254112255345106 +0.00390625,64,-60.487180075088666 +0.25390625,64,-61.670981428886236 +0.50390625,64,-62.81044602303481 +0.75390625,64,-63.90979169936391 +1.00390625,64,-64.97263424703117 +1.25390625,64,-66.00207223973959 +1.50390625,64,-67.00075231519223 +1.75390625,64,-67.97091895980779 +2.00390625,64,-68.9144516703972 +2.25390625,64,-69.83289163106494 +2.50390625,64,-70.72745958603358 +2.75390625,64,-71.59906604274934 +3.00390625,64,-72.44831481406149 +3.25390625,64,-73.27550078906846 +3.50390625,64,-74.08060260782203 +0.00390625,66,-74.86327118145368 +0.25390625,66,-75.62281501653037 +0.50390625,66,-76.3581837122302 +0.75390625,66,-77.06795123343426 +1.00390625,66,-77.75030134743082 +1.25390625,66,-78.40301796809624 +1.50390625,66,-79.023484073084 +1.75390625,66,-79.60869338795197 +2.00390625,66,-80.15527953800544 +2.25390625,66,-80.6595674812529 +2.50390625,66,-81.11765132992684 +2.75390625,66,-81.52550130384923 +3.00390625,66,-81.87909977317193 +3.25390625,66,-82.17460254919675 +3.50390625,66,-82.408517188714 +0.00390625,68,-82.57788504576308 +0.25390625,68,-82.68045060576435 +0.50390625,68,-47.94651199059207 +0.75390625,68,-44.82953816467432 +1.00390625,68,-40.15234217096369 +1.25390625,68,-35.805715734919474 +1.50390625,68,-31.485634415390496 +1.75390625,68,-26.807922326355893 +2.00390625,68,-21.365949182377456 +2.25390625,68,-14.602219266890557 +2.50390625,68,-5.708625834922221 +2.75390625,68,5.889549566609519 +3.00390625,68,18.783039485476955 +3.25390625,68,28.601487340950392 +3.50390625,68,28.18521528318732 +0.00390625,70,17.627335799245433 +0.25390625,70,4.687367068012067 +0.50390625,70,-6.591254969764715 +0.75390625,70,-15.116640465514042 +1.00390625,70,-21.54190444388284 +1.25390625,70,-26.616123290934706 +1.50390625,70,-30.80269997571483 +1.75390625,70,-34.37149401553445 +2.00390625,70,-37.48751486959641 +2.25390625,70,-40.25797051023386 +2.50390625,70,-42.756305205442786 +2.75390625,70,-45.03506684429964 +3.00390625,70,-47.133206230853055 +3.25390625,70,-49.08044682744052 +3.50390625,70,-50.90002615246328 +0.00390625,72,-52.6104859951057 +0.25390625,72,-54.226882271955894 +0.50390625,72,-55.761627101556215 +0.75390625,72,-57.22508995080975 +1.00390625,72,-58.62603629436834 +1.25390625,72,-59.97195381735685 +1.50390625,72,-61.26929892854512 +1.75390625,72,-62.52368558437866 +2.00390625,72,-63.74003149929165 +2.25390625,72,-64.92267215138831 +2.50390625,72,-66.07545006231821 +2.75390625,72,-67.20178444813197 +3.00390625,72,-68.30472504011526 +3.25390625,72,-69.38699251220142 +3.50390625,72,-70.45100724775837 +0.00390625,74,-71.49890736065862 +0.25390625,74,-72.53255622687934 +0.50390625,74,-73.5535393550931 +0.75390625,74,-74.56314951110657 +1.00390625,74,-75.5623586605368 +1.25390625,74,-76.55177424821139 +1.50390625,74,-77.53157648784655 +1.75390625,74,-78.50143233376446 +2.00390625,74,-79.46038024554942 +2.25390625,74,-80.40667905435637 +2.50390625,74,-81.33761280613078 +2.75390625,74,-82.24924423355381 +3.00390625,74,-83.13611256339718 +3.25390625,74,-83.99088124649748 +3.50390625,74,-84.803961700216007 +0.00390625,76,-85.56317861935472 +0.25390625,76,-86.25360256411221 +0.50390625,76,-86.85775088012099 +0.75390625,76,-87.35641083873264 +1.00390625,76,-87.73029288931554 +1.25390625,76,-87.9624894446579 +1.50390625,76,-50.74613709149672 +1.75390625,76,-48.14077038926672 +2.00390625,76,-43.96815919013206 +2.25390625,76,-40.073081225206465 +2.50390625,76,-36.31783491410017 +2.75390625,76,-32.42068345089308 +3.00390625,76,-28.1071437825003 +3.25390625,76,-23.049891146299167 +3.50390625,76,-16.7672489931155 +0.00390625,78,-8.5069288560421 +0.25390625,78,2.5229697464216287 +0.50390625,78,15.692740222389933 +0.75390625,78,27.326240922119606 +1.00390625,78,29.147051019526376 +1.25390625,78,20.668384977958247 +1.50390625,78,7.738666674787098 +1.75390625,78,-4.527154868748831 +2.00390625,78,-14.124729199940514 +2.25390625,78,-21.373343328165753 +2.50390625,78,-27.05812309489106 +2.75390625,78,-31.718462019465083 +3.00390625,78,-35.67415518758273 +3.25390625,78,-39.11995923427951 +3.50390625,78,-42.18126292038213 +0.00390625,80,-44.94317495825779 +0.25390625,80,-47.46612712209783 +0.50390625,80,-49.794684647901384 +0.75390625,80,-51.962783897321884 +1.00390625,80,-53.99699414325535 +1.25390625,80,-55.918634027996326 +1.50390625,80,-57.74519604294397 +1.75390625,80,-59.491337824196165 +2.00390625,80,-61.16959421098808 +2.25390625,80,-62.7909051773515 +2.50390625,80,-64.36502071572195 +2.75390625,80,-65.90082341300278 +3.00390625,80,-67.40659736817534 +3.25390625,80,-68.89026479246327 +3.50390625,80,-70.35960786849675 +0.00390625,82,-71.82249180747239 +0.25390625,82,-73.28710608614405 +0.50390625,82,-74.76224364091401 +0.75390625,82,-76.257644637906 +1.00390625,82,-77.7844414767465 +1.25390625,82,-79.35576061859234 +1.50390625,82,-80.98756459659617 +1.75390625,82,-82.69986335815155 +2.00390625,82,-84.51848640551916 +2.25390625,82,-86.4776625233022 +2.50390625,82,-88.62352290037143 +2.75390625,82,-91.01736994069108 +3.00390625,82,-93.73008956208952 +3.25390625,82,-96.77879566188174 +3.50390625,82,-99.78387822743278 +0.00390625,84,-101.22244466362926 +0.25390625,84,-100.18203169237103 +0.50390625,84,-98.31036679196905 +0.75390625,84,-96.64814626164281 +1.00390625,84,-95.349319843026 +1.25390625,84,-94.36513038658 +1.50390625,84,-93.63266869189152 +1.75390625,84,-93.10405386022973 +2.00390625,84,-92.74666528479953 +2.25390625,84,-92.53980919165647 +2.50390625,84,-54.23244710927578 +2.75390625,84,-53.0329197062164 +3.00390625,84,-50.38344653119029 +3.25390625,84,-47.32530909125803 +3.50390625,84,-44.11186421044859 +0.00390625,86,-40.681027531767114 +0.25390625,86,-36.879065743730976 +0.50390625,86,-32.49622408923293 +0.75390625,86,-27.240782254794148 +1.00390625,86,-20.675614321839824 +1.25390625,86,-12.132848811361248 +1.50390625,86,-0.8554140133881917 +1.75390625,86,12.804739769991214 +2.00390625,86,25.54326370022764 +2.25390625,86,29.61784781574326 +2.50390625,86,23.394448082000178 +2.75390625,86,10.357460691013276 +3.00390625,86,-3.030961429704042 +3.25390625,86,-14.04810786905891 +3.50390625,86,-22.620374654814462 +0.00390625,88,-29.478011235019437 +0.25390625,88,-35.19615960066959 +0.50390625,88,-40.1299351365364 +0.75390625,88,-44.498658722081395 +1.00390625,88,-48.44255440657085 +1.25390625,88,-52.052401231626114 +1.50390625,88,-55.384176340831914 +1.75390625,88,-58.46558023550049 +2.00390625,88,-61.298549240078 +2.25390625,88,-63.86172517553101 +2.50390625,88,-66.11798940839861 +2.75390625,88,-68.03115913098203 +3.00390625,88,-69.58838247877858 +3.25390625,88,-70.81446915490032 +3.50390625,88,-71.7665933234777 +0.00390625,90,-72.51473715230695 +0.25390625,90,-73.12322131751036 +0.50390625,90,-73.64183945902978 +0.75390625,90,-74.10520649872113 +1.00390625,90,-74.53579612440903 +1.25390625,90,-74.94754452831593 +1.50390625,90,-75.34878117660756 +1.75390625,90,-75.74428434036216 +2.00390625,90,-76.13662040671866 +2.25390625,90,-76.52698190514991 +2.50390625,90,-76.91569810796808 +2.75390625,90,-77.3025384779822 +3.00390625,90,-77.6868870003754 +3.25390625,90,-78.06783624615488 +3.50390625,90,-78.44423156316309 +0.00390625,92,-78.81468451243313 +0.25390625,92,-79.17756800934161 +0.50390625,92,-79.5310021820275 +0.75390625,92,-79.8728376883569 +1.00390625,92,-80.20064334404638 +1.25390625,92,-80.51170425498206 +1.50390625,92,-80.80303754977095 +1.75390625,92,-81.071432374845 +2.00390625,92,-81.31352028042247 +2.25390625,92,-81.5258794511552 +2.50390625,92,-81.70517255191993 +2.75390625,92,-81.84831211691056 +3.00390625,92,-81.95264051163143 +3.25390625,92,-82.0161054462859 +3.50390625,92,-59.960987771917964 +0.00390625,94,-59.15259528708826 +0.25390625,94,-57.09716591405281 +0.50390625,94,-54.36538358794449 +0.75390625,94,-51.24537992994355 +1.00390625,94,-47.80443562163156 +1.25390625,94,-44.009034736186095 +1.50390625,94,-39.77159496518596 +1.75390625,94,-34.952248106281154 +2.00390625,94,-29.33313370498549 +2.25390625,94,-22.56222044595588 +2.50390625,94,-14.068520791347899 +2.75390625,94,-3.1227426057776397 +3.00390625,94,10.21555294883806 +3.25390625,94,23.253580702465293 +3.50390625,94,29.6045059846192 +0.00390625,96,25.656020707904904 +0.25390625,96,12.95646460380031 +0.50390625,96,-0.6826616091054436 +0.75390625,96,-11.918318291931097 +1.00390625,96,-20.375587400220475 +1.25390625,96,-26.80641283195235 +1.50390625,96,-31.872694339853624 +1.75390625,96,-35.997023627861196 +2.00390625,96,-39.44643987938757 +2.25390625,96,-42.39634181653855 +2.50390625,96,-44.96627773690644 +2.75390625,96,-47.240245050771826 +3.00390625,96,-49.27881004966202 +3.25390625,96,-51.12666529838489 +3.50390625,96,-52.81749689322446 +0.00390625,98,-54.37719916287128 +0.25390625,98,-55.826043828828205 +0.50390625,98,-57.18017237182168 +0.75390625,98,-58.45264162524597 +1.00390625,98,-59.65416911483857 +1.25390625,98,-60.79367314864311 +1.50390625,98,-61.878670267169504 +1.75390625,98,-62.915571932556716 +2.00390625,98,-63.90990890778167 +2.25390625,98,-64.86650276249235 +2.50390625,98,-65.78959813860075 +2.75390625,98,-66.68296503762545 +3.00390625,98,-67.54997773846151 +3.25390625,98,-68.39367452659876 +3.50390625,98,-69.21680102757045 +0.00390625,100,-70.02183823307459 +0.25390625,100,-70.81101511551839 +0.50390625,100,-71.58630437013024 +0.75390625,100,-72.34939792084802 +1.00390625,100,-73.10165722060346 +1.25390625,100,-73.84403064362174 +1.50390625,100,-74.57692765287928 +1.75390625,100,-75.30003623367675 +2.00390625,100,-76.01206723502304 +2.25390625,100,-76.71041038270462 +2.50390625,100,-77.3906909620158 +2.75390625,100,-78.0462387386981 +3.00390625,100,-78.66752278490283 +3.25390625,100,-79.24168766260996 +3.50390625,100,-79.7524419155642 +0.00390625,102,-80.18066457924307 +0.25390625,102,-80.5060987048585 +0.50390625,102,-80.71023531505459 +0.75390625,102,-50.25480094245529 +1.00390625,102,-49.99230938964629 +1.25390625,102,-49.21786824050544 +1.50390625,102,-47.9650494042717 +1.75390625,102,-46.27465246852293 +2.00390625,102,-44.18015676321404 +2.25390625,102,-41.696202093030415 +2.50390625,102,-38.81007279768844 +2.75390625,102,-35.47310721822009 +3.00390625,102,-31.587031255076493 +3.25390625,102,-26.977326393255943 +3.50390625,102,-21.33989787624732 +0.00390625,104,-14.14780410991879 +0.25390625,104,-4.62062454444272 +0.50390625,104,7.581979344153989 +0.75390625,104,20.505213218189724 +1.00390625,104,29.10630747911209 +1.25390625,104,27.410849497492627 +1.50390625,104,15.861709286478012 +1.75390625,104,2.7191161654098854 +2.00390625,104,-8.309928587238517 +2.25390625,104,-16.55082646385113 +2.50390625,104,-22.768844472745286 +2.75390625,104,-27.689654638391627 +3.00390625,104,-31.752228049970057 +3.25390625,104,-35.21357492989875 +3.50390625,104,-38.23203765048043 +0.00390625,106,-40.91101796530932 +0.25390625,106,-43.32154646329237 +0.50390625,106,-45.51450562769264 +0.75390625,106,-47.52763020133716 +1.00390625,106,-49.38972799316454 +1.25390625,106,-51.12334160290754 +1.50390625,106,-52.746493369450576 +1.75390625,106,-54.273868518438476 +2.00390625,106,-55.71764155302533 +2.25390625,106,-57.08806905515007 +2.50390625,106,-58.39392549605306 +2.75390625,106,-59.642831265179154 +3.00390625,106,-60.841505481127484 +3.25390625,106,-61.99596572530461 +3.50390625,106,-63.11169031507431 +0.00390625,108,-64.193754398205 +0.25390625,108,-65.24694863249627 +0.50390625,108,-66.27588727991923 +0.75390625,108,-67.28511206358229 +1.00390625,108,-68.2791975969759 +1.25390625,108,-69.26286488772388 +1.50390625,108,-70.24111053491411 +1.75390625,108,-71.21936167536039 +2.00390625,108,-72.2036703267894 +2.25390625,108,-73.20096749680523 +2.50390625,108,-74.21940765590298 +2.75390625,108,-75.2688526206642 +3.00390625,108,-76.36157518343963 +3.25390625,108,-77.51332147069678 +3.50390625,108,-78.74498304375572 +0.00390625,110,-80.08535819096821 +0.25390625,110,-81.5759852450388 +0.50390625,110,-83.28023318884365 +0.75390625,110,-85.30205004811296 +1.00390625,110,-87.82962474006901 +1.25390625,110,-91.25479453129535 +1.50390625,110,-96.54823966909305 +1.75390625,110,-48.690713326800136 +2.00390625,110,-48.49607457290017 +2.25390625,110,-47.91866377486158 +2.50390625,110,-46.975501951857666 +2.75390625,110,-45.687897203083196 +3.00390625,110,-44.07413055256501 +3.25390625,110,-42.14304289674462 +3.50390625,110,-39.88885678532767 +0.00390625,112,-37.28616967387086 +0.25390625,112,-34.28310274365623 +0.50390625,112,-30.789388251207136 +0.75390625,112,-26.653482877845974 +1.00390625,112,-21.616797627308802 +1.25390625,112,-15.226296033502729 +1.50390625,112,-6.741074364784632 +1.75390625,112,4.501424981178435 +2.00390625,112,17.454249444494614 +2.25390625,112,28.11674943237772 +2.50390625,112,28.65802753316117 +2.75390625,112,18.953752793227952 +3.00390625,112,6.062545535092059 +3.25390625,112,-5.579080289034611 +3.50390625,112,-14.494920882577356 +0.00390625,114,-21.218215833919103 +0.25390625,114,-26.52000216443126 +0.50390625,114,-30.893109209722816 +0.75390625,114,-34.62455666967 +1.00390625,114,-37.88858287712665 +1.25390625,114,-40.797448456062185 +1.50390625,114,-43.42744146358246 +1.75390625,114,-45.83274040302088 +2.00390625,114,-48.05323576319053 +2.25390625,114,-50.119187123188965 +2.50390625,114,-52.05413041208375 +2.75390625,114,-53.876768467747596 +3.00390625,114,-55.60224469091722 +3.25390625,114,-57.24302815526666 +3.50390625,114,-58.8095460199727 +0.00390625,116,-60.31064720645995 +0.25390625,116,-61.75395083492746 +0.50390625,116,-63.14611484117393 +0.75390625,116,-64.49304866427495 +1.00390625,116,-65.8000870792962 +1.25390625,116,-67.07213743059324 +1.50390625,116,-68.31381006140617 +1.75390625,116,-69.5295398041443 +2.00390625,116,-70.72370597234462 +2.25390625,116,-71.9007582786535 +2.50390625,116,-73.06535710462107 +2.75390625,116,-74.22253895295461 +3.00390625,116,-75.37792128665996 +3.25390625,116,-76.53796787780219 +3.50390625,116,-77.7103460712766 +0.00390625,118,-78.90442629122131 +0.25390625,118,-80.13200628262308 +0.50390625,118,-81.40840376518808 +0.75390625,118,-82.75417722455332 +1.00390625,118,-84.19797725297015 +1.25390625,118,-85.7815679012492 +1.50390625,118,-87.56937186662341 +1.75390625,118,-89.6685159561734 +2.00390625,118,-92.2771003747914 +2.25390625,118,-95.82626415051224 +2.50390625,118,-101.55231011618875 +2.75390625,118,-50.595876626384765 +3.00390625,118,-50.45503997697726 +3.25390625,118,-50.03408683413114 +3.50390625,118,-49.33705892063048 +0.00390625,120,-48.36875197461498 +0.25390625,120,-47.132247895386676 +0.50390625,120,-45.62605909567331 +0.75390625,120,-43.84100715399892 +1.00390625,120,-41.75660607128694 +1.25390625,120,-39.336278632016366 +1.50390625,120,-36.520097049297114 +1.75390625,120,-33.21257967010117 +2.00390625,120,-29.26067174250705 +2.25390625,120,-24.412095457020875 +2.50390625,120,-18.23764641222074 +2.75390625,120,-10.029485699756034 +3.00390625,120,1.012072188140099 +3.25390625,120,14.412934109301485 +3.50390625,120,26.62471456197136 +0.00390625,122,29.408433251810003 +0.25390625,122,21.887452299304382 +0.50390625,122,8.892960066473663 +0.75390625,122,-3.897111011638778 +1.00390625,122,-14.13327319385876 +1.25390625,122,-21.952661501528507 +1.50390625,122,-28.12260906687691 +1.75390625,122,-33.20647405037126 +2.00390625,122,-37.546176834244406 +2.25390625,122,-41.35227416572381 +2.50390625,122,-44.76110693478879 +2.75390625,122,-47.865443203420796 +3.00390625,122,-50.731162538659575 +3.25390625,122,-53.40677040471804 +3.50390625,122,-55.92908438422904 +0.00390625,124,-58.32677245591381 +0.25390625,124,-60.62262433329109 +0.50390625,124,-62.83503641511019 +0.75390625,124,-64.97897993173987 +1.00390625,124,-67.06660405000952 +1.25390625,124,-69.10755603768807 +1.50390625,124,-71.10905665562731 +1.75390625,124,-73.07574269350329 +2.00390625,124,-75.00927843577537 +2.25390625,124,-76.90775523845988 +2.50390625,124,-78.76496252187141 +2.75390625,124,-80.56975286439021 +3.00390625,124,-82.30594980590789 +3.25390625,124,-83.95348375401333 +3.50390625,124,-85.49144443914645 +0.00390625,126,-86.90312487518248 +0.25390625,126,-88.18181732714969 +0.50390625,126,-89.33492574553058 +0.75390625,126,-90.38429804956463 +1.00390625,126,-91.36280107386007 +1.25390625,126,-92.30929699409388 +1.50390625,126,-93.26457053647863 +1.75390625,126,-94.26961410099082 +2.00390625,126,-95.36645031873014 +2.25390625,126,-96.60117313579852 +2.50390625,126,-98.02902039256179 +2.75390625,126,-99.72115095669977 +3.00390625,126,-101.76909168158394 +3.25390625,126,-104.25568343069475 +3.50390625,126,-106.99706829873136 diff --git a/r/R/axis.R b/r/R/axis.R index e4383e6..5d434fe 100644 --- a/r/R/axis.R +++ b/r/R/axis.R @@ -41,7 +41,7 @@ AxisLine <- S7::new_class( "must be TRUE, FALSE, 'auto', or NULL" } ), - on_zero = bool_or_null_property(), + on_zero = optional_logical_scalar, on_zero_axis_index = numeric_or_null_property(), symbol = S7::new_property( class = S7::class_any, @@ -124,9 +124,9 @@ AxisTick <- S7::new_class( "must be TRUE, FALSE, 'auto', or NULL" } ), - inside = bool_or_null_property(), + inside = optional_logical_scalar, length = numeric_or_null_property(), - align_with_label = bool_or_null_property(), + align_with_label = optional_logical_scalar, line_style = class_or_null_property(LineStyle) ) ) @@ -152,7 +152,7 @@ S7::method(to_list, AxisTick) <- function(x, ...) { MinorTick <- S7::new_class( "MinorTick", properties = list( - show = bool_or_null_property(), + show = optional_logical_scalar, split_number = numeric_or_null_property(), length = numeric_or_null_property(), line_style = class_or_null_property(LineStyle) @@ -181,7 +181,7 @@ S7::method(to_list, MinorTick) <- function(x, ...) { SplitLine <- S7::new_class( "SplitLine", properties = list( - show = bool_or_null_property(), + show = optional_logical_scalar, interval = S7::new_property( class = S7::class_any, default = NULL, @@ -198,8 +198,8 @@ SplitLine <- S7::new_class( "must be a number, 'auto', or NULL" } ), - show_min_line = bool_or_null_property(), - show_max_line = bool_or_null_property(), + show_min_line = optional_logical_scalar, + show_max_line = optional_logical_scalar, line_style = class_or_null_property(LineStyle) ) ) @@ -223,7 +223,7 @@ S7::method(to_list, SplitLine) <- function(x, ...) { MinorSplitLine <- S7::new_class( "MinorSplitLine", properties = list( - show = bool_or_null_property(), + show = optional_logical_scalar, line_style = class_or_null_property(LineStyle) ) ) @@ -248,7 +248,7 @@ S7::method(to_list, MinorSplitLine) <- function(x, ...) { SplitArea <- S7::new_class( "SplitArea", properties = list( - show = bool_or_null_property(), + show = optional_logical_scalar, interval = S7::new_property( class = S7::class_any, default = NULL, @@ -297,14 +297,14 @@ S7::method(to_list, SplitArea) <- function(x, ...) { AxisLabel <- S7::new_class( "AxisLabel", properties = list( - show = bool_or_null_property(), - inside = bool_or_null_property(), + show = optional_logical_scalar, + inside = optional_logical_scalar, rotate = numeric_or_null_property(), margin = numeric_or_null_property(), formatter = S7::new_property(class = S7::class_any, default = NULL), - show_min_label = bool_or_null_property(), - show_max_label = bool_or_null_property(), - hide_overlap = bool_or_null_property(), + show_min_label = optional_logical_scalar, + show_max_label = optional_logical_scalar, + hide_overlap = optional_logical_scalar, interval = S7::new_property( class = S7::class_any, default = NULL, @@ -386,16 +386,19 @@ Axis <- S7::new_class( "Axis", properties = list( # AxisBaseOptionCommon - type = enum_property(c("value", "category", "time", "log")), - show = bool_or_null_property(), - inverse = bool_or_null_property(), - name = string_or_null_property(), - name_location = enum_property(c("start", "middle", "center", "end")), + type = enum(c("value", "category", "time", "log"), nullable = TRUE), + show = optional_logical_scalar, + inverse = optional_logical_scalar, + name = optional_character_scalar, + name_location = enum( + c("start", "middle", "center", "end"), + nullable = TRUE + ), name_rotate = numeric_or_null_property(), name_gap = numeric_or_null_property(), name_text_style = class_or_null_property(TextStyle), - silent = bool_or_null_property(), - trigger_event = bool_or_null_property(), + silent = optional_logical_scalar, + trigger_event = optional_logical_scalar, # Min/max: number, string ("dataMin"/"dataMax"), or NULL min = S7::new_property( class = S7::class_any, @@ -438,13 +441,13 @@ Axis <- S7::new_class( } ), # ValueAxisBaseOption - scale = bool_or_null_property(), + scale = optional_logical_scalar, # NumericAxisBaseOptionCommon split_number = numeric_or_null_property(), interval = numeric_or_null_property(), min_interval = numeric_or_null_property(), max_interval = numeric_or_null_property(), - align_ticks = bool_or_null_property(), + align_ticks = optional_logical_scalar, # LogAxisBaseOption log_base = numeric_or_null_property(), # CategoryAxisBaseOption: boundaryGap is boolean @@ -476,7 +479,7 @@ Axis <- S7::new_class( minor_split_line = class_or_null_property(MinorSplitLine), split_area = class_or_null_property(SplitArea), # Axis position: "left"/"right" for yAxis, "top"/"bottom" for xAxis - position = enum_property(c("left", "right", "top", "bottom")), + position = enum(c("left", "right", "top", "bottom"), nullable = TRUE), # Multi-grid support: index of the grid this axis belongs to grid_index = numeric_or_null_property() ) diff --git a/r/R/components.R b/r/R/components.R index 1591458..696ce91 100644 --- a/r/R/components.R +++ b/r/R/components.R @@ -37,7 +37,7 @@ Grid <- S7::new_class( "Grid", properties = list( - show = bool_or_null_property(), + show = optional_logical_scalar, # BoxLayoutOptionMixin left = numeric_or_string_property(), right = numeric_or_string_property(), @@ -45,13 +45,13 @@ Grid <- S7::new_class( bottom = numeric_or_string_property(), width = numeric_or_string_property(), height = numeric_or_string_property(), - contain_label = bool_or_null_property(), - background_color = color_property(), + contain_label = optional_logical_scalar, + background_color = optional_character_scalar, border_width = numeric_or_null_property(), - border_color = color_property(), + border_color = optional_character_scalar, # ShadowOptionMixin shadow_blur = numeric_or_null_property(), - shadow_color = color_property(), + shadow_color = optional_character_scalar, shadow_offset_x = numeric_or_null_property(), shadow_offset_y = numeric_or_null_property() ) @@ -96,15 +96,18 @@ S7::method(to_list, Grid) <- function(x, ...) { Title <- S7::new_class( "Title", properties = list( - show = bool_or_null_property(), - text = string_or_null_property(), - link = string_or_null_property(), - target = enum_property(c("self", "blank")), - subtext = string_or_null_property(), - sublink = string_or_null_property(), - subtarget = enum_property(c("self", "blank")), - text_align = enum_property(c("auto", "left", "center", "right")), - text_vertical_align = enum_property(c("auto", "top", "middle", "bottom")), + show = optional_logical_scalar, + text = optional_character_scalar, + link = optional_character_scalar, + target = enum(c("self", "blank"), nullable = TRUE), + subtext = optional_character_scalar, + sublink = optional_character_scalar, + subtarget = enum(c("self", "blank"), nullable = TRUE), + text_align = enum(c("auto", "left", "center", "right"), nullable = TRUE), + text_vertical_align = enum( + c("auto", "top", "middle", "bottom"), + nullable = TRUE + ), padding = S7::new_property( class = S7::class_any, default = NULL, @@ -121,9 +124,9 @@ Title <- S7::new_class( item_gap = numeric_or_null_property(), text_style = class_or_null_property(TextStyle), subtext_style = class_or_null_property(TextStyle), - trigger_event = bool_or_null_property(), - background_color = color_property(), - border_color = color_property(), + trigger_event = optional_logical_scalar, + background_color = optional_character_scalar, + border_color = optional_character_scalar, border_width = numeric_or_null_property(), border_radius = S7::new_property( class = S7::class_any, @@ -190,9 +193,9 @@ S7::method(to_list, Title) <- function(x, ...) { Legend <- S7::new_class( "Legend", properties = list( - show = bool_or_null_property(), - orient = enum_property(c("horizontal", "vertical")), - align = enum_property(c("auto", "left", "right")), + show = optional_logical_scalar, + orient = enum(c("horizontal", "vertical"), nullable = TRUE), + align = enum(c("auto", "left", "right"), nullable = TRUE), # BoxLayoutOptionMixin left = numeric_or_string_property(), right = numeric_or_string_property(), @@ -216,7 +219,7 @@ Legend <- S7::new_class( item_gap = numeric_or_null_property(), item_width = numeric_or_null_property(), item_height = numeric_or_null_property(), - icon = string_or_null_property(), + icon = optional_character_scalar, selected_mode = S7::new_property( class = S7::class_any, default = NULL, @@ -251,12 +254,12 @@ Legend <- S7::new_class( } ), formatter = S7::new_property(class = S7::class_any, default = NULL), - inactive_color = color_property(), - inactive_border_color = color_property(), + inactive_color = optional_character_scalar, + inactive_border_color = optional_character_scalar, text_style = class_or_null_property(TextStyle), item_style = class_or_null_property(ItemStyle), - background_color = color_property(), - border_color = color_property(), + background_color = optional_character_scalar, + border_color = optional_character_scalar, border_width = numeric_or_null_property(), border_radius = S7::new_property( class = S7::class_any, @@ -271,7 +274,7 @@ Legend <- S7::new_class( "must be a number or length-4 numeric vector, or NULL" } ), - trigger_event = bool_or_null_property(), + trigger_event = optional_logical_scalar, data = S7::new_property(class = S7::class_any, default = NULL) ) ) @@ -318,26 +321,29 @@ Tooltip <- S7::new_class( "Tooltip", properties = list( # CommonTooltipOption - show = bool_or_null_property(), - trigger = enum_property(c("item", "axis", "none")), - trigger_on = enum_property(c( - "mousemove", - "click", - "none", - "mousemove|click" - )), - show_content = bool_or_null_property(), - always_show_content = bool_or_null_property(), + show = optional_logical_scalar, + trigger = enum(c("item", "axis", "none"), nullable = TRUE), + trigger_on = enum( + c( + "mousemove", + "click", + "none", + "mousemove|click" + ), + nullable = TRUE + ), + show_content = optional_logical_scalar, + always_show_content = optional_logical_scalar, formatter = S7::new_property(class = S7::class_any, default = NULL), value_formatter = S7::new_property(class = S7::class_any, default = NULL), position = S7::new_property(class = S7::class_any, default = NULL), - confine = bool_or_null_property(), - enterable = bool_or_null_property(), + confine = optional_logical_scalar, + enterable = optional_logical_scalar, show_delay = numeric_or_null_property(), hide_delay = numeric_or_null_property(), transition_duration = numeric_or_null_property(), - background_color = color_property(), - border_color = color_property(), + background_color = optional_character_scalar, + border_color = optional_character_scalar, border_width = numeric_or_null_property(), border_radius = numeric_or_null_property(), padding = S7::new_property( @@ -354,15 +360,18 @@ Tooltip <- S7::new_class( } ), text_style = class_or_null_property(TextStyle), - extra_css_text = string_or_null_property(), + extra_css_text = optional_character_scalar, # TooltipOption-specific - order = enum_property(c( - "seriesAsc", - "seriesDesc", - "valueAsc", - "valueDesc" - )), - class_name = string_or_null_property() + order = enum( + c( + "seriesAsc", + "seriesDesc", + "valueAsc", + "valueDesc" + ), + nullable = TRUE + ), + class_name = optional_character_scalar ) ) @@ -404,13 +413,17 @@ S7::method(to_list, Tooltip) <- function(x, ...) { VisualMap <- S7::new_class( "VisualMap", properties = list( - type = enum_property(c("continuous", "piecewise"), default = "continuous"), + type = enum( + c("continuous", "piecewise"), + default = "continuous", + nullable = TRUE + ), min = numeric_or_null_property(), max = numeric_or_null_property(), precision = numeric_or_null_property(), - calculable = bool_or_null_property(), - show = bool_or_null_property(), - orient = enum_property(c("vertical", "horizontal")), + calculable = optional_logical_scalar, + show = optional_logical_scalar, + orient = enum(c("vertical", "horizontal"), nullable = TRUE), in_range = S7::new_property(class = S7::class_any, default = NULL), out_of_range = S7::new_property(class = S7::class_any, default = NULL), # BoxLayoutOptionMixin @@ -562,20 +575,23 @@ DataZoom <- S7::new_class( "DataZoom", properties = list( # Common (DataZoomOption) - type = enum_property(c("slider", "inside"), default = "slider"), - id = string_or_null_property(), - disabled = bool_or_null_property(), + type = enum(c("slider", "inside"), default = "slider", nullable = TRUE), + id = optional_character_scalar, + disabled = optional_logical_scalar, x_axis_index = axis_index_property(), y_axis_index = axis_index_property(), radius_axis_index = axis_index_property(), angle_axis_index = axis_index_property(), single_axis_index = axis_index_property(), - filter_mode = enum_property(c( - "filter", - "weakFilter", - "empty", - "none" - )), + filter_mode = enum( + c( + "filter", + "weakFilter", + "empty", + "none" + ), + nullable = TRUE + ), start = numeric_or_null_property(), end = numeric_or_null_property(), start_value = S7::new_property(class = S7::class_any, default = NULL), @@ -584,7 +600,7 @@ DataZoom <- S7::new_class( max_span = numeric_or_null_property(), min_value_span = numeric_or_string_property(), max_value_span = numeric_or_string_property(), - orient = enum_property(c("horizontal", "vertical")), + orient = enum(c("horizontal", "vertical"), nullable = TRUE), throttle = numeric_or_null_property(), range_mode = S7::new_property( class = S7::class_any, @@ -604,20 +620,20 @@ DataZoom <- S7::new_class( } ), # SliderDataZoomOption-specific - show = bool_or_null_property(), - background_color = color_property(), + show = optional_logical_scalar, + background_color = optional_character_scalar, left = numeric_or_string_property(), right = numeric_or_string_property(), top = numeric_or_string_property(), bottom = numeric_or_string_property(), width = numeric_or_string_property(), height = numeric_or_string_property(), - zoom_lock = bool_or_null_property(), + zoom_lock = optional_logical_scalar, # InsideDataZoomOption-specific zoom_on_mouse_wheel = mouse_modifier_property(), move_on_mouse_move = mouse_modifier_property(), move_on_mouse_wheel = mouse_modifier_property(), - prevent_default_mouse_move = bool_or_null_property() + prevent_default_mouse_move = optional_logical_scalar ) ) diff --git a/r/R/draw.R b/r/R/draw.R index c3b4299..37dbfc2 100644 --- a/r/R/draw.R +++ b/r/R/draw.R @@ -313,9 +313,7 @@ draw_line <- function( } } - if (!is.logical(points) || length(points) != 1L || is.na(points)) { - cli::cli_abort("{.arg points} must be a single non-missing logical value.") - } + check_logical_scalar(points) # ECharts treats missing `showSymbol` as TRUE; only pass FALSE when hiding. show_symbol <- if (points) NULL else FALSE @@ -520,15 +518,7 @@ build_block_mark_area <- function(x, blocks, block_color, block_opacity) { "{.arg block_color} must be provided when {.arg blocks} is set." ) } - if ( - !is.numeric(block_opacity) || - length(block_opacity) != 1L || - !is.finite(block_opacity) || - block_opacity < 0 || - block_opacity > 1 - ) { - cli::cli_abort("{.arg block_opacity} must be a single number in [0, 1].") - } + check_prob_scalar(block_opacity) # Determine level order so positional matching is stable. For factors, drop # unused levels so `block_color` doesn't need entries for absent categories. @@ -848,9 +838,7 @@ draw_scatter <- function( yv <- yv[ok] df <- data.frame(.x = xv, .y = yv) if (fit_method == "gam") { - if (!requireNamespace("mgcv", quietly = TRUE)) { - stop("Package 'mgcv' is required for fit = \"gam\"", call. = FALSE) - } + check_dependencies("mgcv") model <- mgcv::gam(.y ~ s(.x), data = df) } else { model <- stats::glm(.y ~ .x, data = df) diff --git a/r/R/draw_spectrogram.R b/r/R/draw_spectrogram.R index 9520c4a..bc0de47 100644 --- a/r/R/draw_spectrogram.R +++ b/r/R/draw_spectrogram.R @@ -246,7 +246,7 @@ draw_spectrogram <- function( xlab = NULL, ylab = NULL, theme = NULL, - margins = DEFAULT_MARGINS, + margins = NULL, width = NULL, height = NULL, filename = NULL @@ -330,6 +330,15 @@ draw_spectrogram <- function( validate_range(time_range, "time_range") validate_range(zlim, "zlim") + # Auto margins if title is set + if (is.null(margins)) { + margins <- DEFAULT_MARGINS + margins[["right"]] <- "90" + if (!is.null(title)) { + margins[["top"]] <- "50" + } + } + # -- 2. STFT (raw signal) or matrix input ------------------------------------ if (is_raw_signal) { if (is.null(sample_rate)) { diff --git a/r/R/label.R b/r/R/label.R index 92851af..eed5022 100644 --- a/r/R/label.R +++ b/r/R/label.R @@ -35,7 +35,7 @@ LabelOption <- S7::new_class( "LabelOption", properties = list( - show = bool_or_null_property(), + show = optional_logical_scalar, position = S7::new_property( class = S7::class_any, default = NULL, @@ -89,7 +89,7 @@ LabelOption <- S7::new_class( } ), formatter = S7::new_property(class = S7::class_any, default = NULL), - silent = bool_or_null_property(), + silent = optional_logical_scalar, precision = S7::new_property( class = S7::class_any, default = NULL, @@ -106,7 +106,7 @@ LabelOption <- S7::new_class( "must be a number, 'auto', or NULL" } ), - value_animation = bool_or_null_property(), + value_animation = optional_logical_scalar, min_margin = numeric_or_null_property(), # Text styling -- flattened into the same JSON object in to_list() text_style = class_or_null_property(TextStyle) @@ -145,8 +145,8 @@ S7::method(to_list, LabelOption) <- function(x, ...) { LabelLine <- S7::new_class( "LabelLine", properties = list( - show = bool_or_null_property(), - show_above = bool_or_null_property(), + show = optional_logical_scalar, + show_above = optional_logical_scalar, length = numeric_or_null_property(), length2 = numeric_or_null_property(), smooth = S7::new_property( diff --git a/r/R/option.R b/r/R/option.R index 134726e..f70de30 100644 --- a/r/R/option.R +++ b/r/R/option.R @@ -54,13 +54,13 @@ EChartsOption <- S7::new_class( series = S7::new_property(class = S7::class_any, default = NULL), # Global settings color = color_palette_property(), - background_color = color_property(), + background_color = optional_character_scalar, text_style = class_or_null_property(TextStyle), # Animation - animation = bool_or_null_property(), + animation = optional_logical_scalar, animation_threshold = numeric_or_null_property(), animation_duration = numeric_or_null_property(), - animation_easing = string_or_null_property(), + animation_easing = optional_character_scalar, animation_delay = numeric_or_null_property(), # Other dark_mode = S7::new_property( @@ -79,7 +79,7 @@ EChartsOption <- S7::new_class( "must be TRUE, FALSE, 'auto', or NULL" } ), - use_utc = bool_or_null_property() + use_utc = optional_logical_scalar ) ) diff --git a/r/R/series.R b/r/R/series.R index 76f0e1b..f927d7f 100644 --- a/r/R/series.R +++ b/r/R/series.R @@ -66,7 +66,7 @@ MarkAreaDataPoint <- S7::new_class( "must be a single number, string, or NULL" } ), - name = string_or_null_property(), + name = optional_character_scalar, value = S7::new_property( class = S7::class_any, default = NULL, @@ -114,7 +114,7 @@ MarkArea <- S7::new_class( "MarkArea", properties = list( data = S7::new_property(class = S7::class_any, default = NULL), - silent = bool_or_null_property(), + silent = optional_logical_scalar, precision = numeric_or_null_property(), item_style = class_or_null_property(ItemStyle), label = class_or_null_property(LabelOption), @@ -175,14 +175,14 @@ LineSeries <- S7::new_class( "LineSeries", properties = list( # Common series fields - name = string_or_null_property(), + name = optional_character_scalar, data = S7::new_property(class = S7::class_any, default = NULL), x_axis_index = numeric_or_null_property(), y_axis_index = numeric_or_null_property(), - stack = string_or_null_property(), - silent = bool_or_null_property(), - legend_hover_link = bool_or_null_property(), - color = color_property(), + stack = optional_character_scalar, + silent = optional_logical_scalar, + legend_hover_link = optional_logical_scalar, + color = optional_character_scalar, z_level = numeric_or_null_property(), z = numeric_or_null_property(), # Line-specific @@ -222,10 +222,10 @@ LineSeries <- S7::new_class( "must be FALSE, 'start', 'end', 'middle', or NULL" } ), - connect_nulls = bool_or_null_property(), - clip = bool_or_null_property(), - show_symbol = bool_or_null_property(), - symbol = string_or_null_property(), + connect_nulls = optional_logical_scalar, + clip = optional_logical_scalar, + show_symbol = optional_logical_scalar, + symbol = optional_character_scalar, symbol_size = S7::new_property( class = S7::class_any, default = NULL, @@ -292,26 +292,26 @@ BarSeries <- S7::new_class( "BarSeries", properties = list( # Common series fields - name = string_or_null_property(), + name = optional_character_scalar, data = S7::new_property(class = S7::class_any, default = NULL), x_axis_index = numeric_or_null_property(), y_axis_index = numeric_or_null_property(), - stack = string_or_null_property(), - silent = bool_or_null_property(), - legend_hover_link = bool_or_null_property(), - color = color_property(), + stack = optional_character_scalar, + silent = optional_logical_scalar, + legend_hover_link = optional_logical_scalar, + color = optional_character_scalar, z_level = numeric_or_null_property(), z = numeric_or_null_property(), # Bar-specific - clip = bool_or_null_property(), + clip = optional_logical_scalar, bar_width = numeric_or_string_property(), bar_max_width = numeric_or_string_property(), bar_min_width = numeric_or_string_property(), bar_min_height = numeric_or_null_property(), bar_gap = numeric_or_string_property(), bar_category_gap = numeric_or_string_property(), - round_cap = bool_or_null_property(), - show_background = bool_or_null_property(), + round_cap = optional_logical_scalar, + show_background = optional_logical_scalar, item_style = class_or_null_property(ItemStyle), label = class_or_null_property(LabelOption) ) @@ -356,18 +356,18 @@ ScatterSeries <- S7::new_class( "ScatterSeries", properties = list( # Common series fields - name = string_or_null_property(), + name = optional_character_scalar, data = S7::new_property(class = S7::class_any, default = NULL), x_axis_index = numeric_or_null_property(), y_axis_index = numeric_or_null_property(), - silent = bool_or_null_property(), - legend_hover_link = bool_or_null_property(), - color = color_property(), + silent = optional_logical_scalar, + legend_hover_link = optional_logical_scalar, + color = optional_character_scalar, z_level = numeric_or_null_property(), z = numeric_or_null_property(), # Scatter-specific - clip = bool_or_null_property(), - symbol = string_or_null_property(), + clip = optional_logical_scalar, + symbol = optional_character_scalar, symbol_size = S7::new_property( class = S7::class_any, default = NULL, @@ -384,7 +384,7 @@ ScatterSeries <- S7::new_class( "must be a number, length-2 numeric vector, function, or NULL" } ), - large = bool_or_null_property(), + large = optional_logical_scalar, large_threshold = numeric_or_null_property(), item_style = class_or_null_property(ItemStyle), label = class_or_null_property(LabelOption) @@ -443,11 +443,11 @@ PieSeries <- S7::new_class( "PieSeries", properties = list( # Common series fields - name = string_or_null_property(), + name = optional_character_scalar, data = S7::new_property(class = S7::class_any, default = NULL), - silent = bool_or_null_property(), - legend_hover_link = bool_or_null_property(), - color = color_property(), + silent = optional_logical_scalar, + legend_hover_link = optional_logical_scalar, + color = optional_character_scalar, z_level = numeric_or_null_property(), z = numeric_or_null_property(), # Pie-specific @@ -477,8 +477,8 @@ PieSeries <- S7::new_class( "must be a number/string or length-2 vector, or NULL" } ), - rose_type = enum_property(c("radius", "area")), - clockwise = bool_or_null_property(), + rose_type = enum(c("radius", "area"), nullable = TRUE), + clockwise = optional_logical_scalar, start_angle = numeric_or_null_property(), end_angle = S7::new_property( class = S7::class_any, @@ -500,10 +500,10 @@ PieSeries <- S7::new_class( min_angle = numeric_or_null_property(), min_show_label_angle = numeric_or_null_property(), selected_offset = numeric_or_null_property(), - avoid_label_overlap = bool_or_null_property(), + avoid_label_overlap = optional_logical_scalar, percent_precision = numeric_or_null_property(), - still_show_zero_sum = bool_or_null_property(), - animation_type = enum_property(c("expansion", "scale")), + still_show_zero_sum = optional_logical_scalar, + animation_type = enum(c("expansion", "scale"), nullable = TRUE), item_style = class_or_null_property(ItemStyle), label = class_or_null_property(LabelOption), label_line = class_or_null_property(LabelLine), @@ -551,17 +551,17 @@ BoxplotSeries <- S7::new_class( "BoxplotSeries", properties = list( # Common series fields - name = string_or_null_property(), + name = optional_character_scalar, data = S7::new_property(class = S7::class_any, default = NULL), x_axis_index = numeric_or_null_property(), y_axis_index = numeric_or_null_property(), - silent = bool_or_null_property(), - legend_hover_link = bool_or_null_property(), - color = color_property(), + silent = optional_logical_scalar, + legend_hover_link = optional_logical_scalar, + color = optional_character_scalar, z_level = numeric_or_null_property(), z = numeric_or_null_property(), # Boxplot-specific - layout = enum_property(c("horizontal", "vertical")), + layout = enum(c("horizontal", "vertical"), nullable = TRUE), box_width = S7::new_property( class = S7::class_any, default = NULL, @@ -618,12 +618,12 @@ HeatmapSeries <- S7::new_class( "HeatmapSeries", properties = list( # Common series fields - name = string_or_null_property(), + name = optional_character_scalar, data = S7::new_property(class = S7::class_any, default = NULL), x_axis_index = numeric_or_null_property(), y_axis_index = numeric_or_null_property(), - silent = bool_or_null_property(), - legend_hover_link = bool_or_null_property(), + silent = optional_logical_scalar, + legend_hover_link = optional_logical_scalar, z_level = numeric_or_null_property(), z = numeric_or_null_property(), # Heatmap-specific diff --git a/r/R/styles.R b/r/R/styles.R index 81ec60e..03be27c 100644 --- a/r/R/styles.R +++ b/r/R/styles.R @@ -29,17 +29,17 @@ LineStyle <- S7::new_class( "LineStyle", properties = list( - color = color_property(), + color = optional_character_scalar, width = numeric_or_null_property(), opacity = numeric_or_null_property(), - type = enum_property(c("solid", "dashed", "dotted")), - cap = enum_property(c("butt", "round", "square")), - join = enum_property(c("bevel", "round", "miter")), + type = enum(c("solid", "dashed", "dotted"), nullable = TRUE), + cap = enum(c("butt", "round", "square"), nullable = TRUE), + join = enum(c("bevel", "round", "miter"), nullable = TRUE), dash_offset = numeric_or_null_property(), miter_limit = numeric_or_null_property(), # ShadowOptionMixin shadow_blur = numeric_or_null_property(), - shadow_color = color_property(), + shadow_color = optional_character_scalar, shadow_offset_x = numeric_or_null_property(), shadow_offset_y = numeric_or_null_property() ) @@ -69,7 +69,7 @@ S7::method(to_list, LineStyle) <- function(x, ...) { AreaStyle <- S7::new_class( "AreaStyle", properties = list( - color = color_property(), + color = optional_character_scalar, opacity = numeric_or_null_property(), origin = S7::new_property( class = S7::class_any, @@ -89,7 +89,7 @@ AreaStyle <- S7::new_class( ), # ShadowOptionMixin shadow_blur = numeric_or_null_property(), - shadow_color = color_property(), + shadow_color = optional_character_scalar, shadow_offset_x = numeric_or_null_property(), shadow_offset_y = numeric_or_null_property() ) @@ -128,14 +128,14 @@ S7::method(to_list, AreaStyle) <- function(x, ...) { ItemStyle <- S7::new_class( "ItemStyle", properties = list( - color = color_property(), + color = optional_character_scalar, opacity = numeric_or_null_property(), # BorderOptionMixin - border_color = color_property(), + border_color = optional_character_scalar, border_width = numeric_or_null_property(), - border_type = enum_property(c("solid", "dashed", "dotted")), - border_cap = enum_property(c("butt", "round", "square")), - border_join = enum_property(c("bevel", "round", "miter")), + border_type = enum(c("solid", "dashed", "dotted"), nullable = TRUE), + border_cap = enum(c("butt", "round", "square"), nullable = TRUE), + border_join = enum(c("bevel", "round", "miter"), nullable = TRUE), border_dash_offset = numeric_or_null_property(), border_miter_limit = numeric_or_null_property(), border_radius = S7::new_property( @@ -157,7 +157,7 @@ ItemStyle <- S7::new_class( decal = S7::new_property(class = S7::class_any, default = NULL), # ShadowOptionMixin shadow_blur = numeric_or_null_property(), - shadow_color = color_property(), + shadow_color = optional_character_scalar, shadow_offset_x = numeric_or_null_property(), shadow_offset_y = numeric_or_null_property() ) @@ -213,8 +213,8 @@ S7::method(to_list, ItemStyle) <- function(x, ...) { TextStyle <- S7::new_class( "TextStyle", properties = list( - color = color_property(), - font_style = enum_property(c("normal", "italic", "oblique")), + color = optional_character_scalar, + font_style = enum(c("normal", "italic", "oblique"), nullable = TRUE), font_weight = S7::new_property( class = S7::class_any, default = NULL, @@ -234,16 +234,16 @@ TextStyle <- S7::new_class( "must be 'normal', 'bold', 'bolder', 'lighter', a number, or NULL" } ), - font_family = string_or_null_property(), + font_family = optional_character_scalar, font_size = numeric_or_string_property(), - align = enum_property(c("left", "center", "right")), - vertical_align = enum_property(c("top", "middle", "bottom")), + align = enum(c("left", "center", "right"), nullable = TRUE), + vertical_align = enum(c("top", "middle", "bottom"), nullable = TRUE), opacity = numeric_or_null_property(), line_height = numeric_or_null_property(), - background_color = color_property(), - border_color = color_property(), + background_color = optional_character_scalar, + border_color = optional_character_scalar, border_width = numeric_or_null_property(), - border_type = enum_property(c("solid", "dashed", "dotted")), + border_type = enum(c("solid", "dashed", "dotted"), nullable = TRUE), border_dash_offset = numeric_or_null_property(), border_radius = S7::new_property( class = S7::class_any, @@ -273,20 +273,23 @@ TextStyle <- S7::new_class( ), width = numeric_or_string_property(), height = numeric_or_null_property(), - text_border_color = color_property(), + text_border_color = optional_character_scalar, text_border_width = numeric_or_null_property(), - text_border_type = enum_property(c("solid", "dashed", "dotted")), + text_border_type = enum(c("solid", "dashed", "dotted"), nullable = TRUE), text_border_dash_offset = numeric_or_null_property(), text_shadow_blur = numeric_or_null_property(), - text_shadow_color = color_property(), + text_shadow_color = optional_character_scalar, text_shadow_offset_x = numeric_or_null_property(), text_shadow_offset_y = numeric_or_null_property(), - overflow = enum_property(c("truncate", "break", "breakAll", "none")), - ellipsis = string_or_null_property(), + overflow = enum( + c("truncate", "break", "breakAll", "none"), + nullable = TRUE + ), + ellipsis = optional_character_scalar, rich = S7::new_property(class = S7::class_any, default = NULL), # ShadowOptionMixin (box-level shadow) shadow_blur = numeric_or_null_property(), - shadow_color = color_property(), + shadow_color = optional_character_scalar, shadow_offset_x = numeric_or_null_property(), shadow_offset_y = numeric_or_null_property() ) diff --git a/r/R/theme.R b/r/R/theme.R index 159d7ab..ceb0a0b 100644 --- a/r/R/theme.R +++ b/r/R/theme.R @@ -38,7 +38,7 @@ Theme <- S7::new_class( "Theme", properties = list( color = color_palette_property(), - background_color = color_property(), + background_color = optional_character_scalar, text_style = class_or_null_property(TextStyle), # Component overrides (plain lists) title = S7::new_property(class = S7::class_any, default = NULL), diff --git a/r/R/utils.R b/r/R/utils.R index dcacdd8..edd5f5c 100644 --- a/r/R/utils.R +++ b/r/R/utils.R @@ -35,7 +35,9 @@ snake_to_camel <- function(x) { #' @keywords internal #' @noRd drop_nulls <- function(x) { - x[!vapply(x, is.null, logical(1))] + # Also drop zero-length values: S7 0.2.2 stores empty prototypes (e.g. logical(0)) + # instead of NULL for new_union(type, NULL) properties with default = NULL. + x[!vapply(x, function(v) is.null(v) || length(v) == 0L, logical(1))] } #' Convert an S7 object to an echarts-compatible named list @@ -110,37 +112,6 @@ to_list <- S7::new_generic("to_list", "x") # -- Type validators for S7 properties ------------------------------------------- # These return validator functions or S7 class unions. -#' @keywords internal -#' @noRd -nullable <- function(type) { - type | S7::class_missing | NULL -} - -#' Validate that a value is one of the allowed choices -#' -#' Returns a custom S7 property object with validation. -#' -#' @param values Character: Allowed values. -#' @param default Optional Character: Default value. -#' @param nullable Logical: Whether `NULL` is accepted. -#' @return S7 property: Property definition. -#' @keywords internal -#' @noRd -enum_property <- function(values, default = NULL, nullable = TRUE) { - S7::new_property( - class = if (nullable) S7::class_any else S7::class_character, - default = default, - validator = function(value) { - if (is.null(value) && nullable) { - return(NULL) - } - if (!is.character(value) || length(value) != 1L || !(value %in% values)) { - paste0("must be one of: ", paste(dQuote(values), collapse = ", ")) - } - } - ) -} - #' Property that accepts a number or NULL #' @keywords internal #' @noRd @@ -159,42 +130,6 @@ numeric_or_null_property <- function(default = NULL) { ) } -#' Property that accepts a string or NULL -#' @keywords internal -#' @noRd -string_or_null_property <- function(default = NULL) { - S7::new_property( - class = S7::class_any, - default = default, - validator = function(value) { - if (is.null(value)) { - return(NULL) - } - if (!is.character(value) || length(value) != 1L) { - "must be a single string or NULL" - } - } - ) -} - -#' Property that accepts a logical or NULL -#' @keywords internal -#' @noRd -bool_or_null_property <- function(default = NULL) { - S7::new_property( - class = S7::class_any, - default = default, - validator = function(value) { - if (is.null(value)) { - return(NULL) - } - if (!is.logical(value) || length(value) != 1L) { - "must be TRUE, FALSE, or NULL" - } - } - ) -} - #' Property that accepts a number, string, or NULL #' #' Used for echarts fields that accept both pixel values (number) and @@ -377,16 +312,6 @@ validate_axis_lim <- function(value, arg) { } -#' Property that accepts a color string, or NULL -#' -#' Currently accepts any string. Future versions may validate -#' hex, rgb(), rgba(), hsl(), or named CSS colors. -#' @keywords internal -#' @noRd -color_property <- function(default = NULL) { - string_or_null_property(default = default) -} - #' Property that accepts a character vector of colors, or NULL #' #' Used for color palettes (e.g. theme and option `color` fields). diff --git a/r/man/Axis.Rd b/r/man/Axis.Rd index 5037e3a..267c830 100644 --- a/r/man/Axis.Rd +++ b/r/man/Axis.Rd @@ -5,11 +5,11 @@ \title{Axis} \usage{ Axis( - type = NULL, + type = character(0), show = NULL, inverse = NULL, name = NULL, - name_location = NULL, + name_location = character(0), name_rotate = NULL, name_gap = NULL, name_text_style = NULL, @@ -33,7 +33,7 @@ Axis( split_line = NULL, minor_split_line = NULL, split_area = NULL, - position = NULL, + position = character(0), grid_index = NULL ) } diff --git a/r/man/BoxplotSeries.Rd b/r/man/BoxplotSeries.Rd index 346939a..a94f176 100644 --- a/r/man/BoxplotSeries.Rd +++ b/r/man/BoxplotSeries.Rd @@ -14,7 +14,7 @@ BoxplotSeries( color = NULL, z_level = NULL, z = NULL, - layout = NULL, + layout = character(0), box_width = NULL, item_style = NULL, label = NULL diff --git a/r/man/DataZoom.Rd b/r/man/DataZoom.Rd index 28d824b..f9c8651 100644 --- a/r/man/DataZoom.Rd +++ b/r/man/DataZoom.Rd @@ -13,7 +13,7 @@ DataZoom( radius_axis_index = NULL, angle_axis_index = NULL, single_axis_index = NULL, - filter_mode = NULL, + filter_mode = character(0), start = NULL, end = NULL, start_value = NULL, @@ -22,7 +22,7 @@ DataZoom( max_span = NULL, min_value_span = NULL, max_value_span = NULL, - orient = NULL, + orient = character(0), throttle = NULL, range_mode = NULL, show = NULL, diff --git a/r/man/ItemStyle.Rd b/r/man/ItemStyle.Rd index 4c51ea1..a37640a 100644 --- a/r/man/ItemStyle.Rd +++ b/r/man/ItemStyle.Rd @@ -9,9 +9,9 @@ ItemStyle( opacity = NULL, border_color = NULL, border_width = NULL, - border_type = NULL, - border_cap = NULL, - border_join = NULL, + border_type = character(0), + border_cap = character(0), + border_join = character(0), border_dash_offset = NULL, border_miter_limit = NULL, border_radius = NULL, diff --git a/r/man/Legend.Rd b/r/man/Legend.Rd index 68f1b53..dfce239 100644 --- a/r/man/Legend.Rd +++ b/r/man/Legend.Rd @@ -6,8 +6,8 @@ \usage{ Legend( show = NULL, - orient = NULL, - align = NULL, + orient = character(0), + align = character(0), left = NULL, right = NULL, top = NULL, diff --git a/r/man/LineStyle.Rd b/r/man/LineStyle.Rd index 8814091..e78e67c 100644 --- a/r/man/LineStyle.Rd +++ b/r/man/LineStyle.Rd @@ -8,9 +8,9 @@ LineStyle( color = NULL, width = NULL, opacity = NULL, - type = NULL, - cap = NULL, - join = NULL, + type = character(0), + cap = character(0), + join = character(0), dash_offset = NULL, miter_limit = NULL, shadow_blur = NULL, diff --git a/r/man/PieSeries.Rd b/r/man/PieSeries.Rd index 3f41d98..1911cc6 100644 --- a/r/man/PieSeries.Rd +++ b/r/man/PieSeries.Rd @@ -14,7 +14,7 @@ PieSeries( z = NULL, center = NULL, radius = NULL, - rose_type = NULL, + rose_type = character(0), clockwise = NULL, start_angle = NULL, end_angle = NULL, @@ -25,7 +25,7 @@ PieSeries( avoid_label_overlap = NULL, percent_precision = NULL, still_show_zero_sum = NULL, - animation_type = NULL, + animation_type = character(0), item_style = NULL, label = NULL, label_line = NULL, diff --git a/r/man/TextStyle.Rd b/r/man/TextStyle.Rd index 01a11d7..d1274c1 100644 --- a/r/man/TextStyle.Rd +++ b/r/man/TextStyle.Rd @@ -6,18 +6,18 @@ \usage{ TextStyle( color = NULL, - font_style = NULL, + font_style = character(0), font_weight = NULL, font_family = NULL, font_size = NULL, - align = NULL, - vertical_align = NULL, + align = character(0), + vertical_align = character(0), opacity = NULL, line_height = NULL, background_color = NULL, border_color = NULL, border_width = NULL, - border_type = NULL, + border_type = character(0), border_dash_offset = NULL, border_radius = NULL, padding = NULL, @@ -25,13 +25,13 @@ TextStyle( height = NULL, text_border_color = NULL, text_border_width = NULL, - text_border_type = NULL, + text_border_type = character(0), text_border_dash_offset = NULL, text_shadow_blur = NULL, text_shadow_color = NULL, text_shadow_offset_x = NULL, text_shadow_offset_y = NULL, - overflow = NULL, + overflow = character(0), ellipsis = NULL, rich = NULL, shadow_blur = NULL, diff --git a/r/man/Title.Rd b/r/man/Title.Rd index c59c79b..14d8621 100644 --- a/r/man/Title.Rd +++ b/r/man/Title.Rd @@ -8,12 +8,12 @@ Title( show = NULL, text = NULL, link = NULL, - target = NULL, + target = character(0), subtext = NULL, sublink = NULL, - subtarget = NULL, - text_align = NULL, - text_vertical_align = NULL, + subtarget = character(0), + text_align = character(0), + text_vertical_align = character(0), padding = NULL, item_gap = NULL, text_style = NULL, diff --git a/r/man/Tooltip.Rd b/r/man/Tooltip.Rd index 7960fc6..c6f704e 100644 --- a/r/man/Tooltip.Rd +++ b/r/man/Tooltip.Rd @@ -6,8 +6,8 @@ \usage{ Tooltip( show = NULL, - trigger = NULL, - trigger_on = NULL, + trigger = character(0), + trigger_on = character(0), show_content = NULL, always_show_content = NULL, formatter = NULL, @@ -25,7 +25,7 @@ Tooltip( padding = NULL, text_style = NULL, extra_css_text = NULL, - order = NULL, + order = character(0), class_name = NULL ) } diff --git a/r/man/VisualMap.Rd b/r/man/VisualMap.Rd index 5392ef3..766dad3 100644 --- a/r/man/VisualMap.Rd +++ b/r/man/VisualMap.Rd @@ -11,7 +11,7 @@ VisualMap( precision = NULL, calculable = NULL, show = NULL, - orient = NULL, + orient = character(0), in_range = NULL, out_of_range = NULL, left = NULL, diff --git a/r/man/draw_spectrogram.Rd b/r/man/draw_spectrogram.Rd index cf4af16..e1dac69 100644 --- a/r/man/draw_spectrogram.Rd +++ b/r/man/draw_spectrogram.Rd @@ -30,7 +30,7 @@ draw_spectrogram( xlab = NULL, ylab = NULL, theme = NULL, - margins = DEFAULT_MARGINS, + margins = NULL, width = NULL, height = NULL, filename = NULL From 10869c4772254c78f300a4b2e362e19289ede842 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Fri, 1 May 2026 08:22:20 -0700 Subject: [PATCH 02/10] rtemis.core >= 0.1.0 --- r/DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/r/DESCRIPTION b/r/DESCRIPTION index 5b72acb..d67e21c 100644 --- a/r/DESCRIPTION +++ b/r/DESCRIPTION @@ -20,7 +20,7 @@ Imports: cli, htmlwidgets, jsonlite, - rtemis.core, + rtemis.core (>= 0.1.0), S7, signal, viridisLite From 2e1fd7942b877b6734476bf40cb3a47dbca88847 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Mon, 4 May 2026 02:06:19 -0700 Subject: [PATCH 03/10] up --- Makefile | 98 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 0a9043b..86cfbdd 100644 --- a/Makefile +++ b/Makefile @@ -1,43 +1,101 @@ -.PHONY: format format-r document document-r install install-r test test-r +R_DIR := r +PKG := $(shell awk '/^Package:/{print $$2; exit}' $(R_DIR)/DESCRIPTION) +R ?= R +RSCRIPT ?= Rscript -# ── Format ─────────────────────────────────────────────────────────────────── +msg = @printf '\033[38;2;108;163;160m[%s] %s\033[0m\n' "$$(date -u '+%Y-%m-%d %H:%M:%SZ')" "$(1)" + +.DEFAULT_GOAL := help + +.PHONY: help format format-r document document-r install install-r test test-r \ + build build-r check check-r check-cran site site-r clean clean-r + +# ── Help ───────────────────────────────────────────────────────────────────── +help: + $(call msg,Available targets:) + @printf '%s\n' \ + ' format Format R code with air CLI (if available)' \ + ' document Generate roxygen2 documentation' \ + ' install Document and install the R package with pak' \ + ' test Run testthat::test_local(stop_on_failure = TRUE)' \ + ' build Build the R source tarball' \ + ' check Run R CMD check on the built tarball' \ + ' check-cran Run R CMD check --as-cran' \ + ' site Build pkgdown site' \ + ' clean Remove tarballs and .Rcheck output' + +# ── Format ──────────────────────────────────────────────────────────────────── format: format-r format-r: - @echo "==> R: Formatting rtemis.draw" - cd r && air format . + $(call msg,─── Formatting $(PKG) [R]... ───) + @if command -v air >/dev/null 2>&1; then \ + cd $(R_DIR) && air format .; \ + else \ + echo " Note: 'air' CLI not found — skipping R code formatting."; \ + fi + $(call msg,Done) -# ── Document ───────────────────────────────────────────────────────────────── +# ── Document ────────────────────────────────────────────────────────────────── document: document-r document-r: format-r - @echo "=> R: Documenting rtemis.draw" - cd r && Rscript -e "devtools::document()" + $(call msg,─── Documenting $(PKG) [R]... ───) + cd $(R_DIR) && $(RSCRIPT) -e "roxygen2::roxygenize()" + $(call msg,Done) -# ── Document & Install ─────────────────────────────────────────────────────── +# ── Install ─────────────────────────────────────────────────────────────────── install: install-r install-r: document-r - @echo "==> R: Installing rtemis.draw" - cd r && Rscript -e "devtools::install()" + $(call msg,─── Installing $(PKG) [R]... ───) + cd $(R_DIR) && $(RSCRIPT) -e "pak::local_install(upgrade = TRUE)" + $(call msg,Done) -# ── Test ───────────────────────────────────────────────────────────────────── +# ── Test ────────────────────────────────────────────────────────────────────── test: test-r test-r: - @echo "==> R: Testing rtemis.draw" - cd r && Rscript -e "devtools::test(stop_on_failure = TRUE)" + $(call msg,─── Testing $(PKG) [R]... ───) + cd $(R_DIR) && $(RSCRIPT) -e "testthat::test_local(stop_on_failure = TRUE)" + $(call msg,Done) + +# ── Build ───────────────────────────────────────────────────────────────────── +build: build-r -# ── Check ──────────────────────────────────────────────────────────────────── +build-r: clean-r + $(call msg,─── Building $(PKG) [R]... ───) + cd $(R_DIR) && $(R) CMD build . + $(call msg,Done) + +# ── Check ───────────────────────────────────────────────────────────────────── check: check-r -check-r: - @echo "==> R: Checking rtemis.draw" - cd r && Rscript -e "devtools::check()" +check-r: build-r + $(call msg,─── Running R CMD check on $(PKG) [R]... ───) + cd $(R_DIR) && $(R) CMD check $(PKG)_*.tar.gz + rm -f $(R_DIR)/$(PKG)_*.tar.gz + $(call msg,Done) + +check-cran: build-r + $(call msg,─── Running R CMD check --as-cran on $(PKG) [R]... ───) + cd $(R_DIR) && $(R) CMD check $(PKG)_*.tar.gz --as-cran + rm -f $(R_DIR)/$(PKG)_*.tar.gz + $(call msg,Done) -# ── Build Site ─────────────────────────────────────────────────────────────── +# ── Site ────────────────────────────────────────────────────────────────────── site: site-r site-r: - @echo "==> R: Building pkgdown site for rtemis.draw" - cd r && Rscript -e "pkgdown::build_site()" + $(call msg,─── Building pkgdown site for $(PKG) [R]... ───) + cd $(R_DIR) && $(RSCRIPT) -e "pkgdown::build_site()" + $(call msg,Done) + +# ── Clean ───────────────────────────────────────────────────────────────────── +clean: clean-r + +clean-r: + $(call msg,─── Cleaning build artifacts [R]... ───) + rm -rf $(R_DIR)/$(PKG).Rcheck + rm -f $(R_DIR)/$(PKG)_*.tar.gz + $(call msg,Done) From 5d1a0863f5a11012e72b973111ff71905942598a Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 7 May 2026 01:51:03 -0700 Subject: [PATCH 04/10] docs --- r/man/Axis.Rd | 6 +-- r/man/BoxplotSeries.Rd | 2 +- r/man/DataZoom.Rd | 4 +- r/man/ItemStyle.Rd | 6 +-- r/man/Legend.Rd | 4 +- r/man/LineStyle.Rd | 6 +-- r/man/PieSeries.Rd | 4 +- r/man/SankeyEdgeItem.Rd | 41 +++++++++++++++ r/man/SankeyLevelOption.Rd | 33 ++++++++++++ r/man/SankeyNodeItem.Rd | 49 ++++++++++++++++++ r/man/SankeySeries.Rd | 99 ++++++++++++++++++++++++++++++++++++ r/man/TextStyle.Rd | 12 ++--- r/man/Title.Rd | 8 +-- r/man/Tooltip.Rd | 6 +-- r/man/VisualMap.Rd | 2 +- r/man/draw_sankey.Rd | 63 +++++++++++++++++++++++ r/man/rtemis.draw-package.Rd | 5 ++ r/man/rtemis_colors.Rd | 5 -- r/man/to_json.Rd | 2 +- 19 files changed, 321 insertions(+), 36 deletions(-) create mode 100644 r/man/SankeyEdgeItem.Rd create mode 100644 r/man/SankeyLevelOption.Rd create mode 100644 r/man/SankeyNodeItem.Rd create mode 100644 r/man/SankeySeries.Rd create mode 100644 r/man/draw_sankey.Rd diff --git a/r/man/Axis.Rd b/r/man/Axis.Rd index 267c830..5037e3a 100644 --- a/r/man/Axis.Rd +++ b/r/man/Axis.Rd @@ -5,11 +5,11 @@ \title{Axis} \usage{ Axis( - type = character(0), + type = NULL, show = NULL, inverse = NULL, name = NULL, - name_location = character(0), + name_location = NULL, name_rotate = NULL, name_gap = NULL, name_text_style = NULL, @@ -33,7 +33,7 @@ Axis( split_line = NULL, minor_split_line = NULL, split_area = NULL, - position = character(0), + position = NULL, grid_index = NULL ) } diff --git a/r/man/BoxplotSeries.Rd b/r/man/BoxplotSeries.Rd index a94f176..346939a 100644 --- a/r/man/BoxplotSeries.Rd +++ b/r/man/BoxplotSeries.Rd @@ -14,7 +14,7 @@ BoxplotSeries( color = NULL, z_level = NULL, z = NULL, - layout = character(0), + layout = NULL, box_width = NULL, item_style = NULL, label = NULL diff --git a/r/man/DataZoom.Rd b/r/man/DataZoom.Rd index f9c8651..28d824b 100644 --- a/r/man/DataZoom.Rd +++ b/r/man/DataZoom.Rd @@ -13,7 +13,7 @@ DataZoom( radius_axis_index = NULL, angle_axis_index = NULL, single_axis_index = NULL, - filter_mode = character(0), + filter_mode = NULL, start = NULL, end = NULL, start_value = NULL, @@ -22,7 +22,7 @@ DataZoom( max_span = NULL, min_value_span = NULL, max_value_span = NULL, - orient = character(0), + orient = NULL, throttle = NULL, range_mode = NULL, show = NULL, diff --git a/r/man/ItemStyle.Rd b/r/man/ItemStyle.Rd index a37640a..4c51ea1 100644 --- a/r/man/ItemStyle.Rd +++ b/r/man/ItemStyle.Rd @@ -9,9 +9,9 @@ ItemStyle( opacity = NULL, border_color = NULL, border_width = NULL, - border_type = character(0), - border_cap = character(0), - border_join = character(0), + border_type = NULL, + border_cap = NULL, + border_join = NULL, border_dash_offset = NULL, border_miter_limit = NULL, border_radius = NULL, diff --git a/r/man/Legend.Rd b/r/man/Legend.Rd index dfce239..68f1b53 100644 --- a/r/man/Legend.Rd +++ b/r/man/Legend.Rd @@ -6,8 +6,8 @@ \usage{ Legend( show = NULL, - orient = character(0), - align = character(0), + orient = NULL, + align = NULL, left = NULL, right = NULL, top = NULL, diff --git a/r/man/LineStyle.Rd b/r/man/LineStyle.Rd index e78e67c..8814091 100644 --- a/r/man/LineStyle.Rd +++ b/r/man/LineStyle.Rd @@ -8,9 +8,9 @@ LineStyle( color = NULL, width = NULL, opacity = NULL, - type = character(0), - cap = character(0), - join = character(0), + type = NULL, + cap = NULL, + join = NULL, dash_offset = NULL, miter_limit = NULL, shadow_blur = NULL, diff --git a/r/man/PieSeries.Rd b/r/man/PieSeries.Rd index 1911cc6..3f41d98 100644 --- a/r/man/PieSeries.Rd +++ b/r/man/PieSeries.Rd @@ -14,7 +14,7 @@ PieSeries( z = NULL, center = NULL, radius = NULL, - rose_type = character(0), + rose_type = NULL, clockwise = NULL, start_angle = NULL, end_angle = NULL, @@ -25,7 +25,7 @@ PieSeries( avoid_label_overlap = NULL, percent_precision = NULL, still_show_zero_sum = NULL, - animation_type = character(0), + animation_type = NULL, item_style = NULL, label = NULL, label_line = NULL, diff --git a/r/man/SankeyEdgeItem.Rd b/r/man/SankeyEdgeItem.Rd new file mode 100644 index 0000000..4f1b5e0 --- /dev/null +++ b/r/man/SankeyEdgeItem.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/series_sankey.R +\name{SankeyEdgeItem} +\alias{SankeyEdgeItem} +\title{Sankey Edge Item} +\usage{ +SankeyEdgeItem( + source = NULL, + target = NULL, + value = NULL, + line_style = NULL, + edge_label = NULL, + focus_node_adjacency = NULL +) +} +\arguments{ +\item{source}{Optional Character: Source node name.} + +\item{target}{Optional Character: Target node name.} + +\item{value}{Optional Numeric: Flow value (determines link width).} + +\item{line_style}{Optional \link{LineStyle} or list: Link line styling. Accepts +a \link{LineStyle} object or a plain named list; a list also allows the +Sankey-specific \code{curveness} field (e.g. \code{list(curveness = 0.5, opacity = 0.3)}).} + +\item{edge_label}{Optional \link{LabelOption}: Label shown on the link itself.} + +\item{focus_node_adjacency}{Optional Logical or Character +\{"inEdges", "outEdges", "allEdges"\}: Which adjacent elements to highlight +on hover.} +} +\description{ +One directed link (edge) in a Sankey diagram. +} +\details{ +Corresponds to \code{SankeyEdgeItemOption} in +\code{src/chart/sankey/SankeySeries.ts} (line 88). +ECharts docs: +\url{https://echarts.apache.org/en/option.html#series-sankey.links} +} diff --git a/r/man/SankeyLevelOption.Rd b/r/man/SankeyLevelOption.Rd new file mode 100644 index 0000000..c53c040 --- /dev/null +++ b/r/man/SankeyLevelOption.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/series_sankey.R +\name{SankeyLevelOption} +\alias{SankeyLevelOption} +\title{Sankey Level Option} +\usage{ +SankeyLevelOption( + depth = NULL, + label = NULL, + item_style = NULL, + line_style = NULL +) +} +\arguments{ +\item{depth}{Numeric \verb{[0, Inf)}: Column depth to target (0 = leftmost). Required.} + +\item{label}{Optional \link{LabelOption}: Node label configuration at this depth.} + +\item{item_style}{Optional \link{ItemStyle}: Node fill/border styling at this depth.} + +\item{line_style}{Optional \link{LineStyle} or list: Link styling for edges leaving +this depth. Accepts a \link{LineStyle} object or a plain list (allows \code{curveness}).} +} +\description{ +Style overrides applied to all nodes at a given column depth. \code{depth = 0} +targets the leftmost column. +} +\details{ +Corresponds to \code{SankeyLevelOption} in +\code{src/chart/sankey/SankeySeries.ts} (line 96). +ECharts docs: +\url{https://echarts.apache.org/en/option.html#series-sankey.levels} +} diff --git a/r/man/SankeyNodeItem.Rd b/r/man/SankeyNodeItem.Rd new file mode 100644 index 0000000..816cee7 --- /dev/null +++ b/r/man/SankeyNodeItem.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/series_sankey.R +\name{SankeyNodeItem} +\alias{SankeyNodeItem} +\title{Sankey Node Item} +\usage{ +SankeyNodeItem( + name = NULL, + value = NULL, + depth = NULL, + draggable = NULL, + local_x = NULL, + local_y = NULL, + label = NULL, + item_style = NULL, + focus_node_adjacency = NULL +) +} +\arguments{ +\item{name}{Optional Character: Node name (must match source/target strings).} + +\item{value}{Optional Numeric: Node value shown in tooltip.} + +\item{depth}{Optional Numeric \verb{[0, Inf)}: Fixed column depth (0 = leftmost).} + +\item{draggable}{Optional Logical: Whether the node can be dragged.} + +\item{local_x}{Optional Numeric: Horizontal position inside the layout rect.} + +\item{local_y}{Optional Numeric: Vertical position inside the layout rect.} + +\item{label}{Optional \link{LabelOption}: Node label configuration.} + +\item{item_style}{Optional \link{ItemStyle}: Node fill/border styling.} + +\item{focus_node_adjacency}{Optional Logical or Character +\{"inEdges", "outEdges", "allEdges"\}: Which adjacent elements to highlight +on hover.} +} +\description{ +One node in a Sankey diagram. Node names must match the \code{source} / \code{target} +strings used in the corresponding \link{SankeyEdgeItem} objects. +} +\details{ +Corresponds to \code{SankeyNodeItemOption} in +\code{src/chart/sankey/SankeySeries.ts} (line 73). +ECharts docs: +\url{https://echarts.apache.org/en/option.html#series-sankey.data} +} diff --git a/r/man/SankeySeries.Rd b/r/man/SankeySeries.Rd new file mode 100644 index 0000000..09542e2 --- /dev/null +++ b/r/man/SankeySeries.Rd @@ -0,0 +1,99 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/series_sankey.R +\name{SankeySeries} +\alias{SankeySeries} +\title{Sankey Series} +\usage{ +SankeySeries( + name = NULL, + data = NULL, + links = NULL, + levels = NULL, + orient = NULL, + node_width = NULL, + node_gap = NULL, + node_align = NULL, + draggable = NULL, + layout_iterations = NULL, + sort = NULL, + roam = NULL, + label = NULL, + edge_label = NULL, + line_style = NULL, + item_style = NULL, + color = NULL, + left = NULL, + right = NULL, + top = NULL, + bottom = NULL, + silent = NULL, + z_level = NULL, + z = NULL +) +} +\arguments{ +\item{name}{Optional Character: Series name for legend and tooltip.} + +\item{data}{Optional list: Nodes as a list of \link{SankeyNodeItem} objects or +plain named lists, each with at least a \code{name} field.} + +\item{links}{Optional list: Edges as a list of \link{SankeyEdgeItem} objects or +plain named lists with \code{source}, \code{target}, and \code{value} fields.} + +\item{levels}{Optional list: List of \link{SankeyLevelOption} for per-depth styling.} + +\item{orient}{Optional Character \{"horizontal", "vertical"\}: Flow direction.} + +\item{node_width}{Optional Numeric \verb{[0, Inf)}: Width of each node rectangle in pixels.} + +\item{node_gap}{Optional Numeric \verb{[0, Inf)}: Vertical gap between nodes in pixels.} + +\item{node_align}{Optional Character \{"justify", "left", "right"\}: Node +alignment within columns.} + +\item{draggable}{Optional Logical: Whether nodes can be dragged interactively.} + +\item{layout_iterations}{Optional Numeric \verb{[0, Inf)}: Number of layout iterations.} + +\item{sort}{Optional Character \{"desc"\} or \code{NULL}: Column-internal node +ordering. \code{NULL} preserves the original data order.} + +\item{roam}{Optional Logical or Character \{"move", "scale"\}: Whether to enable +pan/zoom.} + +\item{label}{Optional \link{LabelOption}: Node label configuration.} + +\item{edge_label}{Optional \link{LabelOption}: Edge label configuration.} + +\item{line_style}{Optional \link{LineStyle} or list: Default link line styling. +Accepts a \link{LineStyle} object or a plain list; a plain list also allows the +Sankey-specific \code{curveness} field (e.g. \code{list(curveness = 0.5)}).} + +\item{item_style}{Optional \link{ItemStyle}: Default node styling.} + +\item{color}{Optional Character: Color palette for nodes as a character vector.} + +\item{left}{Optional Numeric or Character: Series left offset.} + +\item{right}{Optional Numeric or Character: Series right offset.} + +\item{top}{Optional Numeric or Character: Series top offset.} + +\item{bottom}{Optional Numeric or Character: Series bottom offset.} + +\item{silent}{Optional Logical: Whether to disable mouse/touch events.} + +\item{z_level}{Optional Numeric: Canvas layer index.} + +\item{z}{Optional Numeric: Front-back order within the same canvas layer.} +} +\description{ +Configuration for a Sankey diagram series. Nodes and directed links are +represented as two separate lists. Node names in \code{data} must match the +\code{source} and \code{target} strings in \code{links}. +} +\details{ +Corresponds to \code{SankeySeriesOption} in +\code{src/chart/sankey/SankeySeries.ts} (line 100). +ECharts docs: \url{https://echarts.apache.org/en/option.html#series-sankey} +} diff --git a/r/man/TextStyle.Rd b/r/man/TextStyle.Rd index d1274c1..01a11d7 100644 --- a/r/man/TextStyle.Rd +++ b/r/man/TextStyle.Rd @@ -6,18 +6,18 @@ \usage{ TextStyle( color = NULL, - font_style = character(0), + font_style = NULL, font_weight = NULL, font_family = NULL, font_size = NULL, - align = character(0), - vertical_align = character(0), + align = NULL, + vertical_align = NULL, opacity = NULL, line_height = NULL, background_color = NULL, border_color = NULL, border_width = NULL, - border_type = character(0), + border_type = NULL, border_dash_offset = NULL, border_radius = NULL, padding = NULL, @@ -25,13 +25,13 @@ TextStyle( height = NULL, text_border_color = NULL, text_border_width = NULL, - text_border_type = character(0), + text_border_type = NULL, text_border_dash_offset = NULL, text_shadow_blur = NULL, text_shadow_color = NULL, text_shadow_offset_x = NULL, text_shadow_offset_y = NULL, - overflow = character(0), + overflow = NULL, ellipsis = NULL, rich = NULL, shadow_blur = NULL, diff --git a/r/man/Title.Rd b/r/man/Title.Rd index 14d8621..c59c79b 100644 --- a/r/man/Title.Rd +++ b/r/man/Title.Rd @@ -8,12 +8,12 @@ Title( show = NULL, text = NULL, link = NULL, - target = character(0), + target = NULL, subtext = NULL, sublink = NULL, - subtarget = character(0), - text_align = character(0), - text_vertical_align = character(0), + subtarget = NULL, + text_align = NULL, + text_vertical_align = NULL, padding = NULL, item_gap = NULL, text_style = NULL, diff --git a/r/man/Tooltip.Rd b/r/man/Tooltip.Rd index c6f704e..7960fc6 100644 --- a/r/man/Tooltip.Rd +++ b/r/man/Tooltip.Rd @@ -6,8 +6,8 @@ \usage{ Tooltip( show = NULL, - trigger = character(0), - trigger_on = character(0), + trigger = NULL, + trigger_on = NULL, show_content = NULL, always_show_content = NULL, formatter = NULL, @@ -25,7 +25,7 @@ Tooltip( padding = NULL, text_style = NULL, extra_css_text = NULL, - order = character(0), + order = NULL, class_name = NULL ) } diff --git a/r/man/VisualMap.Rd b/r/man/VisualMap.Rd index 766dad3..5392ef3 100644 --- a/r/man/VisualMap.Rd +++ b/r/man/VisualMap.Rd @@ -11,7 +11,7 @@ VisualMap( precision = NULL, calculable = NULL, show = NULL, - orient = character(0), + orient = NULL, in_range = NULL, out_of_range = NULL, left = NULL, diff --git a/r/man/draw_sankey.Rd b/r/man/draw_sankey.Rd new file mode 100644 index 0000000..6aa7def --- /dev/null +++ b/r/man/draw_sankey.Rd @@ -0,0 +1,63 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/draw.R +\name{draw_sankey} +\alias{draw_sankey} +\title{Draw a Sankey Diagram} +\usage{ +draw_sankey( + links, + orient = "horizontal", + node_width = NULL, + node_gap = NULL, + node_align = NULL, + title = NULL, + color = NULL, + theme = NULL, + width = NULL, + height = NULL, + filename = NULL +) +} +\arguments{ +\item{links}{data.frame: Directed links with columns \code{source} (Character), +\code{target} (Character), and \code{value} (Numeric).} + +\item{orient}{Optional Character \{"horizontal", "vertical"\}: Flow direction.} + +\item{node_width}{Optional Numeric \verb{[0, Inf)}: Node rectangle width in pixels.} + +\item{node_gap}{Optional Numeric \verb{[0, Inf)}: Vertical gap between nodes in pixels.} + +\item{node_align}{Optional Character \{"justify", "left", "right"\}: Node +alignment within each column.} + +\item{title}{Optional Character: Chart title.} + +\item{color}{Optional Character: Node color palette — a single color string or +a character vector that overrides the theme palette for this chart.} + +\item{theme}{Optional \link{Theme}: Theme override.} + +\item{width}{Optional Character or Numeric: Widget width.} + +\item{height}{Optional Character or Numeric: Widget height.} + +\item{filename}{Optional Character: If provided, save the widget to this file +via \code{\link[=save_drawing]{save_drawing()}}.} +} +\value{ +htmlwidget: Widget object. +} +\description{ +Sankey diagram from a data frame of directed links. Node names are derived +automatically from the unique values in the \code{source} and \code{target} columns; +no separate node list is required. +} +\examples{ +links <- data.frame( + source = c("A", "A", "B", "C"), + target = c("B", "C", "D", "D"), + value = c(8, 4, 6, 3) +) +draw_sankey(links) +} diff --git a/r/man/rtemis.draw-package.Rd b/r/man/rtemis.draw-package.Rd index 55b73f5..bb5f912 100644 --- a/r/man/rtemis.draw-package.Rd +++ b/r/man/rtemis.draw-package.Rd @@ -23,4 +23,9 @@ Useful links: \author{ \strong{Maintainer}: E.D. Gennatas \email{gennatas@gmail.com} (\href{https://orcid.org/0000-0001-9280-3609}{ORCID}) [copyright holder] +Authors: +\itemize{ + \item E.D. Gennatas \email{gennatas@gmail.com} (\href{https://orcid.org/0000-0001-9280-3609}{ORCID}) [copyright holder] +} + } diff --git a/r/man/rtemis_colors.Rd b/r/man/rtemis_colors.Rd index 00c6cad..5cc91d4 100644 --- a/r/man/rtemis_colors.Rd +++ b/r/man/rtemis_colors.Rd @@ -1,12 +1,8 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/rtemis_color_system.R -\docType{data} \name{rtemis_colors} \alias{rtemis_colors} \title{rtemis Color Palette} -\format{ -An object of class \code{character} of length 10. -} \usage{ rtemis_colors } @@ -16,4 +12,3 @@ rtemis Color Palette \author{ EDG } -\keyword{datasets} diff --git a/r/man/to_json.Rd b/r/man/to_json.Rd index 28f9c73..f0b7c3b 100644 --- a/r/man/to_json.Rd +++ b/r/man/to_json.Rd @@ -13,7 +13,7 @@ to_json(x, pretty = FALSE, auto_unbox = TRUE, ...) \item{auto_unbox}{Logical: Whether to auto-unbox single-element vectors.} -\item{...}{Dots: Additional arguments passed to \code{\link[jsonlite:fromJSON]{jsonlite::toJSON()}}.} +\item{...}{Dots: Additional arguments passed to \code{\link[jsonlite:toJSON]{jsonlite::toJSON()}}.} } \value{ Character: JSON string. From 3afb33d82b6bc97716a8a7b1970cb96eae0f6254 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 7 May 2026 01:51:44 -0700 Subject: [PATCH 05/10] init test sankey --- r/tests/testthat/test-series_sankey.R | 265 ++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 r/tests/testthat/test-series_sankey.R diff --git a/r/tests/testthat/test-series_sankey.R b/r/tests/testthat/test-series_sankey.R new file mode 100644 index 0000000..c484763 --- /dev/null +++ b/r/tests/testthat/test-series_sankey.R @@ -0,0 +1,265 @@ +# test-series_sankey.R +# Tests for SankeyNodeItem, SankeyEdgeItem, SankeyLevelOption, +# SankeySeries, and draw_sankey() + +# -- SankeyNodeItem ------------------------------------------------------------- + +test_that("SankeyNodeItem creates with defaults", { + n <- SankeyNodeItem() + expect_true(S7::S7_inherits(n, SankeyNodeItem)) + out <- to_list(n) + expect_equal(length(out), 0L) +}) + +test_that("SankeyNodeItem to_list() converts names correctly", { + n <- SankeyNodeItem( + name = "A", + value = 10, + depth = 1, + draggable = FALSE, + local_x = 0.1, + local_y = 0.5 + ) + out <- to_list(n) + expect_equal(out[["name"]], "A") + expect_equal(out[["value"]], 10) + expect_equal(out[["depth"]], 1) + expect_equal(out[["draggable"]], FALSE) + expect_equal(out[["localX"]], 0.1) + expect_equal(out[["localY"]], 0.5) +}) + +test_that("SankeyNodeItem focus_node_adjacency validation", { + expect_equal( + SankeyNodeItem(focus_node_adjacency = TRUE)@focus_node_adjacency, + TRUE + ) + expect_equal( + SankeyNodeItem(focus_node_adjacency = "inEdges")@focus_node_adjacency, + "inEdges" + ) + expect_equal( + SankeyNodeItem(focus_node_adjacency = "outEdges")@focus_node_adjacency, + "outEdges" + ) + expect_equal( + SankeyNodeItem(focus_node_adjacency = "allEdges")@focus_node_adjacency, + "allEdges" + ) + expect_error(SankeyNodeItem(focus_node_adjacency = "bad")) + expect_error(SankeyNodeItem(focus_node_adjacency = 1)) +}) + +test_that("SankeyNodeItem NULL fields are dropped", { + n <- SankeyNodeItem(name = "X") + out <- to_list(n) + expect_true("name" %in% names(out)) + expect_false("value" %in% names(out)) + expect_false("depth" %in% names(out)) +}) + +# -- SankeyEdgeItem ------------------------------------------------------------- + +test_that("SankeyEdgeItem creates with defaults", { + e <- SankeyEdgeItem() + expect_true(S7::S7_inherits(e, SankeyEdgeItem)) + out <- to_list(e) + expect_equal(length(out), 0L) +}) + +test_that("SankeyEdgeItem to_list() converts names correctly", { + e <- SankeyEdgeItem(source = "A", target = "B", value = 5) + out <- to_list(e) + expect_equal(out[["source"]], "A") + expect_equal(out[["target"]], "B") + expect_equal(out[["value"]], 5) +}) + +test_that("SankeyEdgeItem accepts plain list for line_style (curveness)", { + e <- SankeyEdgeItem( + source = "A", + target = "B", + value = 3, + line_style = list(curveness = 0.5, opacity = 0.3) + ) + out <- to_list(e) + expect_equal(out[["lineStyle"]][["curveness"]], 0.5) + expect_equal(out[["lineStyle"]][["opacity"]], 0.3) +}) + +test_that("SankeyEdgeItem accepts LineStyle S7 object for line_style", { + e <- SankeyEdgeItem( + source = "X", + target = "Y", + value = 2, + line_style = LineStyle(opacity = 0.4) + ) + out <- to_list(e) + expect_equal(out[["lineStyle"]][["opacity"]], 0.4) +}) + +# -- SankeyLevelOption ---------------------------------------------------------- + +test_that("SankeyLevelOption creates with depth", { + lv <- SankeyLevelOption(depth = 0) + expect_true(S7::S7_inherits(lv, SankeyLevelOption)) + out <- to_list(lv) + expect_equal(out[["depth"]], 0) +}) + +test_that("SankeyLevelOption depth is required", { + expect_error(SankeyLevelOption()) +}) + +test_that("SankeyLevelOption depth rejects negative", { + expect_error(SankeyLevelOption(depth = -1)) +}) + +test_that("SankeyLevelOption to_list() includes item_style", { + lv <- SankeyLevelOption( + depth = 1, + item_style = ItemStyle(color = "#f00") + ) + out <- to_list(lv) + expect_equal(out[["depth"]], 1) + expect_equal(out[["itemStyle"]][["color"]], "#f00") +}) + +# -- SankeySeries --------------------------------------------------------------- + +test_that("SankeySeries creates with defaults", { + s <- SankeySeries() + expect_true(S7::S7_inherits(s, SankeySeries)) + out <- to_list(s) + expect_equal(out[["type"]], "sankey") + expect_equal(length(out), 1L) +}) + +test_that("SankeySeries to_list() sets type = 'sankey'", { + s <- SankeySeries(name = "flow") + out <- to_list(s) + expect_equal(out[["type"]], "sankey") + expect_equal(out[["name"]], "flow") +}) + +test_that("SankeySeries data and links serialize as unnamed arrays", { + s <- SankeySeries( + data = list( + list(name = "A"), + list(name = "B"), + list(name = "C") + ), + links = list( + list(source = "A", target = "B", value = 5), + list(source = "A", target = "C", value = 3) + ) + ) + out <- to_list(s) + expect_null(names(out[["data"]])) + expect_null(names(out[["links"]])) + expect_equal(length(out[["data"]]), 3L) + expect_equal(length(out[["links"]]), 2L) + expect_equal(out[["data"]][[1L]][["name"]], "A") + expect_equal(out[["links"]][[1L]][["source"]], "A") + expect_equal(out[["links"]][[2L]][["value"]], 3) +}) + +test_that("SankeySeries data accepts SankeyNodeItem objects", { + s <- SankeySeries( + data = list(SankeyNodeItem(name = "X", depth = 0)), + links = list(list(source = "X", target = "Y", value = 1)) + ) + out <- to_list(s) + expect_equal(out[["data"]][[1L]][["name"]], "X") + expect_equal(out[["data"]][[1L]][["depth"]], 0) +}) + +test_that("SankeySeries orient validation", { + expect_equal(SankeySeries(orient = "vertical")@orient, "vertical") + expect_equal(SankeySeries(orient = "horizontal")@orient, "horizontal") + expect_error(SankeySeries(orient = "diagonal")) +}) + +test_that("SankeySeries sort validation", { + expect_equal(SankeySeries(sort = "desc")@sort, "desc") + expect_null(SankeySeries(sort = NULL)@sort) + expect_error(SankeySeries(sort = "asc")) +}) + +test_that("SankeySeries roam validation", { + expect_equal(SankeySeries(roam = TRUE)@roam, TRUE) + expect_equal(SankeySeries(roam = "move")@roam, "move") + expect_equal(SankeySeries(roam = "scale")@roam, "scale") + expect_error(SankeySeries(roam = "zoom")) +}) + +test_that("SankeySeries levels serialize as unnamed array", { + s <- SankeySeries( + levels = list( + SankeyLevelOption(depth = 0, item_style = ItemStyle(color = "#f00")), + SankeyLevelOption(depth = 1) + ) + ) + out <- to_list(s) + expect_null(names(out[["levels"]])) + expect_equal(length(out[["levels"]]), 2L) + expect_equal(out[["levels"]][[1L]][["depth"]], 0) + expect_equal(out[["levels"]][[1L]][["itemStyle"]][["color"]], "#f00") +}) + +test_that("SankeySeries line_style accepts plain list with curveness", { + s <- SankeySeries(line_style = list(curveness = 0.5, opacity = 0.2)) + out <- to_list(s) + expect_equal(out[["lineStyle"]][["curveness"]], 0.5) +}) + +test_that("SankeySeries NULL fields are dropped", { + s <- SankeySeries(orient = "horizontal") + out <- to_list(s) + expect_true("orient" %in% names(out)) + expect_false("name" %in% names(out)) + expect_false("data" %in% names(out)) +}) + +# -- draw_sankey ---------------------------------------------------------------- + +test_that("draw_sankey returns an htmlwidget", { + links <- data.frame( + source = c("A", "A", "B"), + target = c("B", "C", "C"), + value = c(5, 3, 4), + stringsAsFactors = FALSE + ) + w <- draw_sankey(links) + expect_true(inherits(w, "htmlwidget")) +}) + +test_that("draw_sankey derives nodes from links automatically", { + links <- data.frame( + source = c("X", "Y"), + target = c("Y", "Z"), + value = c(10, 6), + stringsAsFactors = FALSE + ) + w <- draw_sankey(links) + opt <- w[["x"]][["option"]] + node_names <- vapply(opt[["series"]][[1L]][["data"]], `[[`, "", "name") + expect_setequal(node_names, c("X", "Y", "Z")) +}) + +test_that("draw_sankey rejects links missing required columns", { + bad <- data.frame(from = "A", to = "B", stringsAsFactors = FALSE) + expect_error(draw_sankey(bad), regexp = "source") +}) + +test_that("draw_sankey respects orient argument", { + links <- data.frame( + source = "A", + target = "B", + value = 1, + stringsAsFactors = FALSE + ) + w <- draw_sankey(links, orient = "vertical") + series <- w[["x"]][["option"]][["series"]][[1L]] + expect_equal(series[["orient"]], "vertical") +}) From 9d603067a839a7502e265df0d506397557985a1e Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 7 May 2026 01:51:54 -0700 Subject: [PATCH 06/10] add draw_sankey --- r/R/draw.R | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/r/R/draw.R b/r/R/draw.R index 37dbfc2..21becf0 100644 --- a/r/R/draw.R +++ b/r/R/draw.R @@ -2502,3 +2502,89 @@ draw_heatmap <- function( meta = heatmap_meta ) } + +# -- draw_sankey ---------------------------------------------------------------- + +#' Draw a Sankey Diagram +#' +#' Sankey diagram from a data frame of directed links. Node names are derived +#' automatically from the unique values in the `source` and `target` columns; +#' no separate node list is required. +#' +#' @param links data.frame: Directed links with columns `source` (Character), +#' `target` (Character), and `value` (Numeric). +#' @param orient Optional Character \{"horizontal", "vertical"\}: Flow direction. +#' @param node_width Optional Numeric `[0, Inf)`: Node rectangle width in pixels. +#' @param node_gap Optional Numeric `[0, Inf)`: Vertical gap between nodes in pixels. +#' @param node_align Optional Character \{"justify", "left", "right"\}: Node +#' alignment within each column. +#' @param title Optional Character: Chart title. +#' @param color Optional Character: Node color palette — a single color string or +#' a character vector that overrides the theme palette for this chart. +#' @param theme Optional [Theme]: Theme override. +#' @param width Optional Character or Numeric: Widget width. +#' @param height Optional Character or Numeric: Widget height. +#' @param filename Optional Character: If provided, save the widget to this file +#' via [save_drawing()]. +#' @return htmlwidget: Widget object. +#' @examples +#' links <- data.frame( +#' source = c("A", "A", "B", "C"), +#' target = c("B", "C", "D", "D"), +#' value = c(8, 4, 6, 3) +#' ) +#' draw_sankey(links) +#' @export +draw_sankey <- function( + links, + orient = "horizontal", + node_width = NULL, + node_gap = NULL, + node_align = NULL, + title = NULL, + color = NULL, + theme = NULL, + width = NULL, + height = NULL, + filename = NULL +) { + required_cols <- c("source", "target", "value") + missing_cols <- setdiff(required_cols, names(links)) + if (length(missing_cols) > 0L) { + cli::cli_abort( + "{.arg links} must have columns {.val {required_cols}}; missing: {.val {missing_cols}}." + ) + } + + # Derive unique node names from source and target columns + node_names <- unique(c(links[["source"]], links[["target"]])) + nodes <- lapply(node_names, function(n) list(name = n)) + + # Convert links data.frame rows to a list of named lists + edge_list <- lapply(seq_len(nrow(links)), function(i) { + list( + source = links[["source"]][[i]], + target = links[["target"]][[i]], + value = links[["value"]][[i]] + ) + }) + + # ECharts ignores a palette longer than the node count; trim (or cycle) to + # exactly one entry per node. + palette <- rep_len(color %||% rtemis_colors, length(node_names)) + opt <- EChartsOption( + title = if (!is.null(title)) Title(text = title, left = "center") else NULL, + tooltip = Tooltip(trigger = "item"), + color = palette, + series = SankeySeries( + data = nodes, + links = edge_list, + orient = orient, + node_width = node_width, + node_gap = node_gap, + node_align = node_align + ) + ) + + draw(opt, theme = theme, width = width, height = height, filename = filename) +} From d894d7e7f303c0ea3e82716dbff7cce528a0e61f Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 7 May 2026 01:52:04 -0700 Subject: [PATCH 07/10] init series_sankey --- r/R/series_sankey.R | 321 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 r/R/series_sankey.R diff --git a/r/R/series_sankey.R b/r/R/series_sankey.R new file mode 100644 index 0000000..04b94c4 --- /dev/null +++ b/r/R/series_sankey.R @@ -0,0 +1,321 @@ +# series_sankey.R +# Sankey diagram S7 classes: SankeyNodeItem, SankeyEdgeItem, +# SankeyLevelOption, SankeySeries +# +# TS sources: +# SankeySeriesOption: src/chart/sankey/SankeySeries.ts (line 100) +# SankeyNodeItemOption: src/chart/sankey/SankeySeries.ts (line 73) +# SankeyEdgeItemOption: src/chart/sankey/SankeySeries.ts (line 88) +# SankeyLevelOption: src/chart/sankey/SankeySeries.ts (line 96) + +# -- SankeyNodeItem ------------------------------------------------------------- + +#' Sankey Node Item +#' +#' One node in a Sankey diagram. Node names must match the `source` / `target` +#' strings used in the corresponding [SankeyEdgeItem] objects. +#' +#' Corresponds to `SankeyNodeItemOption` in +#' `src/chart/sankey/SankeySeries.ts` (line 73). +#' ECharts docs: +#' \url{https://echarts.apache.org/en/option.html#series-sankey.data} +#' +#' @param name Optional Character: Node name (must match source/target strings). +#' @param value Optional Numeric: Node value shown in tooltip. +#' @param depth Optional Numeric `[0, Inf)`: Fixed column depth (0 = leftmost). +#' @param draggable Optional Logical: Whether the node can be dragged. +#' @param local_x Optional Numeric: Horizontal position inside the layout rect. +#' @param local_y Optional Numeric: Vertical position inside the layout rect. +#' @param label Optional [LabelOption]: Node label configuration. +#' @param item_style Optional [ItemStyle]: Node fill/border styling. +#' @param focus_node_adjacency Optional Logical or Character +#' \{"inEdges", "outEdges", "allEdges"\}: Which adjacent elements to highlight +#' on hover. +#' @export +SankeyNodeItem <- S7::new_class( + "SankeyNodeItem", + properties = list( + name = optional_character_scalar, + value = numeric_or_null_property(), + depth = numeric_or_null_property(), + draggable = optional_logical_scalar, + local_x = numeric_or_null_property(), + local_y = numeric_or_null_property(), + label = class_or_null_property(LabelOption), + item_style = class_or_null_property(ItemStyle), + focus_node_adjacency = S7::new_property( + class = S7::class_any, + default = NULL, + validator = function(value) { + if (is.null(value)) { + return(NULL) + } + if (is.logical(value) && length(value) == 1L) { + return(NULL) + } + if ( + is.character(value) && + length(value) == 1L && + value %in% c("inEdges", "outEdges", "allEdges") + ) { + return(NULL) + } + "must be TRUE/FALSE, 'inEdges', 'outEdges', 'allEdges', or NULL" + } + ) + ) +) + +S7::method(to_list, SankeyNodeItem) <- function(x, ...) { + props_to_list(x) +} + +# -- SankeyEdgeItem ------------------------------------------------------------- + +#' Sankey Edge Item +#' +#' One directed link (edge) in a Sankey diagram. +#' +#' Corresponds to `SankeyEdgeItemOption` in +#' `src/chart/sankey/SankeySeries.ts` (line 88). +#' ECharts docs: +#' \url{https://echarts.apache.org/en/option.html#series-sankey.links} +#' +#' @param source Optional Character: Source node name. +#' @param target Optional Character: Target node name. +#' @param value Optional Numeric: Flow value (determines link width). +#' @param line_style Optional [LineStyle] or list: Link line styling. Accepts +#' a [LineStyle] object or a plain named list; a list also allows the +#' Sankey-specific `curveness` field (e.g. `list(curveness = 0.5, +#' opacity = 0.3)`). +#' @param edge_label Optional [LabelOption]: Label shown on the link itself. +#' @param focus_node_adjacency Optional Logical or Character +#' \{"inEdges", "outEdges", "allEdges"\}: Which adjacent elements to highlight +#' on hover. +#' @export +SankeyEdgeItem <- S7::new_class( + "SankeyEdgeItem", + properties = list( + source = optional_character_scalar, + target = optional_character_scalar, + value = numeric_or_null_property(), + # Accepts LineStyle S7 object or a plain list (allows curveness extension) + line_style = S7::new_property(class = S7::class_any, default = NULL), + edge_label = class_or_null_property(LabelOption), + focus_node_adjacency = S7::new_property( + class = S7::class_any, + default = NULL, + validator = function(value) { + if (is.null(value)) { + return(NULL) + } + if (is.logical(value) && length(value) == 1L) { + return(NULL) + } + if ( + is.character(value) && + length(value) == 1L && + value %in% c("inEdges", "outEdges", "allEdges") + ) { + return(NULL) + } + "must be TRUE/FALSE, 'inEdges', 'outEdges', 'allEdges', or NULL" + } + ) + ) +) + +S7::method(to_list, SankeyEdgeItem) <- function(x, ...) { + out <- props_to_list(x) + # line_style may be a LineStyle S7 object; props_to_list handles recursion + # but the rename from snake_case is already applied. Ensure edge_label is + # serialized under "edgeLabel" (props_to_list does this via snake_to_camel). + out +} + +# -- SankeyLevelOption ---------------------------------------------------------- + +#' Sankey Level Option +#' +#' Style overrides applied to all nodes at a given column depth. `depth = 0` +#' targets the leftmost column. +#' +#' Corresponds to `SankeyLevelOption` in +#' `src/chart/sankey/SankeySeries.ts` (line 96). +#' ECharts docs: +#' \url{https://echarts.apache.org/en/option.html#series-sankey.levels} +#' +#' @param depth Numeric `[0, Inf)`: Column depth to target (0 = leftmost). Required. +#' @param label Optional [LabelOption]: Node label configuration at this depth. +#' @param item_style Optional [ItemStyle]: Node fill/border styling at this depth. +#' @param line_style Optional [LineStyle] or list: Link styling for edges leaving +#' this depth. Accepts a [LineStyle] object or a plain list (allows `curveness`). +#' @export +SankeyLevelOption <- S7::new_class( + "SankeyLevelOption", + properties = list( + depth = S7::new_property( + class = S7::class_any, + default = NULL, + validator = function(value) { + if (is.null(value)) { + return("depth is required and must be a non-negative integer") + } + if (is.numeric(value) && length(value) == 1L && value >= 0) { + return(NULL) + } + "must be a non-negative number" + } + ), + label = class_or_null_property(LabelOption), + item_style = class_or_null_property(ItemStyle), + line_style = S7::new_property(class = S7::class_any, default = NULL) + ) +) + +S7::method(to_list, SankeyLevelOption) <- function(x, ...) { + props_to_list(x) +} + +# -- SankeySeries --------------------------------------------------------------- + +#' Sankey Series +#' +#' Configuration for a Sankey diagram series. Nodes and directed links are +#' represented as two separate lists. Node names in `data` must match the +#' `source` and `target` strings in `links`. +#' +#' Corresponds to `SankeySeriesOption` in +#' `src/chart/sankey/SankeySeries.ts` (line 100). +#' ECharts docs: \url{https://echarts.apache.org/en/option.html#series-sankey} +#' +#' @param name Optional Character: Series name for legend and tooltip. +#' @param data Optional list: Nodes as a list of [SankeyNodeItem] objects or +#' plain named lists, each with at least a `name` field. +#' @param links Optional list: Edges as a list of [SankeyEdgeItem] objects or +#' plain named lists with `source`, `target`, and `value` fields. +#' @param levels Optional list: List of [SankeyLevelOption] for per-depth styling. +#' @param orient Optional Character \{"horizontal", "vertical"\}: Flow direction. +#' @param node_width Optional Numeric `[0, Inf)`: Width of each node rectangle in pixels. +#' @param node_gap Optional Numeric `[0, Inf)`: Vertical gap between nodes in pixels. +#' @param node_align Optional Character \{"justify", "left", "right"\}: Node +#' alignment within columns. +#' @param draggable Optional Logical: Whether nodes can be dragged interactively. +#' @param layout_iterations Optional Numeric `[0, Inf)`: Number of layout iterations. +#' @param sort Optional Character \{"desc"\} or `NULL`: Column-internal node +#' ordering. `NULL` preserves the original data order. +#' @param roam Optional Logical or Character \{"move", "scale"\}: Whether to enable +#' pan/zoom. +#' @param label Optional [LabelOption]: Node label configuration. +#' @param edge_label Optional [LabelOption]: Edge label configuration. +#' @param line_style Optional [LineStyle] or list: Default link line styling. +#' Accepts a [LineStyle] object or a plain list; a plain list also allows the +#' Sankey-specific `curveness` field (e.g. `list(curveness = 0.5)`). +#' @param item_style Optional [ItemStyle]: Default node styling. +#' @param color Optional Character: Color palette for nodes as a character vector. +#' @param left Optional Numeric or Character: Series left offset. +#' @param right Optional Numeric or Character: Series right offset. +#' @param top Optional Numeric or Character: Series top offset. +#' @param bottom Optional Numeric or Character: Series bottom offset. +#' @param silent Optional Logical: Whether to disable mouse/touch events. +#' @param z_level Optional Numeric: Canvas layer index. +#' @param z Optional Numeric: Front-back order within the same canvas layer. +#' @export +SankeySeries <- S7::new_class( + "SankeySeries", + properties = list( + name = optional_character_scalar, + data = S7::new_property(class = S7::class_any, default = NULL), + links = S7::new_property(class = S7::class_any, default = NULL), + levels = S7::new_property(class = S7::class_any, default = NULL), + orient = enum(c("horizontal", "vertical"), nullable = TRUE), + node_width = numeric_or_null_property(), + node_gap = numeric_or_null_property(), + node_align = enum(c("justify", "left", "right"), nullable = TRUE), + draggable = optional_logical_scalar, + layout_iterations = numeric_or_null_property(), + sort = S7::new_property( + class = S7::class_any, + default = NULL, + validator = function(value) { + if (is.null(value)) { + return(NULL) + } + if (is.character(value) && length(value) == 1L && value == "desc") { + return(NULL) + } + "must be 'desc' or NULL" + } + ), + roam = S7::new_property( + class = S7::class_any, + default = NULL, + validator = function(value) { + if (is.null(value)) { + return(NULL) + } + if (is.logical(value) && length(value) == 1L) { + return(NULL) + } + if ( + is.character(value) && + length(value) == 1L && + value %in% c("move", "scale") + ) { + return(NULL) + } + "must be TRUE/FALSE, 'move', 'scale', or NULL" + } + ), + label = class_or_null_property(LabelOption), + edge_label = class_or_null_property(LabelOption), + # Accepts LineStyle S7 object or plain list (allows curveness extension) + line_style = S7::new_property(class = S7::class_any, default = NULL), + item_style = class_or_null_property(ItemStyle), + color = color_palette_property(), + left = numeric_or_string_property(), + right = numeric_or_string_property(), + top = numeric_or_string_property(), + bottom = numeric_or_string_property(), + silent = optional_logical_scalar, + z_level = numeric_or_null_property(), + z = numeric_or_null_property() + ) +) + +#' @keywords internal +#' @noRd +serialize_sankey_items <- function(items) { + if (is.null(items)) { + return(NULL) + } + out <- lapply(items, function(item) { + if (S7::S7_inherits(item)) to_list(item) else item + }) + unname(out) +} + +S7::method(to_list, SankeySeries) <- function(x, ...) { + out <- props_to_list(x) + out[["type"]] <- "sankey" + + # Serialize data (nodes) and links as unnamed arrays + if (!is.null(out[["data"]])) { + out[["data"]] <- serialize_sankey_items(x@data) + } + if (!is.null(out[["links"]])) { + out[["links"]] <- serialize_sankey_items(x@links) + } + + # Serialize levels as unnamed array + if (!is.null(out[["levels"]])) { + out[["levels"]] <- serialize_sankey_items(x@levels) + } + + # line_style: S7 object -> list; plain list passed through as-is + if (!is.null(out[["lineStyle"]]) && S7::S7_inherits(x@line_style)) { + out[["lineStyle"]] <- to_list(x@line_style) + } + + out +} From baa039c01809debd8cfbd6118c5b8f593c9549bd Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 7 May 2026 01:52:42 -0700 Subject: [PATCH 08/10] up --- r/DESCRIPTION | 5 +++-- r/NAMESPACE | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/r/DESCRIPTION b/r/DESCRIPTION index d67e21c..c9206a0 100644 --- a/r/DESCRIPTION +++ b/r/DESCRIPTION @@ -1,6 +1,6 @@ Package: rtemis.draw Title: Interactive Visualization Using ECharts -Version: 0.1.0 +Version: 0.2.1 Authors@R: person(given = "E.D.", family = "Gennatas", role = c("aut", "cre", "cph"), email = "gennatas@gmail.com", @@ -15,7 +15,6 @@ URL: https://www.rtemis.org, https://docs.rtemis.org/draw/r, https://docs.rtemis License: GPL (>= 3) Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.3 Imports: cli, htmlwidgets, @@ -44,6 +43,7 @@ Collate: 'axis.R' 'components.R' 'series.R' + 'series_sankey.R' 'option.R' 'theme.R' 'draw.R' @@ -51,3 +51,4 @@ Collate: 'draw_spectrogram.R' 'export.R' 'zzz.R' +Config/roxygen2/version: 8.0.0 diff --git a/r/NAMESPACE b/r/NAMESPACE index 5be499f..6d08a74 100644 --- a/r/NAMESPACE +++ b/r/NAMESPACE @@ -22,6 +22,10 @@ export(MarkAreaDataPoint) export(MinorSplitLine) export(MinorTick) export(PieSeries) +export(SankeyEdgeItem) +export(SankeyLevelOption) +export(SankeyNodeItem) +export(SankeySeries) export(ScatterSeries) export(SplitArea) export(SplitLine) @@ -40,6 +44,7 @@ export(draw_heatmap) export(draw_histogram) export(draw_line) export(draw_pie) +export(draw_sankey) export(draw_scatter) export(draw_spectrogram) export(renderDraw) From 2e95f51860417265bf706e5c353653c1d6c63fb7 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 7 May 2026 01:53:38 -0700 Subject: [PATCH 09/10] add manual-r to Makefile --- Makefile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 86cfbdd..3dad0fc 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ msg = @printf '\033[38;2;108;163;160m[%s] %s\033[0m\n' "$$(date -u '+%Y-%m-%d %H .DEFAULT_GOAL := help .PHONY: help format format-r document document-r install install-r test test-r \ - build build-r check check-r check-cran site site-r clean clean-r + build build-r check check-r check-cran manual-r site site-r clean clean-r # ── Help ───────────────────────────────────────────────────────────────────── help: @@ -83,6 +83,12 @@ check-cran: build-r rm -f $(R_DIR)/$(PKG)_*.tar.gz $(call msg,Done) +# ── Manual ─────────────────────────────────────────────────────────────────── +manual-r: + $(call msg,─── Building manual for $(PKG)... ───) + cd $(R_DIR) && $(R) CMD Rd2pdf . --output=$(PKG).pdf + $(call msg,Done) + # ── Site ────────────────────────────────────────────────────────────────────── site: site-r From 5e616533b2b4299cfc2b0ce79ec4c3248edc55fe Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Tue, 12 May 2026 11:55:29 -0700 Subject: [PATCH 10/10] address review comments --- demo_spectrogram.csv | 961 -------------------------- r/DESCRIPTION | 1 + r/R/00_init.R | 28 + r/R/draw.R | 17 +- r/R/series_sankey.R | 16 +- r/man/SankeyLevelOption.Rd | 2 +- r/tests/testthat/test-series_sankey.R | 49 +- 7 files changed, 82 insertions(+), 992 deletions(-) delete mode 100644 demo_spectrogram.csv create mode 100644 r/R/00_init.R diff --git a/demo_spectrogram.csv b/demo_spectrogram.csv deleted file mode 100644 index afb1eac..0000000 --- a/demo_spectrogram.csv +++ /dev/null @@ -1,961 +0,0 @@ -time,frequency,value -0.00390625,0,20.188491912499536 -0.25390625,0,26.930539965104806 -0.50390625,0,29.213644997299678 -0.75390625,0,21.094338054162133 -1.00390625,0,8.353774875283822 -1.25390625,0,-4.005011607169958 -1.50390625,0,-13.77959066496491 -1.75390625,0,-21.14652314177636 -2.00390625,0,-26.867152252956448 -2.25390625,0,-31.497345811068705 -2.50390625,0,-35.37640025376625 -2.75390625,0,-38.71414584427984 -3.00390625,0,-41.6463822815193 -3.25390625,0,-44.265018054892344 -3.50390625,0,-46.63471706675972 -0.00390625,2,-48.80250337336967 -0.25390625,2,-50.803553362068136 -0.50390625,2,-52.66482987374697 -0.75390625,2,-54.40744283119039 -1.00390625,2,-56.04823000427959 -1.25390625,2,-57.60084433869572 -1.50390625,2,-59.076519913196904 -1.75390625,2,-60.484623125389476 -2.00390625,2,-61.83305699254394 -2.25390625,2,-63.12856288117979 -2.50390625,2,-64.37694924519802 -2.75390625,2,-65.58326752171232 -3.00390625,2,-66.7519491637243 -3.25390625,2,-67.88691367348078 -3.50390625,2,-68.9916547031355 -0.00390625,4,-70.06930935668507 -0.25390625,4,-71.12271446745319 -0.50390625,4,-72.15445265930832 -0.75390625,4,-73.16689029924065 -1.00390625,4,-74.16220892923474 -1.25390625,4,-75.14243138825034 -1.50390625,4,-76.10944353481271 -1.75390625,4,-77.06501225833136 -2.00390625,4,-78.01080028113716 -2.25390625,4,-78.94837810034517 -2.50390625,4,-79.87923328491532 -2.75390625,4,-80.8047772109912 -3.00390625,4,-81.72634918122401 -3.25390625,4,-82.64521771702285 -3.50390625,4,-83.5625786166961 -0.00390625,6,-84.47954909229489 -0.25390625,6,-85.39715691261668 -0.50390625,6,-86.31632291456803 -0.75390625,6,-87.23783432851776 -1.00390625,6,-88.16230504535396 -1.25390625,6,-89.09011679567192 -1.50390625,6,-90.02133197361579 -1.75390625,6,-90.95556376308423 -2.00390625,6,-91.89178176129539 -2.25390625,6,-92.82802048240455 -2.50390625,6,-93.76094404764756 -2.75390625,6,-94.68520511636191 -3.00390625,6,-95.59252852456609 -3.25390625,6,-96.47047360206608 -3.50390625,6,-97.30093448173722 -0.00390625,8,-98.05870770840725 -0.25390625,8,-98.71095952239443 -0.50390625,8,-99.2190268812904 -0.75390625,8,-99.54402735563419 -1.00390625,8,-3.2588587211017 -1.25390625,8,12.331626529155539 -1.50390625,8,25.28777199931879 -1.75390625,8,29.635367127551014 -2.00390625,8,23.759431888567516 -2.25390625,8,10.697943061433746 -2.50390625,8,-2.885659415799144 -2.75390625,8,-14.169022938692784 -3.00390625,8,-23.01239076498019 -3.25390625,8,-30.100849071728497 -3.50390625,8,-35.951509965984656 -0.00390625,10,-40.83891222547181 -0.25390625,10,-44.89243203687703 -0.50390625,10,-48.18727977302498 -0.75390625,10,-50.81183222019191 -1.00390625,10,-52.89001438742542 -1.25390625,10,-54.55994626722247 -1.50390625,10,-55.94329886300379 -1.75390625,10,-57.13102442767702 -2.00390625,10,-58.18485438982121 -2.25390625,10,-59.14480449482635 -2.50390625,10,-60.03651330354225 -2.75390625,10,-60.87658549204761 -3.00390625,10,-61.67605103527886 -3.25390625,10,-62.442496118563696 -3.50390625,10,-63.181353916640084 -0.00390625,12,-63.89668703684051 -0.25390625,12,-64.59166695494932 -0.50390625,12,-65.26887283555034 -0.75390625,12,-65.9304818505011 -1.00390625,12,-66.5783935496462 -1.25390625,12,-67.2143136175334 -1.50390625,12,-67.83981231086675 -1.75390625,12,-68.45636696187633 -2.00390625,12,-69.06539442933217 -2.25390625,12,-69.66827728581804 -2.50390625,12,-70.26638626279205 -2.75390625,12,-70.86110071742266 -3.00390625,12,-71.45382844828868 -3.25390625,12,-72.04602594423625 -3.50390625,12,-72.63922006087363 -0.00390625,14,-73.23503214234678 -0.25390625,14,-73.83520572161741 -0.50390625,14,-74.44163917607655 -0.75390625,14,-75.0564250764844 -1.00390625,14,-75.681898526179907 -1.25390625,14,-76.32069759464257 -1.50390625,14,-76.97584015715408 -1.75390625,14,-77.65082324536466 -2.00390625,14,-78.34975373952997 -2.25390625,14,-79.07752349505023 -2.50390625,14,-79.84004874543372 -2.75390625,14,-80.64460476422838 -3.00390625,14,-81.50030558610277 -3.25390625,14,-82.41881187881661 -3.50390625,14,-83.41541128488024 -0.00390625,16,-84.5107346503511 -0.25390625,16,-85.7336178966063 -0.50390625,16,-87.12616962755449 -0.75390625,16,-88.75345828187989 -1.00390625,16,-90.72400369876436 -1.25390625,16,-93.23970067169648 -1.50390625,16,-96.74635897689794 -1.75390625,16,-102.5883633343375 -2.00390625,16,-9.965494113298414 -2.25390625,16,-3.196171106197826 -2.50390625,16,9.79401987164124 -2.75390625,16,22.878366500533932 -3.00390625,16,29.56598644996901 -3.25390625,16,25.93552264009869 -3.50390625,16,13.349993178743064 -0.00390625,18,-0.21455263307614578 -0.25390625,18,-11.351545131381592 -0.50390625,18,-19.678387345394096 -0.75390625,18,-25.985297771263536 -1.00390625,18,-30.96442029370618 -1.25390625,18,-35.04857198514932 -1.50390625,18,-38.502831995243234 -1.75390625,18,-41.49573036363595 -2.00390625,18,-44.13903982138435 -2.25390625,18,-46.50977644923023 -2.50390625,18,-48.662812119964315 -2.75390625,18,-50.63840103899718 -3.00390625,18,-52.46683982761211 -3.25390625,18,-54.17144957389503 -3.50390625,18,-55.77054134473249 -0.00390625,20,-57.27874661911613 -0.25390625,20,-58.70793973549027 -0.50390625,20,-60.06789149861312 -0.75390625,20,-61.366741475741186 -1.00390625,20,-62.61134537995849 -1.25390625,20,-63.807534689544134 -1.50390625,20,-64.9603134771881 -1.75390625,20,-66.07400954300797 -2.00390625,20,-67.15239176586506 -2.25390625,20,-68.19876208314868 -2.50390625,20,-69.21602814419839 -2.75390625,20,-70.20676100399358 -3.00390625,20,-71.17324106695833 -3.25390625,20,-72.11749462890903 -3.50390625,20,-73.04132277322294 -0.00390625,22,-73.94632387216478 -0.25390625,22,-74.83391064310698 -0.50390625,22,-75.70532236784584 -0.75390625,22,-76.56163270459724 -1.00390625,22,-77.40375328192832 -1.25390625,22,-78.23243309116415 -1.50390625,22,-79.04825353063767 -1.75390625,22,-79.85161872810339 -2.00390625,22,-80.64274064178142 -2.25390625,22,-81.42161816083082 -2.50390625,22,-82.18800930366987 -2.75390625,22,-82.94139534090327 -3.00390625,22,-83.68093562523059 -3.25390625,22,-84.40541183557423 -3.50390625,22,-85.11316068784984 -0.00390625,24,-85.80199487268722 -0.25390625,24,-86.46911364101942 -0.50390625,24,-87.11100709888278 -0.75390625,24,-87.72336297370018 -1.00390625,24,-88.3009909906668 -1.25390625,24,-88.83778849877503 -1.50390625,24,-89.3267800351004 -1.75390625,24,-89.7602697512443 -2.00390625,24,-90.13014400527176 -2.25390625,24,-90.42834417374999 -2.50390625,24,-90.64749333106113 -2.75390625,24,-90.78160717481347 -3.00390625,24,-23.907604928015473 -3.25390625,24,-15.571884306098735 -3.50390625,24,-5.069206747285514 -0.00390625,26,7.182487018929829 -0.25390625,26,20.09235135760465 -0.50390625,26,28.993023224655722 -0.75390625,26,27.621856908212195 -1.00390625,26,16.294125940954984 -1.25390625,26,3.209100735529464 -1.50390625,26,-7.846702819099029 -1.75390625,26,-16.108672968219516 -2.00390625,26,-22.32433420484012 -2.25390625,26,-27.228566388255643 -2.50390625,26,-31.268039179676283 -2.75390625,26,-34.70427108748397 -3.00390625,26,-37.69834267362515 -3.25390625,26,-40.35541848282378 -3.50390625,26,-42.747690351454224 -0.00390625,28,-44.9267934542105 -0.25390625,28,-46.9309195603767 -0.50390625,28,-48.789110355575 -0.75390625,28,-50.523968237148516 -1.00390625,28,-52.15343504385254 -1.25390625,28,-53.69199818350402 -1.50390625,28,-55.15153189831729 -1.75390625,28,-56.5418985509486 -2.00390625,28,-57.8713876492267 -2.25390625,28,-59.147042470645005 -2.50390625,28,-60.37490715280175 -2.75390625,28,-61.56021642761786 -3.00390625,28,-62.70754330000166 -3.25390625,28,-63.82091542200877 -3.50390625,28,-64.90390785022626 -0.00390625,30,-65.95971778160065 -0.25390625,30,-66.99122537446935 -0.50390625,30,-68.00104376287557 -0.75390625,30,-68.99156056415245 -1.00390625,30,-69.96497270618276 -1.25390625,30,-70.92331593686625 -1.50390625,30,-71.86849013494724 -1.75390625,30,-72.80228125564854 -2.00390625,30,-73.72638063387672 -2.25390625,30,-74.64240212292674 -2.50390625,30,-75.55189750153835 -2.75390625,30,-76.45637032348831 -3.00390625,30,-77.35728820163294 -3.25390625,30,-78.25609320487521 -3.50390625,30,-79.15420949796352 -0.00390625,32,-80.05304647712153 -0.25390625,32,-80.95399437478738 -0.50390625,32,-81.85840683697526 -0.75390625,32,-82.76756130864952 -1.00390625,32,-83.68258173529306 -1.25390625,32,-84.60429788247887 -1.50390625,32,-85.53299906762469 -1.75390625,32,-86.46801470640267 -2.00390625,32,-87.40701630605025 -2.25390625,32,-88.34488809772465 -2.50390625,32,-89.27197382298267 -2.75390625,32,-90.17154219302178 -3.00390625,32,-91.0165938663429 -3.25390625,32,-91.76697248272657 -3.50390625,32,-92.36936472623186 -0.00390625,34,-92.76430501041946 -0.25390625,34,-23.69416541546293 -0.50390625,34,-21.204266922769914 -0.75390625,34,-15.345277337584315 -1.00390625,34,-7.044932217292654 -1.25390625,34,4.0605802880899535 -1.50390625,34,17.017316679816005 -1.75390625,34,27.932675781835194 -2.00390625,34,28.796771228286282 -2.25390625,34,19.386977230548574 -2.50390625,34,6.491497010083346 -2.75390625,34,-5.2851840154660925 -3.00390625,34,-14.330372624244292 -3.25390625,34,-21.132986485916938 -3.50390625,34,-26.47368159795125 -0.00390625,36,-30.85889410432493 -0.25390625,36,-34.584468762081 -0.50390625,36,-37.83008359720366 -0.75390625,36,-40.71153512599121 -1.00390625,36,-43.30757226610511 -1.25390625,36,-45.6741977025687 -1.50390625,36,-47.85272702062102 -1.75390625,36,-49.87457982373806 -2.00390625,36,-51.76426567271841 -2.25390625,36,-53.54132245530715 -2.50390625,36,-55.221619800028776 -2.75390625,36,-56.81826273957233 -3.00390625,36,-58.342235256641374 -3.25390625,36,-59.80286961694965 -3.50390625,36,-61.208196074505665 -0.00390625,38,-62.565208598672996 -0.25390625,38,-63.880070545183116 -0.50390625,38,-65.15827668745503 -0.75390625,38,-66.4047831143065 -1.00390625,38,-67.62411326980009 -1.25390625,38,-68.82044615887796 -1.50390625,38,-69.99769126348001 -1.75390625,38,-71.15955361227263 -2.00390625,38,-72.30959177783748 -2.25390625,38,-73.45127100547282 -2.50390625,38,-74.58801339440735 -2.75390625,38,-75.72324683666889 -3.00390625,38,-76.86045426181192 -3.25390625,38,-78.00322485906985 -3.50390625,38,-79.15530880827052 -0.00390625,40,-80.32067739738119 -0.25390625,40,-81.50359048033289 -0.50390625,40,-82.70867351822771 -0.75390625,40,-83.94100673935375 -1.00390625,40,-85.20622923843467 -1.25390625,40,-86.51066060616154 -1.50390625,40,-87.86144243681477 -1.75390625,40,-89.26669968814932 -2.00390625,40,-90.73571687483948 -2.25390625,40,-92.27911231613193 -2.50390625,40,-93.90896700704387 -2.75390625,40,-95.63880656108904 -3.00390625,40,-97.48320782062169 -3.25390625,40,-99.45652286488664 -3.50390625,40,-101.56960624157813 -0.00390625,42,-103.82215319344954 -0.25390625,42,-106.18582623823029 -0.50390625,42,-108.5702004383165 -0.75390625,42,-110.76656574562092 -1.00390625,42,-112.40092544703364 -1.25390625,42,-34.01935460033141 -1.50390625,42,-31.1232085060962 -1.75390625,42,-25.61232258021395 -2.00390625,42,-18.98619630631855 -2.25390625,42,-10.578243643904667 -2.50390625,42,0.5366265762095584 -2.75390625,42,14.005952158828045 -3.00390625,42,26.36937356278915 -3.25390625,42,29.47591641828872 -3.50390625,42,22.276724974505797 -0.00390625,44,9.268714242016571 -0.25390625,44,-3.6719871604688015 -0.50390625,44,-14.087180346703654 -0.75390625,44,-22.047906514178223 -1.00390625,44,-28.30652617685185 -1.25390625,44,-33.428491392211825 -1.50390625,44,-37.76034055094695 -1.75390625,44,-41.517157122023775 -2.00390625,44,-44.83931507065405 -2.25390625,44,-47.82307378375983 -2.50390625,44,-50.53730175229873 -2.75390625,44,-53.033063191258314 -3.00390625,44,-55.349391668453464 -3.25390625,44,-57.5169262332269 -3.50390625,44,-59.56029336516974 -0.00390625,46,-61.49972267549816 -0.25390625,46,-63.352177665802934 -0.50390625,46,-65.13216992667488 -0.75390625,46,-66.85236072412005 -1.00390625,46,-68.52401542181349 -1.25390625,46,-70.15735190072513 -1.50390625,46,-71.76180708104786 -1.75390625,46,-73.34623153874054 -2.00390625,46,-74.91900709228932 -2.25390625,46,-76.48806102293177 -2.50390625,46,-78.06071637350064 -2.75390625,46,-79.64325727994344 -3.00390625,46,-81.23998160033108 -3.25390625,46,-82.85133158119315 -3.50390625,46,-84.47042663776423 -0.00390625,48,-86.07708840518856 -0.25390625,48,-87.62886006856284 -0.50390625,48,-89.05138777408551 -0.75390625,48,-90.23832164091917 -1.00390625,48,-91.07881363590222 -1.25390625,48,-91.51439695197863 -1.50390625,48,-91.57942772385854 -1.75390625,48,-91.37799918634815 -2.00390625,48,-91.02528892027851 -2.25390625,48,-90.60930010452195 -2.50390625,48,-90.18444598587328 -2.75390625,48,-89.7798508654297 -3.00390625,48,-89.40906957641509 -3.25390625,48,-89.07704017440098 -3.50390625,48,-88.78427426672202 -0.00390625,50,-88.52919180929396 -0.25390625,50,-88.30936831251654 -0.50390625,50,-88.1221799164946 -0.75390625,50,-87.96512540610976 -1.00390625,50,-87.83597733383624 -1.25390625,50,-87.73284509275194 -1.50390625,50,-87.65419423858118 -1.75390625,50,-87.5988454290033 -2.00390625,50,-87.56596605760068 -2.25390625,50,-71.99981436031449 -2.50390625,50,-44.473193632715784 -2.75390625,50,-36.62318127299181 -3.00390625,50,-29.9050082799489 -3.25390625,50,-22.575764196722204 -3.50390625,50,-13.668842198581231 -0.00390625,52,-2.32948240765774 -0.25390625,52,11.303037922239966 -0.50390625,52,24.29587305897773 -0.75390625,52,29.66922517057317 -1.00390625,52,24.74812733637865 -1.25390625,52,11.815049910540223 -1.50390625,52,-1.8676377169806373 -1.75390625,52,-13.214944454696347 -2.00390625,52,-21.95760541068949 -2.25390625,52,-28.792898437510686 -2.50390625,52,-34.29672582071058 -2.75390625,52,-38.82313680611104 -3.00390625,52,-42.59462016712752 -3.25390625,52,-45.7653700403887 -3.50390625,52,-48.45298945183992 -0.00390625,54,-50.75239791324087 -0.25390625,54,-52.741358715365436 -0.50390625,54,-54.48296705306784 -0.75390625,54,-56.02753713068601 -1.00390625,54,-57.41458450345515 -1.25390625,54,-58.674856166623485 -1.50390625,54,-59.83220886215286 -1.75390625,54,-60.905205598520666 -2.00390625,54,-61.908395498526346 -2.25390625,54,-62.853302265991545 -2.50390625,54,-63.749171111788826 -2.75390625,54,-64.60352708047877 -3.00390625,54,-65.42259112753919 -3.25390625,54,-66.21159109934815 -3.50390625,54,-66.97499578027382 -0.00390625,56,-67.7166930753271 -0.25390625,56,-68.44012762385229 -0.50390625,56,-69.14840910147375 -0.75390625,56,-69.84439944138049 -1.00390625,56,-70.53078508045992 -1.25390625,56,-71.21013890041606 -1.50390625,56,-71.88497545993815 -1.75390625,56,-72.5578025098561 -2.00390625,56,-73.2311713365306 -2.25390625,56,-73.90772837785997 -2.50390625,56,-74.59027041242972 -2.75390625,56,-75.28180594340932 -3.00390625,56,-75.98562557038387 -3.25390625,56,-76.70538460609005 -3.50390625,56,-77.4452016656839 -0.00390625,58,-78.20977715622705 -0.25390625,58,-79.00453536361314 -0.50390625,58,-79.83579124708741 -0.75390625,58,-80.71093615996699 -1.00390625,58,-81.63861696789569 -1.25390625,58,-82.62883415503465 -1.50390625,58,-83.69276126732095 -1.75390625,58,-84.84178256903374 -2.00390625,58,-86.08448650629201 -2.25390625,58,-87.41853731972972 -2.50390625,58,-88.81057251117683 -2.75390625,58,-90.15330229155224 -3.00390625,58,-91.20601217646433 -3.25390625,58,-43.424511110869155 -3.50390625,58,-41.626191721552416 -0.00390625,60,-37.80095418600909 -0.25390625,60,-33.26907668333019 -0.50390625,60,-28.09205862426667 -0.75390625,60,-21.916994079589216 -1.00390625,60,-14.145629809326401 -1.25390625,60,-3.982584963878828 -1.50390625,60,8.74479214546291 -1.75390625,60,21.73267267593225 -2.00390625,60,29.379519129515966 -2.25390625,60,26.72133945380612 -2.50390625,60,14.576338492720822 -2.75390625,60,1.206855927446748 -3.00390625,60,-9.82405221480236 -3.25390625,60,-18.03091941910652 -3.50390625,60,-24.218549317670654 -0.00390625,62,-29.103013999242037 -0.25390625,62,-33.12044545724447 -0.50390625,62,-36.530295578006246 -0.75390625,62,-39.49426899782277 -1.00390625,62,-42.11857991668455 -1.25390625,62,-44.4762855821628 -1.50390625,62,-46.61961634329317 -1.75390625,62,-48.587134427607275 -2.00390625,62,-50.4080883611046 -2.25390625,62,-52.10517233711915 -2.50390625,62,-53.69633818312035 -2.75390625,62,-55.196022538864895 -3.00390625,62,-56.61600057380205 -3.25390625,62,-57.965993897044626 -3.50390625,62,-59.254112255345106 -0.00390625,64,-60.487180075088666 -0.25390625,64,-61.670981428886236 -0.50390625,64,-62.81044602303481 -0.75390625,64,-63.90979169936391 -1.00390625,64,-64.97263424703117 -1.25390625,64,-66.00207223973959 -1.50390625,64,-67.00075231519223 -1.75390625,64,-67.97091895980779 -2.00390625,64,-68.9144516703972 -2.25390625,64,-69.83289163106494 -2.50390625,64,-70.72745958603358 -2.75390625,64,-71.59906604274934 -3.00390625,64,-72.44831481406149 -3.25390625,64,-73.27550078906846 -3.50390625,64,-74.08060260782203 -0.00390625,66,-74.86327118145368 -0.25390625,66,-75.62281501653037 -0.50390625,66,-76.3581837122302 -0.75390625,66,-77.06795123343426 -1.00390625,66,-77.75030134743082 -1.25390625,66,-78.40301796809624 -1.50390625,66,-79.023484073084 -1.75390625,66,-79.60869338795197 -2.00390625,66,-80.15527953800544 -2.25390625,66,-80.6595674812529 -2.50390625,66,-81.11765132992684 -2.75390625,66,-81.52550130384923 -3.00390625,66,-81.87909977317193 -3.25390625,66,-82.17460254919675 -3.50390625,66,-82.408517188714 -0.00390625,68,-82.57788504576308 -0.25390625,68,-82.68045060576435 -0.50390625,68,-47.94651199059207 -0.75390625,68,-44.82953816467432 -1.00390625,68,-40.15234217096369 -1.25390625,68,-35.805715734919474 -1.50390625,68,-31.485634415390496 -1.75390625,68,-26.807922326355893 -2.00390625,68,-21.365949182377456 -2.25390625,68,-14.602219266890557 -2.50390625,68,-5.708625834922221 -2.75390625,68,5.889549566609519 -3.00390625,68,18.783039485476955 -3.25390625,68,28.601487340950392 -3.50390625,68,28.18521528318732 -0.00390625,70,17.627335799245433 -0.25390625,70,4.687367068012067 -0.50390625,70,-6.591254969764715 -0.75390625,70,-15.116640465514042 -1.00390625,70,-21.54190444388284 -1.25390625,70,-26.616123290934706 -1.50390625,70,-30.80269997571483 -1.75390625,70,-34.37149401553445 -2.00390625,70,-37.48751486959641 -2.25390625,70,-40.25797051023386 -2.50390625,70,-42.756305205442786 -2.75390625,70,-45.03506684429964 -3.00390625,70,-47.133206230853055 -3.25390625,70,-49.08044682744052 -3.50390625,70,-50.90002615246328 -0.00390625,72,-52.6104859951057 -0.25390625,72,-54.226882271955894 -0.50390625,72,-55.761627101556215 -0.75390625,72,-57.22508995080975 -1.00390625,72,-58.62603629436834 -1.25390625,72,-59.97195381735685 -1.50390625,72,-61.26929892854512 -1.75390625,72,-62.52368558437866 -2.00390625,72,-63.74003149929165 -2.25390625,72,-64.92267215138831 -2.50390625,72,-66.07545006231821 -2.75390625,72,-67.20178444813197 -3.00390625,72,-68.30472504011526 -3.25390625,72,-69.38699251220142 -3.50390625,72,-70.45100724775837 -0.00390625,74,-71.49890736065862 -0.25390625,74,-72.53255622687934 -0.50390625,74,-73.5535393550931 -0.75390625,74,-74.56314951110657 -1.00390625,74,-75.5623586605368 -1.25390625,74,-76.55177424821139 -1.50390625,74,-77.53157648784655 -1.75390625,74,-78.50143233376446 -2.00390625,74,-79.46038024554942 -2.25390625,74,-80.40667905435637 -2.50390625,74,-81.33761280613078 -2.75390625,74,-82.24924423355381 -3.00390625,74,-83.13611256339718 -3.25390625,74,-83.99088124649748 -3.50390625,74,-84.803961700216007 -0.00390625,76,-85.56317861935472 -0.25390625,76,-86.25360256411221 -0.50390625,76,-86.85775088012099 -0.75390625,76,-87.35641083873264 -1.00390625,76,-87.73029288931554 -1.25390625,76,-87.9624894446579 -1.50390625,76,-50.74613709149672 -1.75390625,76,-48.14077038926672 -2.00390625,76,-43.96815919013206 -2.25390625,76,-40.073081225206465 -2.50390625,76,-36.31783491410017 -2.75390625,76,-32.42068345089308 -3.00390625,76,-28.1071437825003 -3.25390625,76,-23.049891146299167 -3.50390625,76,-16.7672489931155 -0.00390625,78,-8.5069288560421 -0.25390625,78,2.5229697464216287 -0.50390625,78,15.692740222389933 -0.75390625,78,27.326240922119606 -1.00390625,78,29.147051019526376 -1.25390625,78,20.668384977958247 -1.50390625,78,7.738666674787098 -1.75390625,78,-4.527154868748831 -2.00390625,78,-14.124729199940514 -2.25390625,78,-21.373343328165753 -2.50390625,78,-27.05812309489106 -2.75390625,78,-31.718462019465083 -3.00390625,78,-35.67415518758273 -3.25390625,78,-39.11995923427951 -3.50390625,78,-42.18126292038213 -0.00390625,80,-44.94317495825779 -0.25390625,80,-47.46612712209783 -0.50390625,80,-49.794684647901384 -0.75390625,80,-51.962783897321884 -1.00390625,80,-53.99699414325535 -1.25390625,80,-55.918634027996326 -1.50390625,80,-57.74519604294397 -1.75390625,80,-59.491337824196165 -2.00390625,80,-61.16959421098808 -2.25390625,80,-62.7909051773515 -2.50390625,80,-64.36502071572195 -2.75390625,80,-65.90082341300278 -3.00390625,80,-67.40659736817534 -3.25390625,80,-68.89026479246327 -3.50390625,80,-70.35960786849675 -0.00390625,82,-71.82249180747239 -0.25390625,82,-73.28710608614405 -0.50390625,82,-74.76224364091401 -0.75390625,82,-76.257644637906 -1.00390625,82,-77.7844414767465 -1.25390625,82,-79.35576061859234 -1.50390625,82,-80.98756459659617 -1.75390625,82,-82.69986335815155 -2.00390625,82,-84.51848640551916 -2.25390625,82,-86.4776625233022 -2.50390625,82,-88.62352290037143 -2.75390625,82,-91.01736994069108 -3.00390625,82,-93.73008956208952 -3.25390625,82,-96.77879566188174 -3.50390625,82,-99.78387822743278 -0.00390625,84,-101.22244466362926 -0.25390625,84,-100.18203169237103 -0.50390625,84,-98.31036679196905 -0.75390625,84,-96.64814626164281 -1.00390625,84,-95.349319843026 -1.25390625,84,-94.36513038658 -1.50390625,84,-93.63266869189152 -1.75390625,84,-93.10405386022973 -2.00390625,84,-92.74666528479953 -2.25390625,84,-92.53980919165647 -2.50390625,84,-54.23244710927578 -2.75390625,84,-53.0329197062164 -3.00390625,84,-50.38344653119029 -3.25390625,84,-47.32530909125803 -3.50390625,84,-44.11186421044859 -0.00390625,86,-40.681027531767114 -0.25390625,86,-36.879065743730976 -0.50390625,86,-32.49622408923293 -0.75390625,86,-27.240782254794148 -1.00390625,86,-20.675614321839824 -1.25390625,86,-12.132848811361248 -1.50390625,86,-0.8554140133881917 -1.75390625,86,12.804739769991214 -2.00390625,86,25.54326370022764 -2.25390625,86,29.61784781574326 -2.50390625,86,23.394448082000178 -2.75390625,86,10.357460691013276 -3.00390625,86,-3.030961429704042 -3.25390625,86,-14.04810786905891 -3.50390625,86,-22.620374654814462 -0.00390625,88,-29.478011235019437 -0.25390625,88,-35.19615960066959 -0.50390625,88,-40.1299351365364 -0.75390625,88,-44.498658722081395 -1.00390625,88,-48.44255440657085 -1.25390625,88,-52.052401231626114 -1.50390625,88,-55.384176340831914 -1.75390625,88,-58.46558023550049 -2.00390625,88,-61.298549240078 -2.25390625,88,-63.86172517553101 -2.50390625,88,-66.11798940839861 -2.75390625,88,-68.03115913098203 -3.00390625,88,-69.58838247877858 -3.25390625,88,-70.81446915490032 -3.50390625,88,-71.7665933234777 -0.00390625,90,-72.51473715230695 -0.25390625,90,-73.12322131751036 -0.50390625,90,-73.64183945902978 -0.75390625,90,-74.10520649872113 -1.00390625,90,-74.53579612440903 -1.25390625,90,-74.94754452831593 -1.50390625,90,-75.34878117660756 -1.75390625,90,-75.74428434036216 -2.00390625,90,-76.13662040671866 -2.25390625,90,-76.52698190514991 -2.50390625,90,-76.91569810796808 -2.75390625,90,-77.3025384779822 -3.00390625,90,-77.6868870003754 -3.25390625,90,-78.06783624615488 -3.50390625,90,-78.44423156316309 -0.00390625,92,-78.81468451243313 -0.25390625,92,-79.17756800934161 -0.50390625,92,-79.5310021820275 -0.75390625,92,-79.8728376883569 -1.00390625,92,-80.20064334404638 -1.25390625,92,-80.51170425498206 -1.50390625,92,-80.80303754977095 -1.75390625,92,-81.071432374845 -2.00390625,92,-81.31352028042247 -2.25390625,92,-81.5258794511552 -2.50390625,92,-81.70517255191993 -2.75390625,92,-81.84831211691056 -3.00390625,92,-81.95264051163143 -3.25390625,92,-82.0161054462859 -3.50390625,92,-59.960987771917964 -0.00390625,94,-59.15259528708826 -0.25390625,94,-57.09716591405281 -0.50390625,94,-54.36538358794449 -0.75390625,94,-51.24537992994355 -1.00390625,94,-47.80443562163156 -1.25390625,94,-44.009034736186095 -1.50390625,94,-39.77159496518596 -1.75390625,94,-34.952248106281154 -2.00390625,94,-29.33313370498549 -2.25390625,94,-22.56222044595588 -2.50390625,94,-14.068520791347899 -2.75390625,94,-3.1227426057776397 -3.00390625,94,10.21555294883806 -3.25390625,94,23.253580702465293 -3.50390625,94,29.6045059846192 -0.00390625,96,25.656020707904904 -0.25390625,96,12.95646460380031 -0.50390625,96,-0.6826616091054436 -0.75390625,96,-11.918318291931097 -1.00390625,96,-20.375587400220475 -1.25390625,96,-26.80641283195235 -1.50390625,96,-31.872694339853624 -1.75390625,96,-35.997023627861196 -2.00390625,96,-39.44643987938757 -2.25390625,96,-42.39634181653855 -2.50390625,96,-44.96627773690644 -2.75390625,96,-47.240245050771826 -3.00390625,96,-49.27881004966202 -3.25390625,96,-51.12666529838489 -3.50390625,96,-52.81749689322446 -0.00390625,98,-54.37719916287128 -0.25390625,98,-55.826043828828205 -0.50390625,98,-57.18017237182168 -0.75390625,98,-58.45264162524597 -1.00390625,98,-59.65416911483857 -1.25390625,98,-60.79367314864311 -1.50390625,98,-61.878670267169504 -1.75390625,98,-62.915571932556716 -2.00390625,98,-63.90990890778167 -2.25390625,98,-64.86650276249235 -2.50390625,98,-65.78959813860075 -2.75390625,98,-66.68296503762545 -3.00390625,98,-67.54997773846151 -3.25390625,98,-68.39367452659876 -3.50390625,98,-69.21680102757045 -0.00390625,100,-70.02183823307459 -0.25390625,100,-70.81101511551839 -0.50390625,100,-71.58630437013024 -0.75390625,100,-72.34939792084802 -1.00390625,100,-73.10165722060346 -1.25390625,100,-73.84403064362174 -1.50390625,100,-74.57692765287928 -1.75390625,100,-75.30003623367675 -2.00390625,100,-76.01206723502304 -2.25390625,100,-76.71041038270462 -2.50390625,100,-77.3906909620158 -2.75390625,100,-78.0462387386981 -3.00390625,100,-78.66752278490283 -3.25390625,100,-79.24168766260996 -3.50390625,100,-79.7524419155642 -0.00390625,102,-80.18066457924307 -0.25390625,102,-80.5060987048585 -0.50390625,102,-80.71023531505459 -0.75390625,102,-50.25480094245529 -1.00390625,102,-49.99230938964629 -1.25390625,102,-49.21786824050544 -1.50390625,102,-47.9650494042717 -1.75390625,102,-46.27465246852293 -2.00390625,102,-44.18015676321404 -2.25390625,102,-41.696202093030415 -2.50390625,102,-38.81007279768844 -2.75390625,102,-35.47310721822009 -3.00390625,102,-31.587031255076493 -3.25390625,102,-26.977326393255943 -3.50390625,102,-21.33989787624732 -0.00390625,104,-14.14780410991879 -0.25390625,104,-4.62062454444272 -0.50390625,104,7.581979344153989 -0.75390625,104,20.505213218189724 -1.00390625,104,29.10630747911209 -1.25390625,104,27.410849497492627 -1.50390625,104,15.861709286478012 -1.75390625,104,2.7191161654098854 -2.00390625,104,-8.309928587238517 -2.25390625,104,-16.55082646385113 -2.50390625,104,-22.768844472745286 -2.75390625,104,-27.689654638391627 -3.00390625,104,-31.752228049970057 -3.25390625,104,-35.21357492989875 -3.50390625,104,-38.23203765048043 -0.00390625,106,-40.91101796530932 -0.25390625,106,-43.32154646329237 -0.50390625,106,-45.51450562769264 -0.75390625,106,-47.52763020133716 -1.00390625,106,-49.38972799316454 -1.25390625,106,-51.12334160290754 -1.50390625,106,-52.746493369450576 -1.75390625,106,-54.273868518438476 -2.00390625,106,-55.71764155302533 -2.25390625,106,-57.08806905515007 -2.50390625,106,-58.39392549605306 -2.75390625,106,-59.642831265179154 -3.00390625,106,-60.841505481127484 -3.25390625,106,-61.99596572530461 -3.50390625,106,-63.11169031507431 -0.00390625,108,-64.193754398205 -0.25390625,108,-65.24694863249627 -0.50390625,108,-66.27588727991923 -0.75390625,108,-67.28511206358229 -1.00390625,108,-68.2791975969759 -1.25390625,108,-69.26286488772388 -1.50390625,108,-70.24111053491411 -1.75390625,108,-71.21936167536039 -2.00390625,108,-72.2036703267894 -2.25390625,108,-73.20096749680523 -2.50390625,108,-74.21940765590298 -2.75390625,108,-75.2688526206642 -3.00390625,108,-76.36157518343963 -3.25390625,108,-77.51332147069678 -3.50390625,108,-78.74498304375572 -0.00390625,110,-80.08535819096821 -0.25390625,110,-81.5759852450388 -0.50390625,110,-83.28023318884365 -0.75390625,110,-85.30205004811296 -1.00390625,110,-87.82962474006901 -1.25390625,110,-91.25479453129535 -1.50390625,110,-96.54823966909305 -1.75390625,110,-48.690713326800136 -2.00390625,110,-48.49607457290017 -2.25390625,110,-47.91866377486158 -2.50390625,110,-46.975501951857666 -2.75390625,110,-45.687897203083196 -3.00390625,110,-44.07413055256501 -3.25390625,110,-42.14304289674462 -3.50390625,110,-39.88885678532767 -0.00390625,112,-37.28616967387086 -0.25390625,112,-34.28310274365623 -0.50390625,112,-30.789388251207136 -0.75390625,112,-26.653482877845974 -1.00390625,112,-21.616797627308802 -1.25390625,112,-15.226296033502729 -1.50390625,112,-6.741074364784632 -1.75390625,112,4.501424981178435 -2.00390625,112,17.454249444494614 -2.25390625,112,28.11674943237772 -2.50390625,112,28.65802753316117 -2.75390625,112,18.953752793227952 -3.00390625,112,6.062545535092059 -3.25390625,112,-5.579080289034611 -3.50390625,112,-14.494920882577356 -0.00390625,114,-21.218215833919103 -0.25390625,114,-26.52000216443126 -0.50390625,114,-30.893109209722816 -0.75390625,114,-34.62455666967 -1.00390625,114,-37.88858287712665 -1.25390625,114,-40.797448456062185 -1.50390625,114,-43.42744146358246 -1.75390625,114,-45.83274040302088 -2.00390625,114,-48.05323576319053 -2.25390625,114,-50.119187123188965 -2.50390625,114,-52.05413041208375 -2.75390625,114,-53.876768467747596 -3.00390625,114,-55.60224469091722 -3.25390625,114,-57.24302815526666 -3.50390625,114,-58.8095460199727 -0.00390625,116,-60.31064720645995 -0.25390625,116,-61.75395083492746 -0.50390625,116,-63.14611484117393 -0.75390625,116,-64.49304866427495 -1.00390625,116,-65.8000870792962 -1.25390625,116,-67.07213743059324 -1.50390625,116,-68.31381006140617 -1.75390625,116,-69.5295398041443 -2.00390625,116,-70.72370597234462 -2.25390625,116,-71.9007582786535 -2.50390625,116,-73.06535710462107 -2.75390625,116,-74.22253895295461 -3.00390625,116,-75.37792128665996 -3.25390625,116,-76.53796787780219 -3.50390625,116,-77.7103460712766 -0.00390625,118,-78.90442629122131 -0.25390625,118,-80.13200628262308 -0.50390625,118,-81.40840376518808 -0.75390625,118,-82.75417722455332 -1.00390625,118,-84.19797725297015 -1.25390625,118,-85.7815679012492 -1.50390625,118,-87.56937186662341 -1.75390625,118,-89.6685159561734 -2.00390625,118,-92.2771003747914 -2.25390625,118,-95.82626415051224 -2.50390625,118,-101.55231011618875 -2.75390625,118,-50.595876626384765 -3.00390625,118,-50.45503997697726 -3.25390625,118,-50.03408683413114 -3.50390625,118,-49.33705892063048 -0.00390625,120,-48.36875197461498 -0.25390625,120,-47.132247895386676 -0.50390625,120,-45.62605909567331 -0.75390625,120,-43.84100715399892 -1.00390625,120,-41.75660607128694 -1.25390625,120,-39.336278632016366 -1.50390625,120,-36.520097049297114 -1.75390625,120,-33.21257967010117 -2.00390625,120,-29.26067174250705 -2.25390625,120,-24.412095457020875 -2.50390625,120,-18.23764641222074 -2.75390625,120,-10.029485699756034 -3.00390625,120,1.012072188140099 -3.25390625,120,14.412934109301485 -3.50390625,120,26.62471456197136 -0.00390625,122,29.408433251810003 -0.25390625,122,21.887452299304382 -0.50390625,122,8.892960066473663 -0.75390625,122,-3.897111011638778 -1.00390625,122,-14.13327319385876 -1.25390625,122,-21.952661501528507 -1.50390625,122,-28.12260906687691 -1.75390625,122,-33.20647405037126 -2.00390625,122,-37.546176834244406 -2.25390625,122,-41.35227416572381 -2.50390625,122,-44.76110693478879 -2.75390625,122,-47.865443203420796 -3.00390625,122,-50.731162538659575 -3.25390625,122,-53.40677040471804 -3.50390625,122,-55.92908438422904 -0.00390625,124,-58.32677245591381 -0.25390625,124,-60.62262433329109 -0.50390625,124,-62.83503641511019 -0.75390625,124,-64.97897993173987 -1.00390625,124,-67.06660405000952 -1.25390625,124,-69.10755603768807 -1.50390625,124,-71.10905665562731 -1.75390625,124,-73.07574269350329 -2.00390625,124,-75.00927843577537 -2.25390625,124,-76.90775523845988 -2.50390625,124,-78.76496252187141 -2.75390625,124,-80.56975286439021 -3.00390625,124,-82.30594980590789 -3.25390625,124,-83.95348375401333 -3.50390625,124,-85.49144443914645 -0.00390625,126,-86.90312487518248 -0.25390625,126,-88.18181732714969 -0.50390625,126,-89.33492574553058 -0.75390625,126,-90.38429804956463 -1.00390625,126,-91.36280107386007 -1.25390625,126,-92.30929699409388 -1.50390625,126,-93.26457053647863 -1.75390625,126,-94.26961410099082 -2.00390625,126,-95.36645031873014 -2.25390625,126,-96.60117313579852 -2.50390625,126,-98.02902039256179 -2.75390625,126,-99.72115095669977 -3.00390625,126,-101.76909168158394 -3.25390625,126,-104.25568343069475 -3.50390625,126,-106.99706829873136 diff --git a/r/DESCRIPTION b/r/DESCRIPTION index c9206a0..0cf4cba 100644 --- a/r/DESCRIPTION +++ b/r/DESCRIPTION @@ -35,6 +35,7 @@ VignetteBuilder: knitr Config/testthat/edition: 3 Config/testthat/parallel: true Collate: + '00_init.R' 'rtemis.draw-package.R' 'rtemis_color_system.R' 'utils.R' diff --git a/r/R/00_init.R b/r/R/00_init.R new file mode 100644 index 0000000..c51a0b6 --- /dev/null +++ b/r/R/00_init.R @@ -0,0 +1,28 @@ +# 00_init.R +# Local property definitions that mirror upstream rtemis.core exports not yet +# on CRAN. Remove each entry here once the corresponding rtemis.core version +# is available on CRAN and declared in DESCRIPTION. + +# nonneg_integer_scalar / optional_nonneg_integer_scalar +# Added to rtemis.core >= 0.1.1; mirrors 00_S7_properties.R. +nonneg_integer_scalar <- S7::new_property( + S7::class_integer, + validator = function(value) { + if (length(value) != 1L || is.na(value) || value < 0L) { + return("must be a non-negative integer scalar (>= 0, e.g. 0L)") + } + NULL + } +) + +optional_nonneg_integer_scalar <- S7::new_property( + class = S7::new_union(NULL, S7::class_integer), + validator = function(value) { + if ( + !is.null(value) && (length(value) != 1L || is.na(value) || value < 0L) + ) { + return("must be NULL or a non-negative integer scalar (>= 0, e.g. 0L)") + } + NULL + } +) diff --git a/r/R/draw.R b/r/R/draw.R index 21becf0..cfdb174 100644 --- a/r/R/draw.R +++ b/r/R/draw.R @@ -2548,6 +2548,7 @@ draw_sankey <- function( height = NULL, filename = NULL ) { + rtemis.core::check_tabular(links) required_cols <- c("source", "target", "value") missing_cols <- setdiff(required_cols, names(links)) if (length(missing_cols) > 0L) { @@ -2556,22 +2557,26 @@ draw_sankey <- function( ) } - # Derive unique node names from source and target columns - node_names <- unique(c(links[["source"]], links[["target"]])) + # Coerce to character: factor columns with different levels produce an + # integer vector when concatenated with c(). + node_names <- unique( + c(as.character(links[["source"]]), as.character(links[["target"]])) + ) nodes <- lapply(node_names, function(n) list(name = n)) # Convert links data.frame rows to a list of named lists edge_list <- lapply(seq_len(nrow(links)), function(i) { list( - source = links[["source"]][[i]], - target = links[["target"]][[i]], + source = as.character(links[["source"]][[i]]), + target = as.character(links[["target"]][[i]]), value = links[["value"]][[i]] ) }) # ECharts ignores a palette longer than the node count; trim (or cycle) to - # exactly one entry per node. - palette <- rep_len(color %||% rtemis_colors, length(node_names)) + # exactly one entry per node. unname() prevents named vectors from + # serializing as a JSON object instead of an array. + palette <- unname(rep_len(color %||% rtemis_colors, length(node_names))) opt <- EChartsOption( title = if (!is.null(title)) Title(text = title, left = "center") else NULL, tooltip = Tooltip(trigger = "item"), diff --git a/r/R/series_sankey.R b/r/R/series_sankey.R index 04b94c4..dc8e0bf 100644 --- a/r/R/series_sankey.R +++ b/r/R/series_sankey.R @@ -37,7 +37,7 @@ SankeyNodeItem <- S7::new_class( properties = list( name = optional_character_scalar, value = numeric_or_null_property(), - depth = numeric_or_null_property(), + depth = optional_nonneg_integer_scalar, draggable = optional_logical_scalar, local_x = numeric_or_null_property(), local_y = numeric_or_null_property(), @@ -154,19 +154,7 @@ S7::method(to_list, SankeyEdgeItem) <- function(x, ...) { SankeyLevelOption <- S7::new_class( "SankeyLevelOption", properties = list( - depth = S7::new_property( - class = S7::class_any, - default = NULL, - validator = function(value) { - if (is.null(value)) { - return("depth is required and must be a non-negative integer") - } - if (is.numeric(value) && length(value) == 1L && value >= 0) { - return(NULL) - } - "must be a non-negative number" - } - ), + depth = nonneg_integer_scalar, label = class_or_null_property(LabelOption), item_style = class_or_null_property(ItemStyle), line_style = S7::new_property(class = S7::class_any, default = NULL) diff --git a/r/man/SankeyLevelOption.Rd b/r/man/SankeyLevelOption.Rd index c53c040..76c1162 100644 --- a/r/man/SankeyLevelOption.Rd +++ b/r/man/SankeyLevelOption.Rd @@ -5,7 +5,7 @@ \title{Sankey Level Option} \usage{ SankeyLevelOption( - depth = NULL, + depth = integer(0), label = NULL, item_style = NULL, line_style = NULL diff --git a/r/tests/testthat/test-series_sankey.R b/r/tests/testthat/test-series_sankey.R index c484763..519d06c 100644 --- a/r/tests/testthat/test-series_sankey.R +++ b/r/tests/testthat/test-series_sankey.R @@ -15,7 +15,7 @@ test_that("SankeyNodeItem to_list() converts names correctly", { n <- SankeyNodeItem( name = "A", value = 10, - depth = 1, + depth = 1L, draggable = FALSE, local_x = 0.1, local_y = 0.5 @@ -23,7 +23,7 @@ test_that("SankeyNodeItem to_list() converts names correctly", { out <- to_list(n) expect_equal(out[["name"]], "A") expect_equal(out[["value"]], 10) - expect_equal(out[["depth"]], 1) + expect_equal(out[["depth"]], 1L) expect_equal(out[["draggable"]], FALSE) expect_equal(out[["localX"]], 0.1) expect_equal(out[["localY"]], 0.5) @@ -50,6 +50,13 @@ test_that("SankeyNodeItem focus_node_adjacency validation", { expect_error(SankeyNodeItem(focus_node_adjacency = 1)) }) +test_that("SankeyNodeItem depth validates non-negative integer scalar", { + expect_equal(SankeyNodeItem(depth = 0L)@depth, 0L) + expect_equal(SankeyNodeItem(depth = 2L)@depth, 2L) + expect_error(SankeyNodeItem(depth = -1L)) + expect_error(SankeyNodeItem(depth = 1.5)) +}) + test_that("SankeyNodeItem NULL fields are dropped", { n <- SankeyNodeItem(name = "X") out <- to_list(n) @@ -101,10 +108,10 @@ test_that("SankeyEdgeItem accepts LineStyle S7 object for line_style", { # -- SankeyLevelOption ---------------------------------------------------------- test_that("SankeyLevelOption creates with depth", { - lv <- SankeyLevelOption(depth = 0) + lv <- SankeyLevelOption(depth = 0L) expect_true(S7::S7_inherits(lv, SankeyLevelOption)) out <- to_list(lv) - expect_equal(out[["depth"]], 0) + expect_equal(out[["depth"]], 0L) }) test_that("SankeyLevelOption depth is required", { @@ -115,13 +122,18 @@ test_that("SankeyLevelOption depth rejects negative", { expect_error(SankeyLevelOption(depth = -1)) }) +test_that("SankeyLevelOption depth rejects non-integer and negative", { + expect_error(SankeyLevelOption(depth = 1.5)) + expect_error(SankeyLevelOption(depth = -1L)) +}) + test_that("SankeyLevelOption to_list() includes item_style", { lv <- SankeyLevelOption( - depth = 1, + depth = 1L, item_style = ItemStyle(color = "#f00") ) out <- to_list(lv) - expect_equal(out[["depth"]], 1) + expect_equal(out[["depth"]], 1L) expect_equal(out[["itemStyle"]][["color"]], "#f00") }) @@ -166,12 +178,12 @@ test_that("SankeySeries data and links serialize as unnamed arrays", { test_that("SankeySeries data accepts SankeyNodeItem objects", { s <- SankeySeries( - data = list(SankeyNodeItem(name = "X", depth = 0)), + data = list(SankeyNodeItem(name = "X", depth = 0L)), links = list(list(source = "X", target = "Y", value = 1)) ) out <- to_list(s) expect_equal(out[["data"]][[1L]][["name"]], "X") - expect_equal(out[["data"]][[1L]][["depth"]], 0) + expect_equal(out[["data"]][[1L]][["depth"]], 0L) }) test_that("SankeySeries orient validation", { @@ -196,8 +208,8 @@ test_that("SankeySeries roam validation", { test_that("SankeySeries levels serialize as unnamed array", { s <- SankeySeries( levels = list( - SankeyLevelOption(depth = 0, item_style = ItemStyle(color = "#f00")), - SankeyLevelOption(depth = 1) + SankeyLevelOption(depth = 0L, item_style = ItemStyle(color = "#f00")), + SankeyLevelOption(depth = 1L) ) ) out <- to_list(s) @@ -247,11 +259,28 @@ test_that("draw_sankey derives nodes from links automatically", { expect_setequal(node_names, c("X", "Y", "Z")) }) +test_that("draw_sankey rejects non-tabular links", { + expect_error(draw_sankey(list(source = "A", target = "B", value = 1))) +}) + test_that("draw_sankey rejects links missing required columns", { bad <- data.frame(from = "A", to = "B", stringsAsFactors = FALSE) expect_error(draw_sankey(bad), regexp = "source") }) +test_that("draw_sankey handles factor source/target columns", { + links <- data.frame( + source = factor(c("A", "B"), levels = c("A", "B")), + target = factor(c("B", "C"), levels = c("B", "C")), + value = c(5, 3) + ) + w <- draw_sankey(links) + opt <- w[["x"]][["option"]] + node_names <- vapply(opt[["series"]][[1L]][["data"]], `[[`, "", "name") + expect_setequal(node_names, c("A", "B", "C")) + expect_type(node_names, "character") +}) + test_that("draw_sankey respects orient argument", { links <- data.frame( source = "A",