fix(B07): 집기 반경을 화면 기준으로 잡고, 누른 채 끄는 선택을 살린다
- 집기 반경이 월드 거리였다. HIGHLIGHT_ENTITY_DISTANCE를 월드 거리와 그대로 비교해 확대할수록 화면상 반경이 같이 커졌다(1369%에서 15월드 ≈ 205px). helpers/pick-radius로 확대율을 나눠 쓰고 상수를 화면 픽셀 10으로 낮췄다. 하이라이트·선택·그립·시퀀스 도구가 같은 반경을 본다. - 누른 채 끄는 선택이 없었다. 왼쪽 버튼은 mouseup에서만 클릭을 보내 누르고 끌어도 점이 하나만 찍혔고, 그래서 좌→우(창)·우→좌(교차) 구분이 먹히지 않는 것처럼 보였다. 선택 도구일 때만 누를 때 첫 점을 보내고, 4px 넘게 끌고 놓으면 그 자리로 사각형을 닫는다. 다른 도구는 예전처럼 놓을 때 한 점만 받는다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,6 @@ import {
|
||||
CANVAS_INPUT_FIELD_MOUSE_OFFSET,
|
||||
CANVAS_INPUT_FIELD_TEXT_COLOR,
|
||||
CANVAS_INPUT_FIELD_WIDTH,
|
||||
HIGHLIGHT_ENTITY_DISTANCE,
|
||||
PINCH_ZOOM_EXPONENT,
|
||||
SNAP_POINT_DISTANCE,
|
||||
WHEEL_LINE_PX,
|
||||
@@ -25,6 +24,7 @@ import type { ScreenCanvasDrawController } from '../drawControllers/screenCanvas
|
||||
import { calculateAngleGuidesAndSnapPoints } from '../helpers/calculate-angle-guides-and-snap-points.ts';
|
||||
import { findClosestEntity } from '../helpers/find-closest-entity.ts';
|
||||
import { getClosestSnapPointWithinRadius } from '../helpers/get-closest-snap-point.ts';
|
||||
import { pickRadius } from '../helpers/pick-radius.ts';
|
||||
import {
|
||||
getActiveToolActor,
|
||||
getAngleStep,
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
undo,
|
||||
} from '../state.ts';
|
||||
import { Tool } from '../tools.ts';
|
||||
import { SelectState } from '../tools/select-tool.ts';
|
||||
import {
|
||||
type AbsolutePointInputEvent,
|
||||
ActorEvent,
|
||||
@@ -74,8 +75,13 @@ function polarToPoint(distance: number, degrees: number): Point {
|
||||
return new Point(distance * Math.cos(radians), distance * Math.sin(radians));
|
||||
}
|
||||
|
||||
/** 이만큼 넘게 끌면 클릭이 아니라 드래그로 본다 (화면 픽셀) */
|
||||
const DRAG_SELECT_MIN_PX = 4;
|
||||
|
||||
export class InputController {
|
||||
private text = '';
|
||||
/** 왼쪽 버튼을 누른 화면 좌표 — 놓을 때 끌었는지 판단한다 */
|
||||
private leftPressLocation: Point | null = null;
|
||||
|
||||
constructor() {
|
||||
if (typeof process === 'object' && process?.env?.NODE_ENV === 'test') {
|
||||
@@ -179,29 +185,58 @@ export class InputController {
|
||||
setPanStartLocation(null);
|
||||
}
|
||||
if (evt.button === MouseButton.Left) {
|
||||
const screenCanvasDrawController = getScreenCanvasDrawController();
|
||||
const closestSnapPoint = getClosestSnapPointWithinRadius(
|
||||
compact([getSnapPoint(), getSnapPointOnAngleGuide()]),
|
||||
screenCanvasDrawController.getWorldMouseLocation(),
|
||||
SNAP_POINT_DISTANCE / screenCanvasDrawController.getScreenScale()
|
||||
);
|
||||
|
||||
const worldMouseLocationTemp = getScreenCanvasDrawController().targetToWorld(
|
||||
this.getCanvasPoint(evt)
|
||||
);
|
||||
const worldMouseLocation = closestSnapPoint ? closestSnapPoint.point : worldMouseLocationTemp;
|
||||
|
||||
const activeToolActor = getActiveToolActor();
|
||||
activeToolActor?.send({
|
||||
type: ActorEvent.MOUSE_CLICK,
|
||||
worldMouseLocation,
|
||||
screenMouseLocation: screenCanvasDrawController.worldToTarget(worldMouseLocation),
|
||||
holdingCtrl: evt.ctrlKey,
|
||||
holdingShift: evt.shiftKey,
|
||||
} as MouseClickEvent);
|
||||
if (this.isSelectToolActive()) {
|
||||
// 선택 도구는 누를 때 첫 점을 이미 보냈다. 끌었으면 놓는 자리로 사각형을 닫는다.
|
||||
const pressLocation = this.leftPressLocation;
|
||||
this.leftPressLocation = null;
|
||||
const releaseLocation = this.getCanvasPoint(evt);
|
||||
const dragged =
|
||||
!!pressLocation &&
|
||||
Math.hypot(releaseLocation.x - pressLocation.x, releaseLocation.y - pressLocation.y) >
|
||||
DRAG_SELECT_MIN_PX;
|
||||
if (dragged && this.isWaitingForSecondSelectPoint()) {
|
||||
this.sendMouseClick(evt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.sendMouseClick(evt);
|
||||
}
|
||||
}
|
||||
|
||||
/** 활성 도구가 선택 도구인가 */
|
||||
private isSelectToolActive(): boolean {
|
||||
return getActiveToolActor()?.getSnapshot()?.context?.type === Tool.SELECT;
|
||||
}
|
||||
|
||||
/** 선택 도구가 선택 사각형의 두 번째 점을 기다리는 중인가 */
|
||||
private isWaitingForSecondSelectPoint(): boolean {
|
||||
return (
|
||||
getActiveToolActor()?.getSnapshot()?.value === SelectState.WAITING_FOR_SECOND_SELECT_POINT
|
||||
);
|
||||
}
|
||||
|
||||
/** 스냅을 반영한 클릭 한 번을 활성 도구에 보낸다 */
|
||||
private sendMouseClick(evt: MouseEvent) {
|
||||
const screenCanvasDrawController = getScreenCanvasDrawController();
|
||||
const closestSnapPoint = getClosestSnapPointWithinRadius(
|
||||
compact([getSnapPoint(), getSnapPointOnAngleGuide()]),
|
||||
screenCanvasDrawController.getWorldMouseLocation(),
|
||||
SNAP_POINT_DISTANCE / screenCanvasDrawController.getScreenScale()
|
||||
);
|
||||
const worldMouseLocationTemp = screenCanvasDrawController.targetToWorld(
|
||||
this.getCanvasPoint(evt)
|
||||
);
|
||||
const worldMouseLocation = closestSnapPoint ? closestSnapPoint.point : worldMouseLocationTemp;
|
||||
|
||||
getActiveToolActor()?.send({
|
||||
type: ActorEvent.MOUSE_CLICK,
|
||||
worldMouseLocation,
|
||||
screenMouseLocation: screenCanvasDrawController.worldToTarget(worldMouseLocation),
|
||||
holdingCtrl: evt.ctrlKey,
|
||||
holdingShift: evt.shiftKey,
|
||||
} as MouseClickEvent);
|
||||
}
|
||||
|
||||
public handleMouseEnter() {
|
||||
setShouldDrawCursor(true);
|
||||
}
|
||||
@@ -233,7 +268,7 @@ export class InputController {
|
||||
screenCanvasDrawController.targetToWorld(newScreenMouseLocation),
|
||||
getEntities()
|
||||
);
|
||||
if (closestEntityInfo.distance < HIGHLIGHT_ENTITY_DISTANCE) {
|
||||
if (closestEntityInfo.distance < pickRadius()) {
|
||||
setHighlightedEntityIds([closestEntityInfo.entity.id]);
|
||||
} else {
|
||||
setHighlightedEntityIds([]);
|
||||
@@ -279,9 +314,17 @@ export class InputController {
|
||||
}
|
||||
|
||||
public handleMouseDown(evt: MouseEvent) {
|
||||
if (evt.button !== MouseButton.Middle) return;
|
||||
|
||||
setPanStartLocation(this.getCanvasPoint(evt));
|
||||
if (evt.button === MouseButton.Middle) {
|
||||
setPanStartLocation(this.getCanvasPoint(evt));
|
||||
return;
|
||||
}
|
||||
if (evt.button !== MouseButton.Left) return;
|
||||
if ((evt.target as HTMLElement | null)?.closest('.controls')) return;
|
||||
// AutoCAD처럼 누른 자리에서 사각형이 시작되도록 선택 도구에만 첫 점을 미리 보낸다.
|
||||
// 다른 도구는 예전처럼 놓을 때 한 점을 받는다 (끌다가 점이 두 개 찍히지 않게).
|
||||
if (!this.isSelectToolActive()) return;
|
||||
this.leftPressLocation = this.getCanvasPoint(evt);
|
||||
this.sendMouseClick(evt);
|
||||
}
|
||||
|
||||
private getCanvasPoint(evt: MouseEvent): Point {
|
||||
|
||||
Reference in New Issue
Block a user