fix: flipping and resizing arrows (#979)

* fix: flipping horizontal for arrows

* fix: horizontal flip

* fix return

* add test for flipping arrow

* visualize arc points

* adding test for vertically flipping

* remove arc points

Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
Judicael 2022-09-13 14:38:42 +03:00 committed by GitHub
parent 8e55e263bf
commit 4ea39a0263
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 13 deletions

View file

@ -1,5 +1,5 @@
import { TldrawTestApp, mockDocument } from '~test' import { TldrawTestApp, mockDocument } from '~test'
import type { RectangleShape } from '~types' import { ArrowShape, RectangleShape, TDShapeType } from '~types'
describe('Flip command', () => { describe('Flip command', () => {
const app = new TldrawTestApp() const app = new TldrawTestApp()
@ -107,4 +107,70 @@ describe('Flip command', () => {
expect(app.getShape<RectangleShape>('rect1').point).toStrictEqual([0, 0]) expect(app.getShape<RectangleShape>('rect1').point).toStrictEqual([0, 0])
expect(app.getShape<RectangleShape>('rect2').point).toStrictEqual([100, 100]) expect(app.getShape<RectangleShape>('rect2').point).toStrictEqual([100, 100])
}) })
it('flip curved arrow horizontally', () => {
app.createShapes({
id: 'arrow1',
type: TDShapeType.Arrow,
bend: -0.5464405676717543,
handles: {
start: {
bindingId: undefined,
canBind: true,
id: 'start',
index: 0,
point: [-13, 107],
},
end: {
bindingId: undefined,
canBind: true,
id: 'end',
index: 1,
point: [388, 112],
},
bend: {
id: 'bend',
index: 2,
point: [185, -0],
},
},
point: [1877, 677],
})
app.select('arrow1')
app.flipHorizontal()
expect(app.getShape<ArrowShape>('arrow1').point).toStrictEqual([1864, 677.19])
expect(app.getShape<ArrowShape>('arrow1').handles.bend.point).toStrictEqual([214.87, 219.06])
})
it('flip arrow vertically', () => {
app.createShapes({
id: 'arrow1',
type: TDShapeType.Arrow,
bend: -0.5464405676717543,
handles: {
start: {
bindingId: undefined,
canBind: true,
id: 'start',
index: 0,
point: [-13, 107],
},
end: {
bindingId: undefined,
canBind: true,
id: 'end',
index: 1,
point: [388, 112],
},
bend: {
id: 'bend',
index: 2,
point: [185, -0],
},
},
point: [1877, 677],
})
app.select('arrow1')
app.flipVertical()
expect(app.getShape<ArrowShape>('arrow1').point).toStrictEqual([1864, 677.19])
expect(app.getShape<ArrowShape>('arrow1').handles.bend.point).toStrictEqual([186.13, -107.25])
})
}) })

View file

@ -376,7 +376,10 @@ export class ArrowUtil extends TDShapeUtil<T, E> {
const initialShapeBounds = this.getBounds(initialShape) const initialShapeBounds = this.getBounds(initialShape)
const handles: (keyof T['handles'])[] = ['start', 'end'] const handles: (keyof T['handles'])[] = ['start', 'end']
const nextHandles = { ...initialShape.handles } const nextHandles = { ...initialShape.handles }
handles.forEach((handle) => { handles.forEach((handle) => {
if (handle === 'bend') return
const [x, y] = nextHandles[handle].point const [x, y] = nextHandles[handle].point
const nw = x / initialShapeBounds.width const nw = x / initialShapeBounds.width
const nh = y / initialShapeBounds.height const nh = y / initialShapeBounds.height
@ -388,19 +391,22 @@ export class ArrowUtil extends TDShapeUtil<T, E> {
], ],
} }
}) })
const { start, bend, end } = nextHandles
const dist = Vec.dist(start.point, end.point) // If we've flipped one of the dimensions (but not both) then invert shape.bend
const midPoint = Vec.med(start.point, end.point)
const bendDist = (dist / 2) * initialShape.bend const nextBend =
const u = Vec.uni(Vec.vec(start.point, end.point)) (scaleX > 0 && scaleY < 0) || (scaleX < 0 && scaleY > 0)
const point = Vec.add(midPoint, Vec.mul(Vec.per(u), bendDist)) ? -initialShape.bend
nextHandles['bend'] = { : initialShape.bend
...bend,
point: Vec.toFixed(Math.abs(bendDist) < 10 ? midPoint : point), // Find the position of the bend handle based on the new start / end handles and the new bend
} const bendPoint = getBendPoint(nextHandles, nextBend)
nextHandles.bend.point = bendPoint
return { return {
point: Vec.toFixed([bounds.minX, bounds.minY]), point: Vec.toFixed([bounds.minX, bounds.minY]),
handles: nextHandles, handles: nextHandles,
bend: nextBend, // does this change?
} }
} }

View file

@ -195,8 +195,9 @@ export function getArcPoints(start: number[], bend: number[], end: number[]) {
const radius = circle[2] const radius = circle[2]
const startAngle = Vec.angle(center, start) const startAngle = Vec.angle(center, start)
const endAngle = Vec.angle(center, end) const endAngle = Vec.angle(center, end)
for (let i = 1 / 20; i < 1; i += 1 / 20) { for (let i = 0; i < 20; i++) {
const angle = Utils.lerpAngles(startAngle, endAngle, i) const t = i / 19
const angle = Utils.lerpAngles(startAngle, endAngle, t)
points.push(Vec.nudgeAtAngle(center, angle, radius)) points.push(Vec.nudgeAtAngle(center, angle, radius))
} }
return points return points