mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Replace the legacy Bezier struct with kurbo::PathSeg throughout (#4455)
This commit is contained in:
committed by
Dennis Kobert
parent
bf0f3510df
commit
26f4c655a1
@@ -13,9 +13,10 @@ use graph_craft::document::NodeId;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::raster::Image;
|
||||
use graphene_std::subpath::BezierHandles;
|
||||
use graphene_std::vector::misc::HandleId;
|
||||
use graphene_std::vector::misc::{HandleId, point_to_dvec2, segment_to_handles};
|
||||
use graphene_std::vector::{PointId, SegmentId, VectorModificationType};
|
||||
use graphite_proc_macros::{ExtractField, message_handler_data};
|
||||
use kurbo::ParamCurve;
|
||||
use std::sync::Arc;
|
||||
|
||||
const CLIPBOARD_PREFIX: &str = "graphite: ";
|
||||
@@ -414,7 +415,7 @@ impl MessageHandler<ClipboardMessage, ClipboardMessageContext<'_>> for Clipboard
|
||||
|
||||
// Create new segment ids and add the segments into the existing Vector path
|
||||
let mut segments_map = HashMap::new();
|
||||
for (segment_id, bezier, start, end) in new_vector.segment_bezier_iter() {
|
||||
for (segment_id, segment, start, end) in new_vector.segment_iter() {
|
||||
let (Some(&start_point), Some(&end_point)) = (points_map.get(&start), points_map.get(&end)) else {
|
||||
warn!("Skipping pasted vector segment with an unknown endpoint");
|
||||
continue;
|
||||
@@ -423,10 +424,12 @@ impl MessageHandler<ClipboardMessage, ClipboardMessageContext<'_>> for Clipboard
|
||||
let new_segment_id = SegmentId::generate();
|
||||
segments_map.insert(segment_id, new_segment_id);
|
||||
|
||||
let handles = match bezier.handles {
|
||||
let segment_start = point_to_dvec2(segment.start());
|
||||
let segment_end = point_to_dvec2(segment.end());
|
||||
let handles = match segment_to_handles(&segment) {
|
||||
BezierHandles::Linear => [None, None],
|
||||
BezierHandles::Quadratic { handle } => [Some(handle - bezier.start), None],
|
||||
BezierHandles::Cubic { handle_start, handle_end } => [Some(handle_start - bezier.start), Some(handle_end - bezier.end)],
|
||||
BezierHandles::Quadratic { handle } => [Some(handle - segment_start), None],
|
||||
BezierHandles::Cubic { handle_start, handle_end } => [Some(handle_start - segment_start), Some(handle_end - segment_end)],
|
||||
};
|
||||
|
||||
let points = [start_point, end_point];
|
||||
|
||||
@@ -6,9 +6,10 @@ pub use crate::messages::portfolio::document::utility_types::text_metrics::text_
|
||||
use crate::messages::tool::common_functionality::shape_editor::{SelectedLayerState, ShapeState};
|
||||
use crate::messages::tool::tool_messages::tool_prelude::DocumentMessageHandler;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_std::subpath::{Bezier, BezierHandles};
|
||||
use graphene_std::vector::misc::ManipulatorPointId;
|
||||
use graphene_std::subpath::BezierHandles;
|
||||
use graphene_std::vector::misc::{ManipulatorPointId, point_to_dvec2, segment_to_handles};
|
||||
use graphene_std::vector::{PointId, SegmentId, Vector};
|
||||
use kurbo::{Affine, ParamCurve, PathSeg};
|
||||
use std::collections::HashMap;
|
||||
#[cfg(target_family = "wasm")]
|
||||
use wasm_bindgen::JsCast;
|
||||
@@ -59,7 +60,7 @@ pub fn selected_segments_for_layer(vector: &Vector, state: &SelectedLayerState)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Adding segments which are are connected to selected anchors
|
||||
for (segment_id, _bezier, start, end) in vector.segment_bezier_iter() {
|
||||
for (segment_id, _, start, end) in vector.segment_iter() {
|
||||
if selected_anchors.contains(&start) || selected_anchors.contains(&end) {
|
||||
selected_segments.push(segment_id);
|
||||
}
|
||||
@@ -67,23 +68,25 @@ pub fn selected_segments_for_layer(vector: &Vector, state: &SelectedLayerState)
|
||||
selected_segments
|
||||
}
|
||||
|
||||
fn overlay_bezier_handles(bezier: Bezier, segment_id: SegmentId, transform: DAffine2, is_selected: impl Fn(ManipulatorPointId) -> bool, overlay_context: &mut OverlayContext) {
|
||||
let bezier = bezier.apply_transformation(|point| transform.transform_point2(point));
|
||||
fn overlay_bezier_handles(segment: PathSeg, segment_id: SegmentId, transform: DAffine2, is_selected: impl Fn(ManipulatorPointId) -> bool, overlay_context: &mut OverlayContext) {
|
||||
let segment = Affine::new(transform.to_cols_array()) * segment;
|
||||
let segment_start = point_to_dvec2(segment.start());
|
||||
let segment_end = point_to_dvec2(segment.end());
|
||||
let not_under_anchor = |position: DVec2, anchor: DVec2| position.distance_squared(anchor) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE;
|
||||
|
||||
match bezier.handles {
|
||||
BezierHandles::Quadratic { handle } if not_under_anchor(handle, bezier.start) && not_under_anchor(handle, bezier.end) => {
|
||||
overlay_context.line(handle, bezier.start, None, None);
|
||||
overlay_context.line(handle, bezier.end, None, None);
|
||||
match segment_to_handles(&segment) {
|
||||
BezierHandles::Quadratic { handle } if not_under_anchor(handle, segment_start) && not_under_anchor(handle, segment_end) => {
|
||||
overlay_context.line(handle, segment_start, None, None);
|
||||
overlay_context.line(handle, segment_end, None, None);
|
||||
overlay_context.manipulator_handle(handle, is_selected(ManipulatorPointId::PrimaryHandle(segment_id)), None);
|
||||
}
|
||||
BezierHandles::Cubic { handle_start, handle_end } => {
|
||||
if not_under_anchor(handle_start, bezier.start) {
|
||||
overlay_context.line(handle_start, bezier.start, None, None);
|
||||
if not_under_anchor(handle_start, segment_start) {
|
||||
overlay_context.line(handle_start, segment_start, None, None);
|
||||
overlay_context.manipulator_handle(handle_start, is_selected(ManipulatorPointId::PrimaryHandle(segment_id)), None);
|
||||
}
|
||||
if not_under_anchor(handle_end, bezier.end) {
|
||||
overlay_context.line(handle_end, bezier.end, None, None);
|
||||
if not_under_anchor(handle_end, segment_end) {
|
||||
overlay_context.line(handle_end, segment_end, None, None);
|
||||
overlay_context.manipulator_handle(handle_end, is_selected(ManipulatorPointId::EndHandle(segment_id)), None);
|
||||
}
|
||||
}
|
||||
@@ -92,7 +95,7 @@ fn overlay_bezier_handles(bezier: Bezier, segment_id: SegmentId, transform: DAff
|
||||
}
|
||||
|
||||
fn overlay_bezier_handle_specific_point(
|
||||
bezier: Bezier,
|
||||
segment: PathSeg,
|
||||
segment_id: SegmentId,
|
||||
(start, end): (PointId, PointId),
|
||||
point_to_render: PointId,
|
||||
@@ -100,22 +103,24 @@ fn overlay_bezier_handle_specific_point(
|
||||
is_selected: impl Fn(ManipulatorPointId) -> bool,
|
||||
overlay_context: &mut OverlayContext,
|
||||
) {
|
||||
let bezier = bezier.apply_transformation(|point| transform.transform_point2(point));
|
||||
let segment = Affine::new(transform.to_cols_array()) * segment;
|
||||
let segment_start = point_to_dvec2(segment.start());
|
||||
let segment_end = point_to_dvec2(segment.end());
|
||||
let not_under_anchor = |position: DVec2, anchor: DVec2| position.distance_squared(anchor) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE;
|
||||
|
||||
match bezier.handles {
|
||||
BezierHandles::Quadratic { handle } if not_under_anchor(handle, bezier.start) && not_under_anchor(handle, bezier.end) => {
|
||||
let end = if start == point_to_render { bezier.start } else { bezier.end };
|
||||
overlay_context.line(handle, end, None, None);
|
||||
match segment_to_handles(&segment) {
|
||||
BezierHandles::Quadratic { handle } if not_under_anchor(handle, segment_start) && not_under_anchor(handle, segment_end) => {
|
||||
let anchor = if start == point_to_render { segment_start } else { segment_end };
|
||||
overlay_context.line(handle, anchor, None, None);
|
||||
overlay_context.manipulator_handle(handle, is_selected(ManipulatorPointId::PrimaryHandle(segment_id)), None);
|
||||
}
|
||||
BezierHandles::Cubic { handle_start, handle_end } => {
|
||||
if not_under_anchor(handle_start, bezier.start) && (point_to_render == start) {
|
||||
overlay_context.line(handle_start, bezier.start, None, None);
|
||||
if not_under_anchor(handle_start, segment_start) && (point_to_render == start) {
|
||||
overlay_context.line(handle_start, segment_start, None, None);
|
||||
overlay_context.manipulator_handle(handle_start, is_selected(ManipulatorPointId::PrimaryHandle(segment_id)), None);
|
||||
}
|
||||
if not_under_anchor(handle_end, bezier.end) && (point_to_render == end) {
|
||||
overlay_context.line(handle_end, bezier.end, None, None);
|
||||
if not_under_anchor(handle_end, segment_end) && (point_to_render == end) {
|
||||
overlay_context.line(handle_end, segment_end, None, None);
|
||||
overlay_context.manipulator_handle(handle_end, is_selected(ManipulatorPointId::EndHandle(segment_id)), None);
|
||||
}
|
||||
}
|
||||
@@ -150,23 +155,23 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
|
||||
|
||||
match draw_handles {
|
||||
DrawHandles::All => {
|
||||
vector.segment_bezier_iter().for_each(|(segment_id, bezier, _start, _end)| {
|
||||
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
|
||||
vector.segment_iter().for_each(|(segment_id, segment, _start, _end)| {
|
||||
overlay_bezier_handles(segment, segment_id, transform, is_selected, overlay_context);
|
||||
});
|
||||
}
|
||||
DrawHandles::SelectedAnchors(ref selected_segments) => {
|
||||
let Some(focused_segments) = selected_segments.get(&layer) else { continue };
|
||||
|
||||
vector
|
||||
.segment_bezier_iter()
|
||||
.segment_iter()
|
||||
.filter(|(segment_id, ..)| focused_segments.contains(segment_id))
|
||||
.for_each(|(segment_id, bezier, _start, _end)| {
|
||||
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
|
||||
.for_each(|(segment_id, segment, _start, _end)| {
|
||||
overlay_bezier_handles(segment, segment_id, transform, is_selected, overlay_context);
|
||||
});
|
||||
|
||||
for (segment_id, bezier, start, end) in vector.segment_bezier_iter() {
|
||||
for (segment_id, segment, start, end) in vector.segment_iter() {
|
||||
if let Some((corresponding_anchor, _)) = opposite_handles_data.iter().find(|(_, adj_segment_id)| adj_segment_id == &segment_id) {
|
||||
overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), *corresponding_anchor, transform, is_selected, overlay_context);
|
||||
overlay_bezier_handle_specific_point(segment, segment_id, (start, end), *corresponding_anchor, transform, is_selected, overlay_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,14 +179,14 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
|
||||
let Some(segment_endpoints) = segment_endpoints_by_layer.get(&layer) else { continue };
|
||||
|
||||
vector
|
||||
.segment_bezier_iter()
|
||||
.segment_iter()
|
||||
.filter(|(segment_id, ..)| segment_endpoints.contains_key(segment_id))
|
||||
.for_each(|(segment_id, bezier, start, end)| {
|
||||
.for_each(|(segment_id, segment, start, end)| {
|
||||
if segment_endpoints.get(&segment_id).unwrap().len() == 1 {
|
||||
let point_to_render = segment_endpoints.get(&segment_id).unwrap()[0];
|
||||
overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), point_to_render, transform, is_selected, overlay_context);
|
||||
overlay_bezier_handle_specific_point(segment, segment_id, (start, end), point_to_render, transform, is_selected, overlay_context);
|
||||
} else {
|
||||
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
|
||||
overlay_bezier_handles(segment, segment_id, transform, is_selected, overlay_context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ impl OriginalTransforms {
|
||||
|
||||
let mut selected_points = selected_points.clone();
|
||||
|
||||
for (segment_id, _, start, end) in vector.segment_bezier_iter() {
|
||||
for (segment_id, _, start, end) in vector.segment_iter() {
|
||||
if selected_segments.contains(&segment_id) {
|
||||
selected_points.insert(ManipulatorPointId::Anchor(start));
|
||||
selected_points.insert(ManipulatorPointId::Anchor(end));
|
||||
|
||||
@@ -168,7 +168,7 @@ pub fn merge_points(document: &DocumentMessageHandler, layer: LayerNodeIdentifie
|
||||
let transform = document.metadata().transform_to_document(layer);
|
||||
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
|
||||
|
||||
let segment = vector.segment_bezier_iter().find(|(_, _, start, end)| *end == second_endpont || *start == second_endpont);
|
||||
let segment = vector.segment_iter().find(|(_, _, start, end)| *end == second_endpont || *start == second_endpont);
|
||||
let Some((segment, _, mut segment_start_point, mut segment_end_point)) = segment else {
|
||||
log::error!("Could not get the segment for second_endpoint.");
|
||||
return;
|
||||
|
||||
@@ -15,7 +15,7 @@ use glam::{DAffine2, DVec2};
|
||||
use graphene_std::subpath::{BezierHandles, Subpath};
|
||||
use graphene_std::subpath::{PathSegPoints, pathseg_points};
|
||||
use graphene_std::vector::algorithms::bezpath_algorithms::pathseg_compute_lookup_table;
|
||||
use graphene_std::vector::misc::{HandleId, ManipulatorPointId, dvec2_to_point, point_to_dvec2};
|
||||
use graphene_std::vector::misc::{HandleId, ManipulatorPointId, dvec2_to_point, point_to_dvec2, segment_to_handles};
|
||||
use graphene_std::vector::{HandleExt, PointId, SegmentId, Vector, VectorModificationType};
|
||||
use kurbo::{Affine, DEFAULT_ACCURACY, Line, ParamCurve, ParamCurveNearest, PathSeg, Rect, Shape};
|
||||
use std::f64::consts::TAU;
|
||||
@@ -251,7 +251,7 @@ impl ClosestSegment {
|
||||
// Transform to viewport space
|
||||
let transform = document_metadata.transform_to_viewport_if_feeds(self.layer, network_interface);
|
||||
|
||||
// Split the Bezier at the parameter `t`
|
||||
// Split the segment at the parameter `t`
|
||||
let first = self.bezier.subsegment(0_f64..self.t);
|
||||
let second = self.bezier.subsegment(self.t..1.);
|
||||
|
||||
@@ -788,7 +788,7 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
if segments {
|
||||
for (id, _, start, end) in vector.segment_bezier_iter() {
|
||||
for (id, _, start, end) in vector.segment_iter() {
|
||||
if connected_points.contains(&start) || connected_points.contains(&end) {
|
||||
state.select_segment(id);
|
||||
}
|
||||
@@ -950,14 +950,14 @@ impl ShapeState {
|
||||
|
||||
// Move the other handle for a quadratic bezier
|
||||
for segment in vector.end_connected(point) {
|
||||
let Some((start, _end, bezier)) = vector.segment_points_from_id(segment) else { continue };
|
||||
let Some((start, _end, path_segment)) = vector.segment_points_from_id(segment) else { continue };
|
||||
|
||||
if let BezierHandles::Quadratic { handle } = bezier.handles {
|
||||
if let BezierHandles::Quadratic { handle } = segment_to_handles(&path_segment) {
|
||||
if selected.is_some_and(|selected| selected.is_point_selected(ManipulatorPointId::Anchor(start))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let relative_position = handle - bezier.start + delta;
|
||||
let relative_position = handle - point_to_dvec2(path_segment.start()) + delta;
|
||||
let modification_type = VectorModificationType::SetPrimaryHandle { segment, relative_position };
|
||||
|
||||
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
||||
@@ -1284,7 +1284,7 @@ impl ShapeState {
|
||||
// Make a new set of anchor points which needs to be moved
|
||||
let mut affected_points = state.selected_points.clone();
|
||||
|
||||
for (segment_id, _, start, end) in vector.segment_bezier_iter() {
|
||||
for (segment_id, _, start, end) in vector.segment_iter() {
|
||||
if state.is_segment_selected(segment_id) {
|
||||
affected_points.insert(ManipulatorPointId::Anchor(start));
|
||||
affected_points.insert(ManipulatorPointId::Anchor(end));
|
||||
@@ -1570,7 +1570,7 @@ impl ShapeState {
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
|
||||
|
||||
for (segment, _, start, end) in vector.segment_bezier_iter() {
|
||||
for (segment, _, start, end) in vector.segment_iter() {
|
||||
if state.selected_segments.contains(&segment) {
|
||||
if start_transaction && !transaction_started {
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
@@ -1785,20 +1785,23 @@ impl ShapeState {
|
||||
let viewspace = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
|
||||
|
||||
// Handles
|
||||
for (segment_id, bezier, _, _) in vector.segment_bezier_iter() {
|
||||
let bezier = bezier.apply_transformation(|point| viewspace.transform_point2(point));
|
||||
for (segment_id, segment, _, _) in vector.segment_iter() {
|
||||
let segment = Affine::new(viewspace.to_cols_array()) * segment;
|
||||
let handles = segment_to_handles(&segment);
|
||||
let segment_start = point_to_dvec2(segment.start());
|
||||
let segment_end = point_to_dvec2(segment.end());
|
||||
let valid = |handle: DVec2, control: DVec2| handle.distance_squared(control) > crate::consts::HIDE_HANDLE_DISTANCE.powi(2);
|
||||
|
||||
if let Some(primary_handle) = bezier.handle_start()
|
||||
&& valid(primary_handle, bezier.start)
|
||||
&& (bezier.handle_end().is_some() || valid(primary_handle, bezier.end))
|
||||
if let Some(primary_handle) = handles.start()
|
||||
&& valid(primary_handle, segment_start)
|
||||
&& (handles.end().is_some() || valid(primary_handle, segment_end))
|
||||
&& primary_handle.distance_squared(pos) <= closest_distance_squared
|
||||
{
|
||||
closest_distance_squared = primary_handle.distance_squared(pos);
|
||||
manipulator_point = Some(ManipulatorPointId::PrimaryHandle(segment_id));
|
||||
}
|
||||
if let Some(end_handle) = bezier.handle_end()
|
||||
&& valid(end_handle, bezier.end)
|
||||
if let Some(end_handle) = handles.end()
|
||||
&& valid(end_handle, segment_end)
|
||||
&& end_handle.distance_squared(pos) <= closest_distance_squared
|
||||
{
|
||||
closest_distance_squared = end_handle.distance_squared(pos);
|
||||
@@ -2071,9 +2074,9 @@ impl ShapeState {
|
||||
self.convert_manipulator_handles_to_colinear(&vector, point_id, responses, layer);
|
||||
} else {
|
||||
for handle in vector.all_connected(point_id) {
|
||||
let Some(bezier) = vector.segment_from_id(handle.segment) else { continue };
|
||||
let Some(path_segment) = vector.segment_from_id(handle.segment) else { continue };
|
||||
|
||||
match bezier.handles {
|
||||
match segment_to_handles(&path_segment) {
|
||||
BezierHandles::Linear => {}
|
||||
BezierHandles::Quadratic { .. } => {
|
||||
let segment = handle.segment;
|
||||
@@ -2173,7 +2176,7 @@ impl ShapeState {
|
||||
let Some(vector) = network_interface.compute_modified_vector(layer) else { continue };
|
||||
if !select_points && select_segments {
|
||||
vector
|
||||
.segment_bezier_iter()
|
||||
.segment_iter()
|
||||
.filter(|(segment, _, _, _)| segments.contains(segment))
|
||||
.for_each(|(_, _, start, end)| match selection_change {
|
||||
SelectionChange::Shrink => {
|
||||
|
||||
@@ -30,7 +30,7 @@ use graphene_std::transform::ReferencePoint;
|
||||
use graphene_std::uuid::NodeId;
|
||||
use graphene_std::vector::algorithms::util::pathseg_tangent;
|
||||
use graphene_std::vector::click_target::ClickTargetType;
|
||||
use graphene_std::vector::misc::{HandleId, ManipulatorPointId, dvec2_to_point, point_to_dvec2};
|
||||
use graphene_std::vector::misc::{HandleId, ManipulatorPointId, dvec2_to_point, point_to_dvec2, segment_to_handles};
|
||||
use graphene_std::vector::{HandleExt, NoHashBuilder, PointId, SegmentId, Vector, VectorModificationType};
|
||||
use kurbo::{DEFAULT_ACCURACY, ParamCurve, ParamCurveNearest, PathSeg, Rect};
|
||||
use std::vec;
|
||||
@@ -1647,7 +1647,7 @@ impl Fsm for PathToolFsmState {
|
||||
|
||||
let selected_state = shape_editor.selected_shape_state.entry(layer).or_default();
|
||||
|
||||
for (segment, _, start, end) in vector.segment_bezier_iter() {
|
||||
for (segment, _, start, end) in vector.segment_iter() {
|
||||
if selected_state.is_segment_selected(segment) {
|
||||
selected_state.select_point(ManipulatorPointId::Anchor(start));
|
||||
selected_state.select_point(ManipulatorPointId::Anchor(end));
|
||||
@@ -1700,7 +1700,7 @@ impl Fsm for PathToolFsmState {
|
||||
|
||||
let selected_state = shape_editor.selected_shape_state.entry(layer).or_default();
|
||||
|
||||
for (segment, _, start, end) in vector.segment_bezier_iter() {
|
||||
for (segment, _, start, end) in vector.segment_iter() {
|
||||
let first_selected = selected_state.is_point_selected(ManipulatorPointId::Anchor(start));
|
||||
let second_selected = selected_state.is_point_selected(ManipulatorPointId::Anchor(end));
|
||||
if first_selected && second_selected {
|
||||
@@ -1764,7 +1764,7 @@ impl Fsm for PathToolFsmState {
|
||||
// The points which are part of only one segment will be rendered
|
||||
let mut selected_segments_by_point: HashMap<PointId, Vec<SegmentId>> = HashMap::new();
|
||||
|
||||
for (segment_id, _bezier, start, end) in vector.segment_bezier_iter() {
|
||||
for (segment_id, _, start, end) in vector.segment_iter() {
|
||||
if focused_segments.contains(&segment_id) {
|
||||
selected_segments_by_point.entry(start).or_default().push(segment_id);
|
||||
selected_segments_by_point.entry(end).or_default().push(segment_id);
|
||||
@@ -2764,7 +2764,7 @@ impl Fsm for PathToolFsmState {
|
||||
|
||||
let mut selected_points_by_segment = HashSet::new();
|
||||
old_vector
|
||||
.segment_bezier_iter()
|
||||
.segment_iter()
|
||||
.filter(|(segment, _, _, _)| layer_selection_state.is_segment_selected(*segment))
|
||||
.for_each(|(_, _, start, end)| {
|
||||
selected_points_by_segment.insert(start);
|
||||
@@ -2781,7 +2781,7 @@ impl Fsm for PathToolFsmState {
|
||||
let find_index = |id: PointId| new_vector.point_domain.iter().enumerate().find(|(_, (point_id, _))| *point_id == id).map(|(index, _)| index);
|
||||
|
||||
// Add segments which have selected ends
|
||||
for ((segment_id, bezier, start, end), stroke) in old_vector.segment_bezier_iter().zip(old_vector.segment_domain.stroke().iter()) {
|
||||
for ((segment_id, segment, start, end), stroke) in old_vector.segment_iter().zip(old_vector.segment_domain.stroke().iter()) {
|
||||
let both_ends_selected = layer_selection_state.is_point_selected(ManipulatorPointId::Anchor(start)) && layer_selection_state.is_point_selected(ManipulatorPointId::Anchor(end));
|
||||
|
||||
let segment_selected = layer_selection_state.is_segment_selected(segment_id);
|
||||
@@ -2791,7 +2791,7 @@ impl Fsm for PathToolFsmState {
|
||||
error!("Point does not exist in point domain");
|
||||
return PathToolFsmState::Ready;
|
||||
};
|
||||
new_vector.segment_domain.push(segment_id, start_index, end_index, bezier.handles, *stroke);
|
||||
new_vector.segment_domain.push(segment_id, start_index, end_index, segment_to_handles(&segment), *stroke);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2945,7 +2945,7 @@ impl Fsm for PathToolFsmState {
|
||||
// Add all the selected points
|
||||
let mut selected_points_by_segment = HashSet::new();
|
||||
old_vector
|
||||
.segment_bezier_iter()
|
||||
.segment_iter()
|
||||
.filter(|(segment, _, _, _)| layer_selection_state.is_segment_selected(*segment))
|
||||
.for_each(|(_, _, start, end)| {
|
||||
selected_points_by_segment.insert(start);
|
||||
@@ -3398,7 +3398,7 @@ fn calculate_adjacent_anchor_tangent(currently_dragged_handle: ManipulatorPointI
|
||||
0 => {
|
||||
// Find non-shared segments
|
||||
let non_shared_segment: Vec<_> = vector
|
||||
.segment_bezier_iter()
|
||||
.segment_iter()
|
||||
.filter_map(|(segment_id, _, start, end)| {
|
||||
let touches_adjacent = start == adjacent_anchor || end == adjacent_anchor;
|
||||
let shares_with_dragged = start == dragged_handle_anchor || end == dragged_handle_anchor;
|
||||
|
||||
@@ -162,7 +162,7 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
|
||||
let selected_segments = shape_editor.selected_segments().collect::<HashSet<_>>();
|
||||
let mut affected_points = shape_editor.selected_points().copied().collect::<Vec<_>>();
|
||||
|
||||
for (segment_id, _, start, end) in vector.segment_bezier_iter() {
|
||||
for (segment_id, _, start, end) in vector.segment_iter() {
|
||||
if selected_segments.contains(&segment_id) {
|
||||
affected_points.push(ManipulatorPointId::Anchor(start));
|
||||
affected_points.push(ManipulatorPointId::Anchor(end));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::vector::algorithms::intersection::filtered_segment_intersections;
|
||||
use crate::vector::misc::{dvec2_to_point, handles_to_segment};
|
||||
use crate::vector::misc::dvec2_to_point;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use kurbo::{CubicBez, Line, PathSeg, QuadBez, Shape};
|
||||
use kurbo::{CubicBez, Line, PathSeg, QuadBez};
|
||||
use std::fmt::{Debug, Formatter, Result};
|
||||
use std::hash::Hash;
|
||||
|
||||
@@ -163,187 +162,3 @@ impl BezierHandles {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of a bezier curve with 2D points.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Bezier {
|
||||
/// Start point of the bezier curve.
|
||||
pub start: DVec2,
|
||||
/// End point of the bezier curve.
|
||||
pub end: DVec2,
|
||||
/// Handles of the bezier curve.
|
||||
pub handles: BezierHandles,
|
||||
}
|
||||
|
||||
impl Debug for Bezier {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
let mut debug_struct = f.debug_struct("Bezier");
|
||||
let mut debug_struct_ref = debug_struct.field("start", &self.start);
|
||||
debug_struct_ref = match self.handles {
|
||||
BezierHandles::Linear => debug_struct_ref,
|
||||
BezierHandles::Quadratic { handle } => debug_struct_ref.field("handle", &handle),
|
||||
BezierHandles::Cubic { handle_start, handle_end } => debug_struct_ref.field("handle_start", &handle_start).field("handle_end", &handle_end),
|
||||
};
|
||||
debug_struct_ref.field("end", &self.end).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Functionality for the getters and setters of the various points in a Bezier
|
||||
impl Bezier {
|
||||
/// Set the coordinates of the start point.
|
||||
pub fn set_start(&mut self, s: DVec2) {
|
||||
self.start = s;
|
||||
}
|
||||
|
||||
/// Set the coordinates of the end point.
|
||||
pub fn set_end(&mut self, e: DVec2) {
|
||||
self.end = e;
|
||||
}
|
||||
|
||||
/// Set the coordinates of the first handle point. This represents the only handle in a quadratic segment. If used on a linear segment, it will be changed to a quadratic.
|
||||
pub fn set_handle_start(&mut self, h1: DVec2) {
|
||||
match self.handles {
|
||||
BezierHandles::Linear => {
|
||||
self.handles = BezierHandles::Quadratic { handle: h1 };
|
||||
}
|
||||
BezierHandles::Quadratic { ref mut handle } => {
|
||||
*handle = h1;
|
||||
}
|
||||
BezierHandles::Cubic { ref mut handle_start, .. } => {
|
||||
*handle_start = h1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Set the coordinates of the second handle point. This will convert both linear and quadratic segments into cubic ones. For a linear segment, the first handle will be set to the start point.
|
||||
pub fn set_handle_end(&mut self, h2: DVec2) {
|
||||
match self.handles {
|
||||
BezierHandles::Linear => {
|
||||
self.handles = BezierHandles::Cubic {
|
||||
handle_start: self.start,
|
||||
handle_end: h2,
|
||||
};
|
||||
}
|
||||
BezierHandles::Quadratic { handle } => {
|
||||
self.handles = BezierHandles::Cubic { handle_start: handle, handle_end: h2 };
|
||||
}
|
||||
BezierHandles::Cubic { ref mut handle_end, .. } => {
|
||||
*handle_end = h2;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Get the coordinates of the bezier segment's start point.
|
||||
pub fn start(&self) -> DVec2 {
|
||||
self.start
|
||||
}
|
||||
|
||||
/// Get the coordinates of the bezier segment's end point.
|
||||
pub fn end(&self) -> DVec2 {
|
||||
self.end
|
||||
}
|
||||
|
||||
/// Get the coordinates of the bezier segment's first handle point. This represents the only handle in a quadratic segment.
|
||||
pub fn handle_start(&self) -> Option<DVec2> {
|
||||
self.handles.start()
|
||||
}
|
||||
|
||||
/// Get the coordinates of the second handle point. This will return `None` for a quadratic segment.
|
||||
pub fn handle_end(&self) -> Option<DVec2> {
|
||||
self.handles.end()
|
||||
}
|
||||
|
||||
/// Get an iterator over the coordinates of all points in a vector.
|
||||
/// - For a linear segment, the order of the points will be: `start`, `end`.
|
||||
/// - For a quadratic segment, the order of the points will be: `start`, `handle`, `end`.
|
||||
/// - For a cubic segment, the order of the points will be: `start`, `handle_start`, `handle_end`, `end`.
|
||||
pub fn get_points(&self) -> impl Iterator<Item = DVec2> + use<> {
|
||||
match self.handles {
|
||||
BezierHandles::Linear => [self.start, self.end, DVec2::ZERO, DVec2::ZERO].into_iter().take(2),
|
||||
BezierHandles::Quadratic { handle } => [self.start, handle, self.end, DVec2::ZERO].into_iter().take(3),
|
||||
BezierHandles::Cubic { handle_start, handle_end } => [self.start, handle_start, handle_end, self.end].into_iter().take(4),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider removing this function
|
||||
/// Create a linear bezier using the provided coordinates as the start and end points.
|
||||
pub fn from_linear_coordinates(x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
|
||||
Bezier {
|
||||
start: DVec2::new(x1, y1),
|
||||
handles: BezierHandles::Linear,
|
||||
end: DVec2::new(x2, y2),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a linear bezier using the provided DVec2s as the start and end points.
|
||||
pub fn from_linear_dvec2(p1: DVec2, p2: DVec2) -> Self {
|
||||
Bezier {
|
||||
start: p1,
|
||||
handles: BezierHandles::Linear,
|
||||
end: p2,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider removing this function
|
||||
/// Create a quadratic bezier using the provided coordinates as the start, handle, and end points.
|
||||
pub fn from_quadratic_coordinates(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) -> Self {
|
||||
Bezier {
|
||||
start: DVec2::new(x1, y1),
|
||||
handles: BezierHandles::Quadratic { handle: DVec2::new(x2, y2) },
|
||||
end: DVec2::new(x3, y3),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a quadratic bezier using the provided DVec2s as the start, handle, and end points.
|
||||
pub fn from_quadratic_dvec2(p1: DVec2, p2: DVec2, p3: DVec2) -> Self {
|
||||
Bezier {
|
||||
start: p1,
|
||||
handles: BezierHandles::Quadratic { handle: p2 },
|
||||
end: p3,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider removing this function
|
||||
/// Create a cubic bezier using the provided coordinates as the start, handles, and end points.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn from_cubic_coordinates(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> Self {
|
||||
Bezier {
|
||||
start: DVec2::new(x1, y1),
|
||||
handles: BezierHandles::Cubic {
|
||||
handle_start: DVec2::new(x2, y2),
|
||||
handle_end: DVec2::new(x3, y3),
|
||||
},
|
||||
end: DVec2::new(x4, y4),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a cubic bezier using the provided DVec2s as the start, handles, and end points.
|
||||
pub fn from_cubic_dvec2(p1: DVec2, p2: DVec2, p3: DVec2, p4: DVec2) -> Self {
|
||||
Bezier {
|
||||
start: p1,
|
||||
handles: BezierHandles::Cubic { handle_start: p2, handle_end: p3 },
|
||||
end: p4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a Bezier curve that results from applying the transformation function to each point in the Bezier.
|
||||
pub fn apply_transformation(&self, transformation_function: impl Fn(DVec2) -> DVec2) -> Bezier {
|
||||
Self {
|
||||
start: transformation_function(self.start),
|
||||
end: transformation_function(self.end),
|
||||
handles: self.handles.apply_transformation(transformation_function),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intersections(&self, other: &Bezier, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<f64> {
|
||||
let this = handles_to_segment(self.start, self.handles, self.end);
|
||||
let other = handles_to_segment(other.start, other.handles, other.end);
|
||||
filtered_segment_intersections(this, other, accuracy, minimum_separation)
|
||||
}
|
||||
|
||||
pub fn winding(&self, point: DVec2) -> i32 {
|
||||
let this = handles_to_segment(self.start, self.handles, self.end);
|
||||
this.winding(dvec2_to_point(point))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,8 +449,8 @@ impl ManipulatorPointId {
|
||||
pub fn get_position(&self, vector: &Vector) -> Option<DVec2> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(id) => vector.point_domain.position_from_id(*id),
|
||||
ManipulatorPointId::PrimaryHandle(id) => vector.segment_from_id(*id).and_then(|bezier| bezier.handle_start()),
|
||||
ManipulatorPointId::EndHandle(id) => vector.segment_from_id(*id).and_then(|bezier| bezier.handle_end()),
|
||||
ManipulatorPointId::PrimaryHandle(id) => vector.segment_from_id(*id).and_then(|segment| segment_to_handles(&segment).start()),
|
||||
ManipulatorPointId::EndHandle(id) => vector.segment_from_id(*id).and_then(|segment| segment_to_handles(&segment).end()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::subpath::{Bezier, BezierHandles, Identifier, ManipulatorGroup, Subpath};
|
||||
use crate::subpath::{BezierHandles, Identifier, ManipulatorGroup, Subpath};
|
||||
use crate::vector::misc::{HandleId, Tangent, dvec2_to_point};
|
||||
use crate::vector::vector_types::Vector;
|
||||
use dyn_any::DynAny;
|
||||
@@ -881,33 +881,26 @@ impl Vector {
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a [`Bezier`] curve spanning from the resolved position of the start and end points with the specified handles.
|
||||
fn segment_to_bezier_with_index(&self, start: usize, end: usize, handles: BezierHandles) -> Bezier {
|
||||
let start = self.point_domain.positions()[start];
|
||||
let end = self.point_domain.positions()[end];
|
||||
Bezier { start, end, handles }
|
||||
/// Tries to convert a segment with the specified id to a [`PathSeg`], returning None if the id is invalid.
|
||||
pub fn segment_from_id(&self, id: SegmentId) -> Option<PathSeg> {
|
||||
self.segment_points_from_id(id).map(|(_, _, segment)| segment)
|
||||
}
|
||||
|
||||
/// Tries to convert a segment with the specified id to a [`Bezier`], returning None if the id is invalid.
|
||||
pub fn segment_from_id(&self, id: SegmentId) -> Option<Bezier> {
|
||||
self.segment_points_from_id(id).map(|(_, _, bezier)| bezier)
|
||||
}
|
||||
|
||||
/// Tries to convert a segment with the specified id to the start and end points and a [`Bezier`], returning None if the id is invalid.
|
||||
pub fn segment_points_from_id(&self, id: SegmentId) -> Option<(PointId, PointId, Bezier)> {
|
||||
/// Tries to convert a segment with the specified id to the start and end points and a [`PathSeg`], returning None if the id is invalid.
|
||||
pub fn segment_points_from_id(&self, id: SegmentId) -> Option<(PointId, PointId, PathSeg)> {
|
||||
Some(self.segment_points_from_index(self.segment_domain.id_to_index(id)?))
|
||||
}
|
||||
|
||||
/// Tries to convert a segment with the specified index to the start and end points and a [`Bezier`].
|
||||
pub fn segment_points_from_index(&self, index: usize) -> (PointId, PointId, Bezier) {
|
||||
/// Converts a segment with the specified index to the start and end points and a [`PathSeg`].
|
||||
pub fn segment_points_from_index(&self, index: usize) -> (PointId, PointId, PathSeg) {
|
||||
let start = self.segment_domain.start_point[index];
|
||||
let end = self.segment_domain.end_point[index];
|
||||
let start_id = self.point_domain.ids()[start];
|
||||
let end_id = self.point_domain.ids()[end];
|
||||
(start_id, end_id, self.segment_to_bezier_with_index(start, end, self.segment_domain.handles[index]))
|
||||
(start_id, end_id, self.path_segment_from_index(start, end, self.segment_domain.handles[index]))
|
||||
}
|
||||
|
||||
/// Iterator over all of the [`Bezier`] following the order that they are stored in the segment domain, skipping invalid segments.
|
||||
/// Iterator over all of the [`PathSeg`]s following the order that they are stored in the segment domain, skipping invalid segments.
|
||||
pub fn segment_iter(&self) -> impl Iterator<Item = (SegmentId, PathSeg, PointId, PointId)> {
|
||||
let to_segment = |(((&handles, &id), &start), &end)| (id, self.path_segment_from_index(start, end, handles), self.point_domain.ids()[start], self.point_domain.ids()[end]);
|
||||
|
||||
@@ -920,18 +913,6 @@ impl Vector {
|
||||
.map(to_segment)
|
||||
}
|
||||
|
||||
/// Iterator over all of the [`Bezier`] following the order that they are stored in the segment domain, skipping invalid segments.
|
||||
pub fn segment_bezier_iter(&self) -> impl Iterator<Item = (SegmentId, Bezier, PointId, PointId)> + '_ {
|
||||
let to_bezier = |(((&handles, &id), &start), &end)| (id, self.segment_to_bezier_with_index(start, end, handles), self.point_domain.ids()[start], self.point_domain.ids()[end]);
|
||||
self.segment_domain
|
||||
.handles
|
||||
.iter()
|
||||
.zip(&self.segment_domain.id)
|
||||
.zip(self.segment_domain.start_point())
|
||||
.zip(self.segment_domain.end_point())
|
||||
.map(to_bezier)
|
||||
}
|
||||
|
||||
pub fn auto_join_paths(&self) -> Vec<FoundSubpath> {
|
||||
let segments = self.segment_domain.iter().map(|(id, start, end, _)| HalfEdge::new(id, start, end, false));
|
||||
|
||||
@@ -1003,7 +984,7 @@ impl Vector {
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a [`Bezier`] curve from an iterator of segments with (handles, start point, end point) independently of discontinuities.
|
||||
/// Construct a [`Subpath`] from an iterator of segments with (handles, start point, end point) independently of discontinuities.
|
||||
pub fn subpath_from_segments_ignore_discontinuities(&self, segments: impl Iterator<Item = (BezierHandles, usize, usize)>) -> Option<Subpath<PointId>> {
|
||||
let mut first_point = None;
|
||||
let mut manipulators_list = Vec::new();
|
||||
@@ -1055,7 +1036,7 @@ impl Vector {
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a [`Bezier`] curve for stroke.
|
||||
/// Construct a [`Subpath`] for each stroke path.
|
||||
pub fn stroke_bezier_paths(&self) -> impl Iterator<Item = Subpath<PointId>> {
|
||||
self.build_stroke_path_iter().map(|(manipulators_list, closed)| Subpath::new(manipulators_list, closed))
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use super::*;
|
||||
use crate::subpath::BezierHandles;
|
||||
use crate::vector::misc::{HandleId, HandleType, point_to_dvec2};
|
||||
use crate::vector::misc::{HandleId, HandleType, point_to_dvec2, segment_to_handles};
|
||||
use core_types::uuid::generate_uuid;
|
||||
use dyn_any::DynAny;
|
||||
use glam::DVec2;
|
||||
use kurbo::{BezPath, PathEl, Point};
|
||||
use kurbo::{BezPath, ParamCurve, PathEl, Point};
|
||||
use serde::de::{SeqAccess, Visitor};
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
@@ -225,8 +225,14 @@ impl SegmentModification {
|
||||
remove: HashSet::new(),
|
||||
start_point: vector.segment_domain.ids().iter().zip(vector.segment_domain.start_point()).map(point_id).collect(),
|
||||
end_point: vector.segment_domain.ids().iter().zip(vector.segment_domain.end_point()).map(point_id).collect(),
|
||||
handle_primary: vector.segment_bezier_iter().map(|(id, b, _, _)| (id, b.handle_start().map(|handle| handle - b.start))).collect(),
|
||||
handle_end: vector.segment_bezier_iter().map(|(id, b, _, _)| (id, b.handle_end().map(|handle| handle - b.end))).collect(),
|
||||
handle_primary: vector
|
||||
.segment_iter()
|
||||
.map(|(id, segment, _, _)| (id, segment_to_handles(&segment).start().map(|handle| handle - point_to_dvec2(segment.start()))))
|
||||
.collect(),
|
||||
handle_end: vector
|
||||
.segment_iter()
|
||||
.map(|(id, segment, _, _)| (id, segment_to_handles(&segment).end().map(|handle| handle - point_to_dvec2(segment.end()))))
|
||||
.collect(),
|
||||
stroke: vector.segment_domain.ids().iter().copied().zip(vector.segment_domain.stroke().iter().cloned()).collect(),
|
||||
}
|
||||
}
|
||||
@@ -808,7 +814,8 @@ impl HandleExt for HandleId {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::subpath::{Bezier, ManipulatorGroup, Subpath};
|
||||
use crate::subpath::{ManipulatorGroup, Subpath};
|
||||
use kurbo::{PathSeg, QuadBez};
|
||||
|
||||
#[test]
|
||||
fn modify_new() {
|
||||
@@ -856,12 +863,12 @@ mod tests {
|
||||
assert_eq!(vector.point_domain.positions()[0], DVec2::X);
|
||||
assert_eq!(vector.point_domain.positions()[9], DVec2::new(11., 0.));
|
||||
assert_eq!(
|
||||
vector.segment_bezier_iter().nth(8).unwrap().1,
|
||||
Bezier::from_quadratic_dvec2(DVec2::new(0., 0.), DVec2::new(5., 10.), DVec2::new(11., 0.))
|
||||
vector.segment_iter().nth(8).unwrap().1,
|
||||
PathSeg::Quad(QuadBez::new(Point::new(0., 0.), Point::new(5., 10.), Point::new(11., 0.)))
|
||||
);
|
||||
assert_eq!(
|
||||
vector.segment_bezier_iter().nth(9).unwrap().1,
|
||||
Bezier::from_quadratic_dvec2(DVec2::new(11., 0.), DVec2::new(16., 10.), DVec2::new(20., 0.))
|
||||
vector.segment_iter().nth(9).unwrap().1,
|
||||
PathSeg::Quad(QuadBez::new(Point::new(11., 0.), Point::new(16., 10.), Point::new(20., 0.)))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,8 +342,8 @@ impl Vector {
|
||||
|
||||
/// Returns the number of linear segments connected to the given point.
|
||||
pub fn connected_linear_segments(&self, point_id: PointId) -> usize {
|
||||
self.segment_bezier_iter()
|
||||
.filter(|(_, bez, start, end)| (*start == point_id || *end == point_id) && matches!(bez.handles, BezierHandles::Linear))
|
||||
self.segment_iter()
|
||||
.filter(|(_, segment, start, end)| (*start == point_id || *end == point_id) && matches!(segment, kurbo::PathSeg::Line(_)))
|
||||
.count()
|
||||
}
|
||||
|
||||
|
||||
@@ -382,6 +382,8 @@ fn grid<T: GridSpacing>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use kurbo::ParamCurve;
|
||||
use vector_types::vector::misc::point_to_dvec2;
|
||||
#[test]
|
||||
fn isometric_grid_test() {
|
||||
// Doesn't crash with weird angles
|
||||
@@ -391,14 +393,11 @@ mod tests {
|
||||
// Works properly
|
||||
let grid = grid(&(), (), GridType::Isometric, 10., 5, 5, (30., 30.).into(), true);
|
||||
assert_eq!(grid.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.segment_bezier_iter() {
|
||||
assert_eq!(bezier.handles, subpath::BezierHandles::Linear);
|
||||
assert!(
|
||||
((bezier.start - bezier.end).length() - 10.).abs() < 1e-5,
|
||||
"Length of {} should be 10",
|
||||
(bezier.start - bezier.end).length()
|
||||
);
|
||||
assert_eq!(grid.segment_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, segment, _, _) in grid.segment_iter() {
|
||||
assert!(matches!(segment, kurbo::PathSeg::Line(_)));
|
||||
let span = point_to_dvec2(segment.start()) - point_to_dvec2(segment.end());
|
||||
assert!((span.length() - 10.).abs() < 1e-5, "Length of {} should be 10", span.length());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,10 +405,10 @@ mod tests {
|
||||
fn skew_isometric_grid_test() {
|
||||
let grid = grid(&(), (), GridType::Isometric, 10., 5, 5, (40., 30.).into(), true);
|
||||
assert_eq!(grid.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.segment_bezier_iter() {
|
||||
assert_eq!(bezier.handles, subpath::BezierHandles::Linear);
|
||||
let vector = bezier.start - bezier.end;
|
||||
assert_eq!(grid.segment_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, segment, _, _) in grid.segment_iter() {
|
||||
assert!(matches!(segment, kurbo::PathSeg::Line(_)));
|
||||
let vector = point_to_dvec2(segment.start()) - point_to_dvec2(segment.end());
|
||||
let angle = (vector.angle_to(DVec2::X).to_degrees() + 180.) % 180.;
|
||||
assert!([90f64, 150., 40.].into_iter().any(|target| (target - angle).abs() < 1e-10), "unexpected angle of {angle}")
|
||||
}
|
||||
|
||||
@@ -738,16 +738,6 @@ pub mod extrude_algorithms {
|
||||
use vector_types::vector::StrokeId;
|
||||
use vector_types::vector::misc::ExtrudeJoiningAlgorithm;
|
||||
|
||||
/// Convert [`vector_types::subpath::Bezier`] to [`kurbo::PathSeg`].
|
||||
fn bezier_to_path_seg(bezier: vector_types::subpath::Bezier) -> kurbo::PathSeg {
|
||||
let [start, end] = [(bezier.start().x, bezier.start().y), (bezier.end().x, bezier.end().y)];
|
||||
match bezier.handles {
|
||||
BezierHandles::Linear => kurbo::Line::new(start, end).into(),
|
||||
BezierHandles::Quadratic { handle } => kurbo::QuadBez::new(start, (handle.x, handle.y), end).into(),
|
||||
BezierHandles::Cubic { handle_start, handle_end } => kurbo::CubicBez::new(start, (handle_start.x, handle_start.y), (handle_end.x, handle_end.y), end).into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert [`kurbo::CubicBez`] to [`vector_types::subpath::BezierHandles`].
|
||||
fn cubic_to_handles(cubic_bez: kurbo::CubicBez) -> BezierHandles {
|
||||
BezierHandles::Cubic {
|
||||
@@ -778,9 +768,9 @@ pub mod extrude_algorithms {
|
||||
let mut next_segment = vector.segment_domain.next_id();
|
||||
|
||||
for segment_index in 0..segment_count {
|
||||
let (_, _, bezier) = vector.segment_points_from_index(segment_index);
|
||||
let (_, _, segment) = vector.segment_points_from_index(segment_index);
|
||||
let mut start_index = vector.segment_domain.start_point()[segment_index];
|
||||
let pathseg = bezier_to_path_seg(bezier).to_cubic();
|
||||
let pathseg = segment.to_cubic();
|
||||
let mut start_t = 0.;
|
||||
|
||||
for split_t in find_splits(pathseg, direction) {
|
||||
|
||||
Reference in New Issue
Block a user