Files
Aislo/B05_Profile/B05_Profile_UI_RouteEdit_Curve_Pick.ts
T
eomsangdonandClaude Opus 5.5 a0a29fd002 feat(B05 평면): 맴돔 = 고름 20 m 법선 · 고른 곡선 3점 · 곡선 없는 교각점 삼각형 · 잠금 황금 (391)
- 391-1 측점 맴돔 · 고름 = 한 함수(drawStationNormal) · 좌우 20 m 고정 하늘색 · 짧은 12 px 선 · 절성토 색 지움 · 구조물 마름모 그대로
- 391-2 고른 곡선 = 굵은 호 + 교각점→BC · EC 접선 + BC · MC · EC 세 점(새 파일 RouteEdit_Curve_Pick · MC = curveMidVertex) · 곡선 없는 교각점 = 같은 크기 삼각형
- 391-3 잠금 = 황금(#ca8a04 · 어두움 #facc15) · 굵기 4 + 흰 테
- 시험 test_391_routeedit_plan_draw 새로 · test_381_3 맴돔 배선 고쳐 씀

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
2026-10-17 17:16:22 +09:00

75 lines
2.9 KiB
TypeScript

/* =============================================================================
* B05_Profile_UI_RouteEdit_Curve_Pick.ts
* 노선 편집 평면 **고른 곡선 밝힘 · 곡선 없는 교각점 삼각형**(요구 242-12 · 13 · PLAN 391-2).
*
* 고른 곡선(교각점 고름 · 호 누름 = 같은 `picked`) = 호 굵게 + 교각점→BC · EC 접선 + BC · MC · EC
* 세 점(하늘색). `_Render` 가 노드 · 손잡이 뒤 맨 위에서 부름 — 집기(`_Input`)는 그대로.
* ========================================================================== */
import type { RouteEditScene } from "./B05_Profile_UI_RouteEdit_Render";
import { curveArcVertices, curveMidVertex } from "./logic/B05_Profile_UI_RouteEdit_Curve";
import type { Vertex } from "./logic/B05_Profile_UI_RouteEdit_Curve";
/** 고름 색 — 측점 법선(`_Hover`)과 같은 하늘색. */
const PICK_COLOR = "#0ea5e9";
/** 삼각형 외접 반지름 = 노드 반지름 × 이 값 — 원과 넓이가 비슷해 보이게. */
const TRIANGLE_SCALE = 1.4;
/** 곡선 없는 교각점 삼각형(391-2) — 꼭짓점 위 · 원 대신 같은 자리 · 크기. 경로만 그림. */
export function traceNodeTriangle(
context: CanvasRenderingContext2D,
x: number,
y: number,
radius: number,
): void {
const r = radius * TRIANGLE_SCALE;
context.moveTo(x, y - r);
context.lineTo(x + r * Math.cos(Math.PI / 6), y + r / 2);
context.lineTo(x - r * Math.cos(Math.PI / 6), y + r / 2);
context.closePath();
}
/** 고른 곡선 밝힘(391-2) — 곡선을 지운 교각점 · 고름 없음이면 안 그림. */
export function drawPickedCurve(
context: CanvasRenderingContext2D,
scene: RouteEditScene,
style: CSSStyleDeclaration,
): void {
const curve = scene.curveInfo.find((item) => item.node_first === scene.picked);
if (!curve || scene.curveOn[curve.node_first] === false) return;
const halo = style.getPropertyValue("--map-halo") || "rgba(255,255,255,0.9)";
const path = (points: ReadonlyArray<Vertex>): void => {
context.beginPath();
points.forEach((vertex, index) => {
const [x, y] = scene.toScreen(vertex);
if (index === 0) context.moveTo(x, y);
else context.lineTo(x, y);
});
};
context.save();
context.strokeStyle = PICK_COLOR;
context.lineWidth = 2;
context.setLineDash([6, 4]);
path([curve.start, curve.apex, curve.end]);
context.stroke();
context.setLineDash([]);
path(curveArcVertices(curve));
context.strokeStyle = halo;
context.lineWidth = 8;
context.stroke();
context.strokeStyle = PICK_COLOR;
context.lineWidth = 5;
context.stroke();
context.fillStyle = PICK_COLOR;
context.strokeStyle = halo;
context.lineWidth = 2;
[curve.start, curveMidVertex(curve), curve.end].forEach((point) => {
const [x, y] = scene.toScreen(point);
context.beginPath();
context.arc(x, y, 5, 0, Math.PI * 2);
context.fill();
context.stroke();
});
context.restore();
}