Bezier-rs: Add function to smoothly join bezier curves (#1037)
* Added bezier join * Stylistic changes per review
This commit is contained in:
parent
8d3daeae78
commit
8bc290fde9
|
|
@ -52,15 +52,19 @@ impl Bezier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a normalized unit vector representing the tangent at the point `t` along the curve.
|
/// Returns the non-normalized vector representing the tangent at the point `t` along the curve.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/tangent/solo" title="Tangent Demo"></iframe>
|
fn non_normalized_tangent(&self, t: f64) -> DVec2 {
|
||||||
pub fn tangent(&self, t: TValue) -> DVec2 {
|
|
||||||
let t = self.t_value_to_parametric(t);
|
|
||||||
match self.handles {
|
match self.handles {
|
||||||
BezierHandles::Linear => self.end - self.start,
|
BezierHandles::Linear => self.end - self.start,
|
||||||
_ => self.derivative().unwrap().evaluate(TValue::Parametric(t)),
|
_ => self.derivative().unwrap().evaluate(TValue::Parametric(t)),
|
||||||
}
|
}
|
||||||
.normalize()
|
}
|
||||||
|
|
||||||
|
/// Returns a normalized unit vector representing the tangent at the point `t` along the curve.
|
||||||
|
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/tangent/solo" title="Tangent Demo"></iframe>
|
||||||
|
pub fn tangent(&self, t: TValue) -> DVec2 {
|
||||||
|
let t = self.t_value_to_parametric(t);
|
||||||
|
self.non_normalized_tangent(t).normalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a normalized unit vector representing the direction of the normal at the point `t` along the curve.
|
/// Returns a normalized unit vector representing the direction of the normal at the point `t` along the curve.
|
||||||
|
|
@ -390,6 +394,15 @@ impl Bezier {
|
||||||
.flat_map(|bezier| self.intersections(bezier, None, None))
|
.flat_map(|bezier| self.intersections(bezier, None, None))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a cubic bezier which joins this with the provided bezier curve.
|
||||||
|
/// The resulting path formed by the Bezier curves is continuous up to the first derivative.
|
||||||
|
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/join/solo" title="Join Demo"></iframe>
|
||||||
|
pub fn join(&self, other: &Bezier) -> Bezier {
|
||||||
|
let handle1 = self.non_normalized_tangent(1.) / 3. + self.end;
|
||||||
|
let handle2 = other.start - other.non_normalized_tangent(0.) / 3.;
|
||||||
|
Bezier::from_cubic_dvec2(self.end, handle1, handle2, other.start)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -483,6 +483,56 @@ const bezierFeatures = {
|
||||||
},
|
},
|
||||||
chooseTVariant: true,
|
chooseTVariant: true,
|
||||||
},
|
},
|
||||||
|
join: {
|
||||||
|
name: "Join",
|
||||||
|
callback: (bezier: WasmBezierInstance): string => {
|
||||||
|
const points = JSON.parse(bezier.get_points());
|
||||||
|
let examplePoints = [];
|
||||||
|
if (points.length === 2) {
|
||||||
|
examplePoints = [
|
||||||
|
[120, 155],
|
||||||
|
[40, 155],
|
||||||
|
];
|
||||||
|
} else if (points.length === 3) {
|
||||||
|
examplePoints = [
|
||||||
|
[40, 150],
|
||||||
|
[95, 195],
|
||||||
|
[155, 145],
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
examplePoints = [
|
||||||
|
[140, 150],
|
||||||
|
[85, 110],
|
||||||
|
[65, 180],
|
||||||
|
[30, 140],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return bezier.join(examplePoints);
|
||||||
|
},
|
||||||
|
demoOptions: {
|
||||||
|
Linear: {
|
||||||
|
customPoints: [
|
||||||
|
[45, 40],
|
||||||
|
[130, 90],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Quadratic: {
|
||||||
|
customPoints: [
|
||||||
|
[153, 40],
|
||||||
|
[40, 20],
|
||||||
|
[75, 85],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Cubic: {
|
||||||
|
customPoints: [
|
||||||
|
[20, 80],
|
||||||
|
[40, 20],
|
||||||
|
[90, 100],
|
||||||
|
[130, 55],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BezierFeatureKey = keyof typeof bezierFeatures;
|
export type BezierFeatureKey = keyof typeof bezierFeatures;
|
||||||
|
|
|
||||||
|
|
@ -621,4 +621,44 @@ impl WasmBezier {
|
||||||
.fold(original_curve_svg, |acc, item| format!("{acc}{item}"));
|
.fold(original_curve_svg, |acc, item| format!("{acc}{item}"));
|
||||||
wrap_svg_tag(arcs_svg)
|
wrap_svg_tag(arcs_svg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn join(&self, js_points: &JsValue) -> String {
|
||||||
|
let other_bezier: Bezier = match self.0.get_points().count() {
|
||||||
|
2 => {
|
||||||
|
let points: [DVec2; 2] = serde_wasm_bindgen::from_value(js_points.into()).unwrap();
|
||||||
|
Bezier::from_linear_dvec2(points[0], points[1])
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
let points: [DVec2; 3] = serde_wasm_bindgen::from_value(js_points.into()).unwrap();
|
||||||
|
Bezier::from_quadratic_dvec2(points[0], points[1], points[2])
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
let points: [DVec2; 4] = serde_wasm_bindgen::from_value(js_points.into()).unwrap();
|
||||||
|
Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3])
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut other_bezier_svg = String::new();
|
||||||
|
other_bezier.to_svg(
|
||||||
|
&mut other_bezier_svg,
|
||||||
|
CURVE_ATTRIBUTES.to_string().replace(BLACK, GRAY),
|
||||||
|
ANCHOR_ATTRIBUTES.to_string().replace(BLACK, GRAY),
|
||||||
|
String::new(),
|
||||||
|
String::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let joining_bezier: Bezier = self.0.join(&other_bezier);
|
||||||
|
let mut joining_bezier_svg = String::new();
|
||||||
|
joining_bezier.to_svg(
|
||||||
|
&mut joining_bezier_svg,
|
||||||
|
CURVE_ATTRIBUTES.to_string().replace(BLACK, RED),
|
||||||
|
ANCHOR_ATTRIBUTES.to_string().replace(BLACK, RED),
|
||||||
|
String::new(),
|
||||||
|
String::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let bezier_svg = self.get_bezier_path();
|
||||||
|
wrap_svg_tag(format!("{bezier_svg}{joining_bezier_svg}{other_bezier_svg}"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue