mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-21 03:48:12 +08:00
Bezier-rs: Add self_intersection to subpath (#1035)
* add self-intersection to subpath, rebased old work onto master * fix interactive website after rebase * fix rustdoc iframe placement * remove double comment * address comments * revert evaluate change * address comment + fix assert statement bug * update function comments
This commit is contained in:
committed by
Keavon Chambers
parent
4797aed05b
commit
f2d35f50de
@@ -1,4 +1,4 @@
|
||||
import { tSliderOptions } from "@/utils/options";
|
||||
import { tSliderOptions, intersectionErrorOptions, minimumSeparationOptions } from "@/utils/options";
|
||||
import { TVariant, SliderOption, SubpathCallback, WasmSubpathInstance } from "@/utils/types";
|
||||
|
||||
const subpathFeatures = {
|
||||
@@ -54,30 +54,50 @@ const subpathFeatures = {
|
||||
},
|
||||
"intersect-linear": {
|
||||
name: "Intersect (Line Segment)",
|
||||
callback: (subpath: WasmSubpathInstance): string =>
|
||||
subpath.intersect_line_segment([
|
||||
[150, 150],
|
||||
[20, 20],
|
||||
]),
|
||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string =>
|
||||
subpath.intersect_line_segment(
|
||||
[
|
||||
[150, 150],
|
||||
[20, 20],
|
||||
],
|
||||
options.error,
|
||||
options.minimum_seperation
|
||||
),
|
||||
sliderOptions: [intersectionErrorOptions, minimumSeparationOptions],
|
||||
},
|
||||
"intersect-quadratic": {
|
||||
name: "Intersect (Quadratic segment)",
|
||||
callback: (subpath: WasmSubpathInstance): string =>
|
||||
subpath.intersect_quadratic_segment([
|
||||
[20, 80],
|
||||
[180, 10],
|
||||
[90, 120],
|
||||
]),
|
||||
name: "Intersect (Quadratic Segment)",
|
||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string =>
|
||||
subpath.intersect_quadratic_segment(
|
||||
[
|
||||
[20, 80],
|
||||
[180, 10],
|
||||
[90, 120],
|
||||
],
|
||||
options.error,
|
||||
options.minimum_seperation
|
||||
),
|
||||
sliderOptions: [intersectionErrorOptions, minimumSeparationOptions],
|
||||
},
|
||||
"intersect-cubic": {
|
||||
name: "Intersect (Cubic segment)",
|
||||
callback: (subpath: WasmSubpathInstance): string =>
|
||||
subpath.intersect_cubic_segment([
|
||||
[40, 20],
|
||||
[100, 40],
|
||||
[40, 120],
|
||||
[175, 140],
|
||||
]),
|
||||
name: "Intersect (Cubic Segment)",
|
||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string =>
|
||||
subpath.intersect_cubic_segment(
|
||||
[
|
||||
[40, 20],
|
||||
[100, 40],
|
||||
[40, 120],
|
||||
[175, 140],
|
||||
],
|
||||
options.error,
|
||||
options.minimum_seperation
|
||||
),
|
||||
sliderOptions: [intersectionErrorOptions, minimumSeparationOptions],
|
||||
},
|
||||
"self-intersect": {
|
||||
name: "Self Intersect",
|
||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.self_intersections(options.error, options.minimum_seperation),
|
||||
sliderOptions: [intersectionErrorOptions, minimumSeparationOptions],
|
||||
},
|
||||
split: {
|
||||
name: "Split",
|
||||
|
||||
@@ -21,3 +21,11 @@ export const minimumSeparationOptions = {
|
||||
step: 0.001,
|
||||
default: 0.05,
|
||||
};
|
||||
|
||||
export const intersectionErrorOptions = {
|
||||
variable: "error",
|
||||
min: 0.001,
|
||||
max: 0.525,
|
||||
step: 0.0025,
|
||||
default: 0.02,
|
||||
};
|
||||
|
||||
@@ -179,7 +179,7 @@ impl WasmSubpath {
|
||||
wrap_svg_tag(content)
|
||||
}
|
||||
|
||||
pub fn intersect_line_segment(&self, js_points: JsValue) -> String {
|
||||
pub fn intersect_line_segment(&self, js_points: JsValue, error: f64, minimum_separation: f64) -> String {
|
||||
let points: [DVec2; 2] = serde_wasm_bindgen::from_value(js_points).unwrap();
|
||||
let line = Bezier::from_linear_dvec2(points[0], points[1]);
|
||||
|
||||
@@ -197,10 +197,13 @@ impl WasmSubpath {
|
||||
|
||||
let intersections_svg = self
|
||||
.0
|
||||
.intersections(&line, None, None)
|
||||
.intersections(&line, Some(error), Some(minimum_separation))
|
||||
.iter()
|
||||
.map(|intersection_t| {
|
||||
let point = self.0.evaluate(SubpathTValue::GlobalParametric(*intersection_t));
|
||||
.map(|(segment_index, intersection_t)| {
|
||||
let point = self.0.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: *segment_index,
|
||||
t: *intersection_t,
|
||||
});
|
||||
draw_circle(point, 4., RED, 1.5, WHITE)
|
||||
})
|
||||
.fold(String::new(), |acc, item| format!("{acc}{item}"));
|
||||
@@ -208,7 +211,7 @@ impl WasmSubpath {
|
||||
wrap_svg_tag(format!("{subpath_svg}{line_svg}{intersections_svg}"))
|
||||
}
|
||||
|
||||
pub fn intersect_quadratic_segment(&self, js_points: JsValue) -> String {
|
||||
pub fn intersect_quadratic_segment(&self, js_points: JsValue, error: f64, minimum_separation: f64) -> String {
|
||||
let points: [DVec2; 3] = serde_wasm_bindgen::from_value(js_points).unwrap();
|
||||
let line = Bezier::from_quadratic_dvec2(points[0], points[1], points[2]);
|
||||
|
||||
@@ -226,10 +229,13 @@ impl WasmSubpath {
|
||||
|
||||
let intersections_svg = self
|
||||
.0
|
||||
.intersections(&line, None, None)
|
||||
.intersections(&line, Some(error), Some(minimum_separation))
|
||||
.iter()
|
||||
.map(|intersection_t| {
|
||||
let point = self.0.evaluate(SubpathTValue::GlobalParametric(*intersection_t));
|
||||
.map(|(segment_index, intersection_t)| {
|
||||
let point = self.0.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: *segment_index,
|
||||
t: *intersection_t,
|
||||
});
|
||||
draw_circle(point, 4., RED, 1.5, WHITE)
|
||||
})
|
||||
.fold(String::new(), |acc, item| format!("{acc}{item}"));
|
||||
@@ -237,7 +243,7 @@ impl WasmSubpath {
|
||||
wrap_svg_tag(format!("{subpath_svg}{line_svg}{intersections_svg}"))
|
||||
}
|
||||
|
||||
pub fn intersect_cubic_segment(&self, js_points: JsValue) -> String {
|
||||
pub fn intersect_cubic_segment(&self, js_points: JsValue, error: f64, minimum_separation: f64) -> String {
|
||||
let points: [DVec2; 4] = serde_wasm_bindgen::from_value(js_points).unwrap();
|
||||
let line = Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3]);
|
||||
|
||||
@@ -255,10 +261,13 @@ impl WasmSubpath {
|
||||
|
||||
let intersections_svg = self
|
||||
.0
|
||||
.intersections(&line, None, None)
|
||||
.intersections(&line, Some(error), Some(minimum_separation))
|
||||
.iter()
|
||||
.map(|intersection_t| {
|
||||
let point = self.0.evaluate(SubpathTValue::GlobalParametric(*intersection_t));
|
||||
.map(|(segment_index, intersection_t)| {
|
||||
let point = self.0.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: *segment_index,
|
||||
t: *intersection_t,
|
||||
});
|
||||
draw_circle(point, 4., RED, 1.5, WHITE)
|
||||
})
|
||||
.fold(String::new(), |acc, item| format!("{acc}{item}"));
|
||||
@@ -266,6 +275,24 @@ impl WasmSubpath {
|
||||
wrap_svg_tag(format!("{subpath_svg}{line_svg}{intersections_svg}"))
|
||||
}
|
||||
|
||||
pub fn self_intersections(&self, error: f64, minimum_separation: f64) -> String {
|
||||
let subpath_svg = self.to_default_svg();
|
||||
let self_intersections_svg = self
|
||||
.0
|
||||
.self_intersections(Some(error), Some(minimum_separation))
|
||||
.iter()
|
||||
.map(|(segment_index, intersection_t)| {
|
||||
let point = self.0.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: *segment_index,
|
||||
t: *intersection_t,
|
||||
});
|
||||
draw_circle(point, 4., RED, 1.5, WHITE)
|
||||
})
|
||||
.fold(String::new(), |acc, item| format!("{acc}{item}"));
|
||||
|
||||
wrap_svg_tag(format!("{subpath_svg}{self_intersections_svg}"))
|
||||
}
|
||||
|
||||
pub fn split(&self, t: f64, t_variant: String) -> String {
|
||||
let t = parse_t_variant(&t_variant, t);
|
||||
let (main_subpath, optional_subpath) = self.0.split(t);
|
||||
|
||||
Reference in New Issue
Block a user