@@ -2,6 +2,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
22use crate :: messages:: prelude:: * ;
33use crate :: messages:: tool:: common_functionality:: graph_modification_utils:: get_text;
44use crate :: messages:: tool:: tool_messages:: path_tool:: PathOverlayMode ;
5+ use bezier_rs:: Bezier ;
56use glam:: DVec2 ;
67use graphene_core:: renderer:: Quad ;
78use graphene_core:: text:: { FontCache , load_face} ;
@@ -196,3 +197,99 @@ pub fn is_visible_point(
196197 }
197198 }
198199}
200+
201+ /// Calculates similarity metric between new bezier curve and two old beziers by using sampled points.
202+ #[ allow( clippy:: too_many_arguments) ]
203+ pub fn log_optimization ( a : f64 , b : f64 , p1 : DVec2 , p3 : DVec2 , d1 : DVec2 , d2 : DVec2 , points1 : & [ DVec2 ] , n : usize ) -> f64 {
204+ let start_handle_length = a. exp ( ) ;
205+ let end_handle_length = b. exp ( ) ;
206+
207+ // Compute the handle positions of new bezier curve
208+ let c1 = p1 + d1 * start_handle_length;
209+ let c2 = p3 + d2 * end_handle_length;
210+
211+ let new_curve = Bezier :: from_cubic_coordinates ( p1. x , p1. y , c1. x , c1. y , c2. x , c2. y , p3. x , p3. y ) ;
212+
213+ // Sample 2*n points from new curve and get the L2 metric between all of points
214+ let points = new_curve. compute_lookup_table ( Some ( 2 * n) , None ) . collect :: < Vec < _ > > ( ) ;
215+
216+ let dist = points1. iter ( ) . zip ( points. iter ( ) ) . map ( |( p1, p2) | ( p1. x - p2. x ) . powi ( 2 ) + ( p1. y - p2. y ) . powi ( 2 ) ) . sum :: < f64 > ( ) ;
217+
218+ dist / ( 2 * n) as f64
219+ }
220+
221+ /// Calculates optimal handle lengths with adam optimization.
222+ #[ allow( clippy:: too_many_arguments) ]
223+ pub fn find_two_param_best_approximate ( p1 : DVec2 , p3 : DVec2 , d1 : DVec2 , d2 : DVec2 , min_len1 : f64 , min_len2 : f64 , farther_segment : Bezier , other_segment : Bezier ) -> ( DVec2 , DVec2 ) {
224+ let h = 1e-6 ;
225+ let tol = 1e-6 ;
226+ let max_iter = 200 ;
227+
228+ let mut a = ( 5_f64 ) . ln ( ) ;
229+ let mut b = ( 5_f64 ) . ln ( ) ;
230+
231+ let mut m_a = 0. ;
232+ let mut v_a = 0. ;
233+ let mut m_b = 0. ;
234+ let mut v_b = 0. ;
235+
236+ let initial_alpha = 0.05 ;
237+ let decay_rate: f64 = 0.99 ;
238+
239+ let beta1 = 0.9 ;
240+ let beta2 = 0.999 ;
241+ let epsilon = 1e-8 ;
242+
243+ let n = 20 ;
244+
245+ let farther_segment = if farther_segment. start . distance ( p1) >= f64:: EPSILON {
246+ farther_segment. reverse ( )
247+ } else {
248+ farther_segment
249+ } ;
250+
251+ let other_segment = if other_segment. end . distance ( p3) >= f64:: EPSILON { other_segment. reverse ( ) } else { other_segment } ;
252+
253+ // Now we sample points proportional to the lengths of the beziers
254+ let l1 = farther_segment. length ( None ) ;
255+ let l2 = other_segment. length ( None ) ;
256+ let ratio = l1 / ( l1 + l2) ;
257+ let n_points1 = ( ( 2 * n) as f64 * ratio) . floor ( ) as usize ;
258+ let mut points1 = farther_segment. compute_lookup_table ( Some ( n_points1) , None ) . collect :: < Vec < _ > > ( ) ;
259+ let mut points2 = other_segment. compute_lookup_table ( Some ( n) , None ) . collect :: < Vec < _ > > ( ) ;
260+ points1. append ( & mut points2) ;
261+
262+ let f = |a : f64 , b : f64 | -> f64 { log_optimization ( a, b, p1, p3, d1, d2, & points1, n) } ;
263+
264+ for t in 1 ..=max_iter {
265+ let dfa = ( f ( a + h, b) - f ( a - h, b) ) / ( 2. * h) ;
266+ let dfb = ( f ( a, b + h) - f ( a, b - h) ) / ( 2. * h) ;
267+
268+ m_a = beta1 * m_a + ( 1. - beta1) * dfa;
269+ m_b = beta1 * m_b + ( 1. - beta1) * dfb;
270+
271+ v_a = beta2 * v_a + ( 1. - beta2) * dfa * dfa;
272+ v_b = beta2 * v_b + ( 1. - beta2) * dfb * dfb;
273+
274+ let m_a_hat = m_a / ( 1. - beta1. powi ( t) ) ;
275+ let v_a_hat = v_a / ( 1. - beta2. powi ( t) ) ;
276+ let m_b_hat = m_b / ( 1. - beta1. powi ( t) ) ;
277+ let v_b_hat = v_b / ( 1. - beta2. powi ( t) ) ;
278+
279+ let alpha_t = initial_alpha * decay_rate. powi ( t) ;
280+
281+ // Update log-lengths
282+ a -= alpha_t * m_a_hat / ( v_a_hat. sqrt ( ) + epsilon) ;
283+ b -= alpha_t * m_b_hat / ( v_b_hat. sqrt ( ) + epsilon) ;
284+
285+ // Convergence check
286+ if dfa. abs ( ) < tol && dfb. abs ( ) < tol {
287+ break ;
288+ }
289+ }
290+
291+ let len1 = a. exp ( ) . max ( min_len1) ;
292+ let len2 = b. exp ( ) . max ( min_len2) ;
293+
294+ ( d1 * len1, d2 * len2)
295+ }
0 commit comments