Skip to content

Commit be9f978

Browse files
committed
move ClickTarget code to mod vector::click_target
1 parent 7d98eb6 commit be9f978

File tree

10 files changed

+177
-168
lines changed

10 files changed

+177
-168
lines changed

editor/src/messages/portfolio/document/document_message.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use graph_craft::document::NodeId;
1111
use graphene_std::Color;
1212
use graphene_std::raster::BlendMode;
1313
use graphene_std::raster::Image;
14-
use graphene_std::renderer::ClickTarget;
1514
use graphene_std::transform::Footprint;
15+
use graphene_std::vector::click_target::ClickTarget;
1616
use graphene_std::vector::style::ViewMode;
1717

1818
#[impl_message(Message, PortfolioMessage, Document)]

editor/src/messages/portfolio/document/document_message_handler.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ use bezier_rs::Subpath;
2929
use glam::{DAffine2, DVec2, IVec2};
3030
use graph_craft::document::value::TaggedValue;
3131
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
32+
use graphene_std::math::quad::Quad;
3233
use graphene_std::raster::BlendMode;
3334
use graphene_std::raster_types::{Raster, RasterDataTable};
34-
use graphene_std::renderer::{ClickTarget, ClickTargetType, Quad};
35+
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
3536
use graphene_std::vector::style::ViewMode;
3637
use graphene_std::vector::{PointId, path_bool_lib};
3738
use std::time::Duration;

editor/src/messages/portfolio/document/overlays/utility_types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use core::borrow::Borrow;
99
use core::f64::consts::{FRAC_PI_2, TAU};
1010
use glam::{DAffine2, DVec2};
1111
use graphene_std::Color;
12-
use graphene_std::renderer::ClickTargetType;
13-
use graphene_std::renderer::Quad;
12+
use graphene_std::math::quad::Quad;
13+
use graphene_std::vector::click_target::ClickTargetType;
1414
use graphene_std::vector::{PointId, SegmentId, VectorData};
1515
use std::collections::HashMap;
1616
use wasm_bindgen::{JsCast, JsValue};

editor/src/messages/portfolio/document/utility_types/document_metadata.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ use crate::messages::portfolio::document::graph_operation::transform_utils;
33
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
44
use glam::{DAffine2, DVec2};
55
use graph_craft::document::NodeId;
6-
use graphene_std::renderer::{ClickTarget, ClickTargetType, Quad};
6+
use graphene_std::math::quad::Quad;
77
use graphene_std::transform::Footprint;
8+
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
89
use graphene_std::vector::{PointId, VectorData};
910
use std::collections::{HashMap, HashSet};
1011
use std::num::NonZeroU64;

editor/src/messages/portfolio/document/utility_types/network_interface.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ use glam::{DAffine2, DVec2, IVec2};
1212
use graph_craft::document::value::TaggedValue;
1313
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork};
1414
use graph_craft::{Type, concrete};
15-
use graphene_std::renderer::{ClickTarget, ClickTargetType, Quad};
15+
use graphene_std::math::quad::Quad;
1616
use graphene_std::transform::Footprint;
17+
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
1718
use graphene_std::vector::{PointId, VectorData, VectorModificationType};
1819
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes;
1920
use interpreted_executor::node_registry::NODE_REGISTRY;

editor/src/messages/tool/common_functionality/shapes/shape_utility.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use bezier_rs::Subpath;
1313
use glam::{DAffine2, DMat2, DVec2};
1414
use graph_craft::document::NodeInput;
1515
use graph_craft::document::value::TaggedValue;
16-
use graphene_std::renderer::ClickTargetType;
16+
use graphene_std::vector::click_target::ClickTargetType;
1717
use graphene_std::vector::misc::dvec2_to_point;
1818
use kurbo::{BezPath, PathEl, Shape};
1919
use std::collections::VecDeque;

node-graph/gcore/src/graphic_element/renderer.rs

Lines changed: 3 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,23 @@
11
use crate::instances::Instance;
2-
use crate::math::math_ext::QuadExt;
32
pub use crate::math::quad::Quad;
43
pub use crate::math::rect::Rect;
54
use crate::raster::{BlendMode, Image};
65
use crate::raster_types::{CPU, GPU, RasterDataTable};
76
use crate::transform::{Footprint, Transform};
87
use crate::uuid::{NodeId, generate_uuid};
8+
use crate::vector::VectorDataTable;
9+
use crate::vector::click_target::{ClickTarget, FreePoint};
910
use crate::vector::style::{Fill, Stroke, StrokeAlign, ViewMode};
10-
use crate::vector::{PointId, VectorDataTable};
1111
use crate::{Artboard, ArtboardGroupTable, Color, GraphicElement, GraphicGroupTable};
1212
use bezier_rs::Subpath;
1313
use dyn_any::DynAny;
14-
use glam::{DAffine2, DMat2, DVec2};
14+
use glam::{DAffine2, DVec2};
1515
use num_traits::Zero;
1616
use std::collections::{HashMap, HashSet};
1717
use std::fmt::Write;
1818
#[cfg(feature = "vello")]
1919
use vello::*;
2020

21-
#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
22-
pub struct FreePoint {
23-
pub id: PointId,
24-
pub position: DVec2,
25-
}
26-
27-
impl FreePoint {
28-
pub fn new(id: PointId, position: DVec2) -> Self {
29-
Self { id, position }
30-
}
31-
32-
pub fn apply_transform(&mut self, transform: DAffine2) {
33-
self.position = transform.transform_point2(self.position);
34-
}
35-
}
36-
37-
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
38-
pub enum ClickTargetType {
39-
Subpath(Subpath<PointId>),
40-
FreePoint(FreePoint),
41-
}
42-
43-
/// Represents a clickable target for the layer
44-
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
45-
pub struct ClickTarget {
46-
target_type: ClickTargetType,
47-
stroke_width: f64,
48-
bounding_box: Option<[DVec2; 2]>,
49-
}
50-
5121
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
5222
enum MaskType {
5323
Clip,
@@ -71,133 +41,6 @@ impl MaskType {
7141
}
7242
}
7343

