task(D-fin): polyline-edit, stretch, break tools complete phase D
This commit is contained in:
@@ -572,6 +572,245 @@ export const joinTool: ToolExtensionV2 = makeJoinExplodeAlignTool('join');
|
||||
export const explodeTool: ToolExtensionV2 = makeJoinExplodeAlignTool('explode');
|
||||
export const alignTool: ToolExtensionV2 = makeJoinExplodeAlignTool('align');
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Polyline-Editor (Task D-Finish): Vertices einer Polyline verschieben.
|
||||
// Klick 1: Polyline wählen. Klick 2+: Vertex wählen (Toleranz) und ziehen.
|
||||
// ESC beendet.
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
let editPoly: CADElement | null = null;
|
||||
let editVertexIdx = -1;
|
||||
|
||||
export const polylineEditTool: ToolExtensionV2 = {
|
||||
manifest: {
|
||||
id: 'polyline-edit',
|
||||
label: 'Polyline-Editor',
|
||||
icon: '\u2b1a',
|
||||
ribbonTab: 'canvas',
|
||||
tags: ['pro'],
|
||||
description: 'Polyline wählen, dann Vertex ziehen',
|
||||
},
|
||||
optionsSchema: [],
|
||||
handlers: {
|
||||
down(e, ctx) {
|
||||
const c = ctx as FullToolContext;
|
||||
if (!c.doc || !c.selection) return;
|
||||
if (!editPoly) {
|
||||
const hit = c.selection.hitTest(e.world.x, e.world.y, 5);
|
||||
if (!hit || hit.type !== 'polyline') {
|
||||
ctx.setStatus('Polyline-Editor: erst eine Polyline anklicken');
|
||||
return;
|
||||
}
|
||||
editPoly = hit;
|
||||
ctx.setStatus('Polyline-Editor: Vertex ziehen, ESC beendet');
|
||||
return;
|
||||
}
|
||||
// Vertex finden in der Nähe:
|
||||
const props = editPoly.properties as Record<string, unknown>;
|
||||
const pts = Array.isArray(props.points)
|
||||
? (props.points as unknown[]).map((p) => p as Pt)
|
||||
: [];
|
||||
for (let i = 0; i < pts.length; i++) {
|
||||
if (Math.hypot(pts[i].x - e.world.x, pts[i].y - e.world.y) < 8) {
|
||||
editVertexIdx = i;
|
||||
ctx.setStatus(`Vertex ${i + 1} — ziehen`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ctx.setStatus('Kein Vertex in der Nähe');
|
||||
},
|
||||
move(e, ctx) {
|
||||
if (!editPoly || editVertexIdx < 0) return;
|
||||
const c = ctx as FullToolContext;
|
||||
const props = editPoly.properties as Record<string, unknown>;
|
||||
const pts = Array.isArray(props.points)
|
||||
? (props.points as unknown[]).map((p) => p as Pt)
|
||||
: [];
|
||||
pts[editVertexIdx] = e.world;
|
||||
const preview: CADElement = {
|
||||
...editPoly,
|
||||
properties: { ...props, points: pts },
|
||||
};
|
||||
c.setPreview?.(preview);
|
||||
},
|
||||
up(e, ctx) {
|
||||
if (!editPoly || editVertexIdx < 0) return;
|
||||
const c = ctx as FullToolContext;
|
||||
if (!c.doc) { editVertexIdx = -1; return; }
|
||||
const current = c.doc.getElement(editPoly.id);
|
||||
if (!current) { editVertexIdx = -1; return; }
|
||||
const props = current.properties as Record<string, unknown>;
|
||||
const pts = Array.isArray(props.points)
|
||||
? (props.points as unknown[]).map((p) => ({ ...(p as Pt) }))
|
||||
: [];
|
||||
if (editVertexIdx < pts.length) {
|
||||
pts[editVertexIdx] = e.world;
|
||||
const xs = pts.map((p) => p.x);
|
||||
const ys = pts.map((p) => p.y);
|
||||
c.doc.transact(() => {
|
||||
c.doc!.updateElement(editPoly!.id, {
|
||||
x: (Math.min(...xs) + Math.max(...xs)) / 2,
|
||||
y: (Math.min(...ys) + Math.max(...ys)) / 2,
|
||||
width: Math.max(...xs) - Math.min(...xs),
|
||||
height: Math.max(...ys) - Math.min(...ys),
|
||||
properties: { ...props, points: pts },
|
||||
});
|
||||
});
|
||||
ctx.setStatus(`Vertex ${editVertexIdx + 1} verschoben`);
|
||||
}
|
||||
editVertexIdx = -1;
|
||||
},
|
||||
cancel(ctx) {
|
||||
editPoly = null;
|
||||
editVertexIdx = -1;
|
||||
ctx.setStatus('Polyline-Editor beendet');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Stretch (Task D-Finish): Endpunkte von Linien in einem Auswahlbereich
|
||||
// verschieben. Klick 1: Basis, Klick 2: Ziel (Delta auf alle selektierten
|
||||
// Endpunkte, die in der Nähe des Basispunkts liegen).
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
let stretchBase: Pt | null = null;
|
||||
|
||||
export const stretchTool: ToolExtensionV2 = {
|
||||
manifest: {
|
||||
id: 'stretch',
|
||||
label: 'Stretch',
|
||||
icon: '\u2197',
|
||||
ribbonTab: 'canvas',
|
||||
tags: ['pro'],
|
||||
description: 'Linien auswählen, dann Basis→Ziel (Endpunkte in Basisnähe werden gestreckt)',
|
||||
},
|
||||
optionsSchema: [],
|
||||
handlers: {
|
||||
down(e, ctx) {
|
||||
const c = ctx as FullToolContext;
|
||||
if (!c.doc || !c.selection) return;
|
||||
if (!stretchBase) {
|
||||
if (c.selection.getIds().length === 0) {
|
||||
const hit = c.selection.hitTest(e.world.x, e.world.y, 5);
|
||||
if (!hit) {
|
||||
ctx.setStatus('Stretch: zuerst Linien auswählen');
|
||||
return;
|
||||
}
|
||||
c.selection.setIds([hit.id]);
|
||||
}
|
||||
stretchBase = e.world;
|
||||
ctx.setStatus(`Stretch: Zielklick (${c.selection.getIds().length} Linien)`);
|
||||
return;
|
||||
}
|
||||
// Commit: alle Endpunkte, die dem Basispunkt am nächsten sind, verschieben:
|
||||
const dx = e.world.x - stretchBase.x;
|
||||
const dy = e.world.y - stretchBase.y;
|
||||
const ids = c.selection.getIds();
|
||||
c.doc.transact(() => {
|
||||
for (const id of ids) {
|
||||
const el = c.doc!.getElement(id);
|
||||
if (!el || el.type !== 'line') continue;
|
||||
const props = el.properties as { x1?: number; y1?: number; x2?: number; y2?: number };
|
||||
if (props.x1 === undefined || props.y1 === undefined || props.x2 === undefined || props.y2 === undefined) continue;
|
||||
// Endpunkte die dem Basispunkt am nächsten sind verschieben:
|
||||
const d1 = Math.hypot(props.x1! - stretchBase!.x, props.y1! - stretchBase!.y);
|
||||
const d2 = Math.hypot(props.x2! - stretchBase!.x, props.y2! - stretchBase!.y);
|
||||
if (d1 < d2) {
|
||||
props.x1 = props.x1! + dx;
|
||||
props.y1 = props.y1! + dy;
|
||||
} else {
|
||||
props.x2 = props.x2! + dx;
|
||||
props.y2 = props.y2! + dy;
|
||||
}
|
||||
const nx1 = props.x1!;
|
||||
const ny1 = props.y1!;
|
||||
const nx2 = props.x2!;
|
||||
const ny2 = props.y2!;
|
||||
c.doc!.updateElement(id, {
|
||||
x: (nx1 + nx2) / 2,
|
||||
y: (ny1 + ny2) / 2,
|
||||
width: Math.abs(nx2 - nx1),
|
||||
height: Math.abs(ny2 - ny1),
|
||||
properties: props,
|
||||
});
|
||||
}
|
||||
});
|
||||
ctx.setStatus(`Stretch: ${ids.length} Linie(n) gestreckt \u0394(${dx.toFixed(0)}, ${dy.toFixed(0)})`);
|
||||
stretchBase = null;
|
||||
},
|
||||
cancel(ctx) {
|
||||
stretchBase = null;
|
||||
ctx.setStatus('Stretch abgebrochen');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Break (Task D-Finish): Linie an einem Punkt in zwei Teile splitten.
|
||||
// Klick auf Linie → zwei neue Linien, Original wird entfernt.
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
export const breakTool: ToolExtensionV2 = {
|
||||
manifest: {
|
||||
id: 'break',
|
||||
label: 'Break',
|
||||
icon: '\u2702',
|
||||
ribbonTab: 'canvas',
|
||||
tags: ['pro'],
|
||||
description: 'Linie anklicken — wird am Klickpunkt geteilt',
|
||||
},
|
||||
optionsSchema: [],
|
||||
handlers: {
|
||||
down(e, ctx) {
|
||||
const c = ctx as FullToolContext;
|
||||
if (!c.doc || !c.selection) return;
|
||||
const hit = c.selection.hitTest(e.world.x, e.world.y, 5);
|
||||
if (!hit || hit.type !== 'line') {
|
||||
ctx.setStatus('Break: Linie anklicken');
|
||||
return;
|
||||
}
|
||||
const props = hit.properties as { x1?: number; y1?: number; x2?: number; y2?: number };
|
||||
if (props.x1 === undefined || props.y1 === undefined || props.x2 === undefined || props.y2 === undefined) return;
|
||||
|
||||
// Klick auf die Linie projizieren (nächster Punkt auf der Linie):
|
||||
const x1 = props.x1 as number;
|
||||
const y1 = props.y1 as number;
|
||||
const x2 = props.x2 as number;
|
||||
const y2 = props.y2 as number;
|
||||
const dx = x2 - x1;
|
||||
const dy = y2 - y1;
|
||||
const lenSq = dx * dx + dy * dy;
|
||||
if (lenSq === 0) return;
|
||||
const t = Math.max(0, Math.min(1, ((e.world.x - x1) * dx + (e.world.y - y1) * dy) / lenSq));
|
||||
const breakX = x1 + t * dx;
|
||||
const breakY = y1 + t * dy;
|
||||
|
||||
c.doc.transact(() => {
|
||||
// Original entfernen:
|
||||
c.doc!.deleteElements([hit.id]);
|
||||
// Zwei neue Linien hinzufügen:
|
||||
c.doc!.addElement({
|
||||
...hit,
|
||||
id: uid('brk'),
|
||||
x: (x1 + breakX) / 2,
|
||||
y: (y1 + breakY) / 2,
|
||||
width: Math.abs(breakX - x1),
|
||||
height: Math.abs(breakY - y1),
|
||||
properties: { ...props, x2: breakX, y2: breakY },
|
||||
});
|
||||
c.doc!.addElement({
|
||||
...hit,
|
||||
id: uid('brk'),
|
||||
x: (breakX + x2) / 2,
|
||||
y: (breakY + y2) / 2,
|
||||
width: Math.abs(x2 - breakX),
|
||||
height: Math.abs(y2 - breakY),
|
||||
properties: { ...props, x1: breakX, y1: breakY },
|
||||
});
|
||||
});
|
||||
ctx.setStatus(`Break: Linie bei (${breakX.toFixed(0)}, ${breakY.toFixed(0)}) geteilt`);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Array rect / polar (Task D-Extensions):
|
||||
// Vervielfältigt die aktuelle Selektion in einem Raster bzw. im Kreis.
|
||||
@@ -1083,11 +1322,11 @@ export const coreModifyPlugin: PluginV2 = {
|
||||
manifest: {
|
||||
id: 'core-modify',
|
||||
name: 'Bearbeiten (Kern)',
|
||||
version: '1.8.0',
|
||||
version: '1.9.0',
|
||||
author: 'web-cad team',
|
||||
description: 'Auswahl, Schraffur, Move/Copy/Rotate/Scale/Mirror/Delete/Offset/Trim/Extend/Fillet/Arrays/Chamfer/Lengthen/Join/Explode/Align (V2).',
|
||||
description: 'Auswahl, Schraffur, Move/Copy/Rotate/Scale/Mirror/Delete/Offset/Trim/Extend/Fillet/Arrays/Chamfer/Lengthen/Join/Explode/Align/Polyline-Edit/Stretch/Break (V2).',
|
||||
category: 'tools',
|
||||
enabledByDefault: true,
|
||||
},
|
||||
tools: [selectTool, hatchTool, moveTool, copyTool, rotateTool, scaleTool, mirrorTool, deleteTool, offsetTool, trimTool, extendTool, filletTool, arrayRectTool, arrayPolarTool, chamferTool, lengthenTool, joinTool, explodeTool, alignTool],
|
||||
tools: [selectTool, hatchTool, moveTool, copyTool, rotateTool, scaleTool, mirrorTool, deleteTool, offsetTool, trimTool, extendTool, filletTool, arrayRectTool, arrayPolarTool, chamferTool, lengthenTool, joinTool, explodeTool, alignTool, polylineEditTool, stretchTool, breakTool],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user