stub in subtraction. jscad subtract seems to be broken at the api, though

This commit is contained in:
Stewart Allen 2022-04-20 16:53:28 -04:00
commit c7298d2a9f
7 changed files with 113 additions and 47 deletions

View file

@ -12,6 +12,7 @@ Full docs @ https://docs.grid.space/projects/kiri-moto
* `C` substitute some prusa slicer [variables] with KM {variables} on import
* `C` fix CAM output order for tool changes an spindle speed updates
* `C` add CAM pocket operation using surface selection
* `C` fix dogbones on outlines cut by tabs
* `-` refactor cnc to use new core slicer engine
* `-` improve vertex replacement and widget update matrix tracking

View file

@ -136,7 +136,10 @@ function restore_space() {
let selist = space.select || [];
let smodel = api.model.list().filter(m => selist.contains(m.id));
let sgroup = api.group.list().filter(m => selist.contains(m.id));
api.selection.set([...smodel, ...sgroup]);
let tolist = space.tools || [];
let tmodel = api.model.list().filter(m => tolist.contains(m.id));
let tgroup = api.group.list().filter(m => tolist.contains(m.id));
api.selection.set([...smodel, ...sgroup], [...tmodel, ...tgroup]);
// restore edit mode
api.mode.set(mode);
// restore dark mode
@ -350,13 +353,13 @@ function space_init(data) {
case 'Backspace':
case 'Delete':
let mode = api.mode.get();
if (mode !== api.modes.object) {
if ([api.modes.object, api.modes.tool].contains(mode)) {
selection.delete();
} else {
for (let m of selection.models()) {
m.deleteSelections(mode);
space.refresh()
}
} else {
selection.delete();
}
estop(evt);
break;
@ -397,19 +400,19 @@ function space_init(data) {
space.mouse.upSelect((int, event) => {
if (event && event.target.nodeName === "CANVAS") {
let model = int && int.object.model ? int.object.model : undefined;
const model = int && int.object.model ? int.object.model : undefined;
if (temp_mode) {
return temp_mode.select(model);
}
if (model) {
let group = model.group;
let { altKey, ctrlKey, metaKey, shiftKey } = event;
const group = model.group;
const { altKey, ctrlKey, metaKey, shiftKey } = event;
if (metaKey) {
// set focus on intersected face
let { x, y, z } = int.point;
let q = new Quaternion().setFromRotationMatrix(group.object.matrix);
const { x, y, z } = int.point;
const q = new Quaternion().setFromRotationMatrix(group.object.matrix);
// rotate normal using group's matrix
let normal = int.face.normal.applyQuaternion(q);
const normal = int.face.normal.applyQuaternion(q);
// y,z swap due to world rotation for orbit controls
api.focus({center: { x, y:-z, z:y }, normal});
} else if (ctrlKey) {
@ -417,18 +420,18 @@ function space_init(data) {
group.faceDown(int.face.normal);
selection.update();
} else {
let { modes } = api;
let { surface } = api.prefs.map;
let opt = { radians: 0, radius: surface.radius };
switch(api.mode.get()) {
const { modes } = api;
const { surface } = api.prefs.map;
const opt = { radians: 0, radius: surface.radius };
const mode = api.mode.get();
switch(mode) {
case modes.object:
selection.toggle(shiftKey ? model : model.group);
case modes.tool:
selection.toggle(shiftKey ? model : model.group, mode === modes.tool);
break;
case modes.surface:
opt.radians = surface.radians;
case modes.face:
case modes.line:
case modes.vertex:
// find faces adjacent to point/line clicked
model.find(int,
altKey ? { toggle: true } :

View file

@ -21,6 +21,7 @@ const worker = moto.client.fn
const groups = [];
let selected = [];
let tools = [];
const selection = {
// @returns {MeshObject[]} or all groups if not strict and no selection
@ -57,28 +58,32 @@ const selection = {
},
// @param group {MeshObject[]}
set(objects) {
set(objects, toolist) {
selected = objects;
tools = toolist || [];
util.defer(selection.update);
},
// @param group {MeshObject}
add(object) {
add(object, tool) {
// pendantic code necessary to minimize re-entrant api calls
if (object.models) {
// if group, remove discrete selected members
for (let m of object.models) {
if (selected.contains(m)) {
selection.remove(m);
tools.remove(m);
}
}
} else {
// if model, remove selcted group
if (selected.contains(object.group)) {
selection.remove(object.group);
tools.remove(objec.group);
}
}
selected.addOnce(object);
if (tool) tools.addOnce(object);
util.defer(selection.update);
},
@ -92,23 +97,27 @@ const selection = {
delete() {
for (let s of selection.list()) {
selection.remove(s);
tools.remove(s);
s.showBounds(false);
s.remove();
}
},
// @param group {MeshObject}
toggle(object) {
toggle(object, tool) {
if (selected.contains(object)) {
selection.remove(object);
tools.remove(object);
} else {
selection.add(object);
if (tool) tools.addOnce(object);
}
},
clear() {
for (let s of selection.list()) {
selection.remove(s);
tools.remove(s);
}
for (let m of api.model.list()) {
m.clearSelections();
@ -135,10 +144,11 @@ const selection = {
selected = selected.filter(sel => !mgsel.contains(sel)).filter(v => v);
// highlight selected
for (let object of selected) {
object.select(true);
object.select(true, tools.contains(object));
}
// update saved selection id list
prefs.save( prefs.map.space.select = selected.map(s => s.id) );
prefs.save( prefs.map.space.tools = tools.map(s => s.id) );
// force repaint in case of idle
space.update();
return selection;
@ -277,6 +287,7 @@ let add = {
} })
]) ]
});
api.modal.bound.gencyl.focus();
}
}
};
@ -366,6 +377,22 @@ const tool = {
});
},
subtract(models) {
models = fallback(models);
api.log.emit(`subtract ${models.length} model(s)`).pin();
worker.model_subtract(models.map(m => {
return { id: m.id, matrix: m.matrix, tool: m.tool() }
}))
.then(data => {
let group = api.group.new([new mesh.model({
file: `subtracted`,
mesh: data
})]).promote();
api.selection.set([group]);
api.log.emit('subtract complete').unpin();
});
},
duplicate() {
for (let m of selection.models()) {
m.duplicate();
@ -536,9 +563,8 @@ const tool = {
const modes = {
object: "object",
tool: "tool",
face: "face",
line: "line",
vertex: "vertex",
surface: "surface"
};
@ -577,23 +603,22 @@ const mode = {
mode.set(modes.object);
},
tool() {
if (mode.is([ modes.face, modes.surface ])) {
selection.clear();
}
mode.set(modes.tool);
},
face() {
if (mode.is([ modes.object ])) {
if (mode.is([ modes.object, modes.tool ])) {
selection.clear();
}
mode.set(modes.face);
},
line() {
mode.set(modes.line);
},
vertex() {
mode.set(modes.vertex);
},
surface() {
if (mode.is([ modes.object ])) {
if (mode.is([ modes.object, modes.tool ])) {
selection.clear();
}
mode.set(modes.surface);

View file

@ -342,13 +342,13 @@ function ui_build() {
let { file, selection, mode, tool, prefs, add } = api;
// top/center mode selector
h.bind(modes, [
h.bind(modes, [ h.div([
h.button({ _: 'object', id: "mode-object", onclick() { mode.object() } }),
h.button({ _: 'face', id: "mode-face", onclick() { mode.face() } }),
h.button({ _: 'line', id: "mode-line", onclick() { mode.line() } }),
h.button({ _: 'vertex', id: "mode-vertex", onclick() { mode.vertex() } }),
h.button({ _: 'tool', id: "mode-tool", onclick() { mode.tool() } }),
]), h.div([
h.button({ _: 'surface', id: "mode-surface", onclick() { mode.surface() } }),
]);
h.button({ _: 'face', id: "mode-face", onclick() { mode.face() } }),
]) ]);
// create hotkey/action menu (top/left)
h.bind(actions, [
@ -376,9 +376,13 @@ function ui_build() {
h.button({ _: 'regroup', onclick: tool.regroup }),
h.button({ _: 'merge', onclick: tool.merge }),
h.button({ _: 'mirror', onclick: tool.mirror }),
h.button({ _: 'union', onclick: tool.union }),
h.button({ _: 'split', onclick: call.edit_split }),
]),
h.div([
h.div({ _: "bool", class: "head" }),
h.button({ _: 'union', onclick: tool.union }),
h.button({ _: 'subtract', onclick: tool.subtract }),
]),
h.div([
h.div({ _: "normal", class: "head" }),
h.button({ _: 'invert', onclick: tool.invert }),

View file

@ -40,6 +40,15 @@ let materials = mesh.material = {
color: 0x00e000,
opacity: 1
}),
// model selected as tool
tool: new MeshPhongMaterial({
side: DoubleSide,
transparent: true,
shininess: 125,
specular: 0x202020,
color: 0xe00000,
opacity: 1
}),
// face selected (for groups ranges)
face: new MeshPhongMaterial({
side: DoubleSide,
@ -78,6 +87,7 @@ mesh.model = class MeshModel extends mesh.object {
this.mats = {
normal: materials.normal.clone(),
select: materials.select.clone(),
tool: materials.tool.clone(),
face: materials.face.clone()
};
@ -266,14 +276,16 @@ mesh.model = class MeshModel extends mesh.object {
}
// get, set, or toggle selection of model (coloring)
select(bool) {
select(bool, stool) {
const cmat = this.mesh.material[0];
const { normal, select, tool } = this.mats;
if (bool === undefined) {
return this.mesh.material[0] === this.mats.normal;
return cmat !== normal;
}
if (bool.toggle) {
return this.select(!this.select());
return this.select(!this.select(), cmat === tool);
}
this.mesh.material[0] = bool ? this.mats.select : this.mats.normal;
this.mesh.material[0] = bool ? (stool ? tool : select) : normal;
return bool;
}
@ -282,6 +294,13 @@ mesh.model = class MeshModel extends mesh.object {
return this.select();
}
tool(bool) {
if (bool === undefined) {
return this.mesh.material[0] === this.mats.tool;
}
return this.select(this.selected(), bool);
}
opacity(ov, opt = {}) {
let mat = this.mesh.material;
if (ov === undefined) {

View file

@ -150,6 +150,20 @@ let model = {
return base.CSG.toPositionArray(union);
},
subtract(recs) {
let bases = recs.filter(rec => !rec.tool)
.map(rec => translate_encode(rec.id, rec.matrix))
.map(a => base.CSG.fromPositionArray(a));
let tools = recs.filter(rec => rec.tool)
.map(rec => translate_encode(rec.id, rec.matrix))
.map(a => base.CSG.fromPositionArray(a));
let subs = [];
for (let obj of bases) {
subs.appendAll(base.CSG.subtract(obj, ...tools));
}
return base.CSG.toPositionArray(subs);
},
// used to generate a list for split snapping
zlist(data) {
let { id, matrix, round } = data;

View file

@ -408,6 +408,9 @@ button.selected:hover {
top: 0;
left: 50%;
transform: translateX(-50%);
}
#modes > div {
align-items: center;
justify-content: center;
background-color: rgba(255,255,255,0.5);
@ -418,10 +421,6 @@ button.selected:hover {
gap: 2px;
}
#mode-line, #mode-vertex {
display: none;
}
/** slide in/out logging window **/
#logger {
@ -518,6 +517,7 @@ button.selected:hover {
}
#grouplist .models .square {
display: flex;
align-items: center;
justify-content: center;
margin: 0;