|
|
@@ -3,13 +3,56 @@ import { SvgPathUtils, TLDrawShape, TLDrawShapeProps } from '@tldraw/core'
|
|
|
import { SVGContainer, TLComponentProps } from '@tldraw/react'
|
|
|
import { computed, makeObservable } from 'mobx'
|
|
|
import { observer } from 'mobx-react-lite'
|
|
|
-import getStroke from 'perfect-freehand'
|
|
|
+import getStroke, { getStrokeOutlinePoints, getStrokePoints, StrokeOptions } from 'perfect-freehand'
|
|
|
import { CustomStyleProps, withClampedStyles } from './style-props'
|
|
|
|
|
|
export interface PencilShapeProps extends TLDrawShapeProps, CustomStyleProps {
|
|
|
type: 'pencil'
|
|
|
}
|
|
|
|
|
|
+const simulatePressureSettings: StrokeOptions = {
|
|
|
+ easing: t => Math.sin((t * Math.PI) / 2),
|
|
|
+ simulatePressure: true,
|
|
|
+}
|
|
|
+
|
|
|
+const realPressureSettings: StrokeOptions = {
|
|
|
+ easing: t => t * t,
|
|
|
+ simulatePressure: false,
|
|
|
+}
|
|
|
+
|
|
|
+function getFreehandOptions(shape: PencilShapeProps) {
|
|
|
+ const options: StrokeOptions = {
|
|
|
+ size: 1 + shape.strokeWidth * 1.5,
|
|
|
+ thinning: 0.65,
|
|
|
+ streamline: 0.65,
|
|
|
+ smoothing: 0.65,
|
|
|
+ ...(shape.points[1][2] === 0.5 ? simulatePressureSettings : realPressureSettings),
|
|
|
+ last: shape.isComplete,
|
|
|
+ }
|
|
|
+
|
|
|
+ return options
|
|
|
+}
|
|
|
+
|
|
|
+function getFillPath(shape: PencilShapeProps) {
|
|
|
+ if (shape.points.length < 2) return ''
|
|
|
+
|
|
|
+ return SvgPathUtils.getSvgPathFromStroke(
|
|
|
+ getStrokePoints(shape.points, getFreehandOptions(shape)).map(pt => pt.point)
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+function getDrawStrokePoints(shape: PencilShapeProps, options: StrokeOptions) {
|
|
|
+ return getStrokePoints(shape.points, options)
|
|
|
+}
|
|
|
+
|
|
|
+function getDrawStrokePathTDSnapshot(shape: PencilShapeProps) {
|
|
|
+ if (shape.points.length < 2) return ''
|
|
|
+ const options = getFreehandOptions(shape)
|
|
|
+ const strokePoints = getDrawStrokePoints(shape, options)
|
|
|
+ const path = SvgPathUtils.getSvgPathFromStroke(getStrokeOutlinePoints(strokePoints, options))
|
|
|
+ return path
|
|
|
+}
|
|
|
+
|
|
|
export class PencilShape extends TLDrawShape<PencilShapeProps> {
|
|
|
constructor(props = {} as Partial<PencilShapeProps>) {
|
|
|
super(props)
|
|
|
@@ -34,16 +77,7 @@ export class PencilShape extends TLDrawShape<PencilShapeProps> {
|
|
|
}
|
|
|
|
|
|
@computed get pointsPath() {
|
|
|
- const {
|
|
|
- props: { points, isComplete, strokeWidth },
|
|
|
- } = this
|
|
|
- if (points.length < 2) {
|
|
|
- return `M -4, 0
|
|
|
- a 4,4 0 1,0 8,0
|
|
|
- a 4,4 0 1,0 -8,0`
|
|
|
- }
|
|
|
- const stroke = getStroke(points, { size: 4 + strokeWidth * 2, last: isComplete })
|
|
|
- return SvgPathUtils.getCurvedPathForPolygon(stroke)
|
|
|
+ return getDrawStrokePathTDSnapshot(this.props)
|
|
|
}
|
|
|
|
|
|
ReactComponent = observer(({ events, isErasing }: TLComponentProps) => {
|