first sketch union

This commit is contained in:
Stewart Allen 2024-07-25 15:52:17 -04:00
commit 54ffc506bd
4 changed files with 172 additions and 28 deletions

View file

@ -110,6 +110,19 @@ class Polygon {
return this; return this;
} }
toObject() {
return {
points: this.toArray(),
inner: this.inner?.map(i => i.toArray())
};
}
fromObject(obj) {
this.fromArray(obj.points);
this.inner = obj.inner?.map(a => newPolygon().fromArray(a))
return this;
}
matches(poly) { matches(poly) {
let tarr = Array.isArray(poly) ? poly : poly.toArray(); let tarr = Array.isArray(poly) ? poly : poly.toArray();
let parr = this.toArray(); let parr = this.toArray();

View file

@ -62,6 +62,10 @@ const selection = {
return mdl; return mdl;
}, },
sketch() {
return selection.sketches()[0];
},
sketches() { sketches() {
return selection.list(true).filter(s => s.type === 'sketch'); return selection.list(true).filter(s => s.type === 'sketch');
}, },
@ -130,11 +134,10 @@ const selection = {
// remove all // remove all
delete() { delete() {
let list = selection.list(true); if (sketch.selection.delete()) {
if (list[0]?.type === 'sketch' && list[0].selection_delete()) {
return; return;
} }
for (let s of list) { for (let s of selection.list()) {
selection.remove(s); selection.remove(s);
tools.remove(s); tools.remove(s);
s.showBounds(false); s.showBounds(false);
@ -160,9 +163,8 @@ const selection = {
}, },
clear() { clear() {
let list = selection.list(true); if (sketch.selection.clear()) {
if (list[0]?.type === 'sketch' && list[0].selection_clear()) { return;
return false;
} }
for (let s of selection.list()) { for (let s of selection.list()) {
selection.remove(s); selection.remove(s);
@ -442,8 +444,60 @@ let sketch = {
extrude() { extrude() {
selection.sketches(true).forEach(sketch => { selection.sketches(true).forEach(sketch => {
sketch.extrude(); sketch.extrude(...arguments);
}); });
},
selection: {
get one() {
return selection.sketch();
},
list() {
return selection.sketches();
},
clear() {
let list = selection.list(true);
return list[0]?.type === 'sketch' && list[0].selection.clear();
},
delete() {
let list = selection.list(true);
return list[0]?.type === 'sketch' && list[0].selection.delete();
}
},
boolean: {
group() {
sketch.selection.one?.boolean.group();
},
ungroup() {
sketch.selection.one?.boolean.ungroup();
},
union() {
sketch.selection.one?.boolean.union();
},
intersect() {
sketch.selection.one?.boolean.intersect();
},
difference() {
sketch.selection.one?.boolean.difference();
}
},
pattern: {
circle() {
sketch.selection.one?.pattern.circle();
},
grid() {
sketch.selection.one?.pattern.grid();
}
} }
}; };

View file

@ -352,7 +352,7 @@ function ui_build() {
h.bind(logger, [ h.div({ id: 'logtext' }) ]); h.bind(logger, [ h.div({ id: 'logtext' }) ]);
// a few shortcuts to api calls // a few shortcuts to api calls
let { file, selection, mode, tool, prefs, add } = api; let { file, selection, mode, tool, sketch, prefs, add } = api;
// top/center mode selector // top/center mode selector
h.bind(modes, [ h.div([ h.bind(modes, [ h.div([
@ -415,7 +415,7 @@ function ui_build() {
h.button({ _: 'wireframe', onclick() { api.wireframe() } }), h.button({ _: 'wireframe', onclick() { api.wireframe() } }),
]) ])
]), ]),
h.div([ h.div({ class: "sketch-off" }, [
h.div({ _: "boolean", class: "head" }), h.div({ _: "boolean", class: "head" }),
h.div({ class: "back"}), h.div({ class: "back"}),
h.div({ class: "pop"}, [ h.div({ class: "pop"}, [
@ -425,7 +425,19 @@ function ui_build() {
h.button({ _: 'difference', onclick: tool.difference }), h.button({ _: 'difference', onclick: tool.difference }),
]) ])
]), ]),
h.div([ h.div({ class: "sketch-on" }, [
h.div({ _: "boolean", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'union', onclick: sketch.boolean.union }),
h.button({ _: 'intersect', onclick: sketch.boolean.intersect }),
h.button({ _: 'difference', onclick: sketch.boolean.difference }),
h.hr(),
h.button({ _: 'group', onclick: sketch.boolean.union }),
h.button({ _: 'ungroup', onclick: sketch.boolean.union }),
])
]),
h.div({ class: "sketch-off" }, [
h.div({ _: "pattern", class: "head" }), h.div({ _: "pattern", class: "head" }),
h.div({ class: "back"}), h.div({ class: "back"}),
h.div({ class: "pop"}, [ h.div({ class: "pop"}, [
@ -433,6 +445,22 @@ function ui_build() {
h.button({ _: 'grid', onclick() { api.pattern.grid() } }), h.button({ _: 'grid', onclick() { api.pattern.grid() } }),
]) ])
]), ]),
h.div({ class: "sketch-on" }, [
h.div({ _: "pattern", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'circular', onclick() { sketch.pattern.circle() } }),
h.button({ _: 'grid', onclick() { sketch.pattern.grid() } }),
])
]),
h.div({ class: "sketch-on" }, [
h.div({ _: "extrude", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'sketch', onclick() { sketch.extrude() } }),
h.button({ _: 'selection', onclick() { sketch.extrude({ selection: true }) } }),
])
]),
h.div({ class: "sketch-off" }, [ h.div({ class: "sketch-off" }, [
h.div({ _: "models", class: "head" }), h.div({ _: "models", class: "head" }),
h.div({ class: "back"}), h.div({ class: "back"}),

View file

@ -5,6 +5,7 @@
// dep: add.array // dep: add.array
// dep: add.three // dep: add.three
// dep: geo.polygon // dep: geo.polygon
// dep: geo.polygons
// dep: moto.license // dep: moto.license
// dep: moto.client // dep: moto.client
// dep: mesh.object // dep: mesh.object
@ -23,6 +24,7 @@ const { newPolygon } = base;
const mapp = mesh; const mapp = mesh;
const worker = moto.client.fn; const worker = moto.client.fn;
const POLYS = base.polygons;
const material = { const material = {
normal: new MeshBasicMaterial({ color: 0x888888, side: DoubleSide, transparent: true, opacity: 0.25 }), normal: new MeshBasicMaterial({ color: 0x888888, side: DoubleSide, transparent: true, opacity: 0.25 }),
@ -131,28 +133,62 @@ mesh.sketch = class MeshSketch extends mesh.object {
}).reverse(); }).reverse();
} }
get selected_items() { get selection() {
return this.items.filter(i => i.selected); let sketch = this;
return {
// for boolean and patterning, we need the polygon artifacts
mesh_items() {
return sketch.group.children.filter(c => c.sketch_item && c.sketch_item.selected);
},
// return true if any selections were cleared
clear() {
let sel = sketch.items.filter(i => i.selected);
if (sel.length) {
sel.forEach(s => s.selected = false);
sketch.render();
}
return sel.length;
},
// return true if any selections were deleted
delete() {
let sel = sketch.items.filter(i => i.selected);
if (sel.length) {
sketch.items = sketch.items.filter(i => !i.selected);
sketch.render();
}
return sel.length;
}
}
} }
// return true if any selections were cleared get boolean() {
selection_clear() { let sketch = this;
let sel = this.items.filter(i => i.selected); return {
if (sel.length) { union() {
sel.forEach(s => s.selected = false); let items = sketch.selection.mesh_items();
this.render(); let polys = items.map(i => i.sketch_item.poly);
let union = POLYS.union(polys, 0, true);
console.log('sketch union', union);
sketch.selection.delete();
for (let poly of union) {
sketch.add_polygon({ poly });
}
}
} }
return sel.length;
} }
// return true if any selections were deleted get pattern() {
selection_delete() { return {
let sel = this.items.filter(i => i.selected); circle() {
if (sel.length) { console.log('sketch pattern circle');
this.items = this.items.filter(i => !i.selected); },
this.render();
grid() {
console.log('sketch pattern grid');
}
} }
return sel.length;
} }
rename(newname) { rename(newname) {
@ -250,6 +286,13 @@ mesh.sketch = class MeshSketch extends mesh.object {
this.render(); this.render();
} }
add_polygon(opt = {}) {
log(this.file || this.id, '| add polygon');
Object.assign(opt, { center: {x:0, y:0, z:0} }, opt);
this.items.push({ type: "polygon", ...opt, ...opt.poly.toObject() });
this.render();
}
// render items unto the group object // render items unto the group object
render() { render() {
let { group } = this; let { group } = this;
@ -264,9 +307,12 @@ mesh.sketch = class MeshSketch extends mesh.object {
} }
extrude(opt = {}) { extrude(opt = {}) {
let { z } = opt; let { z, selection } = opt;
let models = []; let models = [];
for (let item of this.group.children.filter(c => c.sketch_item)) { let items = this.group.children
.filter(c => c.sketch_item)
.filter(c => !selection || c.sketch_item.selected);
for (let item of items) {
let vert = item.sketch_item.extrude(z || 10); let vert = item.sketch_item.extrude(z || 10);
let nmdl = new mesh.model({ file: "item", mesh: vert.toFloat32() }); let nmdl = new mesh.model({ file: "item", mesh: vert.toFloat32() });
models.push(nmdl); models.push(nmdl);
@ -318,6 +364,9 @@ class SketchItem {
poly = newPolygon().centerCircle(center, radius, points).annotate({ item } ); poly = newPolygon().centerCircle(center, radius, points).annotate({ item } );
} else if (type === 'rectangle') { } else if (type === 'rectangle') {
poly = newPolygon().centerRectangle(center, width, height).annotate({ item }); poly = newPolygon().centerRectangle(center, width, height).annotate({ item });
} else if (type === 'polygon') {
poly = newPolygon().fromObject(item);
poly.move(center);
} else { } else {
throw `invalid sketch type: ${type}`; throw `invalid sketch type: ${type}`;
} }