- 횡단 장 칸 격자선(도각 층 인쇄 개체) 지움 — 칸은 화면 안내선 겹판(guide.cells · 단면마다 점선) · 안내선 숨기기에 함께 꺼짐 · 넘친 칸 붉은 틀은 그대로 - 옛 확정 · 저장본 장은 열 때 칸선만 걷고 칸 안내선은 지금 배치로(refresh_saved_sheet) - CAD 처음 맞춤 · 전체 맞춤 = 내용 + 작도 영역(가이드) — 표준 횡단도가 내용에만 확대돼 도면 한 장 안 자리가 안 보이던 흠 - 낡은 시험 고침: test_169 칸선 단언 → 칸선 없음 + 칸 안내선 · test_212_3 「남는 검은 선 = 인쇄 칸선 지우지 않음」(212-3 결정) → 요구 343-4 로 칸선 지움 · guide-area 시험 칸 그리기 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
3.5 KiB
TypeScript
71 lines
3.5 KiB
TypeScript
/** 전체 맞춤 계산 — 맞출 개체 고르기 · 배율 · 화면 원점(`screenCanvas.drawController.ts` zoomToFitScreen 이 부름). */
|
|
import { Point } from '@flatten-js/core';
|
|
import type { Entity } from '../entities/Entity.ts';
|
|
import { getBoundingBoxOfMultipleEntities } from '../helpers/get-bounding-box-of-multiple-entities.ts';
|
|
import { getEntities, getPickableEntities } from '../state.ts';
|
|
|
|
/** M02 가 구조물 도면에 얹는 도각 층 — 서버 `STRUCTURE_FRAME_LAYER` 와 같음 */
|
|
const STRUCTURE_FRAME_LAYER_ID = 'm02-structure-frame';
|
|
|
|
/** 맞출 개체 */
|
|
export function fitEntities(): Entity[] {
|
|
// 도각·원지반 같은 잠금 도면층은 빼고 설계 콘텐츠에만 맞춘다 (2026-08-31 사용자
|
|
// 확정). 도각까지 넣으면 A1 한 장 전체가 잡혀 화면의 40%가 여백으로 간다.
|
|
// 구조물 도면(M02·B08)은 얹은 도각(작도 틀)이 있으면 도각 전체에 맞춘다 — 옛 DXF 가 자기 도곽·
|
|
// 표제 띠를 품어 범위가 크면 도각이 화면 밖으로 밀린다.
|
|
const structureFrame = getEntities().filter((e) => e.layerId === STRUCTURE_FRAME_LAYER_ID);
|
|
const pickable = getPickableEntities();
|
|
return structureFrame.length ? structureFrame : pickable.length ? pickable : getEntities();
|
|
}
|
|
|
|
/** 맞춤 최소 높이(월드) — 부모가 도면마다 보냄(`fitMinHeight` · 380-4) · 0 = 없음. */
|
|
let fitMinHeight = 0;
|
|
|
|
/** 실을 때마다 부르는 값 — 수 아니면 0(B07 등 안 보내는 쪽 = 종전 그대로). */
|
|
export const setFitMinHeight = (value: unknown): void => {
|
|
fitMinHeight = typeof value === 'number' && value > 0 ? value : 0;
|
|
};
|
|
|
|
/**
|
|
* 개체 범위를 화면 가운데에 놓는 배율 · 화면 원점(월드 좌표).
|
|
* `include` = 범위에 더할 [왼 x, 아래 y, 오른 x, 위 y] — 작도 영역(526-1 · 작은 그림이 칸 크기로 과확대되지 않게)
|
|
*/
|
|
export function fitView(
|
|
entities: Entity[],
|
|
canvasSize: Point,
|
|
include?: readonly [number, number, number, number]
|
|
): { zoom: number; offset: Point } {
|
|
const boundingBox = { ...getBoundingBoxOfMultipleEntities(entities) };
|
|
if (include) {
|
|
boundingBox.minX = Math.min(boundingBox.minX, include[0]);
|
|
boundingBox.minY = Math.min(boundingBox.minY, include[1]);
|
|
boundingBox.maxX = Math.max(boundingBox.maxX, include[2]);
|
|
boundingBox.maxY = Math.max(boundingBox.maxY, include[3]);
|
|
}
|
|
// 글 몇 줄뿐인 도면이 칸 높이로 과확대되지 않게 — 범위를 위 끝에서 아래로 최소 높이까지 늘림
|
|
boundingBox.minY = Math.min(boundingBox.minY, boundingBox.maxY - fitMinHeight);
|
|
const boundingWidth = boundingBox.maxX - boundingBox.minX;
|
|
const boundingHeight = boundingBox.maxY - boundingBox.minY;
|
|
|
|
// 10% 여백을 남기고 두 축 중 더 빡빡한 쪽에 맞춘다 (종횡비 유지)
|
|
const FIT_MARGIN = 0.9;
|
|
const scaleX =
|
|
boundingWidth > 0 ? (canvasSize.x * FIT_MARGIN) / boundingWidth : Number.POSITIVE_INFINITY;
|
|
const scaleY =
|
|
boundingHeight > 0 ? (canvasSize.y * FIT_MARGIN) / boundingHeight : Number.POSITIVE_INFINITY;
|
|
let zoomLevel = Math.min(scaleX, scaleY);
|
|
if (!Number.isFinite(zoomLevel) || zoomLevel <= 0) zoomLevel = 1;
|
|
|
|
// screen = (world - offset) * zoom 이므로, 도면 중심을 화면 중심에 맞추려면
|
|
// offset = worldCenter - (화면 절반 픽셀) / zoom
|
|
const worldCenterX = (boundingBox.minX + boundingBox.maxX) / 2;
|
|
const worldCenterY = (boundingBox.minY + boundingBox.maxY) / 2;
|
|
return {
|
|
zoom: zoomLevel,
|
|
offset: new Point(
|
|
worldCenterX - canvasSize.x / 2 / zoomLevel,
|
|
worldCenterY - canvasSize.y / 2 / zoomLevel
|
|
),
|
|
};
|
|
}
|