70 lines
2.7 KiB
JavaScript
70 lines
2.7 KiB
JavaScript
"use strict";
|
||
|
||
// Layer: ui/physics-shape-editor
|
||
// Shared button labels/templates for rotator, reciprocator, and poison-block
|
||
// segment editors. Low-level editor pointer behavior remains in placement log.
|
||
(function (global) {
|
||
const MODE_LABELS = Object.freeze({
|
||
line: "📏 直線",
|
||
free: "✎ 自由描画",
|
||
erase: "⌫ 消しゴム",
|
||
});
|
||
const TEMPLATE_LABELS = Object.freeze({
|
||
bar: "┃ 棒",
|
||
cross: "+ 十字",
|
||
circle: "○ 円",
|
||
});
|
||
const ACTION_LABELS = Object.freeze({
|
||
undo: "↶ 一つ戻す",
|
||
clear: "🗑 全消し",
|
||
});
|
||
function templateSegments(name) {
|
||
if (name === "bar") return [[-130, 0, 130, 0]];
|
||
if (name === "cross") return [[-118, 0, 118, 0], [0, -90, 0, 90]];
|
||
if (name === "circle") {
|
||
const segments = [];
|
||
const n = 18, r = 108;
|
||
for (let i = 0; i < n; i += 1) {
|
||
const a = i / n * Math.PI * 2;
|
||
const b = (i + 1) / n * Math.PI * 2;
|
||
segments.push([Math.cos(a) * r, Math.sin(a) * r, Math.cos(b) * r, Math.sin(b) * r]);
|
||
}
|
||
return segments;
|
||
}
|
||
return null;
|
||
}
|
||
function decorateToolbar(dialog, prefix = "rotator") {
|
||
if (!dialog) return false;
|
||
for (const btn of dialog.querySelectorAll(`[data-${prefix}-mode]`)) {
|
||
const key = btn.dataset[`${prefix}Mode`];
|
||
if (MODE_LABELS[key]) { btn.textContent = MODE_LABELS[key]; btn.title = MODE_LABELS[key].replace(/^[^\s]+\s*/, ""); }
|
||
}
|
||
for (const btn of dialog.querySelectorAll(`[data-${prefix}-template]`)) {
|
||
const key = btn.dataset[`${prefix}Template`];
|
||
if (TEMPLATE_LABELS[key]) { btn.textContent = TEMPLATE_LABELS[key]; btn.title = TEMPLATE_LABELS[key].replace(/^[^\s]+\s*/, ""); }
|
||
}
|
||
const undo = dialog.querySelector(`#${prefix}Undo`);
|
||
const clear = dialog.querySelector(`#${prefix}Clear`);
|
||
if (undo) undo.textContent = ACTION_LABELS.undo;
|
||
if (clear) clear.textContent = ACTION_LABELS.clear;
|
||
return true;
|
||
}
|
||
function pointSegmentDistance(p, seg) {
|
||
if (!p || !Array.isArray(seg) || seg.length < 4) return Infinity;
|
||
const x1 = Number(seg[0]) || 0, y1 = Number(seg[1]) || 0;
|
||
const x2 = Number(seg[2]) || 0, y2 = Number(seg[3]) || 0;
|
||
const dx = x2 - x1, dy = y2 - y1;
|
||
const lenSq = dx * dx + dy * dy;
|
||
if (lenSq <= 0.0001) return Math.hypot(p.x - x1, p.y - y1);
|
||
const u = Math.max(0, Math.min(1, ((p.x - x1) * dx + (p.y - y1) * dy) / lenSq));
|
||
return Math.hypot(p.x - (x1 + dx * u), p.y - (y1 + dy * u));
|
||
}
|
||
global.TarinaiPhysicsShapeEditorSystem = Object.freeze({
|
||
MODE_LABELS,
|
||
TEMPLATE_LABELS,
|
||
ACTION_LABELS,
|
||
decorateToolbar,
|
||
templateSegments,
|
||
pointSegmentDistance,
|
||
});
|
||
})(typeof window !== "undefined" ? window : globalThis);
|