74-
impl ClickTarget {
75-
pub fn new_with_subpath(subpath: Subpath<PointId>, stroke_width: f64) -> Self {
76-
let bounding_box = subpath.loose_bounding_box();
77-
Self {
78-
target_type: ClickTargetType::Subpath(subpath),
79-
stroke_width,
80-
bounding_box,
81-
}
82-
}
83-
84-
pub fn new_with_free_point(point: FreePoint) -> Self {
85-
const MAX_LENGTH_FOR_NO_WIDTH_OR_HEIGHT: f64 = 1e-4 / 2.;
86-
let stroke_width = 10.;
87-
let bounding_box = Some([
88-
point.position - DVec2::splat(MAX_LENGTH_FOR_NO_WIDTH_OR_HEIGHT),
89-
point.position + DVec2::splat(MAX_LENGTH_FOR_NO_WIDTH_OR_HEIGHT),
90-
]);
91-
92-
Self {
93-
target_type: ClickTargetType::FreePoint(point),
94-
stroke_width,
95-
bounding_box,
96-
}
97-
}
98-
99-
pub fn target_type(&self) -> &ClickTargetType {
100-
&self.target_type
101-
}
102-
103-
pub fn bounding_box(&self) -> Option<[DVec2; 2]> {
104-
self.bounding_box
105-
}
106-
107-
pub fn bounding_box_with_transform(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
108-
self.bounding_box.map(|[a, b]| [transform.transform_point2(a), transform.transform_point2(b)])
109-
}
110-
111-
pub fn apply_transform(&mut self, affine_transform: DAffine2) {
112-
match self.target_type {
113-
ClickTargetType::Subpath(ref mut subpath) => {
114-
subpath.apply_transform(affine_transform);
115-
}
116-
ClickTargetType::FreePoint(ref mut point) => {
117-
point.apply_transform(affine_transform);
118-
}
119-
}
120-
self.update_bbox();
121-
}
122-
123-
fn update_bbox(&mut self) {
124-
match self.target_type {
125-
ClickTargetType::Subpath(ref subpath) => {
126-
self.bounding_box = subpath.bounding_box();
127-
}
128-
ClickTargetType::FreePoint(ref point) => {
129-
self.bounding_box = Some([point.position - DVec2::splat(self.stroke_width / 2.), point.position + DVec2::splat(self.stroke_width / 2.)]);
130-
}
131-
}
132-
}
133-
134-
/// Does the click target intersect the path
135-
pub fn intersect_path<It: Iterator<Item = bezier_rs::Bezier>>(&self, mut bezier_iter: impl FnMut() -> It, layer_transform: DAffine2) -> bool {
136-
// Check if the matrix is not invertible
137-
let mut layer_transform = layer_transform;
138-
if layer_transform.matrix2.determinant().abs() <= f64::EPSILON {
139-
layer_transform.matrix2 += DMat2::IDENTITY * 1e-4; // TODO: Is this the cleanest way to handle this?
140-
}
141-
142-
let inverse = layer_transform.inverse();
143-
let mut bezier_iter = || bezier_iter().map(|bezier| bezier.apply_transformation(|point| inverse.transform_point2(point)));
144-
145-
match self.target_type() {
146-
ClickTargetType::Subpath(subpath) => {
147-
// Check if outlines intersect
148-
let outline_intersects = |path_segment: bezier_rs::Bezier| bezier_iter().any(|line| !path_segment.intersections(&line, None, None).is_empty());
149-
if subpath.iter().any(outline_intersects) {
150-
return true;
151-
}
152-
// Check if selection is entirely within the shape
153-
if subpath.closed() && bezier_iter().next().is_some_and(|bezier| subpath.contains_point(bezier.start)) {
154-
return true;
155-
}
156-
157-
// Check if shape is entirely within selection
158-
let any_point_from_subpath = subpath.manipulator_groups().first().map(|group| group.anchor);
159-
any_point_from_subpath.is_some_and(|shape_point| bezier_iter().map(|bezier| bezier.winding(shape_point)).sum::<i32>() != 0)
160-
}
161-
ClickTargetType::FreePoint(point) => bezier_iter().map(|bezier: bezier_rs::Bezier| bezier.winding(point.position)).sum::<i32>() != 0,
162-
}
163-
}
164-
165-
/// Does the click target intersect the point (accounting for stroke size)
166-
pub fn intersect_point(&self, point: DVec2, layer_transform: DAffine2) -> bool {
167-
let target_bounds = [point - DVec2::splat(self.stroke_width / 2.), point + DVec2::splat(self.stroke_width / 2.)];
168-
let intersects = |a: [DVec2; 2], b: [DVec2; 2]| a[0].x <= b[1].x && a[1].x >= b[0].x && a[0].y <= b[1].y && a[1].y >= b[0].y;
169-
// This bounding box is not very accurate as it is the axis aligned version of the transformed bounding box. However it is fast.
170-
if !self
171-
.bounding_box
172-
.is_some_and(|loose| (loose[0] - loose[1]).abs().cmpgt(DVec2::splat(1e-4)).any() && intersects((layer_transform * Quad::from_box(loose)).bounding_box(), target_bounds))
173-
{
174-
return false;
175-
}
176-
177-
// Allows for selecting lines
178-
// TODO: actual intersection of stroke
179-
let inflated_quad = Quad::from_box(target_bounds);
180-
self.intersect_path(|| inflated_quad.bezier_lines(), layer_transform)
181-
}
182-
183-
/// Does the click target intersect the point (not accounting for stroke size)
184-
pub fn intersect_point_no_stroke(&self, point: DVec2) -> bool {
185-
// Check if the point is within the bounding box
186-
if self
187-
.bounding_box
188-
.is_some_and(|bbox| bbox[0].x <= point.x && point.x <= bbox[1].x && bbox[0].y <= point.y && point.y <= bbox[1].y)
189-
{
190-
// Check if the point is within the shape
191-
match self.target_type() {
192-
ClickTargetType::Subpath(subpath) => subpath.closed() && subpath.contains_point(point),
193-
ClickTargetType::FreePoint(free_point) => free_point.position == point,
194-
}
195-
} else {
196-
false
197-
}
198-
}
199-
}
200-
20144
/// Mutable state used whilst rendering to an SVG
20245
pub struct SvgRender {
20346
pub svg: Vec<SvgSegment>,

0 commit comments

Comments
 (0)