1#[derive(Debug, Clone, Copy, PartialEq)]
7pub struct Point {
8 pub x: f64,
9 pub y: f64,
10}
11
12impl Point {
13 pub fn zero() -> Point {
14 Self { x: 0., y: 0. }
15 }
16
17 pub fn new(x: f64, y: f64) -> Point {
18 Self { x, y }
19 }
20
21 pub fn splat(s: f64) -> Point {
22 Point::new(s, s)
23 }
24
25 pub fn neg(&self) -> Point {
26 Point::new(-self.x, -self.y)
27 }
28
29 pub fn add(&self, other: Point) -> Point {
30 Point::new(self.x + other.x, self.y + other.y)
31 }
32
33 pub fn sub(&self, other: Point) -> Point {
34 self.add(other.neg())
35 }
36
37 pub fn distance_to(&self, other: Point) -> f64 {
38 let d = self.sub(other);
39 (d.x * d.x + d.y * d.y).sqrt()
40 }
41
42 pub fn length(&self) -> f64 {
43 Point::zero().distance_to(*self)
44 }
45
46 pub fn scale(&self, s: f64) -> Point {
47 Point::new(self.x * s, self.y * s)
48 }
49
50 pub fn transpose(&self) -> Point {
51 Point::new(self.y, self.x)
52 }
53
54 pub fn rotate_around(&self, center: Point, angle: f64) -> Point {
55 let normalized = self.sub(center);
56 let rotated = normalized.rotate(angle);
57 rotated.add(center)
58 }
59 pub fn rotate(&self, angle: f64) -> Point {
60 let x = self.x;
61 let y = self.y;
62 Point::new(
63 x * angle.cos() - y * angle.sin(),
64 x * angle.sin() + y * angle.cos(),
65 )
66 }
67}
68
69impl std::fmt::Display for Point {
70 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
71 write!(f, "(x: {:.3}, y: {:.3})", self.x, self.y)
72 }
73}
74
75pub fn ellipse_line_intersection(a: f64, b: f64, m: f64) -> Point {
82 let x: f64 = ((a * a * b * b) / (b * b + a * a * m * m)).sqrt();
83 let y: f64 = m * x;
84 Point::new(x, y)
85}
86
87pub fn get_connection_point_for_circle(
90 loc: Point,
91 size: Point,
92 from: Point,
93 force: f64,
94) -> (Point, Point) {
95 let loc = loc;
96 let dx = from.x - loc.x;
97 let dy = from.y - loc.y;
98
99 let a = size.x / 2.;
100 let b = size.y / 2.;
101 let m = dy / dx;
102
103 if dx == 0. {
104 let b = b * dy.signum();
105 let loc1 = Point::new(loc.x, loc.y + b);
106 return create_vector_of_length(loc1, from, force);
107 }
108
109 let mut v = ellipse_line_intersection(a, b, m);
110
111 if dx < 0. {
114 v = v.neg();
115 }
116
117 let loc1 = loc.add(v);
118 create_vector_of_length(loc1, from, force)
119}
120
121pub fn interpolate(v0: Point, v1: Point, w: f64) -> Point {
124 v0.scale(w).add(v1.scale(1. - w))
125}
126
127pub fn normalize_scale_vector(v: Point, s: f64) -> Point {
129 let len = Point::zero().distance_to(v);
130 assert!(len > 0., "Can't normalize the unit vector");
131 v.scale(s / len)
132}
133pub fn create_vector_of_length(
135 from: Point,
136 to: Point,
137 s: f64,
138) -> (Point, Point) {
139 if from == to {
141 return (from, Point::new(from.x + s, from.y));
142 }
143 let t = to.sub(from);
144 let t = normalize_scale_vector(t, s);
145 (from, t.add(from))
146}
147
148pub fn get_connection_point_for_box(
151 loc: Point,
152 size: Point,
153 from: Point,
154 force: f64,
155) -> (Point, Point) {
156 let mut loc = loc;
157 let mut size = size;
158
159 if from.x > loc.x + size.x / 2. {
165 size.x /= 2.;
167 loc.x += size.x / 2.;
168 } else if from.x < loc.x - size.x / 2. {
169 size.x /= 2.;
171 loc.x -= size.x / 2.;
172 }
173
174 let dx = loc.x - from.x;
175 let dy = loc.y - from.y;
176
177 let mut box_x = size.x / 2.;
178 let mut box_y = size.y / 2.;
179
180 if dx == 0. {
182 if dy > 0. {
184 let loc = Point::new(loc.x, loc.y - box_y);
185 return create_vector_of_length(loc, from, force);
186 } else {
187 let loc = Point::new(loc.x, loc.y + box_y);
189 return create_vector_of_length(loc, from, force);
190 }
191 }
192
193 let slope_from = dy / dx;
194 let mut gain_y = box_x * slope_from;
196
197 if gain_y.abs() < box_y {
199 if dx > 0. {
200 box_x = -box_x;
201 gain_y = -gain_y;
202 }
203
204 let con = Point::new(loc.x + box_x, loc.y + gain_y);
205 return create_vector_of_length(con, from, force);
206 }
207
208 let mut gain_x = box_y / slope_from;
211
212 if dy > 0. {
213 box_y = -box_y;
214 gain_x = -gain_x;
215 }
216
217 let con = Point::new(loc.x + gain_x, loc.y + box_y);
218 create_vector_of_length(con, from, force)
219}
220
221pub fn get_passthrough_path_invisible(
222 _size: Point,
223 center: Point,
224 from: Point,
225 to: Point,
226 force: f64,
227) -> (Point, Point) {
228 let ar = center.sub(from);
241 let rb = to.sub(center);
242
243 let a_outgoing_edge = normalize_scale_vector(ar.neg(), force);
244 let b_outgoing_edge = normalize_scale_vector(rb.neg(), force);
245
246 let sum = a_outgoing_edge.add(b_outgoing_edge);
251 if sum.length() < 1. {
252 let edge = a_outgoing_edge.rotate(90_f64.to_radians());
253 return (center, edge.add(center));
254 }
255
256 let total = ar.length() + rb.length();
257 let mut a_ratio = ar.length() / total;
258
259 if center.x == to.x || center.y == to.y {
263 a_ratio = 1.;
264 } else if center.x == from.x || center.y == from.y {
265 a_ratio = 0.;
266 }
267
268 let res = interpolate(a_outgoing_edge, b_outgoing_edge, 1. - a_ratio);
269 (center, res.add(center))
270}
271
272pub fn make_size_square(sz: Point) -> Point {
274 let l = sz.x.max(sz.y);
275 Point::new(l, l)
276}
277
278pub fn pad_shape_scalar(size: Point, s: f64) -> Point {
280 Point::new(size.x + s, size.y + s)
281}
282
283pub fn get_size_for_str(label: &str, font_size: usize) -> Point {
285 let max_line_len = if !label.is_empty() {
287 label.lines().map(|x| x.chars().count()).max().unwrap()
288 } else {
289 0
290 };
291 let ts = (max_line_len.max(1), label.lines().count().max(1));
292 Point::new(ts.0 as f64, ts.1 as f64).scale(font_size as f64)
293}
294
295pub fn in_range(range: (f64, f64), x: f64) -> bool {
297 x >= range.0 && x <= range.1
298}
299
300fn approx_eq_f64(x: f64, y: f64) -> bool {
302 if x == 0. {
303 y.abs() < f64::EPSILON
304 } else if y == 0. {
305 x.abs() < f64::EPSILON
306 } else {
307 let abs_diff = (x - y).abs();
308 if abs_diff < f64::EPSILON {
309 true
310 } else {
311 abs_diff / x.abs().max(y.abs()) < f64::EPSILON
312 }
313 }
314}
315
316fn smaller_than_or_equal_to_f64(x: f64, y: f64) -> bool {
318 if x > y {
319 false
320 } else {
321 approx_eq_f64(x, y)
323 }
324}
325
326pub fn do_boxes_intersect(p1: (Point, Point), p2: (Point, Point)) -> bool {
328 let overlap_x = smaller_than_or_equal_to_f64(p2.0.x, p1.1.x)
329 && smaller_than_or_equal_to_f64(p1.0.x, p2.1.x);
330 let overlap_y = smaller_than_or_equal_to_f64(p2.0.y, p1.1.y)
331 && smaller_than_or_equal_to_f64(p1.0.y, p2.1.y);
332 overlap_x && overlap_y
333}
334
335pub fn weighted_median(vec: &[f64]) -> f64 {
340 assert!(!vec.is_empty(), "array can't be empty");
341
342 let mut vec = vec.to_vec();
343 vec.sort_by(|a, b| a.partial_cmp(b).unwrap());
344
345 if vec.len() == 1 {
346 return vec[0];
347 }
348
349 if vec.len() == 2 {
350 return (vec[0] + vec[1]) / 2.;
351 }
352 let mid = vec.len() / 2;
353
354 if vec.len() % 2 == 1 {
355 return vec[mid];
356 }
357
358 (vec[mid] + vec[mid - 1]) / 2.
359}
360
361#[derive(Debug, Clone, Copy)]
380pub struct Position {
381 middle: Point, size: Point, center: Point, halo: Point, }
386
387impl Position {
388 pub fn new(middle: Point, size: Point, center: Point, halo: Point) -> Self {
389 Self {
390 middle,
391 size,
392 center,
393 halo,
394 }
395 }
396
397 pub fn distance_to_left(&self, with_halo: bool) -> f64 {
398 self.center().x - self.bbox(with_halo).0.x
399 }
400 pub fn distance_to_right(&self, with_halo: bool) -> f64 {
401 self.bbox(with_halo).1.x - self.center().x
402 }
403 pub fn left(&self, with_halo: bool) -> f64 {
404 self.bbox(with_halo).0.x
405 }
406 pub fn right(&self, with_halo: bool) -> f64 {
407 self.bbox(with_halo).1.x
408 }
409 pub fn top(&self, with_halo: bool) -> f64 {
410 self.bbox(with_halo).0.y
411 }
412 pub fn bottom(&self, with_halo: bool) -> f64 {
413 self.bbox(with_halo).1.y
414 }
415 pub fn bbox(&self, with_halo: bool) -> (Point, Point) {
418 let size = self.size(with_halo);
419 let top_left = self.middle.sub(size.scale(0.5));
420 let bottom_right = top_left.add(size);
421 (top_left, bottom_right)
422 }
423
424 pub fn center(&self) -> Point {
426 self.middle.add(self.center)
427 }
428
429 pub fn middle(&self) -> Point {
431 self.middle
432 }
433
434 pub fn size(&self, with_halo: bool) -> Point {
435 if with_halo {
436 self.size.add(self.halo)
437 } else {
438 self.size
439 }
440 }
441
442 pub fn in_x_range(&self, range: (f64, f64), with_halo: bool) -> bool {
444 self.left(with_halo) >= range.0 && self.right(with_halo) <= range.1
445 }
446
447 pub fn set_size(&mut self, size: Point) {
448 self.size = size;
449 }
450
451 pub fn set_new_center_point(&mut self, center: Point) {
454 self.center = center;
455 assert!(self.center.x.abs() < self.size.x);
456 assert!(self.center.y.abs() < self.size.y);
457 }
458
459 pub fn move_to(&mut self, p: Point) {
462 let delta = p.sub(self.center());
463 self.middle = self.middle.add(delta);
464 }
465
466 pub fn align_to_top(&mut self, y: f64) {
467 self.middle.y = y + self.size.y / 2. + self.halo.y / 2.
468 }
469 pub fn align_to_left(&mut self, x: f64) {
470 self.middle.x = x + self.size.x / 2. + self.halo.x / 2.
471 }
472 pub fn align_to_right(&mut self, x: f64) {
473 self.middle.x = x - self.size.x / 2. - self.halo.x / 2.;
474 }
475 pub fn translate(&mut self, d: Point) {
477 self.middle = self.middle.add(d);
478 }
479
480 pub fn align_x(&mut self, x: f64, to_left: bool) {
483 let half_box = self.size.x / 2. + self.halo.x / 2.;
484 if to_left {
485 self.middle.x = x + half_box;
486 } else {
487 self.middle.x = x - half_box;
488 }
489 }
490
491 pub fn set_x(&mut self, x: f64) {
493 self.middle.x = x - self.center.x;
494 }
495
496 pub fn set_y(&mut self, y: f64) {
498 self.middle.y = y - self.center.y;
499 }
500
501 pub fn transpose(&mut self) {
502 self.middle = self.middle.transpose();
503 self.size = self.size.transpose();
504 self.center = self.center.transpose();
505 self.halo = self.halo.transpose();
506 }
507}
508
509pub fn segment_rect_intersection(
511 seg: (Point, Point),
512 rect: (Point, Point),
513) -> bool {
514 assert!(rect.0.x <= rect.1.x);
516 assert!(rect.0.y <= rect.1.y);
517
518 if seg.0.x == seg.1.x {
520 return seg.1.x >= rect.0.x && seg.1.x <= rect.1.x;
521 }
522
523 let above = seg.0.x < rect.0.x && seg.1.x < rect.0.x;
525 let below = seg.0.x > rect.1.x && seg.1.x > rect.1.x;
526 if above || below {
527 return false;
528 }
529
530 let above = seg.0.y < rect.0.y && seg.1.y < rect.0.y;
532 let below = seg.0.y > rect.1.y && seg.1.y > rect.1.y;
533 if above || below {
534 return false;
535 }
536
537 let dx = seg.1.x - seg.0.x; let dy = seg.1.y - seg.0.y;
546 let a = dy / dx;
547 let b = seg.0.y - a * seg.0.x;
550
551 let y0 = a * rect.0.x + b;
553 let y1 = a * rect.1.x + b;
554
555 let above = y0 < rect.0.y && y1 < rect.0.y;
557 let below = y0 > rect.1.y && y1 > rect.1.y;
558 !(above || below)
559}
560
561#[test]
562fn segment_rect_intersection_test() {
563 let v0 = (
565 Point::new(-48., -27.),
566 Point::new(-196., -55.),
567 Point::new(-50., -50.),
568 Point::new(50., 50.),
569 );
570 let v1 = (
571 Point::new(-70., -156.),
572 Point::new(57., 41.),
573 Point::new(-50., -50.),
574 Point::new(50., 50.),
575 );
576 let v2 = (
577 Point::new(70., -11.),
578 Point::new(-20., -119.),
579 Point::new(-50., -50.),
580 Point::new(50., 50.),
581 );
582 assert!(segment_rect_intersection((v0.0, v0.1), (v0.2, v0.3)));
583 assert!(segment_rect_intersection((v1.0, v1.1), (v1.2, v1.3)));
584 assert!(segment_rect_intersection((v2.0, v2.1), (v2.2, v2.3)));
585
586 let v0 = (
588 Point::new(190., -55.),
589 Point::new(173., 199.),
590 Point::new(-50., -50.),
591 Point::new(50., 50.),
592 );
593 let v1 = (
594 Point::new(142., -19.),
595 Point::new(-108., -133.),
596 Point::new(-50., -50.),
597 Point::new(50., 50.),
598 );
599 let v2 = (
600 Point::new(151., 80.),
601 Point::new(17., 124.),
602 Point::new(-50., -50.),
603 Point::new(50., 50.),
604 );
605 assert!(!segment_rect_intersection((v0.0, v0.1), (v0.2, v0.3)));
606 assert!(!segment_rect_intersection((v1.0, v1.1), (v1.2, v1.3)));
607 assert!(!segment_rect_intersection((v2.0, v2.1), (v2.2, v2.3)));
608}