Improves drawing

This commit is contained in:
Steve Ruiz 2021-05-27 19:51:25 +01:00
parent 7ef83dc508
commit c41def99d3
4 changed files with 80 additions and 5 deletions

View file

@ -12,7 +12,6 @@ export default function Brush() {
y={brush.minY}
width={brush.width}
height={brush.height}
className="brush"
/>
)
}
@ -20,4 +19,5 @@ export default function Brush() {
const BrushRect = styled("rect", {
fill: "$brushFill",
stroke: "$brushStroke",
zStrokeWidth: 1,
})

View file

@ -5,6 +5,9 @@ import { registerShapeUtils } from "./index"
import { intersectPolylineBounds } from "utils/intersections"
import { boundsContainPolygon } from "utils/bounds"
import { getBoundsFromPoints, translateBounds } from "utils/utils"
import { DotCircle } from "components/canvas/misc"
const pathCache = new WeakMap<DrawShape, string>([])
const draw = registerShapeUtils<DrawShape>({
boundsCache: new WeakMap([]),
@ -31,8 +34,34 @@ const draw = registerShapeUtils<DrawShape>({
}
},
render({ id, points }) {
return <polyline id={id} points={points.toString()} />
render(shape) {
const { id, point, points } = shape
if (points.length < 2) {
return <DotCircle cx={point[0]} cy={point[1]} r={3} />
}
if (!pathCache.has(shape)) {
pathCache.set(
shape,
points
.reduce(
(acc, [x0, y0], i, arr) => {
if (i === points.length - 1) {
acc.push("L", x0, y0)
} else {
const [x1, y1] = arr[i + 1]
acc.push(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2)
}
return acc
},
["M", ...points[0], "Q"]
)
.join(" ")
)
}
return <path id={id} d={pathCache.get(shape)} />
},
applyStyles(shape, style) {

View file

@ -2,7 +2,7 @@ import { current } from "immer"
import { Data, DrawShape } from "types"
import BaseSession from "./base-session"
import { getShapeUtils } from "lib/shape-utils"
import { getPage } from "utils/utils"
import { getPage, simplify } from "utils/utils"
import * as vec from "utils/vec"
import commands from "state/commands"
@ -42,7 +42,15 @@ export default class BrushSession extends BaseSession {
}
complete = (data: Data) => {
commands.points(data, this.shapeId, this.snapshot.points, this.points)
commands.points(
data,
this.shapeId,
this.snapshot.points,
simplify(this.points, 1).map(([x, y]) => [
Math.trunc(x * 100) / 100,
Math.trunc(y * 100) / 100,
])
)
}
}

View file

@ -1492,3 +1492,41 @@ export function getCurrent<T extends object>(source: T): T {
Object.entries(source).map(([key, value]) => [key, value])
) as T
}
/**
* Simplify a line (using Ramer-Douglas-Peucker algorithm).
* @param points An array of points as [x, y, ...][]
* @param tolerance The minimum line distance (also called epsilon).
* @returns Simplified array as [x, y, ...][]
*/
export function simplify(points: number[][], tolerance = 1) {
const len = points.length,
a = points[0],
b = points[len - 1],
[x1, y1] = a,
[x2, y2] = b
if (len > 2) {
let distance = 0,
index = 0,
max = Math.hypot(y2 - y1, x2 - x1)
for (let i = 1; i < len - 1; i++) {
const [x0, y0] = points[i],
d = Math.abs((y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1) / max
if (distance > d) continue
distance = d
index = i
}
if (distance > tolerance) {
let l0 = simplify(points.slice(0, index + 1), tolerance)
let l1 = simplify(points.slice(index + 1), tolerance)
return l0.concat(l1.slice(1))
}
}
return [a, b]
}