fix line bugs (#1936)
closes #1913 Some lines aren't rendering: <img width="500" alt="shapes" src="https://github.com/tldraw/tldraw/assets/98838967/bd01bb0f-a967-46ce-9056-a81421224931"> When we begin drawing a line it's nice for the user to be able to see a dot, so we put down two points. The end point for a new line was set to the same position as the first point, which was causing a bunch of divide by zero errors. Offsetting it slightly fixes that. Now when two handles are too close together we extend the second one instead of drawing a third. This will probably only ever happen with the first two points of a line. ### Change Type - [x] `patch` — Bug fix - [ ] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [ ] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan 1. Select the Line tool and set spline to line and dash to draw 2. Click around the canvas 3. You should now be able to actually see a line 4. Now set spline to cubic and dash to solid 5. shift click around the canvas 6. You should be able to see a line! ### Release Notes - This PR patches a couple of bugs which led to straight draw lines and beziered dash lines not rendering on the canvas Before & After: <image width="250" src="https://github.com/tldraw/tldraw/assets/98838967/e0ca7d54-506f-4014-b65a-6b61a98e3665" /> <image width="250" src="https://github.com/tldraw/tldraw/assets/98838967/90c9fa12-1bcb-430d-80c7-97e1faacea16" /> --------- Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
parent
a176522c57
commit
73e61727cc
4 changed files with 40 additions and 26 deletions
|
@ -146,7 +146,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
|
||||||
const line = editor.currentPageShapes[editor.currentPageShapes.length - 1]
|
const line = editor.currentPageShapes[editor.currentPageShapes.length - 1]
|
||||||
assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
|
assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
|
||||||
const handles = Object.values(line.props.handles)
|
const handles = Object.values(line.props.handles)
|
||||||
expect(handles.length).toBe(3)
|
expect(handles.length).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('extends a line by shift-click dragging', () => {
|
it('extends a line by shift-click dragging', () => {
|
||||||
|
|
|
@ -65,8 +65,8 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
||||||
canBind: false,
|
canBind: false,
|
||||||
canSnap: true,
|
canSnap: true,
|
||||||
index: 'a2',
|
index: 'a2',
|
||||||
x: 0,
|
x: 0.1,
|
||||||
y: 0,
|
y: 0.1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -243,11 +243,9 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cubic style spline
|
// Cubic style spline
|
||||||
if (shape.props.spline === 'cubic') {
|
if (shape.props.spline === 'cubic') {
|
||||||
const splinePath = getSvgPathForLineGeometry(spline)
|
const splinePath = getSvgPathForLineGeometry(spline)
|
||||||
|
|
||||||
if (dash === 'solid') {
|
if (dash === 'solid') {
|
||||||
return (
|
return (
|
||||||
<SVGContainer id={shape.id}>
|
<SVGContainer id={shape.id}>
|
||||||
|
|
|
@ -14,6 +14,8 @@ import {
|
||||||
structuredClone,
|
structuredClone,
|
||||||
} from '@tldraw/editor'
|
} from '@tldraw/editor'
|
||||||
|
|
||||||
|
const MINIMUM_DISTANCE_BETWEEN_SHIFT_CLICKED_HANDLES = 2
|
||||||
|
|
||||||
export class Pointing extends StateNode {
|
export class Pointing extends StateNode {
|
||||||
static override id = 'pointing'
|
static override id = 'pointing'
|
||||||
|
|
||||||
|
@ -27,9 +29,11 @@ export class Pointing extends StateNode {
|
||||||
|
|
||||||
this.markId = undefined
|
this.markId = undefined
|
||||||
|
|
||||||
|
// Previously created line shape that we might be extending
|
||||||
const shape = info.shapeId && this.editor.getShape<TLLineShape>(info.shapeId)
|
const shape = info.shapeId && this.editor.getShape<TLLineShape>(info.shapeId)
|
||||||
|
|
||||||
if (shape && inputs.shiftKey) {
|
if (shape && inputs.shiftKey) {
|
||||||
|
// Extending a previous shape
|
||||||
this.markId = `creating:${shape.id}`
|
this.markId = `creating:${shape.id}`
|
||||||
this.editor.mark(this.markId)
|
this.editor.mark(this.markId)
|
||||||
this.shape = shape
|
this.shape = shape
|
||||||
|
@ -39,6 +43,7 @@ export class Pointing extends StateNode {
|
||||||
|
|
||||||
const vertexHandles = handles.filter((h) => h.type === 'vertex').sort(sortByIndex)
|
const vertexHandles = handles.filter((h) => h.type === 'vertex').sort(sortByIndex)
|
||||||
const endHandle = vertexHandles[vertexHandles.length - 1]
|
const endHandle = vertexHandles[vertexHandles.length - 1]
|
||||||
|
const prevEndHandle = vertexHandles[vertexHandles.length - 2]
|
||||||
|
|
||||||
const shapePagePoint = Matrix2d.applyToPoint(
|
const shapePagePoint = Matrix2d.applyToPoint(
|
||||||
this.editor.getShapeParentTransform(this.shape)!,
|
this.editor.getShapeParentTransform(this.shape)!,
|
||||||
|
@ -47,24 +52,31 @@ export class Pointing extends StateNode {
|
||||||
|
|
||||||
let nextEndHandleIndex: string, nextEndHandleId: string, nextEndHandle: TLHandle
|
let nextEndHandleIndex: string, nextEndHandleId: string, nextEndHandle: TLHandle
|
||||||
|
|
||||||
if (vertexHandles.length === 2 && vertexHandles[1].x === 1 && vertexHandles[1].y === 1) {
|
const nextPoint = Vec2d.Sub(currentPagePoint, shapePagePoint)
|
||||||
nextEndHandleIndex = vertexHandles[1].index
|
|
||||||
nextEndHandleId = vertexHandles[1].id
|
if (
|
||||||
|
Vec2d.Dist(endHandle, prevEndHandle) < MINIMUM_DISTANCE_BETWEEN_SHIFT_CLICKED_HANDLES ||
|
||||||
|
Vec2d.Dist(nextPoint, endHandle) < MINIMUM_DISTANCE_BETWEEN_SHIFT_CLICKED_HANDLES
|
||||||
|
) {
|
||||||
|
// If the end handle is too close to the previous end handle, we'll just extend the previous end handle
|
||||||
|
nextEndHandleIndex = endHandle.index
|
||||||
|
nextEndHandleId = endHandle.id
|
||||||
nextEndHandle = {
|
nextEndHandle = {
|
||||||
...vertexHandles[1],
|
...endHandle,
|
||||||
x: currentPagePoint.x - shapePagePoint.x,
|
x: nextPoint.x + 0.1,
|
||||||
y: currentPagePoint.y - shapePagePoint.y,
|
y: nextPoint.y + 0.1,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Otherwise, we'll create a new end handle
|
||||||
nextEndHandleIndex = getIndexAbove(endHandle.index)
|
nextEndHandleIndex = getIndexAbove(endHandle.index)
|
||||||
nextEndHandleId = 'handle:' + nextEndHandleIndex
|
nextEndHandleId = 'handle:' + nextEndHandleIndex
|
||||||
nextEndHandle = {
|
nextEndHandle = {
|
||||||
x: currentPagePoint.x - shapePagePoint.x,
|
|
||||||
y: currentPagePoint.y - shapePagePoint.y,
|
|
||||||
index: nextEndHandleIndex,
|
|
||||||
canBind: false,
|
|
||||||
type: 'vertex',
|
|
||||||
id: nextEndHandleId,
|
id: nextEndHandleId,
|
||||||
|
type: 'vertex',
|
||||||
|
index: nextEndHandleIndex,
|
||||||
|
x: nextPoint.x + 0.1,
|
||||||
|
y: nextPoint.y + 0.1,
|
||||||
|
canBind: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,15 +118,17 @@ export class Pointing extends StateNode {
|
||||||
|
|
||||||
if (this.editor.inputs.isDragging) {
|
if (this.editor.inputs.isDragging) {
|
||||||
const handles = this.editor.getShapeHandles(this.shape)
|
const handles = this.editor.getShapeHandles(this.shape)
|
||||||
|
console
|
||||||
if (!handles) {
|
if (!handles) {
|
||||||
if (this.markId) this.editor.bailToMark(this.markId)
|
if (this.markId) this.editor.bailToMark(this.markId)
|
||||||
throw Error('No handles found')
|
throw Error('No handles found')
|
||||||
}
|
}
|
||||||
|
const lastHandle = last(handles)!
|
||||||
this.editor.setCurrentTool('select.dragging_handle', {
|
this.editor.setCurrentTool('select.dragging_handle', {
|
||||||
shape: this.shape,
|
shape: this.shape,
|
||||||
isCreating: true,
|
isCreating: true,
|
||||||
handle: last(handles)!,
|
// remove the offset that we added to the handle when we created it
|
||||||
|
handle: { ...lastHandle, x: lastHandle.x - 0.1, y: lastHandle.y - 0.1 },
|
||||||
onInteractionEnd: 'line',
|
onInteractionEnd: 'line',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,19 +110,21 @@ export const Toolbar = memo(function Toolbar() {
|
||||||
<div className="tlui-toolbar">
|
<div className="tlui-toolbar">
|
||||||
<div className="tlui-toolbar__inner">
|
<div className="tlui-toolbar__inner">
|
||||||
<div className="tlui-toolbar__left">
|
<div className="tlui-toolbar__left">
|
||||||
{!isReadonly && breakpoint < 6 && !editor.isInAny('hand', 'zoom') && (
|
{!isReadonly && (
|
||||||
<div
|
<div
|
||||||
className={classNames('tlui-toolbar__extras', {
|
className={classNames('tlui-toolbar__extras', {
|
||||||
'tlui-toolbar__extras__hidden': !showExtraActions,
|
'tlui-toolbar__extras__hidden': !showExtraActions,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="tlui-toolbar__extras__controls">
|
{breakpoint < 6 && !editor.isInAny('hand', 'zoom') && (
|
||||||
<UndoButton />
|
<div className="tlui-toolbar__extras__controls">
|
||||||
<RedoButton />
|
<UndoButton />
|
||||||
<TrashButton />
|
<RedoButton />
|
||||||
<DuplicateButton />
|
<TrashButton />
|
||||||
<ActionsMenu />
|
<DuplicateButton />
|
||||||
</div>
|
<ActionsMenu />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<ToggleToolLockedButton activeToolId={activeToolId} />
|
<ToggleToolLockedButton activeToolId={activeToolId} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue