Tldraw/packages/editor/src/lib/utils/svg.ts

126 wiersze
3.3 KiB
TypeScript

import { StrokePoint, toDomPrecision, Vec2d, VecLike } from '@tldraw/primitives'
/** @internal */
export function getPointerInfo(e: React.PointerEvent | PointerEvent, container: HTMLElement) {
;(e as any).isKilled = true
const { top, left } = container.getBoundingClientRect()
return {
point: {
x: e.clientX - left,
y: e.clientY - top,
z: e.pressure,
},
shiftKey: e.shiftKey,
altKey: e.altKey,
ctrlKey: e.metaKey || e.ctrlKey,
pointerId: e.pointerId,
button: e.button,
isPen: e.pointerType === 'pen',
}
}
function precise(A: VecLike) {
return `${toDomPrecision(A.x)},${toDomPrecision(A.y)} `
}
function average(A: VecLike, B: VecLike) {
return `${toDomPrecision((A.x + B.x) / 2)},${toDomPrecision((A.y + B.y) / 2)} `
}
/**
* Turn an array of points into a path of quadradic curves.
*
* @param points - The points returned from perfect-freehand
* @param closed - Whether the stroke is closed
* @public
*/
export function getSvgPathFromStroke(points: Vec2d[], closed = true): string {
const len = points.length
if (len < 2) {
return ''
}
let a = points[0]
let b = points[1]
if (len === 2) {
// If only two points, just draw a line
return `M${precise(a)}L${precise(b)}`
}
let result = ''
for (let i = 2, max = len - 1; i < max; i++) {
a = points[i]
b = points[i + 1]
result += average(a, b)
}
if (closed) {
// If closed, draw a curve from the last point to the first
return `M${average(points[0], points[1])}Q${precise(points[1])}${average(
points[1],
points[2]
)}T${result}${average(points[len - 1], points[0])}${average(points[0], points[1])}Z`
} else {
// If not closed, draw a curve starting at the first point and
// ending at the midpoint of the last and second-last point, then
// complete the curve with a line segment to the last point.
return `M${precise(points[0])}Q${precise(points[1])}${average(points[1], points[2])}${
points.length > 3 ? 'T' : ''
}${result}L${precise(points[len - 1])}`
}
}
/**
* Turn an array of stroke points into a path of quadradic curves.
*
* @param points - The stroke points returned from perfect-freehand
* @param closed - Whether the shape is closed
* @public
*/
export function getSvgPathFromStrokePoints(points: StrokePoint[], closed = false): string {
const len = points.length
if (len < 2) {
return ''
}
let a = points[0].point
let b = points[1].point
if (len === 2) {
return `M${precise(a)}L${precise(b)}`
}
let result = ''
for (let i = 2, max = len - 1; i < max; i++) {
a = points[i].point
b = points[i + 1].point
result += average(a, b)
}
if (closed) {
// If closed, draw a curve from the last point to the first
return `M${average(points[0].point, points[1].point)}Q${precise(points[1].point)}${average(
points[1].point,
points[2].point
)}T${result}${average(points[len - 1].point, points[0].point)}${average(
points[0].point,
points[1].point
)}Z`
} else {
// If not closed, draw a curve starting at the first point and
// ending at the midpoint of the last and second-last point, then
// complete the curve with a line segment to the last point.
return `M${precise(points[0].point)}Q${precise(points[1].point)}${average(
points[1].point,
points[2].point
)}${points.length > 3 ? 'T' : ''}${result}L${precise(points[len - 1].point)}`
}
}