@@ -48,6 +48,13 @@ struct CurveBounds {
4848 y_max : f64 ,
4949}
5050
51+ #[ derive( Debug , Clone , Copy ) ]
52+ struct PlotTransform {
53+ scale : f64 ,
54+ x_center : f64 ,
55+ y_center : f64 ,
56+ }
57+
5158struct PlotContext < ' a > {
5259 values : & ' a HashMap < String , f64 > ,
5360}
@@ -145,6 +152,9 @@ fn function_plot(
145152 /// Auto close
146153 #[ default( true ) ]
147154 auto_close : bool ,
155+ /// Plot Axes
156+ #[ default( true ) ]
157+ plot_axes : bool ,
148158) -> List < Vector > {
149159 let mut plots = List :: new ( ) ;
150160 let ( x_node, y_node, variable_name) = match parse_plot_expressions ( & x_expression, & y_expression) {
@@ -157,10 +167,12 @@ fn function_plot(
157167 None => return plots,
158168 } ;
159169
170+ let plot_transform = calculate_plot_transform ( & bounds, width, height) ;
171+
160172 let segments = detect_curve_segments ( sample_points, discontinuity_sensitivity) ;
161173
162174 for mut segment_anchors in segments {
163- fit_plot_to_bounds ( & mut segment_anchors, & bounds , width , height ) ;
175+ fit_plot_to_bounds ( & mut segment_anchors, & plot_transform ) ;
164176
165177 let mut closed = false ;
166178
@@ -182,6 +194,19 @@ fn function_plot(
182194 }
183195 }
184196
197+ if plot_axes {
198+ let axes = build_plot_axes ( & bounds, & plot_transform) ;
199+ for axis in axes {
200+ let shape = Vector :: from_subpath ( subpath:: Subpath :: from_anchors ( axis, false ) ) ;
201+
202+ if plots. is_empty ( ) {
203+ plots = List :: new_from_element ( shape) ;
204+ } else {
205+ plots. push ( Item :: new_from_element ( shape) ) ;
206+ }
207+ }
208+ }
209+
185210 plots
186211}
187212
@@ -228,6 +253,7 @@ fn sample_interval(
228253 max_depth : u32 ,
229254) {
230255 if current_depth >= max_depth {
256+ update_bounds ( & begin_point, bounds) ;
231257 sample_points. push ( begin_point) ;
232258 return ;
233259 }
@@ -238,9 +264,6 @@ fn sample_interval(
238264 let Some ( point_mid) = evaluate_expressions ( t_mid, x_node, y_node, variable_name) else {
239265 return ;
240266 } ;
241- if let SamplePoint :: Valid { .. } = point_mid {
242- update_bounds ( & point_mid, bounds) ;
243- }
244267
245268 if current_depth > min_depth && begin_point. is_valid ( ) && end_point. is_valid ( ) {
246269 let t_quarter = t_begin + ( t_end - t_begin) * 0.25 ;
@@ -254,14 +277,6 @@ fn sample_interval(
254277 return ;
255278 } ;
256279
257- if let SamplePoint :: Valid { .. } = point_q1 {
258- update_bounds ( & point_q1, bounds) ;
259- }
260-
261- if let SamplePoint :: Valid { .. } = point_q3 {
262- update_bounds ( & point_q3, bounds) ;
263- }
264-
265280 let Some ( begin_coord) = begin_point. coord ( ) else { return } ;
266281 let Some ( end_coord) = end_point. coord ( ) else { return } ;
267282
@@ -285,6 +300,7 @@ fn sample_interval(
285300 }
286301
287302 if max_error < tolerance {
303+ update_bounds ( & begin_point, bounds) ;
288304 sample_points. push ( begin_point) ;
289305 return ;
290306 }
@@ -463,7 +479,7 @@ fn extrapolate_limit(p_coord: DVec2, p1_coord: DVec2, p2_coord: DVec2) -> f64 {
463479 }
464480}
465481
466- fn fit_plot_to_bounds ( anchor_positions : & mut [ DVec2 ] , bounds : & CurveBounds , width : f64 , height : f64 ) {
482+ fn calculate_plot_transform ( bounds : & CurveBounds , width : f64 , height : f64 ) -> PlotTransform {
467483 let x_scale = width / ( bounds. x_max - bounds. x_min ) . max ( f64:: EPSILON ) ;
468484 let y_scale = height / ( bounds. y_max - bounds. y_min ) . max ( f64:: EPSILON ) ;
469485
@@ -472,12 +488,37 @@ fn fit_plot_to_bounds(anchor_positions: &mut [DVec2], bounds: &CurveBounds, widt
472488 let x_center = ( bounds. x_min + bounds. x_max ) / 2. ;
473489 let y_center = ( bounds. y_min + bounds. y_max ) / 2. ;
474490
491+ PlotTransform { scale, x_center, y_center }
492+ }
493+
494+ fn fit_plot_to_bounds ( anchor_positions : & mut [ DVec2 ] , plot_transform : & PlotTransform ) {
475495 for anchor_position in anchor_positions {
476- anchor_position. x = ( anchor_position. x - x_center) * scale;
477- anchor_position. y = ( y_center - anchor_position. y ) * scale;
496+ * anchor_position = fit_point_to_bounds ( * anchor_position, plot_transform) ;
478497 }
479498}
480499
500+ fn fit_point_to_bounds ( point : DVec2 , plot_transform : & PlotTransform ) -> DVec2 {
501+ DVec2 :: new ( ( point. x - plot_transform. x_center ) * plot_transform. scale , ( plot_transform. y_center - point. y ) * plot_transform. scale )
502+ }
503+
504+ fn build_plot_axes ( bounds : & CurveBounds , plot_transform : & PlotTransform ) -> Vec < CurveSegment > {
505+ let axis_x = if bounds. x_min <= 0. && bounds. x_max >= 0. { 0. } else { ( bounds. x_min + bounds. x_max ) / 2. } ;
506+
507+ let axis_y = if bounds. y_min <= 0. && bounds. y_max >= 0. { 0. } else { ( bounds. y_min + bounds. y_max ) / 2. } ;
508+
509+ let horizontal = vec ! [
510+ fit_point_to_bounds( DVec2 :: new( bounds. x_min, axis_y) , plot_transform) ,
511+ fit_point_to_bounds( DVec2 :: new( bounds. x_max, axis_y) , plot_transform) ,
512+ ] ;
513+
514+ let vertical = vec ! [
515+ fit_point_to_bounds( DVec2 :: new( axis_x, bounds. y_min) , plot_transform) ,
516+ fit_point_to_bounds( DVec2 :: new( axis_x, bounds. y_max) , plot_transform) ,
517+ ] ;
518+
519+ vec ! [ horizontal, vertical]
520+ }
521+
481522fn parse_plot_expressions ( x_expression : & str , y_expression : & str ) -> Option < ( ast:: Node , ast:: Node , String ) > {
482523 let ( x_node, x_vars) = parse_expression ( x_expression) ?;
483524 let ( y_node, y_vars) = parse_expression ( y_expression) ?;
0 commit comments