3333
3434try :
3535 import bitmaptools
36- except NameError :
36+ except ImportError :
3737 pass
38+
3839try :
3940 from typing import Tuple
4041except ImportError :
@@ -73,6 +74,7 @@ class Cartesian(Widget):
7374 :param int nudge_y: movement in pixels in the y direction to move the origin.
7475 Defaults to 0
7576
77+ :param bool verbose: print debugging information in some internal functions. Default to False
7678
7779 **Quickstart: Importing and using Cartesian**
7880
@@ -180,11 +182,14 @@ def __init__(
180182 subticks : bool = False ,
181183 nudge_x : int = 0 ,
182184 nudge_y : int = 0 ,
185+ verbose : bool = False ,
183186 ** kwargs ,
184187 ) -> None :
185188
186189 super ().__init__ (** kwargs )
187190
191+ self ._verbose = verbose
192+
188193 self ._background_color = background_color
189194
190195 self ._axes_line_color = axes_color
@@ -249,8 +254,8 @@ def __init__(
249254 self ._axesy_bitmap = displayio .Bitmap (self ._axesy_width , self .height , 4 )
250255 self ._axesy_bitmap .fill (0 )
251256
252- self ._screen_bitmap = displayio .Bitmap (self .width , self .height , 5 )
253- self ._screen_bitmap . fill ( 5 )
257+ self ._plot_bitmap = displayio .Bitmap (self .width , self .height , 5 )
258+ self .clear_plot_lines ( )
254259 self ._screen_palette = displayio .Palette (6 )
255260 self ._screen_palette .make_transparent (0 )
256261 self ._screen_palette [1 ] = self ._tick_color
@@ -292,7 +297,7 @@ def __init__(
292297 )
293298
294299 self ._screen_tilegrid = displayio .TileGrid (
295- self ._screen_bitmap ,
300+ self ._plot_bitmap ,
296301 pixel_shader = self ._screen_palette ,
297302 x = 0 ,
298303 y = 0 ,
@@ -309,11 +314,8 @@ def __init__(
309314 self .append (self ._screen_tilegrid )
310315 self .append (self ._corner_tilegrid )
311316
312- self ._update_line = True
313-
314317 self ._pointer = None
315318 self ._circle_palette = None
316- self ._pointer_vector_shape = None
317319 self .plot_line_point = None
318320
319321 @staticmethod
@@ -389,10 +391,12 @@ def _draw_ticks(self) -> None:
389391
390392 if self ._subticks :
391393 if i in subticks :
394+ # calc subtick_line_height; force min lineheigt to 1.
395+ subtick_line_height = max (1 , self ._tick_line_height // 2 )
392396 rectangle_helper (
393397 text_dist ,
394398 self ._axes_line_thickness ,
395- self . _tick_line_height // 2 ,
399+ subtick_line_height ,
396400 1 ,
397401 self ._axesx_bitmap ,
398402 1 ,
@@ -448,18 +452,138 @@ def _draw_ticks(self) -> None:
448452 )
449453
450454 def _draw_pointers (self , x : int , y : int ) -> None :
451- self ._pointer = vectorio .Circle (self ._pointer_radius )
452- self ._circle_palette = displayio .Palette (2 )
453- self ._circle_palette .make_transparent (0 )
454- self ._circle_palette [1 ] = self ._pointer_color
455-
456- self ._pointer_vector_shape = vectorio .VectorShape (
457- shape = self ._pointer ,
458- pixel_shader = self ._circle_palette ,
459- x = x ,
460- y = y ,
455+
456+ self ._circle_palette = displayio .Palette (1 )
457+ self ._circle_palette [0 ] = self ._pointer_color
458+ self ._pointer = vectorio .Circle (
459+ radius = self ._pointer_radius , x = x , y = y , pixel_shader = self ._circle_palette
461460 )
462- self .append (self ._pointer_vector_shape )
461+
462+ self .append (self ._pointer )
463+
464+ def _calc_local_xy (self , x : int , y : int ) -> Tuple [int , int ]:
465+ local_x = (
466+ int ((x - self ._xrange [0 ]) * self ._factorx * self ._valuex ) + self ._nudge_x
467+ )
468+ # details on `+ (self.height - 1)` :
469+ # the bitmap is set to self.width & self.height
470+ # but we are only allowed to draw to pixels 0..height-1 and 0..width-1
471+ local_y = (
472+ int ((self ._yrange [0 ] - y ) * self ._factory * self ._valuey )
473+ + (self .height - 1 )
474+ + self ._nudge_y
475+ )
476+ return (local_x , local_y )
477+
478+ def _check_local_x_in_range (self , local_x ):
479+ return 0 <= local_x < self .width
480+
481+ def _check_local_y_in_range (self , local_y ):
482+ return 0 <= local_y < self .height
483+
484+ def _check_local_xy_in_range (self , local_x , local_y ):
485+ return self ._check_local_x_in_range (local_x ) and self ._check_local_y_in_range (
486+ local_y
487+ )
488+
489+ def _check_x_in_range (self , x ):
490+ return self ._xrange [0 ] <= x <= self ._xrange [1 ]
491+
492+ def _check_y_in_range (self , y ):
493+ return self ._yrange [0 ] <= y <= self ._yrange [1 ]
494+
495+ def _check_xy_in_range (self , x , y ):
496+ return self ._check_x_in_range (x ) and self ._check_y_in_range (y )
497+
498+ def _add_point (self , x : int , y : int ) -> None :
499+ """_add_point function
500+ helper function to add a point to the graph in the plane
501+ :param int x: ``x`` coordinate in the local plane
502+ :param int y: ``y`` coordinate in the local plane
503+ :return: None
504+ rtype: None
505+ """
506+ local_x , local_y = self ._calc_local_xy (x , y )
507+ if self ._verbose :
508+ print ("" )
509+ print (
510+ "xy: ({: >4}, {: >4}) "
511+ "_xrange: ({: >4}, {: >4}) "
512+ "_yrange: ({: >4}, {: >4}) "
513+ "" .format (
514+ x ,
515+ y ,
516+ self ._xrange [0 ],
517+ self ._xrange [1 ],
518+ self ._yrange [0 ],
519+ self ._yrange [1 ],
520+ )
521+ )
522+ print (
523+ "local_*: ({: >4}, {: >4}) "
524+ " width: ({: >4}, {: >4}) "
525+ " height: ({: >4}, {: >4}) "
526+ "" .format (
527+ local_x ,
528+ local_y ,
529+ 0 ,
530+ self .width ,
531+ 0 ,
532+ self .height ,
533+ )
534+ )
535+ if self ._check_xy_in_range (x , y ):
536+ if self ._check_local_xy_in_range (local_x , local_y ):
537+ if self .plot_line_point is None :
538+ self .plot_line_point = []
539+ self .plot_line_point .append ((local_x , local_y ))
540+ else :
541+ # for better error messages we check in detail what failed...
542+ # this should never happen:
543+ # we already checked the range of the input values.
544+ # but in case our calculation is wrong we handle this case to..
545+ if not self ._check_local_x_in_range (local_x ):
546+ raise ValueError (
547+ "local_x out of range: "
548+ "local_x:{: >4}; _xrange({: >4}, {: >4})"
549+ "" .format (
550+ local_x ,
551+ 0 ,
552+ self .width ,
553+ )
554+ )
555+ if not self ._check_local_y_in_range (local_y ):
556+ raise ValueError (
557+ "local_y out of range: "
558+ "local_y:{: >4}; _yrange({: >4}, {: >4})"
559+ "" .format (
560+ local_y ,
561+ 0 ,
562+ self .height ,
563+ )
564+ )
565+ else :
566+ # for better error messages we check in detail what failed...
567+ if not self ._check_x_in_range (x ):
568+ raise ValueError (
569+ "x out of range: "
570+ "x:{: >4}; xrange({: >4}, {: >4})"
571+ "" .format (
572+ x ,
573+ self ._xrange [0 ],
574+ self ._xrange [1 ],
575+ )
576+ )
577+ if not self ._check_y_in_range (y ):
578+ raise ValueError (
579+ "y out of range: "
580+ "y:{: >4}; yrange({: >4}, {: >4})"
581+ "" .format (
582+ y ,
583+ self ._yrange [0 ],
584+ self ._yrange [1 ],
585+ )
586+ )
463587
464588 def update_pointer (self , x : int , y : int ) -> None :
465589 """updater_pointer function
@@ -469,46 +593,49 @@ def update_pointer(self, x: int, y: int) -> None:
469593 :return: None
470594 rtype: None
471595 """
472- local_x = int ((x - self ._xrange [0 ]) * self ._factorx ) + self ._nudge_x
473- local_y = (
474- int ((self ._yrange [0 ] - y ) * self ._factory ) + self .height + self ._nudge_y
475- )
596+ self ._add_point (x , y )
597+ if not self ._pointer :
598+ self ._draw_pointers (
599+ self .plot_line_point [- 1 ][0 ],
600+ self .plot_line_point [- 1 ][1 ],
601+ )
602+ else :
603+ self ._pointer .x = self .plot_line_point [- 1 ][0 ]
604+ self ._pointer .y = self .plot_line_point [- 1 ][1 ]
476605
477- if local_x >= 0 or local_y <= 100 :
478- if self ._update_line :
479- self ._draw_pointers (local_x , local_y )
480- self ._update_line = False
481- else :
482- self ._pointer_vector_shape .x = local_x
483- self ._pointer_vector_shape .y = local_y
606+ def add_plot_line (self , x : int , y : int ) -> None :
607+ """add_plot_line function.
484608
485- def _set_plotter_line ( self ) -> None :
486- self . plot_line_point = list ()
609+ add line to the plane.
610+ multiple calls create a line-plot graph.
487611
488- def update_line (self , x : int , y : int ) -> None :
489- """updater_line function
490- helper function to update pointer in the plane
491612 :param int x: ``x`` coordinate in the local plane
492613 :param int y: ``y`` coordinate in the local plane
493614 :return: None
615+
494616 rtype: None
495617 """
496- local_x = int ((x - self ._xrange [0 ]) * self ._factorx ) + self ._nudge_x
497- local_y = (
498- int ((self ._yrange [0 ] - y ) * self ._factory ) + self .height + self ._nudge_y
499- )
500- if x < self ._xrange [1 ] and y < self ._yrange [1 ]:
501- if local_x > 0 or local_y < 100 :
502- if self ._update_line :
503- self ._set_plotter_line ()
504- self .plot_line_point .append ((local_x , local_y ))
505- self ._update_line = False
506- else :
507- bitmaptools .draw_line (
508- self ._screen_bitmap ,
509- self .plot_line_point [- 1 ][0 ],
510- self .plot_line_point [- 1 ][1 ],
511- local_x ,
512- local_y ,
513- 1 ,
514- )
618+ self ._add_point (x , y )
619+ if len (self .plot_line_point ) > 1 :
620+ bitmaptools .draw_line (
621+ self ._plot_bitmap ,
622+ self .plot_line_point [- 2 ][0 ],
623+ self .plot_line_point [- 2 ][1 ],
624+ self .plot_line_point [- 1 ][0 ],
625+ self .plot_line_point [- 1 ][1 ],
626+ 1 ,
627+ )
628+
629+ def clear_plot_lines (self , palette_index = 5 ):
630+ """clear_plot_lines function.
631+
632+ clear all added lines
633+ (clear line-plot graph)
634+
635+ :param int palette_index: color palett index. Defaults to 5
636+ :return: None
637+
638+ rtype: None
639+ """
640+ self .plot_line_point = None
641+ self ._plot_bitmap .fill (palette_index )
0 commit comments