mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 18:18:11 +08:00
Implement curvature function in Bezier math library (#725)
* bezier curvature * change comment Co-authored-by: Jackie Chen <jackiechen73>
This commit is contained in:
co-authored by
Jackie Chen
parent
b4070f9c90
commit
18871e5bbf
@@ -25,7 +25,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, markRaw } from "vue";
|
import { defineComponent, markRaw } from "vue";
|
||||||
|
|
||||||
import { drawBezier, drawBezierHelper, drawCurve, drawLine, drawPoint, drawText, getContextFromCanvas, COLORS } from "@/utils/drawing";
|
import { drawBezier, drawBezierHelper, drawCircle, drawCurve, drawLine, drawPoint, drawText, getContextFromCanvas, COLORS } from "@/utils/drawing";
|
||||||
import { BezierCurveType, Point, WasmBezierInstance, WasmSubpathInstance } from "@/utils/types";
|
import { BezierCurveType, Point, WasmBezierInstance, WasmSubpathInstance } from "@/utils/types";
|
||||||
|
|
||||||
import ExamplePane from "@/components/ExamplePane.vue";
|
import ExamplePane from "@/components/ExamplePane.vue";
|
||||||
@@ -205,6 +205,26 @@ export default defineComponent({
|
|||||||
template: markRaw(SliderExample),
|
template: markRaw(SliderExample),
|
||||||
templateOptions: { sliders: [tSliderOptions] },
|
templateOptions: { sliders: [tSliderOptions] },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Curvature",
|
||||||
|
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||||
|
const context = getContextFromCanvas(canvas);
|
||||||
|
const point = JSON.parse(bezier.evaluate(options.t));
|
||||||
|
const normal = JSON.parse(bezier.normal(options.t));
|
||||||
|
const curvature = bezier.curvature(options.t);
|
||||||
|
const radius = 1 / curvature;
|
||||||
|
|
||||||
|
const curvatureCenter = { x: point.x + normal.x * radius, y: point.y + normal.y * radius };
|
||||||
|
|
||||||
|
drawCircle(context, curvatureCenter, Math.abs(radius), COLORS.NON_INTERACTIVE.STROKE_1);
|
||||||
|
drawLine(context, point, curvatureCenter, COLORS.NON_INTERACTIVE.STROKE_1);
|
||||||
|
drawPoint(context, point, 3, COLORS.NON_INTERACTIVE.STROKE_1);
|
||||||
|
drawPoint(context, curvatureCenter, 3, COLORS.NON_INTERACTIVE.STROKE_1);
|
||||||
|
},
|
||||||
|
curveDegrees: new Set([BezierCurveType.Quadratic, BezierCurveType.Cubic]),
|
||||||
|
template: markRaw(SliderExample),
|
||||||
|
templateOptions: { sliders: [tSliderOptions] },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Split",
|
name: "Split",
|
||||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||||
|
|||||||
@@ -73,6 +73,14 @@ export const drawCurve = (ctx: CanvasRenderingContext2D, points: Point[], stroke
|
|||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const drawCircle = (ctx: CanvasRenderingContext2D, point: Point, radius: number, strokeColor = COLORS.INTERACTIVE.STROKE_1): void => {
|
||||||
|
ctx.strokeStyle = strokeColor;
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(point.x, point.y, radius, 0, 2 * Math.PI, false);
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
export const drawBezierHelper = (ctx: CanvasRenderingContext2D, bezier: WasmBezierInstance, bezierStyleConfig: Partial<BezierStyleConfig> = {}): void => {
|
export const drawBezierHelper = (ctx: CanvasRenderingContext2D, bezier: WasmBezierInstance, bezierStyleConfig: Partial<BezierStyleConfig> = {}): void => {
|
||||||
const points = bezier.get_points().map((p: string) => JSON.parse(p));
|
const points = bezier.get_points().map((p: string) => JSON.parse(p));
|
||||||
drawBezier(ctx, points, null, bezierStyleConfig);
|
drawBezier(ctx, points, null, bezierStyleConfig);
|
||||||
|
|||||||
@@ -166,4 +166,8 @@ impl WasmBezier {
|
|||||||
let bezier_points: Vec<Vec<Point>> = self.0.offset(distance).into_iter().map(bezier_to_points).collect();
|
let bezier_points: Vec<Vec<Point>> = self.0.offset(distance).into_iter().map(bezier_to_points).collect();
|
||||||
to_js_value(bezier_points)
|
to_js_value(bezier_points)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn curvature(&self, t: f64) -> f64 {
|
||||||
|
self.0.curvature(t)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -394,6 +394,26 @@ impl Bezier {
|
|||||||
self.tangent(t).perp()
|
self.tangent(t).perp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the curvature, a scalar value for the derivative at the given `t`-value along the curve.
|
||||||
|
/// Curvature is 1 over the radius of a circle with an equivalent derivative.
|
||||||
|
pub fn curvature(&self, t: f64) -> f64 {
|
||||||
|
let (d, dd) = match &self.derivative() {
|
||||||
|
Some(first_derivative) => match first_derivative.derivative() {
|
||||||
|
Some(second_derivative) => (first_derivative.evaluate(t), second_derivative.evaluate(t)),
|
||||||
|
None => (first_derivative.evaluate(t), first_derivative.end - first_derivative.start),
|
||||||
|
},
|
||||||
|
None => (self.end - self.start, DVec2::new(0., 0.)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let numerator = d.x * dd.y - d.y * dd.x;
|
||||||
|
let denominator = (d.x.powf(2.) + d.y.powf(2.)).powf(1.5);
|
||||||
|
if denominator == 0. {
|
||||||
|
0.
|
||||||
|
} else {
|
||||||
|
numerator / denominator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the pair of Bezier curves that result from splitting the original curve at the point corresponding to `t`.
|
/// Returns the pair of Bezier curves that result from splitting the original curve at the point corresponding to `t`.
|
||||||
pub fn split(&self, t: f64) -> [Bezier; 2] {
|
pub fn split(&self, t: f64) -> [Bezier; 2] {
|
||||||
let split_point = self.evaluate(t);
|
let split_point = self.evaluate(t);
|
||||||
|
|||||||
Reference in New Issue
Block a user