add concurrent slicing, new threaded preference checkbox
This commit is contained in:
parent
a4d6c00a8d
commit
8e6a1e5747
9 changed files with 283 additions and 179 deletions
1
app.js
1
app.js
|
|
@ -345,6 +345,7 @@ const script = {
|
||||||
"mode/fdm/driver",
|
"mode/fdm/driver",
|
||||||
"mode/fdm/slice",
|
"mode/fdm/slice",
|
||||||
"kiri/slice",
|
"kiri/slice",
|
||||||
|
"kiri/slicer",
|
||||||
"kiri/layers",
|
"kiri/layers",
|
||||||
"kiri/widget",
|
"kiri/widget",
|
||||||
"kiri/codec",
|
"kiri/codec",
|
||||||
|
|
|
||||||
|
|
@ -676,9 +676,10 @@
|
||||||
exportLocal: false,
|
exportLocal: false,
|
||||||
exportPreview: false,
|
exportPreview: false,
|
||||||
decimate: true,
|
decimate: true,
|
||||||
detail: "good",
|
detail: "100",
|
||||||
animesh: "200",
|
animesh: "200",
|
||||||
healMesh: false,
|
healMesh: false,
|
||||||
|
threaded: false,
|
||||||
ortho: false,
|
ortho: false,
|
||||||
devel: false
|
devel: false
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -115,10 +115,10 @@
|
||||||
level = level.value;
|
level = level.value;
|
||||||
let rez = BASE.config.clipperClean;
|
let rez = BASE.config.clipperClean;
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case 'best': rez = 50; break;
|
case '100': rez = 50; break;
|
||||||
case 'good': rez = BASE.config.clipperClean; break;
|
case '75': rez = BASE.config.clipperClean; break;
|
||||||
case 'fair': rez = 500; break;
|
case '50': rez = 500; break;
|
||||||
case 'poor': rez = 1000; break;
|
case '20': rez = 1000; break;
|
||||||
}
|
}
|
||||||
KIRI.client.config({
|
KIRI.client.config({
|
||||||
base: { clipperClean: rez }
|
base: { clipperClean: rez }
|
||||||
|
|
@ -157,6 +157,7 @@
|
||||||
control.exportPreview = UI.exportPreview.checked;
|
control.exportPreview = UI.exportPreview.checked;
|
||||||
control.decimate = UI.decimate.checked;
|
control.decimate = UI.decimate.checked;
|
||||||
control.healMesh = UI.healMesh.checked;
|
control.healMesh = UI.healMesh.checked;
|
||||||
|
control.threaded = UI.threaded.checked;
|
||||||
control.ortho = UI.ortho.checked;
|
control.ortho = UI.ortho.checked;
|
||||||
control.devel = UI.devel.checked;
|
control.devel = UI.devel.checked;
|
||||||
SPACE.view.setZoom(control.reverseZoom, control.zoomSpeed);
|
SPACE.view.setZoom(control.reverseZoom, control.zoomSpeed);
|
||||||
|
|
@ -1642,6 +1643,7 @@
|
||||||
detail: UC.newSelect(LANG.pt_qual_s, {title: LANG.pt_qual_l, action: detailSave}, "detail"),
|
detail: UC.newSelect(LANG.pt_qual_s, {title: LANG.pt_qual_l, action: detailSave}, "detail"),
|
||||||
decimate: UC.newBoolean(LANG.pt_deci_s, booleanSave, {title: LANG.pt_deci_l}),
|
decimate: UC.newBoolean(LANG.pt_deci_s, booleanSave, {title: LANG.pt_deci_l}),
|
||||||
healMesh: UC.newBoolean(LANG.pt_heal_s, booleanSave, {title: LANG.pt_heal_l}),
|
healMesh: UC.newBoolean(LANG.pt_heal_s, booleanSave, {title: LANG.pt_heal_l}),
|
||||||
|
threaded: UC.newBoolean(LANG.pt_thrd_s, booleanSave, {title: LANG.pt_thrd_l}),
|
||||||
|
|
||||||
prefadd: UC.checkpoint($('prefs-add')),
|
prefadd: UC.checkpoint($('prefs-add')),
|
||||||
|
|
||||||
|
|
@ -2432,6 +2434,7 @@
|
||||||
UI.autoSave.checked = control.autoSave;
|
UI.autoSave.checked = control.autoSave;
|
||||||
UI.decimate.checked = control.decimate;
|
UI.decimate.checked = control.decimate;
|
||||||
UI.healMesh.checked = control.healMesh;
|
UI.healMesh.checked = control.healMesh;
|
||||||
|
UI.threaded.checked = control.threaded;
|
||||||
UI.ortho.checked = control.ortho;
|
UI.ortho.checked = control.ortho;
|
||||||
UI.devel.checked = control.devel;
|
UI.devel.checked = control.devel;
|
||||||
lineTypeSave();
|
lineTypeSave();
|
||||||
|
|
|
||||||
|
|
@ -165,10 +165,10 @@
|
||||||
{ name: "8", id: 8 }
|
{ name: "8", id: 8 }
|
||||||
],
|
],
|
||||||
detail: [
|
detail: [
|
||||||
{ name: "best" },
|
{ name: "100" },
|
||||||
{ name: "good" },
|
{ name: "75" },
|
||||||
{ name: "fair" },
|
{ name: "50" },
|
||||||
{ name: "poor" },
|
{ name: "25" },
|
||||||
],
|
],
|
||||||
linetype: [
|
linetype: [
|
||||||
{ name: "path" },
|
{ name: "path" },
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,22 @@ const funcs = {
|
||||||
reply({ clips });
|
reply({ clips });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sliceBucket: data => {
|
||||||
|
let { points, slices, options } = data;
|
||||||
|
let i = 0, p = 0, realp = new Array(points.length / 3);
|
||||||
|
while (i < points.length) {
|
||||||
|
realp[p++] = BASE.newPoint(points[i++], points[i++], points[i++]);
|
||||||
|
}
|
||||||
|
let output = [];
|
||||||
|
for (let slice of slices) {
|
||||||
|
output.push({
|
||||||
|
slice,
|
||||||
|
tops: KIRI.slicer.sliceZ(slice.z, realp, options).tops
|
||||||
|
});
|
||||||
|
}
|
||||||
|
reply({ output: CODEC.encode(output) });
|
||||||
|
},
|
||||||
|
|
||||||
bad: data => {
|
bad: data => {
|
||||||
reply({error: "invalid command"});
|
reply({error: "invalid command"});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,10 @@
|
||||||
if (self.kiri.slicer) return;
|
if (self.kiri.slicer) return;
|
||||||
|
|
||||||
self.kiri.slicer = {
|
self.kiri.slicer = {
|
||||||
slice: slice,
|
slice,
|
||||||
sliceWidget: sliceWidget,
|
sliceZ,
|
||||||
connectLines: connectLines
|
sliceWidget,
|
||||||
|
connectLines
|
||||||
};
|
};
|
||||||
|
|
||||||
let KIRI = self.kiri,
|
let KIRI = self.kiri,
|
||||||
|
|
@ -67,11 +68,11 @@
|
||||||
zLines = {}, // map count of z index lines
|
zLines = {}, // map count of z index lines
|
||||||
zScale, // bucket span in z units
|
zScale, // bucket span in z units
|
||||||
timeStart = time(),
|
timeStart = time(),
|
||||||
slices = [],
|
|
||||||
zSum = 0.0,
|
zSum = 0.0,
|
||||||
buckets = [],
|
buckets = [],
|
||||||
i, j = 0, k, p1, p2, p3, px,
|
i, j = 0, k, p1, p2, p3, px,
|
||||||
CPRO = KIRI.driver.CAM.process;
|
CPRO = KIRI.driver.CAM.process,
|
||||||
|
concurrent = options.concurrent ? KIRI.minions.concurrent : 0;
|
||||||
|
|
||||||
if (options.add) {
|
if (options.add) {
|
||||||
zMax += zInc;
|
zMax += zInc;
|
||||||
|
|
@ -133,12 +134,20 @@
|
||||||
*/
|
*/
|
||||||
let bucketCount = Math.max(1, Math.ceil(zMax / (zSum / points.length)) - 1);
|
let bucketCount = Math.max(1, Math.ceil(zMax / (zSum / points.length)) - 1);
|
||||||
|
|
||||||
|
if (concurrent > 1) {
|
||||||
|
if (bucketCount < concurrent) {
|
||||||
|
bucketCount = concurrent;
|
||||||
|
} else if (bucketCount > 128) {
|
||||||
|
bucketCount = 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
zScale = 1 / (zMax / bucketCount);
|
zScale = 1 / (zMax / bucketCount);
|
||||||
|
|
||||||
if (bucketCount > 1) {
|
if (bucketCount > 1) {
|
||||||
// create empty buckets
|
// create empty buckets
|
||||||
for (i = 0; i < bucketCount + 1; i++) {
|
for (i = 0; i < bucketCount + 1; i++) {
|
||||||
buckets.push({ points: []});
|
buckets.push({ points: [], slices: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy triples into all matching z-buckets
|
// copy triples into all matching z-buckets
|
||||||
|
|
@ -158,7 +167,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buckets.push({ points });
|
buckets.push({ points, slices: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need Z ordered list for laser auto or adaptive fdm slicing
|
// we need Z ordered list for laser auto or adaptive fdm slicing
|
||||||
|
|
@ -259,7 +268,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a Slice for each z offset in the zIndexes array
|
// create buckets data structure
|
||||||
for (let i = 0; i < zIndexes.length; i++) {
|
for (let i = 0; i < zIndexes.length; i++) {
|
||||||
let ik = zIndexes[i].toFixed(5),
|
let ik = zIndexes[i].toFixed(5),
|
||||||
onFlat = false,
|
onFlat = false,
|
||||||
|
|
@ -270,11 +279,14 @@
|
||||||
if (!useFlats && (onFlat || onLine)) {
|
if (!useFlats && (onFlat || onLine)) {
|
||||||
zIndexes[i] -= -0.001;
|
zIndexes[i] -= -0.001;
|
||||||
}
|
}
|
||||||
// slice next layer and add to slices[] array
|
bucketZ(i, zIndexes[i], zHeights[i], onFlat, onLine, zThick[i]);
|
||||||
sliceZ(zIndexes[i], zHeights[i], onFlat, onLine, zThick[i]);
|
|
||||||
onupdate(i / zIndexes.length);
|
onupdate(i / zIndexes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create slices from each bucketed region
|
||||||
|
sliceBuckets().then(slices => {
|
||||||
|
slices = slices.sort((a,b) => a.index - b.index);
|
||||||
|
|
||||||
// connect slices into linked list for island/bridge projections
|
// connect slices into linked list for island/bridge projections
|
||||||
for (i=1; i<slices.length; i++) {
|
for (i=1; i<slices.length; i++) {
|
||||||
slices[i-1].up = slices[i];
|
slices[i-1].up = slices[i];
|
||||||
|
|
@ -285,6 +297,44 @@
|
||||||
|
|
||||||
// pass Slices array back to ondone function
|
// pass Slices array back to ondone function
|
||||||
ondone(slices);
|
ondone(slices);
|
||||||
|
});
|
||||||
|
|
||||||
|
function bucketZ(index, z, height, thick) {
|
||||||
|
let bucket = buckets[Math.floor(z * zScale)];
|
||||||
|
bucket.slices.push({
|
||||||
|
index, z, height, thick
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sliceBuckets() {
|
||||||
|
let output = [];
|
||||||
|
// console.log({buckets});
|
||||||
|
if (concurrent < 2) {
|
||||||
|
sliceBucketsLocal(buckets, output);
|
||||||
|
} else {
|
||||||
|
let promises = [];
|
||||||
|
for (let bucket of buckets) {
|
||||||
|
promises.push(KIRI.minions.sliceBucket(bucket, options, output));
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sliceBucketsLocal(buckets, output) {
|
||||||
|
for (let bucket of buckets) {
|
||||||
|
for (let slice of bucket.slices) {
|
||||||
|
let { index, z, height, thick } = slice;
|
||||||
|
let { lines, groups, tops } = sliceZ(z, bucket.points, options);
|
||||||
|
slice = newSlice(z).addTops(tops);
|
||||||
|
slice.height = height;
|
||||||
|
slice.index = index;
|
||||||
|
slice.thick = thick;
|
||||||
|
output.push(slice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** ***** SLICING FUNCTIONS ***** */
|
/** ***** SLICING FUNCTIONS ***** */
|
||||||
|
|
||||||
|
|
@ -337,6 +387,7 @@
|
||||||
}
|
}
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given two points and hints about their edges,
|
* Given two points and hints about their edges,
|
||||||
* return a new Line object with points sorted
|
* return a new Line object with points sorted
|
||||||
|
|
@ -366,11 +417,10 @@
|
||||||
* @param {number} z
|
* @param {number} z
|
||||||
* @param {number} [height] optional real height (fdm)
|
* @param {number} [height] optional real height (fdm)
|
||||||
*/
|
*/
|
||||||
function sliceZ(z, height, onflat, online, thick) {
|
function sliceZ(z, points, options = {}) {
|
||||||
let phash = {},
|
let phash = {},
|
||||||
lines = [],
|
lines = [],
|
||||||
slice = newSlice(z),
|
p1, p2, p3;
|
||||||
points = buckets[Math.floor(z * zScale)].points;
|
|
||||||
|
|
||||||
// iterate over matching buckets for this z offset
|
// iterate over matching buckets for this z offset
|
||||||
for (let i = 0; i < points.length; ) {
|
for (let i = 0; i < points.length; ) {
|
||||||
|
|
@ -410,44 +460,41 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow empty slices in CAM swap mode (for topos w/ gaps)
|
if (lines.length == 0 && options.noEmpty) {
|
||||||
// if (lines.length == 0 && !(options.swapX || options.swapY)) return;
|
return;
|
||||||
if (lines.length == 0 && options.noEmpty) return;
|
}
|
||||||
|
|
||||||
slice.height = height;
|
|
||||||
slice.index = slices.length;
|
|
||||||
slice.thick = thick;
|
|
||||||
|
|
||||||
// de-dup and group lines
|
// de-dup and group lines
|
||||||
lines = removeDuplicateLines(lines);
|
lines = removeDuplicateLines(lines);
|
||||||
let groups = connectLines(lines, slice.z);
|
let groups = connectLines(lines, z);
|
||||||
|
|
||||||
// simplistic healing of bad meshes
|
// simplistic healing of bad meshes
|
||||||
if (options.union) {
|
if (options.union) {
|
||||||
groups = POLY.flatten(POLY.union(POLY.nest(groups), null, true), null, true);
|
groups = POLY.flatten(POLY.union(POLY.nest(groups), null, true), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((debug && lines.excessive) || xray) {
|
// if ((debug && lines.excessive) || xray) {
|
||||||
const dash = xray || 3;
|
// const dash = xray || 3;
|
||||||
lines.forEach((line, i) => {
|
// lines.forEach((line, i) => {
|
||||||
const group = i % dash;
|
// const group = i % dash;
|
||||||
const color = [ 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff ][group];
|
// const color = [ 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff ][group];
|
||||||
slice.output().setLayer(`xl-${group}`, color).addLine(line.p1, line.p2);
|
// slice.output().setLayer(`xl-${group}`, color).addLine(line.p1, line.p2);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
let tops = POLY.nest(groups);
|
||||||
|
|
||||||
// identify and add tops to slice
|
// identify and add tops to slice
|
||||||
POLY.nest(groups).forEach((poly, i) => {
|
// POLY.nest(groups).forEach((poly, i) => {
|
||||||
slice.addTop(poly);
|
// slice.addTop(poly);
|
||||||
if (xray) {
|
// if (xray) {
|
||||||
const group = i % (xray || 3);
|
// const group = i % (xray || 3);
|
||||||
const color = [ 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff ][group];
|
// const color = [ 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff ][group];
|
||||||
slice.output().setLayer(`xg-${group}`, color).addPoly(poly);
|
// slice.output().setLayer(`xg-${group}`, color).addPoly(poly);
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
slices.push(slice);
|
return { lines, groups, tops };
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ let BASE = self.base,
|
||||||
POLY = BASE.polygons,
|
POLY = BASE.polygons,
|
||||||
time = UTIL.time,
|
time = UTIL.time,
|
||||||
qtpi = Math.cos(Math.PI/4),
|
qtpi = Math.cos(Math.PI/4),
|
||||||
concurrent = self.Worker ? navigator.hardwareConcurrency || 0 : 0,
|
ccvalue = navigator ? navigator.hardwareConcurrency || 0 : 0,
|
||||||
|
concurrent = self.Worker && ccvalue >= 2 ? ccvalue : 0,
|
||||||
current = self.worker = {
|
current = self.worker = {
|
||||||
print: null,
|
print: null,
|
||||||
snap: null
|
snap: null
|
||||||
|
|
@ -38,7 +39,7 @@ KIRI.minions = {
|
||||||
|
|
||||||
union: function(polys, minarea) {
|
union: function(polys, minarea) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (concurrent === 0 || polys.length * 2 < concurrent) {
|
if (concurrent < 2 || polys.length * 2 < concurrent) {
|
||||||
resolve(POLY.union(polys, 0, true));
|
resolve(POLY.union(polys, 0, true));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +66,7 @@ KIRI.minions = {
|
||||||
|
|
||||||
fill: function(polys, angle, spacing, output, minLen, maxLen) {
|
fill: function(polys, angle, spacing, output, minLen, maxLen) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (concurrent === 0) {
|
if (concurrent < 2) {
|
||||||
resolve(POLY.fillArea(polys, angle, spacing, [], minLen, maxLen));
|
resolve(POLY.fillArea(polys, angle, spacing, [], minLen, maxLen));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +90,7 @@ KIRI.minions = {
|
||||||
|
|
||||||
clip: function(slice, polys, lines) {
|
clip: function(slice, polys, lines) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (concurrent === 0) {
|
if (concurrent < 2) {
|
||||||
reject("concurrent clip unavaiable");
|
reject("concurrent clip unavaiable");
|
||||||
}
|
}
|
||||||
minwork.queue({
|
minwork.queue({
|
||||||
|
|
@ -111,8 +112,40 @@ KIRI.minions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
queue: function(work, ondone) {
|
sliceBucket: function(bucket, options, output) {
|
||||||
minionq.push({work, ondone});
|
return new Promise((resolve, reject) => {
|
||||||
|
if (concurrent < 2) {
|
||||||
|
reject("concurrent slice unavaiable");
|
||||||
|
}
|
||||||
|
let { points, slices } = bucket;
|
||||||
|
let i = 0, floatP = new Float32Array(points.length * 3);
|
||||||
|
for (let p of points) {
|
||||||
|
floatP[i++] = p.x;
|
||||||
|
floatP[i++] = p.y;
|
||||||
|
floatP[i++] = p.z;
|
||||||
|
}
|
||||||
|
minwork.queue({
|
||||||
|
cmd: "sliceBucket",
|
||||||
|
points: floatP,
|
||||||
|
slices,
|
||||||
|
options
|
||||||
|
}, data => {
|
||||||
|
for (let rec of KIRI.codec.decode(data.output)) {
|
||||||
|
let { slice, tops } = rec;
|
||||||
|
let newSlice = KIRI.newSlice(slice.z);
|
||||||
|
newSlice.index = slice.index;
|
||||||
|
newSlice.thick = slice.thick;
|
||||||
|
newSlice.height = slice.height;
|
||||||
|
newSlice.addTops(tops);
|
||||||
|
output.push(newSlice);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
}, [ floatP.buffer ]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
queue: function(work, ondone, direct) {
|
||||||
|
minionq.push({work, ondone, direct});
|
||||||
minwork.kick();
|
minwork.kick();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -125,7 +158,7 @@ KIRI.minions = {
|
||||||
qrec.ondone(msg.data);
|
qrec.ondone(msg.data);
|
||||||
minwork.kick();
|
minwork.kick();
|
||||||
};
|
};
|
||||||
minion.postMessage(qrec.work);
|
minion.postMessage(qrec.work, qrec.direct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@
|
||||||
sliceFillAngle = spro.sliceFillAngle,
|
sliceFillAngle = spro.sliceFillAngle,
|
||||||
supportDensity = spro.sliceSupportDensity,
|
supportDensity = spro.sliceSupportDensity,
|
||||||
beltfact = Math.cos(Math.PI/4),
|
beltfact = Math.cos(Math.PI/4),
|
||||||
doConcurrent = ctrl.danger && KIRI.minions.concurrent;
|
doConcurrent = ctrl.threaded && KIRI.minions.concurrent;
|
||||||
|
|
||||||
isFlat = ctrl.lineType === "flat";
|
isFlat = ctrl.lineType === "flat";
|
||||||
isThin = !isFlat && ctrl.lineType === "line";
|
isThin = !isFlat && ctrl.lineType === "line";
|
||||||
|
|
@ -155,7 +155,8 @@
|
||||||
// debug: true,
|
// debug: true,
|
||||||
// xray: 3,
|
// xray: 3,
|
||||||
// view: view,
|
// view: view,
|
||||||
indices: spro.indices
|
indices: spro.indices,
|
||||||
|
concurrent: doConcurrent
|
||||||
}, onSliceDone, onSliceUpdate);
|
}, onSliceDone, onSliceUpdate);
|
||||||
|
|
||||||
function onSliceUpdate(update) {
|
function onSliceUpdate(update) {
|
||||||
|
|
|
||||||
|
|
@ -235,13 +235,15 @@ kiri.lang['en-us'] = {
|
||||||
|
|
||||||
lo_menu: "layout",
|
lo_menu: "layout",
|
||||||
|
|
||||||
pt_menu: "parts",
|
pt_menu: "slicer",
|
||||||
pt_deci_s: "decimate",
|
pt_deci_s: "decimate",
|
||||||
pt_deci_l: ["enable or disable point decimation","during port import. for faster slicing","and lower memory usage"],
|
pt_deci_l: ["enable or disable point decimation","during port import. for faster slicing","and lower memory usage"],
|
||||||
pt_qual_s: "quality",
|
pt_qual_s: "detail",
|
||||||
pt_qual_l: ["level of detail to retain","during slicing operations","lower is faster"],
|
pt_qual_l: ["level of detail to retain","during slicing operations","lower is faster"],
|
||||||
pt_heal_s: "heal mesh",
|
pt_heal_s: "heal mesh",
|
||||||
pt_heal_l: ["attempt to heal","non-manifold meshes","adds to slicing time"],
|
pt_heal_l: ["attempt to heal","non-manifold meshes","adds to slicing time"],
|
||||||
|
pt_thrd_s: "threaded",
|
||||||
|
pt_thrd_l: ["use parallel processin","when browser permits"],
|
||||||
|
|
||||||
xp_menu: "exports",
|
xp_menu: "exports",
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue