mirror of https://codeberg.org/topola/topola.git
geometry, egui: fixes and cleanup of math related to bend shapes
This commit is contained in:
parent
8c3a3f1e72
commit
e28b7f7f9d
|
|
@ -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![];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
14
src/math.rs
14
src/math.rs
|
|
@ -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();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue