Bezier-rs: Convert 'arcs' wasm function from canvas to SVG rendering (#833)
* Convert rotate demo to svg * Fix bugs in rotate * Fix bugs in rotate * Draft of decasteljau to svg * fixed de casteljau points to_svg impl * clean up wasm impl for de casteljau points * Use svg format in wasm for arcs * Update app.vue * Fix arcs as svg Co-authored-by: Linda Zheng <ll2zheng@uwaterloo.ca> * Remove comments * Reduce code duplication Co-authored-by: Thomas Cheng <contact.chengthomas@gmail.com> Co-authored-by: Hannah Li <hannahli2010@gmail.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
398f6618e4
commit
9eb2b9f6a3
|
|
@ -6,7 +6,8 @@
|
|||
<div v-for="(feature, index) in bezierFeatures" :key="index">
|
||||
<BezierExamplePane :name="feature.name" :callback="feature.callback" :exampleOptions="feature.exampleOptions" :triggerOnMouseMove="feature.triggerOnMouseMove" />
|
||||
</div>
|
||||
<div v-for="(feature, index) in features" :key="index">
|
||||
<!-- TODO: Remove the below and all associated canvas-related code, then rename `bezierFeatures` to `features` -->
|
||||
<div v-for="(feature, index) in ([] as any)" :key="index">
|
||||
<ExamplePane
|
||||
:template="feature.template"
|
||||
:templateOptions="feature.templateOptions"
|
||||
|
|
@ -24,15 +25,13 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { WasmBezier } from "@/../wasm/pkg";
|
||||
import { drawCircleSector, getContextFromCanvas } from "@/utils/drawing";
|
||||
import { BezierCurveType, CircleSector, Point, WasmBezierInstance, WasmSubpathInstance } from "@/utils/types";
|
||||
import { BezierCurveType, ExampleOptions, Point, WasmBezierInstance, WasmSubpathInstance } from "@/utils/types";
|
||||
|
||||
import BezierExamplePane from "@/components/BezierExamplePane.vue";
|
||||
import ExamplePane from "@/components/ExamplePane.vue";
|
||||
import SliderExample from "@/components/SliderExample.vue";
|
||||
import SubpathExamplePane from "@/components/SubpathExamplePane.vue";
|
||||
|
||||
const tSliderOptions = {
|
||||
|
|
@ -296,6 +295,58 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Arcs",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.arcs(options.error, options.max_iterations, options.strategy),
|
||||
exampleOptions: ((): Omit<ExampleOptions, "Linear"> => {
|
||||
const sliderOptions = [
|
||||
{
|
||||
variable: "strategy",
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 1,
|
||||
default: 0,
|
||||
unit: [": Automatic", ": FavorLargerArcs", ": FavorCorrectness"],
|
||||
},
|
||||
{
|
||||
variable: "error",
|
||||
min: 0.05,
|
||||
max: 1,
|
||||
step: 0.05,
|
||||
default: 0.5,
|
||||
},
|
||||
{
|
||||
variable: "max_iterations",
|
||||
min: 50,
|
||||
max: 200,
|
||||
step: 1,
|
||||
default: 100,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
[BezierCurveType.Quadratic]: {
|
||||
customPoints: [
|
||||
[50, 50],
|
||||
[85, 65],
|
||||
[100, 100],
|
||||
],
|
||||
sliderOptions,
|
||||
disabled: false,
|
||||
},
|
||||
[BezierCurveType.Cubic]: {
|
||||
customPoints: [
|
||||
[160, 180],
|
||||
[170, 10],
|
||||
[30, 90],
|
||||
[180, 160],
|
||||
],
|
||||
sliderOptions,
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
})(),
|
||||
},
|
||||
{
|
||||
name: "Intersect (Line Segment)",
|
||||
callback: (bezier: WasmBezierInstance): string => {
|
||||
|
|
@ -384,59 +435,6 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
],
|
||||
features: [
|
||||
{
|
||||
name: "Arcs",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||
const context = getContextFromCanvas(canvas);
|
||||
const arcs: CircleSector[] = JSON.parse(bezier.arcs(options.error, options.max_iterations, options.strategy));
|
||||
arcs.forEach((circleSector, index) => {
|
||||
drawCircleSector(context, circleSector, `hsl(${40 * index}, 100%, 50%, 75%)`, `hsl(${40 * index}, 100%, 50%, 37.5%)`);
|
||||
});
|
||||
},
|
||||
template: markRaw(SliderExample),
|
||||
templateOptions: {
|
||||
sliders: [
|
||||
{
|
||||
variable: "strategy",
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 1,
|
||||
default: 0,
|
||||
unit: [": Automatic", ": FavorLargerArcs", ": FavorCorrectness"],
|
||||
},
|
||||
{
|
||||
variable: "error",
|
||||
min: 0.05,
|
||||
max: 1,
|
||||
step: 0.05,
|
||||
default: 0.5,
|
||||
},
|
||||
{
|
||||
variable: "max_iterations",
|
||||
min: 50,
|
||||
max: 200,
|
||||
step: 1,
|
||||
default: 100,
|
||||
},
|
||||
],
|
||||
},
|
||||
curveDegrees: new Set([BezierCurveType.Quadratic, BezierCurveType.Cubic]),
|
||||
customPoints: {
|
||||
[BezierCurveType.Quadratic]: [
|
||||
[50, 50],
|
||||
[85, 65],
|
||||
[100, 100],
|
||||
],
|
||||
[BezierCurveType.Cubic]: [
|
||||
[160, 180],
|
||||
[170, 10],
|
||||
[30, 90],
|
||||
[180, 160],
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
subpathFeatures: [
|
||||
{
|
||||
name: "Constructor",
|
||||
|
|
|
|||
|
|
@ -545,23 +545,30 @@ impl WasmBezier {
|
|||
}
|
||||
|
||||
/// The wrapped return type is `Vec<CircleSector>`.
|
||||
pub fn arcs(&self, error: f64, max_iterations: usize, maximize_arcs: WasmMaximizeArcs) -> JsValue {
|
||||
pub fn arcs(&self, error: f64, max_iterations: usize, maximize_arcs: WasmMaximizeArcs) -> String {
|
||||
let original_curve_svg = self.get_bezier_path();
|
||||
|
||||
// Get sectors
|
||||
let strategy = convert_wasm_maximize_arcs(maximize_arcs);
|
||||
let options = ArcsOptions { error, max_iterations, strategy };
|
||||
let circle_sectors: Vec<CircleSector> = self
|
||||
let arcs_svg = self
|
||||
.0
|
||||
.arcs(options)
|
||||
.iter()
|
||||
.map(|sector| CircleSector {
|
||||
center: Point {
|
||||
x: sector.center.x,
|
||||
y: sector.center.y,
|
||||
},
|
||||
radius: sector.radius,
|
||||
start_angle: sector.start_angle,
|
||||
end_angle: sector.end_angle,
|
||||
.enumerate()
|
||||
.map(|(idx, sector)| {
|
||||
draw_sector(
|
||||
sector.center.x,
|
||||
sector.center.y,
|
||||
sector.radius,
|
||||
-sector.start_angle,
|
||||
-sector.end_angle,
|
||||
format!("hsl({}, 100%, 50%, 75%)", (40 * idx)).as_str(),
|
||||
1.,
|
||||
format!("hsl({}, 100%, 50%, 37.5%)", (40 * idx)).as_str(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
to_js_value(circle_sectors)
|
||||
.fold(original_curve_svg, |acc, item| format!("{acc}{item}"));
|
||||
wrap_svg_tag(arcs_svg)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,3 +35,21 @@ pub fn draw_circle(x_pos: f64, y_pos: f64, radius: f64, stroke: &str, stroke_wid
|
|||
pub fn draw_line(start_x: f64, start_y: f64, end_x: f64, end_y: f64, stroke: &str, stroke_width: f64) -> String {
|
||||
format!(r#"<line x1="{start_x}" y1="{start_y}" x2="{end_x}" y2="{end_y}" stroke="{stroke}" stroke-width="{stroke_width}"/>"#)
|
||||
}
|
||||
|
||||
// Helper function to convert polar to cartesian coordinates
|
||||
fn polar_to_cartesian(center_x: f64, center_y: f64, radius: f64, angle_in_rad: f64) -> [f64; 2] {
|
||||
let x = center_x + radius * angle_in_rad.cos();
|
||||
let y = center_y + radius * -angle_in_rad.sin();
|
||||
[x, y]
|
||||
}
|
||||
|
||||
// Helper function to create an SVG drawing of a sector
|
||||
pub fn draw_sector(center_x: f64, center_y: f64, radius: f64, start_angle: f64, end_angle: f64, stroke: &str, stroke_width: f64, fill: &str) -> String {
|
||||
let [start_x, start_y] = polar_to_cartesian(center_x, center_y, radius, start_angle);
|
||||
let [end_x, end_y] = polar_to_cartesian(center_x, center_y, radius, end_angle);
|
||||
// draw sector with fill color
|
||||
let sector_svg = format!(r#"<path d="M {start_x} {start_y} A {radius} {radius} 0 0 1 {end_x} {end_y} L {center_x} {center_y} L {start_x} {start_y} Z" stroke="none" fill="{fill}" />"#);
|
||||
// draw arc with stroke color
|
||||
let arc_svg = format!(r#"<path d="M {start_x} {start_y} A {radius} {radius} 0 0 1 {end_x} {end_y}" stroke="{stroke}" stroke-width="{stroke_width}" fill="none"/>"#);
|
||||
format!("{sector_svg}{arc_svg}")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue