geometry, egui: fixes and cleanup of math related to bend shapes

This commit is contained in:
Tomasz Cichoń 2024-07-12 12:01:21 +02:00
parent 8c3a3f1e72
commit e28b7f7f9d
3 changed files with 46 additions and 19 deletions

View File

@ -28,14 +28,9 @@ impl<'a> Painter<'a> {
), ),
PrimitiveShape::Bend(bend) => { PrimitiveShape::Bend(bend) => {
let circle = bend.circle(); let circle = bend.circle();
let delta_from = bend.from - circle.pos;
let delta_to = bend.to - circle.pos;
let angle_from = delta_from.y().atan2(delta_from.x()); let angle_from = bend.start_angle();
let angle_step = bend.spanned_angle() / 100.0;
let cross = math::cross_product(delta_from, delta_to);
let dot = math::dot_product(delta_from, delta_to);
let angle_step = cross.atan2(dot) / 100.0;
let mut points: Vec<egui::Pos2> = vec![]; let mut points: Vec<egui::Pos2> = vec![];

View File

@ -1,3 +1,5 @@
use std::f64::consts::TAU;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use geo::{point, polygon, Contains, EuclideanDistance, Intersects, Point, Polygon, Rotate}; use geo::{point, polygon, Contains, EuclideanDistance, Intersects, Point, Polygon, Rotate};
use rstar::{RTreeObject, AABB}; use rstar::{RTreeObject, AABB};
@ -243,6 +245,10 @@ pub struct BendShape {
} }
impl BendShape { impl BendShape {
pub fn radius(&self) -> f64 {
self.inner_circle.r + self.width / 2.0
}
pub fn inner_circle(&self) -> Circle { pub fn inner_circle(&self) -> Circle {
self.inner_circle self.inner_circle
} }
@ -250,7 +256,7 @@ impl BendShape {
pub fn circle(&self) -> Circle { pub fn circle(&self) -> Circle {
Circle { Circle {
pos: self.inner_circle.pos, pos: self.inner_circle.pos,
r: self.inner_circle.r + self.width / 2.0, r: self.radius(),
} }
} }
@ -268,21 +274,33 @@ impl BendShape {
self.to - self.inner_circle.pos, self.to - self.inner_circle.pos,
) )
} }
pub fn start_angle(&self) -> f64 {
let r = self.from - self.inner_circle.pos;
math::vector_angle(r)
}
pub fn spanned_angle(&self) -> f64 {
let r1 = self.from - self.inner_circle.pos;
let r2 = self.to - self.inner_circle.pos;
// bends always go counterclockwise from `from` to `to`
// (this is the usual convention, no adjustment needed)
let angle = math::angle_between(r1, r2);
// atan2 returns values normalized into the range (-pi, pi]
// so for angles below 0 we add 1 winding to get a nonnegative angle
if angle < 0.0 {
angle + TAU
} else {
angle
}
}
} }
impl MeasureLength for BendShape { impl MeasureLength for BendShape {
fn length(&self) -> f64 { fn length(&self) -> f64 {
// TODO: Not valid for inflated bends, as currently `from` and `to` of these don't lie on self.spanned_angle() * self.radius()
// teir circles.
// We obtain the angle from the law of cosines and multiply with radius to get the length.
let d = self.to.euclidean_distance(&self.from);
if d > 0.0 {
(1.0 - d * d / (2.0 * d * d)).acos()
} else {
0.0
}
} }
} }

View File

@ -230,6 +230,20 @@ pub fn between_vectors(p: Point, from: Point, to: Point) -> bool {
} }
} }
/// Computes the (directed) angle between the positive X axis and the vector.
///
/// The result is measured counterclockwise and normalized into range (-pi, pi] (like atan2).
pub fn vector_angle(vector: Point) -> f64 {
vector.y().atan2(vector.x())
}
/// Computes the (directed) angle between two vectors.
///
/// The result is measured counterclockwise and normalized into range (-pi, pi] (like atan2).
pub fn angle_between(v1: Point, v2: Point) -> f64 {
cross_product(v1, v2).atan2(dot_product(v1, v2))
}
pub fn seq_cross_product(start: Point, stop: Point, reference: Point) -> f64 { pub fn seq_cross_product(start: Point, stop: Point, reference: Point) -> f64 {
let dx1 = stop.x() - start.x(); let dx1 = stop.x() - start.x();
let dy1 = stop.y() - start.y(); let dy1 = stop.y() - start.y();