Fix drawing session, avoids expensive iterations if we can

This commit is contained in:
Steve Ruiz 2021-09-11 17:01:59 +01:00
parent e7987ca451
commit 9787cafc06
6 changed files with 41 additions and 35 deletions

View file

@ -72,7 +72,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGGElement> {
} }
render = React.forwardRef<SVGGElement, TLShapeProps<ArrowShape, SVGGElement>>( render = React.forwardRef<SVGGElement, TLShapeProps<ArrowShape, SVGGElement>>(
({ shape, meta, events }) => { ({ shape, meta, events }, ref) => {
const { const {
handles: { start, bend, end }, handles: { start, bend, end },
decorations = {}, decorations = {},
@ -220,7 +220,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGGElement> {
const sw = strokeWidth * 1.618 const sw = strokeWidth * 1.618
return ( return (
<g {...events}> <g ref={ref} {...events}>
<g pointerEvents="none"> <g pointerEvents="none">
{shaftPath} {shaftPath}
{startArrowHead && ( {startArrowHead && (

View file

@ -31,7 +31,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
} }
render = React.forwardRef<SVGGElement, TLShapeProps<DrawShape, SVGGElement>>( render = React.forwardRef<SVGGElement, TLShapeProps<DrawShape, SVGGElement>>(
({ shape, meta, events, isEditing }) => { ({ shape, meta, events, isEditing }, ref) => {
const { points, style } = shape const { points, style } = shape
const styles = getShapeStyle(style, meta.isDarkMode) const styles = getShapeStyle(style, meta.isDarkMode)
@ -47,7 +47,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
const sw = strokeWidth * 0.618 const sw = strokeWidth * 0.618
return ( return (
<g {...events}> <g ref={ref} {...events}>
<circle <circle
r={strokeWidth * 0.618} r={strokeWidth * 0.618}
fill={styles.stroke} fill={styles.stroke}
@ -76,7 +76,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
: Utils.getFromCache(this.drawPathCache, points, () => getDrawStrokePath(shape, false)) : Utils.getFromCache(this.drawPathCache, points, () => getDrawStrokePath(shape, false))
return ( return (
<g {...events}> <g ref={ref} {...events}>
{shouldFill && ( {shouldFill && (
<path <path
d={polygonPathData} d={polygonPathData}
@ -121,7 +121,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
const sw = strokeWidth * 1.618 const sw = strokeWidth * 1.618
return ( return (
<g {...events}> <g ref={ref} {...events}>
<path <path
d={path} d={path}
fill={shouldFill ? styles.fill : 'none'} fill={shouldFill ? styles.fill : 'none'}
@ -260,19 +260,6 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
): Partial<DrawShape> { ): Partial<DrawShape> {
return this.transform(shape, bounds, info) return this.transform(shape, bounds, info)
} }
onSessionComplete(shape: DrawShape): Partial<DrawShape> {
const bounds = this.getBounds(shape)
const [x1, y1] = Vec.round(Vec.sub([bounds.minX, bounds.minY], shape.point))
const points = shape.points.map(([x0, y0, p]) => Vec.round([x0 - x1, y0 - y1]).concat(p))
return {
points,
point: Vec.add(shape.point, [x1, y1]),
}
}
} }
const simulatePressureSettings = { const simulatePressureSettings = {

View file

@ -38,7 +38,7 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGGElement> {
} }
render = React.forwardRef<SVGGElement, TLShapeProps<EllipseShape, SVGGElement>>( render = React.forwardRef<SVGGElement, TLShapeProps<EllipseShape, SVGGElement>>(
({ shape, meta, isBinding, events }) => { ({ shape, meta, isBinding, events }, ref) => {
const { const {
radius: [radiusX, radiusY], radius: [radiusX, radiusY],
style, style,
@ -56,7 +56,7 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGGElement> {
) )
return ( return (
<g {...events}> <g ref={ref} {...events}>
{isBinding && ( {isBinding && (
<ellipse <ellipse
className="tl-binding-indicator" className="tl-binding-indicator"
@ -102,7 +102,7 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGGElement> {
const sw = strokeWidth * 1.618 const sw = strokeWidth * 1.618
return ( return (
<g {...events}> <g ref={ref} {...events}>
{isBinding && ( {isBinding && (
<ellipse <ellipse
className="tl-binding-indicator" className="tl-binding-indicator"

View file

@ -39,7 +39,7 @@ export class Group extends TLDrawShapeUtil<GroupShape, SVGGElement> {
} }
render = React.forwardRef<SVGGElement, TLShapeProps<GroupShape, SVGGElement>>( render = React.forwardRef<SVGGElement, TLShapeProps<GroupShape, SVGGElement>>(
({ shape, isBinding, isHovered, isSelected, events }) => { ({ shape, isBinding, isHovered, isSelected, events }, ref) => {
const { id, size } = shape const { id, size } = shape
const sw = 2 const sw = 2
@ -77,7 +77,7 @@ export class Group extends TLDrawShapeUtil<GroupShape, SVGGElement> {
}) })
return ( return (
<g {...events}> <g ref={ref} {...events}>
{isBinding && ( {isBinding && (
<rect <rect
className="tl-binding-indicator" className="tl-binding-indicator"

View file

@ -42,10 +42,6 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape, SVGGElement> {
const styles = getShapeStyle(style, meta.isDarkMode) const styles = getShapeStyle(style, meta.isDarkMode)
const strokeWidth = +styles.strokeWidth const strokeWidth = +styles.strokeWidth
React.useEffect(() => {
console.log(this.refMap.get(shape.id))
}, [])
if (style.dash === DashStyle.Draw) { if (style.dash === DashStyle.Draw) {
const pathData = Utils.getFromCache(this.pathCache, shape.size, () => renderPath(shape)) const pathData = Utils.getFromCache(this.pathCache, shape.size, () => renderPath(shape))
@ -166,7 +162,6 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape, SVGGElement> {
} }
getBounds(shape: RectangleShape) { getBounds(shape: RectangleShape) {
console.log(this.refMap.get(shape.id))
const bounds = Utils.getFromCache(this.boundsCache, shape, () => { const bounds = Utils.getFromCache(this.boundsCache, shape, () => {
const [width, height] = shape.size const [width, height] = shape.size
return { return {

View file

@ -8,10 +8,12 @@ import { TLDR } from '~state/tldr'
export class DrawSession implements Session { export class DrawSession implements Session {
id = 'draw' id = 'draw'
status = TLDrawStatus.Creating status = TLDrawStatus.Creating
topLeft: number[]
origin: number[] origin: number[]
previous: number[] previous: number[]
last: number[] last: number[]
points: number[][] points: number[][]
shiftedPoints: number[][] = []
snapshot: DrawSnapshot snapshot: DrawSnapshot
isLocked?: boolean isLocked?: boolean
lockedDirection?: 'horizontal' | 'vertical' lockedDirection?: 'horizontal' | 'vertical'
@ -20,6 +22,7 @@ export class DrawSession implements Session {
this.origin = point this.origin = point
this.previous = point this.previous = point
this.last = point this.last = point
this.topLeft = point
this.snapshot = getDrawSnapshot(data, id) this.snapshot = getDrawSnapshot(data, id)
@ -79,6 +82,12 @@ export class DrawSession implements Session {
// The previous input (not adjusted) point // The previous input (not adjusted) point
this.previous = point this.previous = point
const prevTopLeft = [...this.topLeft]
this.topLeft = [Math.min(this.topLeft[0], point[0]), Math.min(this.topLeft[1], point[1])]
const delta = Vec.sub(this.topLeft, this.origin)
// The new adjusted point // The new adjusted point
const newPoint = Vec.round(Vec.sub(this.previous, this.origin)).concat(pressure) const newPoint = Vec.round(Vec.sub(this.previous, this.origin)).concat(pressure)
@ -89,16 +98,35 @@ export class DrawSession implements Session {
// The new adjusted point is now the previous adjusted point. // The new adjusted point is now the previous adjusted point.
this.last = newPoint this.last = newPoint
let points: number[][]
// Add the new adjusted point to the points array // Add the new adjusted point to the points array
this.points.push(newPoint) this.points.push(newPoint)
// Time to shift some points!
if (Vec.isEqual(prevTopLeft, this.topLeft)) {
// If the new top left is the same as the previous top left,
// we don't need to shift anything: we just shift the new point
// and add it to the shifted points array.
points = [...this.shiftedPoints, Vec.sub(newPoint, delta)]
} else {
// If we have a new top left, then we need to iterate through
// the "unshifted" points array and shift them based on the
// offset between the new top left and the original top left.
points = this.points.map((pt) => Vec.sub(pt, delta))
}
this.shiftedPoints = points
return { return {
document: { document: {
pages: { pages: {
[data.appState.currentPageId]: { [data.appState.currentPageId]: {
shapes: { shapes: {
[snapshot.id]: { [snapshot.id]: {
points: [...this.points], // Set to a new array here point: this.topLeft,
points,
}, },
}, },
}, },
@ -163,11 +191,7 @@ export class DrawSession implements Session {
pages: { pages: {
[pageId]: { [pageId]: {
shapes: { shapes: {
[snapshot.id]: TLDR.onSessionComplete( [snapshot.id]: TLDR.getShape(data, snapshot.id, pageId),
data,
{ ...TLDR.getShape(data, snapshot.id, pageId), points: [...this.points] },
pageId
),
}, },
}, },
}, },