2026-08-27 00:48:28 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* core-measure – Built-in V2-Plugin (Task A4.5).
|
|
|
|
|
|
*
|
|
|
|
|
|
* measure: 2 Klicks → Distanz+Winkel-Anzeige im Status. KEIN Element-Commit
|
|
|
|
|
|
* (Legacy-Verhalten getreu — Messung ist Information, keine Zeichnung).
|
|
|
|
|
|
* Distanzformatierung via bestehendem formatDistance(unit, scaleFactor).
|
|
|
|
|
|
*/
|
|
|
|
|
|
import type { PluginV2, ToolExtensionV2, FullToolContext } from '../../types';
|
2026-08-29 03:09:35 +02:00
|
|
|
|
import { formatDistance, formatArea } from '../../../utils/format';
|
|
|
|
|
|
import { polygonArea } from '../../../tools/modification/geometry';
|
2026-08-27 00:48:28 +02:00
|
|
|
|
|
|
|
|
|
|
type MeasurePhase = 0 | 1; // 0=wartet auf Start, 1=wartet auf Ende
|
|
|
|
|
|
let measureStart: import('../../../tools/modification/geometry').Pt | null = null;
|
|
|
|
|
|
|
|
|
|
|
|
export const measureTool: ToolExtensionV2 = {
|
|
|
|
|
|
manifest: {
|
|
|
|
|
|
id: 'measure',
|
|
|
|
|
|
label: 'Messen',
|
|
|
|
|
|
icon: '\u2194',
|
|
|
|
|
|
ribbonTab: 'view',
|
|
|
|
|
|
tags: ['basic'],
|
|
|
|
|
|
description: 'Distanz und Winkel zwischen zwei Punkten messen',
|
|
|
|
|
|
},
|
|
|
|
|
|
optionsSchema: [],
|
|
|
|
|
|
handlers: {
|
|
|
|
|
|
down(e, ctx) {
|
|
|
|
|
|
const c = ctx as FullToolContext;
|
|
|
|
|
|
if (!measureStart) {
|
|
|
|
|
|
measureStart = e.world;
|
|
|
|
|
|
ctx.setStatus('Messung: Endpunkt klicken');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Zweiter Klick → anzeigen (Legacy-Ausgabeformat)
|
|
|
|
|
|
const rawDist = Math.hypot(e.world.x - measureStart.x, e.world.y - measureStart.y);
|
|
|
|
|
|
const angle = (Math.atan2(e.world.y - measureStart.y, e.world.x - measureStart.x) * 180) / Math.PI;
|
|
|
|
|
|
const unit = (c.options.unit as string | undefined) ?? 'mm';
|
|
|
|
|
|
const scale = (c.options.scaleFactor as number | undefined) ?? 1;
|
|
|
|
|
|
const distStr = formatDistance(rawDist, unit as never, scale);
|
|
|
|
|
|
ctx.setStatus(`distance: ${distStr} angle: ${angle.toFixed(1)}\u00b0`);
|
|
|
|
|
|
measureStart = null;
|
|
|
|
|
|
},
|
|
|
|
|
|
cancel(ctx) {
|
|
|
|
|
|
measureStart = null;
|
|
|
|
|
|
ctx.setStatus('Messung abgebrochen');
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** V2-Plugin: Messwerkzeuge. */
|
2026-08-29 03:09:35 +02:00
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
// Task H5: measure-area — Polygon-Fläche messen (Status-only).
|
|
|
|
|
|
// Klick: Punkt hinzufügen; ENTER: committet Fläche ab 3 Punkten;
|
|
|
|
|
|
// ESC/Rechtsklick: Abbruch. Kein Element-Commit (wie measure).
|
|
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
let areaPts: Array<{ x: number; y: number }> = [];
|
|
|
|
|
|
|
|
|
|
|
|
export const measureAreaTool: ToolExtensionV2 = {
|
|
|
|
|
|
manifest: {
|
|
|
|
|
|
id: 'measure-area',
|
|
|
|
|
|
label: 'Fläche messen',
|
|
|
|
|
|
icon: '\u25a9',
|
|
|
|
|
|
ribbonTab: 'canvas',
|
|
|
|
|
|
tags: ['basic'],
|
|
|
|
|
|
description: 'Polygon-Fläche messen: Punkte klicken, ENTER schließt ab',
|
|
|
|
|
|
},
|
|
|
|
|
|
optionsSchema: [
|
|
|
|
|
|
{ key: 'unit', label: 'Einheit', type: 'select', options: [
|
|
|
|
|
|
{ value: 'mm', label: 'mm' }, { value: 'cm', label: 'cm' }, { value: 'm', label: 'm' },
|
|
|
|
|
|
] },
|
|
|
|
|
|
{ key: 'scaleFactor', label: 'Maßstab (Welt→mm)', type: 'number' },
|
|
|
|
|
|
],
|
|
|
|
|
|
handlers: {
|
|
|
|
|
|
down(e, ctx) {
|
|
|
|
|
|
areaPts.push({ x: e.world.x, y: e.world.y });
|
|
|
|
|
|
if (areaPts.length === 1) {
|
|
|
|
|
|
ctx.setStatus(`Flächenmessung gestartet (1 Punkt) — weitere Punkte klicken, ENTER schließt ab`);
|
|
|
|
|
|
} else if (areaPts.length >= 3) {
|
|
|
|
|
|
const unit = (ctx.options.unit as string | undefined) ?? 'mm';
|
|
|
|
|
|
const scale = (ctx.options.scaleFactor as number | undefined) ?? 1;
|
|
|
|
|
|
ctx.setStatus(`Fläche: ${formatArea(polygonArea(areaPts), unit as never, scale)} — weiter klicken oder ENTER`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ctx.setStatus(`${areaPts.length} Punkte — mindestens 3 benötigt, weitere Punkte klicken`);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
move(e, ctx) {
|
|
|
|
|
|
if (areaPts.length < 2) return;
|
|
|
|
|
|
const probe = [...areaPts, { x: e.world.x, y: e.world.y }];
|
|
|
|
|
|
if (probe.length < 3) return;
|
|
|
|
|
|
const unit = (ctx.options.unit as string | undefined) ?? 'mm';
|
|
|
|
|
|
const scale = (ctx.options.scaleFactor as number | undefined) ?? 1;
|
|
|
|
|
|
ctx.setStatus(`Fläche: ${formatArea(polygonArea(probe), unit as never, scale)}`);
|
|
|
|
|
|
},
|
|
|
|
|
|
key(k, ctx) {
|
|
|
|
|
|
if (k.key === 'Enter' || k.key === 'ENTER') {
|
|
|
|
|
|
if (areaPts.length >= 3) {
|
|
|
|
|
|
const unit = (ctx.options.unit as string | undefined) ?? 'mm';
|
|
|
|
|
|
const scale = (ctx.options.scaleFactor as number | undefined) ?? 1;
|
|
|
|
|
|
ctx.setStatus(`Flächenmessung: ${formatArea(polygonArea(areaPts), unit as never, scale)} (${areaPts.length} Punkte)`);
|
|
|
|
|
|
areaPts = [];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ctx.setStatus(`Flächenmessung benötigt mindestens 3 Punkte (aktuell ${areaPts.length})`);
|
|
|
|
|
|
}
|
|
|
|
|
|
return true; // ENTER konsumiert
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
},
|
|
|
|
|
|
cancel(ctx) {
|
|
|
|
|
|
areaPts = [];
|
|
|
|
|
|
ctx.setStatus('Flächenmessung abgebrochen');
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-08-27 00:48:28 +02:00
|
|
|
|
export const coreMeasurePlugin: PluginV2 = {
|
|
|
|
|
|
manifest: {
|
|
|
|
|
|
id: 'core-measure',
|
|
|
|
|
|
name: 'Messen (Kern)',
|
|
|
|
|
|
version: '1.0.0',
|
|
|
|
|
|
author: 'web-cad team',
|
|
|
|
|
|
description: 'Abstandswinkel-Messung ohne Element-Erzeugung.',
|
|
|
|
|
|
category: 'tools',
|
|
|
|
|
|
enabledByDefault: true,
|
|
|
|
|
|
},
|
2026-08-29 03:09:35 +02:00
|
|
|
|
tools: [measureTool, measureAreaTool],
|
2026-08-27 00:48:28 +02:00
|
|
|
|
};
|