fix(B07): 도각 같은 잠금 도면층을 설계 조작이 집지 않게 한다
잠금 필터가 공용 함수 없이 호출부마다 들어가 있어 넣은 곳은 막히고 안 넣은 곳은 샜다. Ctrl+A는 도각 103개·원지반 16개까지 전부 선택했고, 선택 삭제에는 잠금 검사가 없어 Ctrl+A → Delete 한 번에 도각이 사라졌다. 자르기와 트림·모따기 계열, 커서 강조도 도각을 집었다. state.ts에 getPickableEntities()(잠금 도면층 제외)를 두고 집기 경로가 그걸 쓴다. 선택은 setSelectedEntityIds() 한 곳에서 막아, 앞으로 생길 선택 경로도 자동으로 잠금 객체를 담지 못한다. 화면맞춤(zoomToFitScreen)도 같은 목록에 맞춘다 — 도각까지 넣으면 A1 한 장 전체가 잡혀 화면의 40%가 여백으로 갔다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ import { getAngleWithXAxis } from '../helpers/get-angle-with-x-axis.ts';
|
||||
import { getBoundingBoxOfMultipleEntities } from '../helpers/get-bounding-box-of-multiple-entities.ts';
|
||||
import { mapNumberRange } from '../helpers/map-number-range.ts';
|
||||
import { StateVariable } from '../helpers/undo-stack.ts';
|
||||
import { getEntities, getGridEnabled, triggerReactUpdate } from '../state.ts';
|
||||
import { getEntities, getGridEnabled, getPickableEntities, triggerReactUpdate } from '../state.ts';
|
||||
import { paintColor, themeColor } from '../theme.ts';
|
||||
import { DEFAULT_TEXT_OPTIONS, type DrawController } from './DrawController';
|
||||
|
||||
@@ -142,7 +142,10 @@ export class ScreenCanvasDrawController implements DrawController {
|
||||
* 월드 좌표 offset에 그대로 대입해 중심 배치가 어긋나는 문제가 있었다.)
|
||||
*/
|
||||
public zoomToFitScreen() {
|
||||
const entities = getEntities();
|
||||
// 도각·원지반 같은 잠금 도면층은 빼고 설계 콘텐츠에만 맞춘다 (2026-08-31 사용자
|
||||
// 확정). 도각까지 넣으면 A1 한 장 전체가 잡혀 화면의 40%가 여백으로 간다.
|
||||
const pickable = getPickableEntities();
|
||||
const entities = pickable.length ? pickable : getEntities();
|
||||
if (!entities.length) return;
|
||||
const boundingBox = getBoundingBoxOfMultipleEntities(entities);
|
||||
const boundingWidth = boundingBox.maxX - boundingBox.minX;
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
getActiveToolActor,
|
||||
getAngleStep,
|
||||
getCanvas,
|
||||
getEntities,
|
||||
getPickableEntities,
|
||||
getGridEnabled,
|
||||
getLastStateInstructions,
|
||||
getPanStartLocation,
|
||||
@@ -271,7 +271,7 @@ export class InputController {
|
||||
if (getActiveToolActor()?.getSnapshot()?.context.type === Tool.SELECT) {
|
||||
const closestEntityInfo = findClosestEntity(
|
||||
screenCanvasDrawController.targetToWorld(newScreenMouseLocation),
|
||||
getEntities()
|
||||
getPickableEntities()
|
||||
);
|
||||
if (closestEntityInfo.distance < pickRadius()) {
|
||||
setHighlightedEntityIds([closestEntityInfo.entity.id]);
|
||||
@@ -428,7 +428,7 @@ export class InputController {
|
||||
this.handleRedo(evt);
|
||||
} else if (evt.ctrlKey && evt.key === 'a') {
|
||||
// User wants to select everything
|
||||
setSelectedEntityIds(getEntities().map((entity) => entity.id));
|
||||
setSelectedEntityIds(getPickableEntities().map((entity) => entity.id));
|
||||
} else if (evt.key === 'Backspace') {
|
||||
// Remove the last character from the input field
|
||||
evt.preventDefault();
|
||||
|
||||
@@ -189,6 +189,13 @@ export const getCanvas = () => canvas;
|
||||
export const getActiveToolActor = () => activeToolActor;
|
||||
export const getLastStateInstructions = () => lastStateInstructions;
|
||||
export const getEntities = (): Entity[] => entities;
|
||||
/**
|
||||
* 집을 수 있는 객체 — 잠금 도면층(도각 b08-frame·원지반 b08-ground 등 참조용)을 뺀다.
|
||||
* 선택·지우기·수정 도구의 대상 찾기와 화면맞춤은 이 목록을 쓴다. AutoCAD가 도각을
|
||||
* 배치(도면공간)에 두어 모형공간 작업에 끼지 않게 하는 것과 같은 자리다.
|
||||
*/
|
||||
export const getPickableEntities = (): Entity[] =>
|
||||
entities.filter((entity) => !layersById.get(entity.layerId)?.isLocked);
|
||||
export const getSelectedEntityIds = () => selectedEntityIds;
|
||||
export const getShouldDrawCursor = () => shouldDrawCursor;
|
||||
export const getAngleGuideEntities = () => angleGuideEntities;
|
||||
@@ -305,6 +312,12 @@ export const setHighlightedEntityIds = (newEntityIds: string[]) => {
|
||||
highlightedEntityIdSet = new Set(newEntityIds);
|
||||
};
|
||||
export const setSelectedEntityIds = (newEntityIds: string[]) => {
|
||||
// 잠금 도면층 객체는 어느 경로로도 선택되지 않는다 — 선택이 곧 삭제·이동 대상이라
|
||||
// 호출부(Ctrl+A 등)마다 거르지 않고 여기 한 곳에서 막는다.
|
||||
const locked = new Set(
|
||||
entities.filter((entity) => layersById.get(entity.layerId)?.isLocked).map((entity) => entity.id)
|
||||
);
|
||||
if (locked.size) newEntityIds = newEntityIds.filter((id) => !locked.has(id));
|
||||
selectedEntityIds = newEntityIds;
|
||||
selectedEntityIdSet = new Set(newEntityIds);
|
||||
bumpSceneVersion(); // selection style (dashed) is baked into the scene cache
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
import type {Point, Polygon} from '@flatten-js/core';
|
||||
import {assign, createMachine} from 'xstate';
|
||||
import type {ArcEntity} from '../entities/ArcEntity';
|
||||
import type {CircleEntity} from '../entities/CircleEntity';
|
||||
import {EntityName} from '../entities/Entity';
|
||||
import {LineEntity} from '../entities/LineEntity';
|
||||
import type {RectangleEntity} from '../entities/RectangleEntity';
|
||||
import {findClosestEntity} from '../helpers/find-closest-entity';
|
||||
import {polygonToSegments} from '../helpers/polygon-to-segments';
|
||||
import type { Point, Polygon } from '@flatten-js/core';
|
||||
import { assign, createMachine } from 'xstate';
|
||||
import type { ArcEntity } from '../entities/ArcEntity';
|
||||
import type { CircleEntity } from '../entities/CircleEntity';
|
||||
import { EntityName } from '../entities/Entity';
|
||||
import { LineEntity } from '../entities/LineEntity';
|
||||
import type { RectangleEntity } from '../entities/RectangleEntity';
|
||||
import { findClosestEntity } from '../helpers/find-closest-entity';
|
||||
import { polygonToSegments } from '../helpers/polygon-to-segments';
|
||||
import {
|
||||
addEntities,
|
||||
deleteEntities,
|
||||
getActiveLayerId,
|
||||
getEntities,
|
||||
getPickableEntities,
|
||||
setEntities,
|
||||
setGhostHelperEntities,
|
||||
setShouldDrawHelpers,
|
||||
} from '../state';
|
||||
import {Tool} from '../tools';
|
||||
import {eraseArcSegment, eraseCircleSegment, eraseLineSegment, getAllIntersectionPoints,} from './eraser-tool.helpers';
|
||||
import type {MouseClickEvent, StateEvent, ToolContext} from './tool.types';
|
||||
import { Tool } from '../tools';
|
||||
import {
|
||||
eraseArcSegment,
|
||||
eraseCircleSegment,
|
||||
eraseLineSegment,
|
||||
getAllIntersectionPoints,
|
||||
} from './eraser-tool.helpers';
|
||||
import type { MouseClickEvent, StateEvent, ToolContext } from './tool.types';
|
||||
|
||||
export interface EraserContext extends ToolContext {
|
||||
startPoint: Point | null;
|
||||
@@ -86,7 +92,7 @@ export const eraserToolStateMachine = createMachine(
|
||||
);
|
||||
|
||||
export function handleMouseClick(worldMouseLocation: Point) {
|
||||
const closestEntity = findClosestEntity(worldMouseLocation, getEntities());
|
||||
const closestEntity = findClosestEntity(worldMouseLocation, getPickableEntities());
|
||||
if (!closestEntity) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { getPointFromEvent } from '../../helpers/get-point-from-event';
|
||||
import { pickRadius } from '../../helpers/pick-radius';
|
||||
import { queryEntitiesNearPoint } from '../../helpers/spatial-index';
|
||||
import {
|
||||
getLayerById,
|
||||
getScreenCanvasDrawController,
|
||||
getSelectedEntities,
|
||||
setActiveToolActor,
|
||||
@@ -147,7 +148,10 @@ function cursorPoint(event: StateEvent | undefined): Point {
|
||||
/** 클릭 지점에서 가장 가까운 엔티티 (선택 반경 안에 있을 때만) */
|
||||
function pickEntityAt(worldPoint: Point): Entity | null {
|
||||
const radius = pickRadius();
|
||||
const candidates = queryEntitiesNearPoint(worldPoint.x, worldPoint.y, radius);
|
||||
// 잠금 도면층(도각 등)은 집지 않는다 — 수정 도구가 참조 선을 잡으면 안 된다.
|
||||
const candidates = queryEntitiesNearPoint(worldPoint.x, worldPoint.y, radius).filter(
|
||||
(entity) => !getLayerById(entity.layerId)?.isLocked
|
||||
);
|
||||
const { distance, entity } = findClosestEntity(worldPoint, candidates);
|
||||
return entity && distance <= radius ? entity : null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user