@@ -2,6 +2,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
2
2
use crate :: messages:: prelude:: * ;
3
3
use crate :: messages:: tool:: common_functionality:: graph_modification_utils:: get_text;
4
4
use crate :: messages:: tool:: tool_messages:: path_tool:: PathOverlayMode ;
5
+ use bezier_rs:: Bezier ;
5
6
use glam:: DVec2 ;
6
7
use graphene_core:: renderer:: Quad ;
7
8
use graphene_core:: text:: { FontCache , load_face} ;
@@ -196,3 +197,99 @@ pub fn is_visible_point(
196
197
}
197
198
}
198
199
}
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