add heal option to context menu
This commit is contained in:
parent
083d6256f3
commit
2c78e5e9cc
6 changed files with 97 additions and 12 deletions
|
|
@ -67,6 +67,18 @@
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return non-indexed vertex list (for compatibility)
|
||||||
|
unrolled() {
|
||||||
|
let out = [];
|
||||||
|
let vertices = this.vertices;
|
||||||
|
for (let face of this.faces) {
|
||||||
|
out.push(vertices[face]);
|
||||||
|
out.push(vertices[face+1]);
|
||||||
|
out.push(vertices[face+2]);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
heal() {
|
heal() {
|
||||||
// construct ordered line map with array of connected lines
|
// construct ordered line map with array of connected lines
|
||||||
// for each point pair, create ordered line
|
// for each point pair, create ordered line
|
||||||
|
|
@ -90,6 +102,7 @@
|
||||||
if (this.faces.indexOf(face) < 0) {
|
if (this.faces.indexOf(face) < 0) {
|
||||||
this.faces.push(face);
|
this.faces.push(face);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
unitVector() {
|
unitVector() {
|
||||||
|
|
@ -151,10 +164,9 @@
|
||||||
|
|
||||||
// lines belonging to only one face or with only 2 peers
|
// lines belonging to only one face or with only 2 peers
|
||||||
// are on the edge of a hole
|
// are on the edge of a hole
|
||||||
let edges = lines.filter(l => l.faces.length === 1);
|
let edges = this.edges = lines.filter(l => l.faces.length === 1);
|
||||||
console.log({edges});
|
let loops = this.loops = [];
|
||||||
|
// console.log({edges});
|
||||||
let loops = [];
|
|
||||||
|
|
||||||
// connect edge lines into closed loops
|
// connect edge lines into closed loops
|
||||||
for (let i=0, l=edges.length; i<l; i++) {
|
for (let i=0, l=edges.length; i<l; i++) {
|
||||||
|
|
@ -192,7 +204,8 @@
|
||||||
}
|
}
|
||||||
// drop co-linear points because they can't be connected into valid faces
|
// drop co-linear points because they can't be connected into valid faces
|
||||||
// fixup remaining line by dropping common point and using dropped line's point
|
// fixup remaining line by dropping common point and using dropped line's point
|
||||||
for (let j=0, jl=loop.length; j<jl-1; j++) {
|
// todo: disabled until faces are fixed up, too. otherwise, problems
|
||||||
|
if (false) for (let j=0, jl=loop.length; j<jl-1; j++) {
|
||||||
let l1 = loop[j];
|
let l1 = loop[j];
|
||||||
if (l1.del) {
|
if (l1.del) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -211,11 +224,47 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loop = loop.filter(l => !l.del);
|
loop = loop.filter(l => !l.del);
|
||||||
console.log({loop});
|
|
||||||
loops.push(loop);
|
loops.push(loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log({loops});
|
// console.log({loops});
|
||||||
|
|
||||||
|
// emit loops
|
||||||
|
let faceCount = this.faces.length;
|
||||||
|
for (let loop of loops) {
|
||||||
|
while (loop.length > 2) {
|
||||||
|
// console.log({loop});
|
||||||
|
let nuloop = [];
|
||||||
|
// proceed through line pairs
|
||||||
|
for (let i=0, l=loop.length; i<l; i++) {
|
||||||
|
let l1 = loop[i];
|
||||||
|
let l2 = loop[i+1];
|
||||||
|
// check if two lines share a face
|
||||||
|
// we know edge lines belong to only one face
|
||||||
|
if (!l2 || l1.faces[0] === l2.faces[0]) {
|
||||||
|
nuloop.push(l1);
|
||||||
|
// console.log('end or same face', l1, l2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// synthesize a new face reversing point order
|
||||||
|
let face = faces.length;
|
||||||
|
faces.push(l1.v1);
|
||||||
|
faces.push(l2.v2);
|
||||||
|
faces.push(l2.v1);
|
||||||
|
// add new loop line and increment i to skip l2
|
||||||
|
nuloop.push(getLine(l1.v1, l2.v2).addPeers(l1, face).addPeers(l2, face));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (nuloop.length === loop.length) {
|
||||||
|
console.log("no new faces created");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// console.log({nuloop});
|
||||||
|
loop = nuloop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.newFaces = this.faces.length - faceCount;
|
||||||
|
// console.log({newFaces: this.newFaces});
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2581,6 +2581,7 @@
|
||||||
$('render-wire').onclick = () => { API.view.wireframe(true, 0, 0.5); };
|
$('render-wire').onclick = () => { API.view.wireframe(true, 0, 0.5); };
|
||||||
$('render-solid').onclick = () => { API.view.wireframe(false, 0, 1); };
|
$('render-solid').onclick = () => { API.view.wireframe(false, 0, 1); };
|
||||||
// context menu
|
// context menu
|
||||||
|
$('context-heal-mesh').onclick = () => { API.widgets.heal() };
|
||||||
$('context-export-stl').onclick = () => { objectsExport() };
|
$('context-export-stl').onclick = () => { objectsExport() };
|
||||||
$('context-export-workspace').onclick = () => { profileExport(true) };
|
$('context-export-workspace').onclick = () => { profileExport(true) };
|
||||||
$('context-clear-workspace').onclick = () => {
|
$('context-clear-workspace').onclick = () => {
|
||||||
|
|
|
||||||
|
|
@ -388,6 +388,7 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
filter: (fn) => { WIDGETS = WIDGETS.filter(fn) },
|
filter: (fn) => { WIDGETS = WIDGETS.filter(fn) },
|
||||||
for: forAllWidgets,
|
for: forAllWidgets,
|
||||||
load: Widget.loadFromCatalog,
|
load: Widget.loadFromCatalog,
|
||||||
|
heal: healWidgets,
|
||||||
meshes: meshArray,
|
meshes: meshArray,
|
||||||
opacity: setOpacity,
|
opacity: setOpacity,
|
||||||
annotate: (id) => {
|
annotate: (id) => {
|
||||||
|
|
@ -853,6 +854,26 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function healWidgets() {
|
||||||
|
let widgets = API.widgets.all();
|
||||||
|
if (widgets.length) {
|
||||||
|
API.show.alert("Analyzing objects");
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let healed = 0;
|
||||||
|
for (let w of widgets) {
|
||||||
|
if (w.heal()) {
|
||||||
|
healed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (healed) {
|
||||||
|
API.show.alert(`${healed} Object${healed ? 's':''} healed`);
|
||||||
|
} else {
|
||||||
|
API.show.alert('Nothing found to heal');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function forAllWidgets(f) {
|
function forAllWidgets(f) {
|
||||||
WIDGETS.slice().forEach(function(widget) {
|
WIDGETS.slice().forEach(function(widget) {
|
||||||
f(widget);
|
f(widget);
|
||||||
|
|
|
||||||
|
|
@ -289,9 +289,12 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.mesh) {
|
if (this.mesh) {
|
||||||
this.mesh.geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
|
let geo = this.mesh.geometry;
|
||||||
this.mesh.geometry.computeFaceNormals();
|
geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
|
||||||
this.mesh.geometry.computeVertexNormals();
|
geo.setAttribute('normal', undefined);
|
||||||
|
geo.attributes.position.needsUpdate = true;
|
||||||
|
geo.computeFaceNormals();
|
||||||
|
geo.computeVertexNormals();
|
||||||
this.points = null;
|
this.points = null;
|
||||||
this.meta.vertices = vertices.length / 3;
|
this.meta.vertices = vertices.length / 3;
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -303,9 +306,18 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PRO.heal = function() {
|
||||||
|
let mesh = this.debugMesh();
|
||||||
|
mesh.heal();
|
||||||
|
if (mesh.newFaces) {
|
||||||
|
this.loadVertices(mesh.unrolled().toFloat32());
|
||||||
|
return this.modified = true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
PRO.debugMesh = function(precision) {
|
PRO.debugMesh = function(precision) {
|
||||||
let mesh = new base.Mesh({precision, vertices: this.getVertices().array});
|
return new base.Mesh({precision, vertices: this.getVertices().array});
|
||||||
return mesh;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PRO.getVertices = function() {
|
PRO.getVertices = function() {
|
||||||
|
|
|
||||||
|
|
@ -458,6 +458,7 @@
|
||||||
<button id="context-duplicate" lk="rc_dupl">duplicate</button>
|
<button id="context-duplicate" lk="rc_dupl">duplicate</button>
|
||||||
<button id="context-setfocus" lk="rc_focs">set focus</button>
|
<button id="context-setfocus" lk="rc_focs">set focus</button>
|
||||||
<div class="sep"></div>
|
<div class="sep"></div>
|
||||||
|
<button id="context-heal-mesh" lk="rc_heal">heal meshes</button>
|
||||||
<button id="context-export-stl" lk="rc_xstl">export as STL</button>
|
<button id="context-export-stl" lk="rc_xstl">export as STL</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="fps" class="noshow"></div>
|
<div id="fps" class="noshow"></div>
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ kiri.lang['en-us'] = {
|
||||||
rc_mirr: "mirror",
|
rc_mirr: "mirror",
|
||||||
rc_dupl: "duplicate",
|
rc_dupl: "duplicate",
|
||||||
rc_focs: "set focus",
|
rc_focs: "set focus",
|
||||||
|
rc_heal: "heal meshes",
|
||||||
rc_xstl: "export as STL",
|
rc_xstl: "export as STL",
|
||||||
|
|
||||||
// DEVICE MENU and related dialogs
|
// DEVICE MENU and related dialogs
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue