11
11
12
12
13
13
import math as m
14
- from random import random , gauss
14
+ from random import random , gauss , uniform
15
15
from copy import copy
16
16
17
17
from xled_plus .ledcolor import hsl_color
@@ -86,6 +86,29 @@ def getnext(self):
86
86
def getoccupancy (self ):
87
87
return self .occvec
88
88
89
+ def get_scene_bounds (self ):
90
+ bounds = self .ctr .get_layout_bounds ()
91
+ if bounds ["dim" ] == 3 :
92
+ if self .proj2D3D == "cylbase" :
93
+ fact = self .ctr .get_layout_bounds ()["radius" ] / self .ctr .get_layout_bounds ()["cylradius" ]
94
+ return [(fact * bounds ["bounds" ][0 ][0 ], fact * bounds ["bounds" ][0 ][1 ]),
95
+ (fact * bounds ["bounds" ][2 ][0 ], fact * bounds ["bounds" ][2 ][1 ])]
96
+ elif self .proj2D3D == "cylshell" :
97
+ hyp = ((m .pi * bounds ["cylradius" ]) ** 2 + 0.25 ) ** 0.5
98
+ xfact = m .pi * bounds ["cylradius" ] / hyp
99
+ yfact = 0.5 / hyp
100
+ return [(- xfact , xfact ), (- yfact , yfact )]
101
+ elif self .proj2D3D == "halfsphere" :
102
+ return [(- 1.0 , 1.0 ), (- 1.0 , 1.0 )]
103
+ else :
104
+ return bounds ["bounds" ]
105
+ elif bounds ["dim" ] == 2 :
106
+ fact = 1.0 / bounds ["radius" ]
107
+ return [(fact * bounds ["bounds" ][0 ][0 ], fact * bounds ["bounds" ][0 ][1 ]),
108
+ (fact * bounds ["bounds" ][1 ][0 ], fact * bounds ["bounds" ][1 ][1 ])]
109
+ else :
110
+ return bounds ["bounds" ]
111
+
89
112
90
113
class Shape (object ):
91
114
def __init__ (self , cent , angle ):
@@ -226,6 +249,7 @@ def is_inside(self, coord):
226
249
#
227
250
class Lineart2D (Shape ):
228
251
def __init__ (self ):
252
+ super (Lineart2D , self ).__init__ ((0.0 , 0.0 ), 0.0 )
229
253
self .points = []
230
254
self .lines = []
231
255
self .arcs = []
@@ -574,11 +598,27 @@ def __init__(self, char, pos, angle, size, color):
574
598
for seg in segs :
575
599
self .add_segment (* seg )
576
600
self .color = color
577
- self .off = [- pos [0 ], - pos [1 ]]
578
- ca = m .cos (angle * m .pi / 180.0 ) / size
579
- sa = m .sin (angle * m .pi / 180.0 ) / size
601
+ self .size = size
602
+ self .off = [0.0 , 0.0 ]
603
+ self .angle = 0.0
604
+ self .speed = (0.0 , 0.0 )
605
+ self .torque = 0.0
606
+ self .change_pos (pos )
607
+ self .change_angle (angle )
608
+
609
+ def change_angle (self , deltaangle ):
610
+ self .angle += deltaangle
611
+ ca = m .cos (self .angle * m .pi / 180.0 ) / self .size
612
+ sa = m .sin (self .angle * m .pi / 180.0 ) / self .size
580
613
self .mat = [[ca , sa ], [- sa , ca ]]
581
614
615
+ def change_pos (self , delta ):
616
+ self .off = [self .off [0 ] - delta [0 ], self .off [1 ] - delta [1 ]]
617
+
618
+ def update (self , step ):
619
+ self .change_pos ((step * self .speed [0 ], step * self .speed [1 ]))
620
+ self .change_angle (step * self .torque )
621
+
582
622
583
623
# Example scenes
584
624
@@ -719,6 +759,61 @@ def update(self, step):
719
759
self .time += step
720
760
721
761
762
+ class RunningText (MovingShapesScene ):
763
+ def __init__ (self , ctr , txt , color , linewidth = 0.1 , size = 0.6 , speed = 1.0 ):
764
+ super (RunningText , self ).__init__ (ctr )
765
+ self .textcolor = color
766
+ self .lw = linewidth
767
+ self .txt = txt
768
+ self .size = size
769
+ self .speed = speed
770
+ self .horizon = 0
771
+ self .proj2D3D = "cylshell"
772
+ self .place_text ()
773
+ self .preferred_frames = self .nsteps
774
+
775
+ def set_fps (self , fps ):
776
+ self .preferred_fps = fps
777
+ self .place_text ()
778
+ self .preferred_frames = self .nsteps
779
+
780
+ def place_text (self ):
781
+ bounds = self .get_scene_bounds ()
782
+ # Size is relative the total height of leds, convert to relative radius
783
+ size = self .size * (bounds [1 ][1 ] - bounds [1 ][0 ])
784
+ # Speed is in letter heights per second, convert to length per step
785
+ speed = size * self .speed / self .preferred_fps
786
+ self .currx = bounds [0 ][1 ]
787
+ self .endx = bounds [0 ][0 ]
788
+ self .liney = - size / 2.0
789
+ self .shapes = []
790
+ for ind , ch in enumerate (self .txt ):
791
+ if isinstance (self .textcolor , list ):
792
+ col = self .textcolor [ind % len (self .textcolor )]
793
+ elif callable (self .textcolor ):
794
+ col = self .textcolor (ind )
795
+ else :
796
+ col = self .textcolor
797
+ sh = Letter (ch , (0 , self .liney ), 0 , size , col )
798
+ sh .lw = self .lw
799
+ sh .off [0 ] = - self .currx + (sh .extent [0 ] - sh .lw * 0.5 ) * size
800
+ sh .set_speed (- speed , 0.0 )
801
+ wdt = max (0.4 , sh .extent [2 ] - sh .extent [0 ] + 2 * sh .lw )
802
+ self .currx += wdt * size
803
+ self .add_shape (sh )
804
+ self .nsteps = int (round ((self .currx - self .endx ) / speed + 0.5 ))
805
+ self .time = 0
806
+
807
+ def reset (self , numframes ):
808
+ if self .time != 0 :
809
+ self .place_text ()
810
+
811
+ def update (self , step ):
812
+ for sh in self .shapes :
813
+ sh .update (step )
814
+ self .time += step
815
+
816
+
722
817
class CaleidoScene (MovingShapesScene ):
723
818
def __init__ (self , ctr , sym ):
724
819
super (CaleidoScene , self ).__init__ (ctr )
@@ -766,15 +861,22 @@ def get_color(self, coord, ind):
766
861
767
862
768
863
class BouncingScene (MovingShapesScene ):
769
- def __init__ (self , ctr , num ):
864
+ def __init__ (self , ctr , num , speed = 0.25 , size = 0.3 , colorfunc = False ):
770
865
super (BouncingScene , self ).__init__ (ctr )
866
+ self .bounds = ctr .get_layout_bounds ()
867
+ self .size = size
868
+ self .speed = speed
869
+ self .colfunc = colorfunc or random_hsl_color_func (light = 0.0 )
771
870
for i in range (num ):
772
- self .create ()
871
+ self .ind = i
872
+ self .add_shape (self .create ())
773
873
774
874
def create (self ):
775
- cent = (gauss (0.0 , 0.2 ), gauss (0.0 , 0.2 ))
776
- shape = Blob (cent , 0.12 , random_hsl_color_func (light = 0.0 )())
777
- shape .speed = (gauss (0.0 , 0.1 ), gauss (0.0 , 0.1 ))
875
+ cent = tuple (uniform (* self .bounds ["bounds" ][d ]) for d in range (self .bounds ["dim" ]))
876
+ vec = tuple (gauss (0.0 , 0.1 ) for d in range (self .bounds ["dim" ]))
877
+ vlen = self .dot (vec , vec ) ** 0.5
878
+ shape = Blob (cent , self .size / 2.0 , self .colfunc (self .ind ))
879
+ shape .speed = tuple (ele / vlen * self .speed / self .preferred_fps for ele in vec )
778
880
return shape
779
881
780
882
def dot (self , v1 , v2 ):
@@ -786,13 +888,33 @@ def scale(self, v1, sc):
786
888
def add (self , v1 , v2 ):
787
889
return tuple (map (lambda x1 , x2 : x1 + x2 , v1 , v2 ))
788
890
891
+ def bounces (self , sh ):
892
+ rad = self .dot (sh .cent , sh .cent )
893
+ dir = self .dot (sh .cent , sh .speed )
894
+ if rad > 1.0 and dir > 0.0 :
895
+ return True
896
+ for d in range (self .bounds ["dim" ]):
897
+ c = sh .cent [d ] * self .bounds ["radius" ] + self .bounds ["center" ][d ]
898
+ if ((c < self .bounds ["bounds" ][d ][0 ] and sh .speed [d ] < 0.0 ) or
899
+ (c > self .bounds ["bounds" ][d ][1 ] and sh .speed [d ] > 0.0 )):
900
+ return True
901
+ if self .bounds ["dim" ] == 3 :
902
+ vec = (sh .cent [0 ], sh .cent [2 ])
903
+ sp = (sh .speed [0 ], sh .speed [2 ])
904
+ rad = self .dot (vec , vec ) ** 0.5
905
+ dir = self .dot (vec , sp )
906
+ if rad * self .bounds ["radius" ] > self .bounds ["cylradius" ] and dir > 0.0 :
907
+ return True
908
+ return False
909
+
789
910
def update (self , step ):
790
- # om utanför, vänd håll med slumpbrus
791
911
for sh in self .shapes :
792
- if self .dot (sh .cent , sh .cent ) > 1.0 and self .dot (sh .cent , sh .speed ) > 0.0 :
793
- delta = self .dot (sh .cent , sh .speed ) / self .dot (sh .cent , sh .cent )
794
- sh .speed = self .add (sh .speed , self .scale (sh .cent , - 2 * delta ))
795
- super (BouncingScene , self ).update (step )
912
+ if self .bounces (sh ):
913
+ vec = tuple (c + gauss (0.0 , 0.1 ) for c in sh .cent )
914
+ vlen2 = self .dot (vec , vec )
915
+ delta = self .dot (vec , sh .speed ) / vlen2
916
+ sh .speed = self .add (sh .speed , self .scale (vec , - 2 * delta ))
917
+ sh .update (step )
796
918
797
919
798
920
# ---------------------------------------------------------
0 commit comments