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 type { RectangleShape } from '~types'
import { ArrowShape, RectangleShape, TDShapeType } from '~types'
describe('Flip command', () => {
const app = new TldrawTestApp()
@ -107,4 +107,70 @@ describe('Flip command', () => {
expect(app.getShape<RectangleShape>('rect1').point).toStrictEqual([0, 0])
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 handles: (keyof T['handles'])[] = ['start', 'end']
const nextHandles = { ...initialShape.handles }
handles.forEach((handle) => {
if (handle === 'bend') return
const [x, y] = nextHandles[handle].point
const nw = x / initialShapeBounds.width
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)
const midPoint = Vec.med(start.point, end.point)
const bendDist = (dist / 2) * initialShape.bend
const u = Vec.uni(Vec.vec(start.point, end.point))
const point = Vec.add(midPoint, Vec.mul(Vec.per(u), bendDist))
nextHandles['bend'] = {
...bend,
point: Vec.toFixed(Math.abs(bendDist) < 10 ? midPoint : point),
}
// If we've flipped one of the dimensions (but not both) then invert shape.bend
const nextBend =
(scaleX > 0 && scaleY < 0) || (scaleX < 0 && scaleY > 0)
? -initialShape.bend
: initialShape.bend
// 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 {
point: Vec.toFixed([bounds.minX, bounds.minY]),
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 startAngle = Vec.angle(center, start)
const endAngle = Vec.angle(center, end)
for (let i = 1 / 20; i < 1; i += 1 / 20) {
const angle = Utils.lerpAngles(startAngle, endAngle, i)
for (let i = 0; i < 20; i++) {
const t = i / 19
const angle = Utils.lerpAngles(startAngle, endAngle, t)
points.push(Vec.nudgeAtAngle(center, angle, radius))
}
return points