mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 10:58:11 +08:00
made spiral node
This commit is contained in:
@@ -176,7 +176,7 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !using_path_tool {
|
if !using_path_tool || !using_shape_tool {
|
||||||
*selected.pivot = selected.mean_average_of_pivots();
|
*selected.pivot = selected.mean_average_of_pivots();
|
||||||
self.local_pivot = document.metadata().document_to_viewport.inverse().transform_point2(*selected.pivot);
|
self.local_pivot = document.metadata().document_to_viewport.inverse().transform_point2(*selected.pivot);
|
||||||
self.grab_target = document.metadata().document_to_viewport.inverse().transform_point2(selected.mean_average_of_pivots());
|
self.grab_target = document.metadata().document_to_viewport.inverse().transform_point2(selected.mean_average_of_pivots());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::consts::*;
|
|
||||||
use crate::utils::format_point;
|
use crate::utils::format_point;
|
||||||
|
use crate::{BezierHandles, consts::*};
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
@@ -271,6 +271,55 @@ impl<PointId: crate::Identifier> Subpath<PointId> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn spiral_point(theta: f64, a: f64, b: f64) -> DVec2 {
|
||||||
|
let r = a + b * theta;
|
||||||
|
DVec2::new(r * theta.cos(), -r * theta.sin())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spiral_tangent(theta: f64, a: f64, b: f64) -> DVec2 {
|
||||||
|
let r = a + b * theta;
|
||||||
|
let dx = b * theta.cos() - r * theta.sin();
|
||||||
|
let dy = b * theta.sin() + r * theta.cos();
|
||||||
|
DVec2::new(dx, -dy).normalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrap_angle(angle: f64) -> f64 {
|
||||||
|
(angle + std::f64::consts::PI).rem_euclid(2.0 * std::f64::consts::PI) - std::f64::consts::PI
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spiral_arc_length(theta: f64, a: f64, b: f64) -> f64 {
|
||||||
|
let r = a + b * theta;
|
||||||
|
let sqrt_term = (r * r + b * b).sqrt();
|
||||||
|
(r * sqrt_term + b * b * ((r + sqrt_term).ln())) / (2.0 * b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_equal_arc_bezier_spiral2(a: f64, b: f64, turns: u32, delta_theta: f64, angle_offset: f64) -> Self {
|
||||||
|
let mut manipulator_groups = Vec::new();
|
||||||
|
let mut prev_in_handle = None;
|
||||||
|
let mut theta = 0.;
|
||||||
|
let theta_end = angle_offset + turns as f64 * std::f64::consts::TAU;
|
||||||
|
|
||||||
|
while theta < theta_end {
|
||||||
|
let theta_next = f64::min(theta + delta_theta, theta_end);
|
||||||
|
let p0 = Self::spiral_point(theta, a, b);
|
||||||
|
let p3 = Self::spiral_point(theta_next, a, b);
|
||||||
|
let t0 = Self::spiral_tangent(theta, a, b);
|
||||||
|
let t1 = Self::spiral_tangent(theta_next, a, b);
|
||||||
|
|
||||||
|
let arc_len = Self::spiral_arc_length(theta_next, a, b) - Self::spiral_arc_length(theta, a, b);
|
||||||
|
let d = arc_len / 3.0;
|
||||||
|
|
||||||
|
let p1 = p0 + d * t0;
|
||||||
|
let p2 = p3 - d * t1;
|
||||||
|
|
||||||
|
manipulator_groups.push(ManipulatorGroup::new(p0, prev_in_handle, Some(p1)));
|
||||||
|
prev_in_handle = Some(p2);
|
||||||
|
theta = theta_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::new(manipulator_groups, false)
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructs an ellipse with `corner1` and `corner2` as the two corners of the bounding box.
|
/// Constructs an ellipse with `corner1` and `corner2` as the two corners of the bounding box.
|
||||||
pub fn new_ellipse(corner1: DVec2, corner2: DVec2) -> Self {
|
pub fn new_ellipse(corner1: DVec2, corner2: DVec2) -> Self {
|
||||||
let size = (corner1 - corner2).abs();
|
let size = (corner1 - corner2).abs();
|
||||||
|
|||||||
@@ -302,6 +302,49 @@ pub fn format_point(svg: &mut String, prefix: &str, x: f64, y: f64) -> std::fmt:
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn spiral_point(theta: f64, a: f64, b: f64) -> DVec2 {
|
||||||
|
let r = a + b * theta;
|
||||||
|
DVec2::new(r * theta.cos(), -r * theta.sin())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spiral_tangent(theta: f64, b: f64) -> DVec2 {
|
||||||
|
let dx = b * (theta.cos() - theta * theta.sin());
|
||||||
|
let dy = b * (theta.sin() + theta * theta.cos());
|
||||||
|
DVec2::new(dx, dy).normalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrap_angle(angle: f64) -> f64 {
|
||||||
|
(angle + std::f64::consts::PI).rem_euclid(2.0 * std::f64::consts::PI) - std::f64::consts::PI
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bezier_point(p0: DVec2, p1: DVec2, p2: DVec2, p3: DVec2, t: f64) -> DVec2 {
|
||||||
|
let u = 1.0 - t;
|
||||||
|
p0 * u * u * u + p1 * 3.0 * u * u * t + p2 * 3.0 * u * t * t + p3 * t * t * t
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bezier_derivative(p0: DVec2, p1: DVec2, p2: DVec2, p3: DVec2, t: f64) -> DVec2 {
|
||||||
|
let u = 1.0 - t;
|
||||||
|
-3.0 * u * u * p0 + 3.0 * (u * u - 2.0 * u * t) * p1 + 3.0 * (2.0 * u * t - t * t) * p2 + 3.0 * t * t * p3
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn esq_for_d(p0: DVec2, t0: DVec2, p3: DVec2, t1: DVec2, theta0: f64, theta1: f64, d: f64, a: f64, b: f64, samples: usize) -> f64 {
|
||||||
|
let p1 = p0 + d * t0;
|
||||||
|
let p2 = p3 - d * t1;
|
||||||
|
let mut total = 0.0;
|
||||||
|
for i in 1..samples {
|
||||||
|
let t = i as f64 / samples as f64;
|
||||||
|
let bez = bezier_point(p0, p1, p2, p3, t);
|
||||||
|
let bez_d = bezier_derivative(p0, p1, p2, p3, t);
|
||||||
|
let bez_angle = bez_d.y.atan2(bez_d.x);
|
||||||
|
|
||||||
|
let theta = theta0 + (theta1 - theta0) * t;
|
||||||
|
let spiral_angle = theta;
|
||||||
|
let diff = wrap_angle(bez_angle - spiral_angle);
|
||||||
|
total += diff * diff * bez_d.length();
|
||||||
|
}
|
||||||
|
total / samples as f64
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::f64::consts::{FRAC_PI_4, FRAC_PI_8, TAU};
|
||||||
|
|
||||||
use super::misc::{ArcType, AsU64, GridType};
|
use super::misc::{ArcType, AsU64, GridType};
|
||||||
use super::{PointId, SegmentId, StrokeId};
|
use super::{PointId, SegmentId, StrokeId};
|
||||||
use crate::Ctx;
|
use crate::Ctx;
|
||||||
@@ -65,6 +67,28 @@ fn arc(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Vector: Shape"))]
|
||||||
|
fn spiral(
|
||||||
|
_: impl Ctx,
|
||||||
|
_primary: (),
|
||||||
|
#[default(1.)] inner_radius: f64,
|
||||||
|
#[default(1.)] tightness: f64,
|
||||||
|
#[default(6)]
|
||||||
|
#[hard_min(1.)]
|
||||||
|
turns: u32,
|
||||||
|
#[default(0.)]
|
||||||
|
#[range((0., 360.))]
|
||||||
|
angle_offset: f64,
|
||||||
|
) -> VectorDataTable {
|
||||||
|
VectorDataTable::new(VectorData::from_subpath(Subpath::generate_equal_arc_bezier_spiral2(
|
||||||
|
inner_radius,
|
||||||
|
tightness,
|
||||||
|
turns,
|
||||||
|
FRAC_PI_4,
|
||||||
|
angle_offset.to_radians(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Shape"))]
|
#[node_macro::node(category("Vector: Shape"))]
|
||||||
fn ellipse(_: impl Ctx, _primary: (), #[default(50)] radius_x: f64, #[default(25)] radius_y: f64) -> VectorDataTable {
|
fn ellipse(_: impl Ctx, _primary: (), #[default(50)] radius_x: f64, #[default(25)] radius_y: f64) -> VectorDataTable {
|
||||||
let radius = DVec2::new(radius_x, radius_y);
|
let radius = DVec2::new(radius_x, radius_y);
|
||||||
|
|||||||
Reference in New Issue
Block a user