feat(CAD): 331-2 이동 · 복사 · 회전 키보드 값
- 이동 · 복사 — 기준점 `x,y` · 둘째 점 거리 · `x,y` · `@x,y` · 각도 고정 · 미리보기도 같은 점 계산(getPointFromEvent) - 회전 — 기준점 `x,y` · 각도 숫자 바로(반시계 +) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
This commit is contained in:
@@ -10,7 +10,8 @@ import {
|
||||
setShouldDrawHelpers,
|
||||
} from '../state';
|
||||
import { Tool } from '../tools';
|
||||
import type { DrawEvent, MouseClickEvent, StateEvent, ToolContext } from './tool.types';
|
||||
import type { DrawEvent, PointInputEvent, StateEvent, ToolContext } from './tool.types';
|
||||
import { getPointFromEvent } from '../helpers/get-point-from-event';
|
||||
import { assign, createMachine, sendTo } from 'xstate';
|
||||
import { selectToolStateMachine } from './select-tool';
|
||||
import type { Entity } from '../entities/Entity';
|
||||
@@ -147,6 +148,10 @@ export const copyToolStateMachine = createMachine(
|
||||
actions: [CopyAction.RECORD_START_POINT, CopyAction.COPY_SELECTION_BEFORE_COPY],
|
||||
target: CopyState.WAITING_FOR_END_COPY_POINT,
|
||||
},
|
||||
ABSOLUTE_POINT_INPUT: {
|
||||
actions: [CopyAction.RECORD_START_POINT, CopyAction.COPY_SELECTION_BEFORE_COPY],
|
||||
target: CopyState.WAITING_FOR_END_COPY_POINT,
|
||||
},
|
||||
ESC: {
|
||||
actions: CopyAction.DESELECT_ENTITIES,
|
||||
target: CopyState.INIT,
|
||||
@@ -166,6 +171,18 @@ export const copyToolStateMachine = createMachine(
|
||||
actions: [CopyAction.COPY_SELECTION],
|
||||
target: CopyState.WAITING_FOR_END_COPY_POINT,
|
||||
},
|
||||
NUMBER_INPUT: {
|
||||
actions: [CopyAction.COPY_SELECTION],
|
||||
target: CopyState.WAITING_FOR_END_COPY_POINT,
|
||||
},
|
||||
ABSOLUTE_POINT_INPUT: {
|
||||
actions: [CopyAction.COPY_SELECTION],
|
||||
target: CopyState.WAITING_FOR_END_COPY_POINT,
|
||||
},
|
||||
RELATIVE_POINT_INPUT: {
|
||||
actions: [CopyAction.COPY_SELECTION],
|
||||
target: CopyState.WAITING_FOR_END_COPY_POINT,
|
||||
},
|
||||
ESC: {
|
||||
actions: CopyAction.DESELECT_ENTITIES,
|
||||
target: CopyState.INIT,
|
||||
@@ -191,10 +208,9 @@ export const copyToolStateMachine = createMachine(
|
||||
setShouldDrawHelpers(true);
|
||||
},
|
||||
[CopyAction.RECORD_START_POINT]: assign(({ event }) => {
|
||||
setAngleGuideOriginPoint((event as MouseClickEvent).worldMouseLocation);
|
||||
return {
|
||||
startPoint: (event as MouseClickEvent).worldMouseLocation,
|
||||
};
|
||||
const startPoint = getPointFromEvent(null, event as PointInputEvent);
|
||||
setAngleGuideOriginPoint(startPoint);
|
||||
return { startPoint };
|
||||
}),
|
||||
[CopyAction.COPY_SELECTION_BEFORE_COPY]: assign(({ context }) => {
|
||||
const selectedEntities = getSelectedEntities();
|
||||
@@ -208,7 +224,9 @@ export const copyToolStateMachine = createMachine(
|
||||
return {
|
||||
startPoint: context.startPoint,
|
||||
// Make a copy of the selected entities before copying them, so we can restore them when the user cancels the copy action
|
||||
originalSelectedEntities: compact(selectedEntities.map((entity) => cloneKeepingLook(entity))),
|
||||
originalSelectedEntities: compact(
|
||||
selectedEntities.map((entity) => cloneKeepingLook(entity))
|
||||
),
|
||||
copiedEntities: selectedEntities,
|
||||
};
|
||||
}),
|
||||
@@ -217,11 +235,13 @@ export const copyToolStateMachine = createMachine(
|
||||
throw new Error('[COPY] Calling draw temp copy line without a start point');
|
||||
}
|
||||
|
||||
const endPointTemp = (event as DrawEvent).drawController.getWorldMouseLocation();
|
||||
const endPointTemp = getPointFromEvent(context.startPoint, event as DrawEvent);
|
||||
|
||||
// Copy the entities to the new location
|
||||
// Draw all selected entities according to translation vector, so the user gets visual feedback of where the entities will be copied;
|
||||
const movedEntities = context.originalSelectedEntities.map((entity) => cloneKeepingLook(entity));
|
||||
const movedEntities = context.originalSelectedEntities.map((entity) =>
|
||||
cloneKeepingLook(entity)
|
||||
);
|
||||
moveEntities(
|
||||
movedEntities,
|
||||
endPointTemp.x - context.startPoint.x,
|
||||
@@ -245,8 +265,11 @@ export const copyToolStateMachine = createMachine(
|
||||
}
|
||||
|
||||
// Copy the entities one final time
|
||||
const currentEndPoint = (event as MouseClickEvent).worldMouseLocation;
|
||||
const copiedEntities = context.originalSelectedEntities.map((entity) => cloneKeepingLook(entity));
|
||||
// 클릭 · 거리 · 절대 · 상대 좌표(키보드)
|
||||
const currentEndPoint = getPointFromEvent(context.startPoint, event as PointInputEvent);
|
||||
const copiedEntities = context.originalSelectedEntities.map((entity) =>
|
||||
cloneKeepingLook(entity)
|
||||
);
|
||||
moveEntities(
|
||||
copiedEntities,
|
||||
currentEndPoint.x - context.startPoint.x,
|
||||
|
||||
@@ -11,7 +11,8 @@ import {
|
||||
setShouldDrawHelpers,
|
||||
} from '../state';
|
||||
import { Tool } from '../tools';
|
||||
import type { DrawEvent, MouseClickEvent, StateEvent, ToolContext } from './tool.types';
|
||||
import type { DrawEvent, PointInputEvent, StateEvent, ToolContext } from './tool.types';
|
||||
import { getPointFromEvent } from '../helpers/get-point-from-event';
|
||||
import { assign, createMachine, sendTo } from 'xstate';
|
||||
import { selectToolStateMachine } from './select-tool';
|
||||
import type { Entity } from '../entities/Entity';
|
||||
@@ -148,6 +149,10 @@ export const moveToolStateMachine = createMachine(
|
||||
actions: [MoveAction.RECORD_START_POINT, MoveAction.COPY_SELECTION_BEFORE_MOVE],
|
||||
target: MoveState.WAITING_FOR_END_MOVE_POINT,
|
||||
},
|
||||
ABSOLUTE_POINT_INPUT: {
|
||||
actions: [MoveAction.RECORD_START_POINT, MoveAction.COPY_SELECTION_BEFORE_MOVE],
|
||||
target: MoveState.WAITING_FOR_END_MOVE_POINT,
|
||||
},
|
||||
ESC: {
|
||||
actions: MoveAction.DESELECT_ENTITIES,
|
||||
target: MoveState.INIT,
|
||||
@@ -167,6 +172,18 @@ export const moveToolStateMachine = createMachine(
|
||||
actions: [MoveAction.MOVE_SELECTION, MoveAction.DESELECT_ENTITIES],
|
||||
target: MoveState.WAITING_FOR_SELECTION,
|
||||
},
|
||||
NUMBER_INPUT: {
|
||||
actions: [MoveAction.MOVE_SELECTION, MoveAction.DESELECT_ENTITIES],
|
||||
target: MoveState.WAITING_FOR_SELECTION,
|
||||
},
|
||||
ABSOLUTE_POINT_INPUT: {
|
||||
actions: [MoveAction.MOVE_SELECTION, MoveAction.DESELECT_ENTITIES],
|
||||
target: MoveState.WAITING_FOR_SELECTION,
|
||||
},
|
||||
RELATIVE_POINT_INPUT: {
|
||||
actions: [MoveAction.MOVE_SELECTION, MoveAction.DESELECT_ENTITIES],
|
||||
target: MoveState.WAITING_FOR_SELECTION,
|
||||
},
|
||||
ESC: {
|
||||
actions: MoveAction.RESTORE_ORIGINAL_ENTITIES,
|
||||
target: MoveState.INIT,
|
||||
@@ -192,10 +209,9 @@ export const moveToolStateMachine = createMachine(
|
||||
setShouldDrawHelpers(true);
|
||||
},
|
||||
[MoveAction.RECORD_START_POINT]: assign(({ event }) => {
|
||||
setAngleGuideOriginPoint((event as MouseClickEvent).worldMouseLocation);
|
||||
return {
|
||||
startPoint: (event as MouseClickEvent).worldMouseLocation,
|
||||
};
|
||||
const startPoint = getPointFromEvent(null, event as PointInputEvent);
|
||||
setAngleGuideOriginPoint(startPoint);
|
||||
return { startPoint };
|
||||
}),
|
||||
[MoveAction.COPY_SELECTION_BEFORE_MOVE]: assign(({ context }) => {
|
||||
const selectedEntities = getSelectedEntities();
|
||||
@@ -211,7 +227,9 @@ export const moveToolStateMachine = createMachine(
|
||||
return {
|
||||
startPoint: context.startPoint,
|
||||
// Make a copy of the selected entities before moving them, so we can restore them when the user cancels the move action
|
||||
originalSelectedEntities: compact(selectedEntities.map((entity) => cloneKeepingLook(entity, true))),
|
||||
originalSelectedEntities: compact(
|
||||
selectedEntities.map((entity) => cloneKeepingLook(entity, true))
|
||||
),
|
||||
movedEntities: selectedEntities,
|
||||
};
|
||||
}),
|
||||
@@ -220,11 +238,13 @@ export const moveToolStateMachine = createMachine(
|
||||
throw new Error('[MOVE] Calling draw temp move line without a start point');
|
||||
}
|
||||
|
||||
const endPointTemp = (event as DrawEvent).drawController.getWorldMouseLocation();
|
||||
const endPointTemp = getPointFromEvent(context.startPoint, event as DrawEvent);
|
||||
|
||||
// Move the entities to the new location
|
||||
// Draw all selected entities according to translation vector, so the user gets visual feedback of where the entities will be moved;
|
||||
const movedEntities = context.originalSelectedEntities.map((entity) => cloneKeepingLook(entity, true));
|
||||
const movedEntities = context.originalSelectedEntities.map((entity) =>
|
||||
cloneKeepingLook(entity, true)
|
||||
);
|
||||
moveEntities(
|
||||
movedEntities,
|
||||
endPointTemp.x - context.startPoint.x,
|
||||
@@ -248,7 +268,8 @@ export const moveToolStateMachine = createMachine(
|
||||
}
|
||||
|
||||
// Move the entities one final time
|
||||
const currentEndPoint = (event as MouseClickEvent).worldMouseLocation;
|
||||
// 클릭 · 거리 · 절대 · 상대 좌표(키보드)
|
||||
const currentEndPoint = getPointFromEvent(context.startPoint, event as PointInputEvent);
|
||||
moveEntities(
|
||||
context.originalSelectedEntities,
|
||||
currentEndPoint.x - context.startPoint.x,
|
||||
|
||||
@@ -10,7 +10,15 @@ import {
|
||||
setShouldDrawHelpers,
|
||||
} from '../state';
|
||||
import { Tool } from '../tools';
|
||||
import type { DrawEvent, MouseClickEvent, StateEvent, ToolContext } from './tool.types';
|
||||
import type {
|
||||
DrawEvent,
|
||||
MouseClickEvent,
|
||||
NumberInputEvent,
|
||||
PointInputEvent,
|
||||
StateEvent,
|
||||
ToolContext,
|
||||
} from './tool.types';
|
||||
import { getPointFromEvent } from '../helpers/get-point-from-event';
|
||||
import { assign, createMachine, sendTo } from 'xstate';
|
||||
import { selectToolStateMachine } from './select-tool';
|
||||
import type { Entity } from '../entities/Entity';
|
||||
@@ -41,6 +49,7 @@ export enum RotateAction {
|
||||
COPY_SELECTION_BEFORE_ROTATE = 'COPY_SELECTION_BEFORE_ROTATE',
|
||||
DRAW_TEMP_ROTATE_ENTITIES = 'DRAW_TEMP_ROTATE_ENTITIES',
|
||||
ROTATE_SELECTION = 'ROTATE_SELECTION',
|
||||
ROTATE_BY_ANGLE = 'ROTATE_BY_ANGLE',
|
||||
DESELECT_ENTITIES = 'DESELECT_ENTITIES',
|
||||
RESTORE_ORIGINAL_ENTITIES = 'RESTORE_ORIGINAL_ENTITIES',
|
||||
}
|
||||
@@ -147,6 +156,10 @@ export const rotateToolStateMachine = createMachine(
|
||||
actions: [RotateAction.RECORD_ROTATION_ORIGIN],
|
||||
target: RotateState.WAITING_FOR_ANGLE_START_POINT,
|
||||
},
|
||||
ABSOLUTE_POINT_INPUT: {
|
||||
actions: [RotateAction.RECORD_ROTATION_ORIGIN],
|
||||
target: RotateState.WAITING_FOR_ANGLE_START_POINT,
|
||||
},
|
||||
ESC: {
|
||||
actions: RotateAction.DESELECT_ENTITIES,
|
||||
target: RotateState.INIT,
|
||||
@@ -166,6 +179,15 @@ export const rotateToolStateMachine = createMachine(
|
||||
],
|
||||
target: RotateState.WAITING_FOR_ANGLE_END_POINT,
|
||||
},
|
||||
NUMBER_INPUT: {
|
||||
// 각도(도) 바로 — 반시계 +
|
||||
actions: [
|
||||
RotateAction.COPY_SELECTION_BEFORE_ROTATE,
|
||||
RotateAction.ROTATE_BY_ANGLE,
|
||||
RotateAction.DESELECT_ENTITIES,
|
||||
],
|
||||
target: RotateState.WAITING_FOR_SELECTION,
|
||||
},
|
||||
ESC: {
|
||||
actions: RotateAction.RESTORE_ORIGINAL_ENTITIES,
|
||||
target: RotateState.INIT,
|
||||
@@ -204,11 +226,9 @@ export const rotateToolStateMachine = createMachine(
|
||||
setShouldDrawHelpers(true);
|
||||
},
|
||||
[RotateAction.RECORD_ROTATION_ORIGIN]: assign(({ context, event }): RotateContext => {
|
||||
setAngleGuideOriginPoint((event as MouseClickEvent).worldMouseLocation);
|
||||
return {
|
||||
...context,
|
||||
rotationOrigin: (event as MouseClickEvent).worldMouseLocation,
|
||||
};
|
||||
const rotationOrigin = getPointFromEvent(null, event as PointInputEvent);
|
||||
setAngleGuideOriginPoint(rotationOrigin);
|
||||
return { ...context, rotationOrigin };
|
||||
}),
|
||||
[RotateAction.RECORD_ROTATION_ANGLE_START_POINT]: assign(
|
||||
({ context, event }): RotateContext => {
|
||||
@@ -232,7 +252,9 @@ export const rotateToolStateMachine = createMachine(
|
||||
return {
|
||||
...context,
|
||||
// Make a copy of the selected entities before rotating them, so we can restore them when the user cancels the rotate action
|
||||
originalSelectedEntities: compact(selectedEntities.map((entity) => cloneKeepingLook(entity, true))),
|
||||
originalSelectedEntities: compact(
|
||||
selectedEntities.map((entity) => cloneKeepingLook(entity, true))
|
||||
),
|
||||
};
|
||||
}),
|
||||
[RotateAction.DRAW_TEMP_ROTATE_ENTITIES]: ({ context, event }) => {
|
||||
@@ -279,6 +301,19 @@ export const rotateToolStateMachine = createMachine(
|
||||
setGhostHelperEntities([]);
|
||||
setSelectedEntityIds([]);
|
||||
},
|
||||
[RotateAction.ROTATE_BY_ANGLE]: ({ context, event }) => {
|
||||
if (!context.rotationOrigin) {
|
||||
throw new Error('[ROTATE] Calling rotate by angle without a rotation origin');
|
||||
}
|
||||
const radians = ((event as NumberInputEvent).value * Math.PI) / 180;
|
||||
const rotatedEntities = compact(
|
||||
context.originalSelectedEntities.map((entity) => cloneKeepingLook(entity, true))
|
||||
);
|
||||
for (const entity of rotatedEntities) entity.rotate(context.rotationOrigin, radians);
|
||||
addEntities(rotatedEntities, true);
|
||||
setGhostHelperEntities([]);
|
||||
setSelectedEntityIds([]);
|
||||
},
|
||||
[RotateAction.DESELECT_ENTITIES]: assign(({ context }): RotateContext => {
|
||||
setGhostHelperEntities([]);
|
||||
setSelectedEntityIds([]);
|
||||
|
||||
Reference in New Issue
Block a user