Skip to content

Commit

Permalink
feat: arrow bend center snapping
Browse files Browse the repository at this point in the history
  • Loading branch information
gkuzin13 committed Feb 25, 2024
1 parent 43e13f3 commit b41e1a1
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,38 @@ import { findCanvas, renderWithProviders } from '@/test/test-utils';
import ArrowTransformer from './ArrowTransformer';
import { ARROW_TRANSFORMER } from '@/constants/shape';

describe('ArrowTransformer', () => {
describe('cursor', () => {
it('grab on transformer anchor hover', async () => {
renderWithProviders(
<Stage>
<Layer>
<ArrowTransformer
start={[20, 30]}
control={[30, 40]}
end={[40, 50]}
stageScale={1}
onTranformStart={vi.fn}
onTransform={vi.fn}
onTransformEnd={vi.fn}
/>
</Layer>
</Stage>,
);

const { container } = await findCanvas();

const arrowTransformerAnchor = Konva.stages[0].findOne(
`.${ARROW_TRANSFORMER.ANCHOR_NAME}`,
);

arrowTransformerAnchor?.fire('mouseenter');

expect(container.style.cursor).toBe('grab');

arrowTransformerAnchor?.fire('mouseleave');

expect(container.style.cursor).toBe('');
});
describe('cursor', () => {
it('grab on transformer anchor hover', async () => {
renderWithProviders(
<Stage>
<Layer>
<ArrowTransformer
start={[20, 30]}
control={[30, 40]}
end={[40, 50]}
stageScale={1}
onTranformStart={vi.fn}
onTransform={vi.fn}
onTransformEnd={vi.fn}
/>
</Layer>
</Stage>,
);

const { container } = await findCanvas();

const arrowTransformerAnchor = Konva.stages[0].findOne(
`.${ARROW_TRANSFORMER.ANCHOR_NAME}`,
);

arrowTransformerAnchor?.fire('mouseenter');

expect(container.style.cursor).toBe('grab');

arrowTransformerAnchor?.fire('mouseleave');

expect(container.style.cursor).toBe('');
});
});

// [TODO] - test bend snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { calculateClampedMidPoint, getAnchorType } from './helpers';
import { ARROW_TRANSFORMER } from '@/constants/shape';
import useDefaultThemeColors from '@/hooks/useThemeColors';
import { hexToRGBa } from '@/utils/string';
import { calculateMidPointFromRange } from '@/utils/math';
import { inRange } from '@/utils/position';
import {
resetCursor,
setCursor,
Expand Down Expand Up @@ -150,23 +152,28 @@ const ArrowTransformer = ({
(event: Konva.KonvaEventObject<DragEvent>) => {
const node = event.target as Konva.Circle;
const anchorType = getAnchorType(node);

if (!anchorType) {
return;
}

const { x, y } = node.position();

if (anchorType === 'control') {
const { x: clampedX, y: clampedY } = calculateClampedMidPoint(
[x, y],
start,
end,
);
let updatedPoint = calculateClampedMidPoint([x, y], start, end);
const mid = calculateMidPointFromRange(start, end);
const offset = ARROW_TRANSFORMER.SNAP_OFFSET;

if (
inRange(updatedPoint[0], mid[0] - offset, mid[0] + offset) &&
inRange(updatedPoint[1], mid[1] - offset, mid[1] + offset)
) {
updatedPoint = mid;
}

node.position({ x: clampedX, y: clampedY });
node.position({ x: updatedPoint[0], y: updatedPoint[1] });

onTransform({ anchorType, point: [clampedX, clampedY] });
onTransform({ anchorType, point: updatedPoint });
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function calculateClampedMidPoint(
dragPosition: Point,
start: Point,
end: Point,
) {
): Point {
const { mid, perp, length } = calculateMidPointAndPerp(start, end);

// Calculate the distance of the drag from the midpoint along the perpendicular vector
Expand All @@ -67,10 +67,10 @@ export function calculateClampedMidPoint(

dragDist = Math.max(Math.min(dragDist, length / 2), -length / 2);

return {
x: mid.x + dragDist * perp.x,
y: mid.y + dragDist * perp.y,
};
return [
mid.x + dragDist * perp.x,
mid.y + dragDist * perp.y,
];
}

export function getDefaultPoints(node: NodeObject<'arrow'>) {
Expand Down
1 change: 1 addition & 0 deletions apps/client/src/constants/shape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const ARROW_TRANSFORMER = {
STROKE: colors.green300,
NAME: 'arrow-transformer',
ANCHOR_NAME: 'arrow-transformer-anchor',
SNAP_OFFSET: 8
} as const;

export const ARROW = {
Expand Down
17 changes: 16 additions & 1 deletion apps/client/src/utils/__tests__/position.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import type { Point } from 'shared';
import { nodesGenerator } from '@/test/data-generators';
import { createNode } from '../node';
import { getMiddleNode, getNodeRect, isNodeFullyInView } from '../position';
import {
getMiddleNode,
getNodeRect,
inRange,
isNodeFullyInView,
} from '../position';

describe('getMiddleNode', () => {
const nodes = nodesGenerator(3, 'rectangle').map((node) => ({
Expand Down Expand Up @@ -104,3 +109,13 @@ describe('isNodeFullyInView', () => {
expect(isNodeFullyInView(node, stageRect, 1)).toBe(true);
});
});

describe('inRange', () => {
it('returns true if value is in range', () => {
expect(inRange(5, 1, 10)).toBe(true);
});

it('returns false if value is not in range', () => {
expect(inRange(15, 1, 10)).toBe(false);
});
});
4 changes: 4 additions & 0 deletions apps/client/src/utils/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export function calculateMiddlePoint(rect: IRect): Vector2d {
};
}

export function calculateMidPointFromRange(start: Point, end: Point): Point {
return [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2];
}

export function clamp(value: number, range: [min: number, max: number]) {
return Math.min(Math.max(value, range[0]), range[1]);
}
Expand Down
4 changes: 4 additions & 0 deletions apps/client/src/utils/position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,7 @@ export function getNormalizedInvertedRect(rect: IRect, scale: number): IRect {
export function calculateCenterPoint(width: number, height: number) {
return { x: width / 2, y: height / 2 };
}

export function inRange(value: number, start: number, end: number) {
return value > start && value < end;
}

0 comments on commit b41e1a1

Please sign in to comment.