1+ # VGA simple plotter by Hugh - May 2025
2+ from VGA .VGA_fonts import Font
3+
4+ # 3 bit color names
5+ RED = 0b001
6+ GREEN = 0b010
7+ BLUE = 0b100
8+ YELLOW = 0b011
9+ BLACK = 0
10+ WHITE = 0b111
11+ CYAN = 0b110
12+ MAGENTA = 0b101
13+
14+
15+ class Plot :
16+ """A two dimensionnal plot object for the VGA 800x600 display.
17+
18+ Note: All coordinates are zero based.
19+ """
20+
21+ def __init__ (self , display , ul_x = 1 , ul_y = 1 , br_x = 400 , br_y = 300 ,ax_color = WHITE , min_x = 0 ,\
22+ max_x = 100 , min_y = 0 , max_y = 100 , x_ticks_num = 10 ,y_ticks_num = 10 ):
23+ """Initialize plot.
24+
25+ Args:
26+ display : the VGA screen object
27+ ul_x,y : upper left coordinates of the rectangle plotting area (including ticks)
28+ br_x,y : bottom righy coordinates of the rectangle plotting area (including ticks)
29+ ax_color : color of the axes and ticks
30+ plot_color : color of the plot
31+ min_max_x_y : min/max values of the x and y axes
32+ """
33+ #set parameters
34+ self .display = display
35+ self .ul_x = ul_x
36+ self .ul_y = ul_y
37+ self .br_x = br_x
38+ self .br_y = br_y
39+ self .ax_color = ax_color
40+ self .min_x = min_x
41+ self .max_x = max_x
42+ self .min_y = min_y
43+ self .max_y = max_y
44+ self .x_ticks_num = x_ticks_num
45+ self .y_ticks_num = y_ticks_num
46+ # Load a small font
47+ self .x_font_size = 8
48+ self .y_font_size = 10
49+ self .display .font = Font ('fonts/Small_Fonts8x10.c' , self .x_font_size , self .y_font_size ,char_spacing = 0 , line_spacing = 1 )
50+ # Define and draw plotting area (taking out space for the ticks and labels)
51+ self .tick_size = 3
52+ self .top_margin = int (self .y_font_size / 2 )+ 1
53+ self .bottom_margin = 12
54+ self .left_margin = 20
55+ self .x_origin = self .ul_x + self .left_margin
56+ self .x_range = self .br_x - self .x_origin
57+ self .y_origin = self .br_y - self .bottom_margin
58+ self .y_range = self .y_origin - self .top_margin - self .ul_y
59+ self .display .draw_rect (self .ul_x + self .left_margin , self .ul_y + self .top_margin , self .br_x , self .br_y - self .bottom_margin ,self .ax_color )
60+ self .display .settextcolor (self .ax_color )
61+ # prepare for legend
62+ self .line_labels = []
63+ self .line_colors = []
64+ #draw axes
65+ self .draw_axes_x ()
66+ self .draw_axes_y ()
67+
68+ def draw_axes_x (self ):
69+ # draw ticks
70+ for i in range (self .x_ticks_num + 1 ):
71+ x_tick = self .ul_x + self .left_margin + int (i * self .x_range / self .x_ticks_num )
72+ self .display .draw_fastVline (x_tick ,self .br_y - self .bottom_margin - self .tick_size ,self .br_y - self .bottom_margin ,self .ax_color )
73+ x_tick_val = str (round (self .min_x + (i * (self .max_x - self .min_x )/ self .x_ticks_num ),2 ))
74+ self .display .settextcursor (x_tick - int (self .display .strlen (x_tick_val )/ 2 ), self .br_y - self .bottom_margin + 1 )
75+ self .display .printh (x_tick_val )
76+
77+ def draw_axes_y (self ):
78+ # draw ticks
79+ for i in range (self .y_ticks_num + 1 ):
80+ y_tick = self .br_y - self .bottom_margin - int (i * self .y_range / self .y_ticks_num )
81+ self .display .draw_fastHline (self .ul_x + self .left_margin ,self .ul_x + self .left_margin + self .tick_size ,y_tick ,self .ax_color )
82+ self .display .settextcursor (self .ul_x , y_tick - int (self .y_font_size / 2 ))
83+ y_tick_val = round (self .min_y + (i * (self .max_y - self .min_y )/ self .y_ticks_num ),2 )
84+ self .display .printh (str (y_tick_val ))
85+
86+ def draw_labels (self ):
87+ x1_line = self .x_origin + 10
88+ for i in range (len (self .line_labels )):
89+ y1_line = self .top_margin + self .ul_y + int (self .y_font_size / 2 )+ i * self .y_font_size + 1
90+ self .display .draw_fastHline (x1_line ,x1_line + 10 ,y1_line ,self .line_colors [i ])
91+ self .display .settextcursor (x1_line + 10 + 2 , y1_line - int (self .y_font_size / 2 ))
92+ self .display .settextcolor (self .line_colors [i ])
93+ self .display .printh (self .line_labels [i ])
94+
95+
96+ class Line :
97+ """An x.y graph object (line) for the VGA 800x600 display.
98+
99+ Note: All coordinates are zero based.
100+ """
101+
102+ def __init__ (self , plot , x_data , y_data , line_color = GREEN ,label = "unknown" ):
103+ """Initialize line.
104+
105+ Args:
106+ plot : the VGA 800x600 plot object
107+ x_data : list of x values
108+ y_data : list of y value
109+ plot_color : color of the line
110+ """
111+ #set parameters
112+ self .plot = plot
113+ self .x_data = x_data
114+ self .y_data = y_data
115+ self .x_pixdata = []
116+ self .y_pixdata = []
117+ self .color = line_color
118+ self .plot .line_labels .append (label )
119+ self .plot .line_colors .append (self .color )
120+ self .plot .draw_labels ()
121+ self .is_first_point = True
122+ #draw line
123+ if len (self .x_data ):
124+ self .draw ()
125+
126+ def draw (self ):
127+ for i in range (len (self .x_data )):
128+ plotx = self .plot .x_origin + int ((self .x_data [i ]* self .plot .x_range / (self .plot .max_x - self .plot .min_x )))
129+ ploty = self .plot .y_origin - int ((self .y_data [i ]* self .plot .y_range / (self .plot .max_y - self .plot .min_y )))
130+ if i > 0 :
131+ self .plot .display .draw_line (self .x_pixdata [- 1 ],self .y_pixdata [- 1 ],plotx ,ploty ,self .color )
132+ else :
133+ self .plot .display .draw_pix (plotx ,ploty ,self .color )
134+ self .x_pixdata .append (plotx )
135+ self .y_pixdata .append (ploty )
136+
137+ def add_data (self ,x ,y ):
138+ if not len (self .x_data ):
139+ plotx = self .plot .x_origin + int ((x * self .plot .x_range / (self .plot .max_x - self .plot .min_x )))
140+ ploty = self .plot .y_origin - int ((y * self .plot .y_range / (self .plot .max_y - self .plot .min_y )))
141+ self .plot .display .draw_pix (plotx ,ploty ,self .color )
142+ self .x_data .append (x )
143+ self .y_data .append (y )
144+ self .x_pixdata .append (plotx )
145+ self .y_pixdata .append (ploty )
146+ else :
147+ plotx = self .plot .x_origin + int ((x * self .plot .x_range / (self .plot .max_x - self .plot .min_x )))
148+ ploty = self .plot .y_origin - int ((y * self .plot .y_range / (self .plot .max_y - self .plot .min_y )))
149+ self .plot .display .draw_line (self .x_pixdata [- 1 ],self .y_pixdata [- 1 ],plotx ,ploty ,self .color )
150+ self .x_pixdata .append (plotx )
151+ self .y_pixdata .append (ploty )
152+ self .x_data .append (x )
153+ self .y_data .append (y )
154+
155+ def add_data_ns (self ,x ,y ,draw_line = True ):
156+ # add_dat without storage (saves memory)
157+ if self .is_first_point :
158+ self .plotx0 = self .plot .x_origin + int ((x * self .plot .x_range / (self .plot .max_x - self .plot .min_x )))
159+ self .ploty0 = self .plot .y_origin - int ((y * self .plot .y_range / (self .plot .max_y - self .plot .min_y )))
160+ self .plot .display .draw_pix (self .plotx0 ,self .ploty0 ,self .color )
161+ self .is_first_point = False
162+ else :
163+ plotx = self .plot .x_origin + int ((x * self .plot .x_range / (self .plot .max_x - self .plot .min_x )))
164+ ploty = self .plot .y_origin - int ((y * self .plot .y_range / (self .plot .max_y - self .plot .min_y )))
165+ if draw_line :
166+ self .plot .display .draw_line (self .plotx0 ,self .ploty0 ,plotx ,ploty ,self .color )
167+ self .plotx0 = plotx
168+ self .ploty0 = ploty
169+ else :
170+ self .plot .display .draw_pix (plotx ,ploty ,self .color )
171+
172+ def add_data_moy (self ,x ,y ,num_samples = 5 ):
173+ # add_dat with mean calc over the last num_samples values
174+ # we only store the last num_samples value
175+ if not len (self .y_data ):
176+ plotx = self .plot .x_origin + int ((x * self .plot .x_range / (self .plot .max_x - self .plot .min_x )))
177+ ploty = self .plot .y_origin - int ((y * self .plot .y_range / (self .plot .max_y - self .plot .min_y )))
178+ self .plot .display .draw_pix (plotx ,ploty ,self .color )
179+ for i in range (num_samples ):
180+ self .y_data .append (y )
181+ self .x_pixdata .append (plotx )
182+ self .y_pixdata .append (ploty )
183+ else :
184+ y_last_sum = 0
185+ for val in self .y_data :
186+ y_last_sum += val
187+ y_mean = (y + y_last_sum )/ (num_samples + 1 )
188+ y = y_mean
189+ plotx = self .plot .x_origin + int ((x * self .plot .x_range / (self .plot .max_x - self .plot .min_x )))
190+ ploty = self .plot .y_origin - int ((y * self .plot .y_range / (self .plot .max_y - self .plot .min_y )))
191+ self .plot .display .draw_line (self .x_pixdata [- 1 ],self .y_pixdata [- 1 ],plotx ,ploty ,self .color )
192+ self .x_pixdata .pop (0 )
193+ self .x_pixdata .append (plotx )
194+ self .y_pixdata .pop (0 )
195+ self .y_pixdata .append (ploty )
196+ self .y_data .pop (0 )
197+ self .y_data .append (y )
198+
0 commit comments