add user control over object import and detail
This commit is contained in:
parent
0027a919a3
commit
24dc8898f5
15 changed files with 382 additions and 291 deletions
5
app.js
5
app.js
|
|
@ -242,6 +242,7 @@ const script = {
|
|||
"geo/debug",
|
||||
"geo/render",
|
||||
"geo/point",
|
||||
"geo/points",
|
||||
"geo/slope",
|
||||
"geo/line",
|
||||
"geo/bounds",
|
||||
|
|
@ -268,12 +269,13 @@ const script = {
|
|||
"mode/sla/client",
|
||||
"mode/cam/driver",
|
||||
"mode/cam/client",
|
||||
"mode/cam/tool",
|
||||
"mode/laser/driver",
|
||||
"kiri/layer",
|
||||
"kiri/widget",
|
||||
"kiri/print",
|
||||
"kiri/codec",
|
||||
"kiri/work",
|
||||
"kiri/client",
|
||||
"kiri/conf",
|
||||
"kiri/main",
|
||||
"kiri/init",
|
||||
|
|
@ -294,6 +296,7 @@ const script = {
|
|||
// "geo/wasm",
|
||||
"geo/debug",
|
||||
"geo/point",
|
||||
"geo/points",
|
||||
"geo/slope",
|
||||
"geo/line",
|
||||
"geo/bounds",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "grid-apps",
|
||||
"version": "2.3.D1",
|
||||
"version": "2.3.D2",
|
||||
"description": "grid.space 3d slice & modeling tools",
|
||||
"author": "Stewart Allen <sa@grid.space>",
|
||||
"license": "MIT",
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@
|
|||
// other values break cube-s9 (wtf)
|
||||
precision_decimate : 0.05,
|
||||
// decimate test over this many points
|
||||
decimate_threshold : 100000,
|
||||
decimate_threshold : 500000,
|
||||
// Point.onLine precision distance (endpoints in Polygon.intersect)
|
||||
precision_point_on_line : 0.01,
|
||||
// Polygon.isEquivalent value for determining similar enough to test
|
||||
|
|
|
|||
129
src/geo/points.js
Normal file
129
src/geo/points.js
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
|
||||
const BASE = self.base,
|
||||
DBUG = BASE.debug,
|
||||
CONF = BASE.config;
|
||||
|
||||
BASE.verticesToPoints = verticesToPoints;
|
||||
BASE.pointsToVertices = pointsToVertices;
|
||||
|
||||
/**
|
||||
* converts a geometry point array into a kiri point array
|
||||
* with auto-decimation
|
||||
*
|
||||
* @param {Float32Array} array
|
||||
* @returns {Array}
|
||||
*/
|
||||
function verticesToPoints(array, options) {
|
||||
let parr = new Array(array.length / 3),
|
||||
i = 0,
|
||||
j = 0,
|
||||
t = time(),
|
||||
hash = {},
|
||||
unique = 0,
|
||||
passes = 0,
|
||||
points,
|
||||
oldpoints = parr.length,
|
||||
newpoints;
|
||||
|
||||
// replace point objects with their equivalents
|
||||
while (i < array.length) {
|
||||
let p = BASE.newPoint(array[i++], array[i++], array[i++]),
|
||||
k = p.key,
|
||||
m = hash[k];
|
||||
if (!m) {
|
||||
m = p;
|
||||
hash[k] = p;
|
||||
unique++;
|
||||
}
|
||||
parr[j++] = m;
|
||||
}
|
||||
|
||||
let {threshold, precision, maxpass} = options || {};
|
||||
// threshold = point count for triggering decimation
|
||||
// precision = under which points are merged
|
||||
// maxpass = max number of decimations
|
||||
threshold = threshold > 0 ? threshold : CONF.decimate_threshold;
|
||||
precision = precision >= 0 ? precision : CONF.precision_decimate;
|
||||
maxpass = maxpass >= 0 ? maxpass : 10;
|
||||
|
||||
// decimate until all point spacing > precision
|
||||
if (maxpass && precision > 0.0)
|
||||
while (parr.length > threshold) {
|
||||
let lines = [], line, dec = 0;
|
||||
for (i=0; i<oldpoints; ) {
|
||||
let p1 = parr[i++],
|
||||
p2 = parr[i++],
|
||||
p3 = parr[i++];
|
||||
lines.push( {p1:p1, p2:p2, d:Math.sqrt(p1.distToSq3D(p2))} );
|
||||
lines.push( {p1:p1, p2:p3, d:Math.sqrt(p1.distToSq3D(p3))} );
|
||||
lines.push( {p1:p2, p2:p3, d:Math.sqrt(p2.distToSq3D(p3))} );
|
||||
}
|
||||
// sort by ascending line length
|
||||
lines.sort(function(a,b) {
|
||||
return a.d - b.d
|
||||
});
|
||||
// create offset mid-points
|
||||
for (i=0; i<lines.length; i++) {
|
||||
line = lines[i];
|
||||
if (line.d >= precision) break;
|
||||
if (line.p1.op || line.p2.op) continue;
|
||||
// todo skip dropping lines where either point is a "sharp" on 3 vectors
|
||||
line.p1.op = line.p2.op = line.p1.midPointTo3D(line.p2);
|
||||
dec++;
|
||||
}
|
||||
// exit if nothing to decimate
|
||||
if (dec === 0) break;
|
||||
passes++;
|
||||
// create new facets
|
||||
points = new Array(oldpoints);
|
||||
newpoints = 0;
|
||||
for (i=0; i<oldpoints; ) {
|
||||
let p1 = parr[i++],
|
||||
p2 = parr[i++],
|
||||
p3 = parr[i++];
|
||||
// drop facets with two offset points
|
||||
if (p1.op && p1.op === p2.op) continue;
|
||||
if (p1.op && p1.op === p3.op) continue;
|
||||
if (p2.op && p2.op === p3.op) continue;
|
||||
// otherwise emit altered facet
|
||||
points[newpoints++] = p1.op || p1;
|
||||
points[newpoints++] = p2.op || p2;
|
||||
points[newpoints++] = p3.op || p3;
|
||||
}
|
||||
parr = points.slice(0,newpoints);
|
||||
oldpoints = newpoints;
|
||||
if (passes >= maxpass) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if (passes) console.trace({passes, threshold, precision, maxpass});
|
||||
|
||||
if (passes) DBUG.log({
|
||||
before: array.length / 3,
|
||||
after: parr.length,
|
||||
unique: unique,
|
||||
decimations: passes,
|
||||
time: (time() - t)
|
||||
});
|
||||
|
||||
return parr;
|
||||
}
|
||||
|
||||
function pointsToVertices(points) {
|
||||
let vertices = new Float32Array(points.length * 3),
|
||||
i = 0, vi = 0;
|
||||
while (i < points.length) {
|
||||
vertices[vi++] = points[i].x;
|
||||
vertices[vi++] = points[i].y;
|
||||
vertices[vi++] = points[i++].z;
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
@ -21,20 +21,24 @@ let loc = self.location,
|
|||
* @param {Function} fn name of function in KIRI.worker
|
||||
* @param {Object} data to send to server
|
||||
* @param {Function} onreply function to call on reply messages
|
||||
* @param {Boolean} async true of function returns many messages
|
||||
* @param {Object[]} zerocopy array of objects to pass using zerocopy
|
||||
*/
|
||||
function send(fn, data, onreply, async, zerocopy) {
|
||||
function send(fn, data, onreply, zerocopy) {
|
||||
let seq = seqid++;
|
||||
|
||||
running[seq] = {fn:onreply, async:async||false};
|
||||
running[seq] = { fn:onreply };
|
||||
// console.log('send', data);
|
||||
worker.postMessage({
|
||||
seq: seq,
|
||||
task: fn,
|
||||
time: time(),
|
||||
data: data
|
||||
}, zerocopy);
|
||||
|
||||
try {
|
||||
worker.postMessage({
|
||||
seq: seq,
|
||||
task: fn,
|
||||
time: time(),
|
||||
data: data
|
||||
}, zerocopy);
|
||||
} catch (error) {
|
||||
console.trace('work send error', {data, error});
|
||||
}
|
||||
}
|
||||
|
||||
// code is running in the browser / client context
|
||||
|
|
@ -89,10 +93,10 @@ KIRI.work = {
|
|||
};
|
||||
},
|
||||
|
||||
decimate : function(vertices, callback) {
|
||||
let alert = KIRI.api.show.alert('decimating imported model', 1000);
|
||||
decimate : function(vertices, options, callback) {
|
||||
let alert = KIRI.api.show.alert('processing model', 1000);
|
||||
vertices = vertices.buffer.slice(0);
|
||||
send("decimate", vertices, function(output) {
|
||||
send("decimate", {vertices, options}, function(output) {
|
||||
KIRI.api.hide.alert(alert);
|
||||
callback(output);
|
||||
});
|
||||
|
|
@ -151,13 +155,13 @@ KIRI.work = {
|
|||
}, function(reply) {
|
||||
if (reply.done || reply.error) delete slicing[widget.id];
|
||||
callback(reply);
|
||||
}, null, [vertices]);
|
||||
}, [vertices]);
|
||||
},
|
||||
|
||||
printSetup : function(settings, callback) {
|
||||
send("printSetup", {settings:settings}, function(reply) {
|
||||
callback(reply);
|
||||
}, undefined);
|
||||
});
|
||||
},
|
||||
|
||||
printExport : function(settings, online, ondone) {
|
||||
|
|
@ -194,7 +198,7 @@ KIRI.work = {
|
|||
send("printGCode", {}, function(reply) {
|
||||
callback(reply);
|
||||
});
|
||||
}, null, [vertices]);
|
||||
}, [vertices]);
|
||||
callback(reply);
|
||||
});
|
||||
}
|
||||
|
|
@ -614,7 +614,9 @@
|
|||
units: "mm",
|
||||
exportOcto: false,
|
||||
exportGhost: false,
|
||||
exportLocal: true
|
||||
exportLocal: true,
|
||||
decimate: true,
|
||||
detail: "good"
|
||||
},
|
||||
// for passing temporary slice hints (topo currently)
|
||||
synth: {},
|
||||
|
|
|
|||
323
src/kiri/db.js
323
src/kiri/db.js
|
|
@ -4,56 +4,174 @@
|
|||
|
||||
(function() {
|
||||
|
||||
const KIRI = self.kiri, DP = Catalog.prototype;
|
||||
const KIRI = self.kiri;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function Catalog(motodb, decimate) {
|
||||
let store = this;
|
||||
store.db = motodb;
|
||||
store.files = {};
|
||||
store.listeners = [];
|
||||
store.autodec = decimate;
|
||||
store.deferredHandler = null;
|
||||
store.refresh();
|
||||
}
|
||||
class Catalog {
|
||||
constructor(motodb, options) {
|
||||
let store = this;
|
||||
this.db = motodb;
|
||||
this.files = {};
|
||||
this.listeners = [];
|
||||
this.options = options || {};
|
||||
this.deferredHandler = null;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
KIRI.openCatalog = function(motodb,decimate) {
|
||||
return new Catalog(motodb,decimate);
|
||||
};
|
||||
setOptions(options) {
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
DP.refresh = function() {
|
||||
let store = this;
|
||||
store.db.get('files', function(files) {
|
||||
if (files) {
|
||||
store.files = files;
|
||||
notifyFileListeners(store);
|
||||
refresh() {
|
||||
let store = this;
|
||||
store.db.get('files', function(files) {
|
||||
if (files) {
|
||||
store.files = files;
|
||||
notifyFileListeners(store);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
wipe() {
|
||||
let key, files = this.files;
|
||||
for (key in files) {
|
||||
if (files.hasOwnProperty(key)) this.deleteFile(key);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
DP.wipe = function() {
|
||||
let key, files = this.files;
|
||||
for (key in files) {
|
||||
if (files.hasOwnProperty(key)) this.deleteFile(key);
|
||||
}
|
||||
};
|
||||
fileList() {
|
||||
return this.files;
|
||||
};
|
||||
|
||||
DP.fileList = function() {
|
||||
return this.files;
|
||||
};
|
||||
addFileListener(listener) {
|
||||
if (!this.listeners.contains(listener)) {
|
||||
this.listeners.push(listener);
|
||||
listener(this.files);
|
||||
}
|
||||
};
|
||||
|
||||
DP.addFileListener = function(listener) {
|
||||
if (!this.listeners.contains(listener)) {
|
||||
this.listeners.push(listener);
|
||||
listener(this.files);
|
||||
}
|
||||
};
|
||||
removeFileListener(listener) {
|
||||
this.listeners.remove(listener);
|
||||
};
|
||||
|
||||
DP.removeFileListener = function(listener) {
|
||||
this.listeners.remove(listener);
|
||||
};
|
||||
decimate(vertices, callback) {
|
||||
let options = this.options;
|
||||
if (vertices.length < (options.threshold || 500000)) {
|
||||
return callback(vertices);
|
||||
}
|
||||
KIRI.work.decimate(vertices, this.options, function(reply) {
|
||||
callback(reply);
|
||||
});
|
||||
};
|
||||
|
||||
setDeferredHandler(handler) {
|
||||
this.deferredHandler = handler;
|
||||
};
|
||||
|
||||
putDeferred(name, mark) {
|
||||
// triggers refresh callback
|
||||
this.files[name] = {
|
||||
deferred: mark
|
||||
};
|
||||
saveFileList(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {String} name
|
||||
* @param {Float32Array} vertices
|
||||
* @param {Function} [callback]
|
||||
*/
|
||||
putFile(name, vertices, callback) {
|
||||
let store = this;
|
||||
store.db.put('file-'+name, vertices, function(ok) {
|
||||
if (ok) {
|
||||
store.files[name] = {
|
||||
vertices: vertices.length/3,
|
||||
updated: new Date().getTime()
|
||||
};
|
||||
saveFileList(store);
|
||||
store.decimate(vertices, function(decimated) {
|
||||
store.db.put('fdec-'+name, decimated);
|
||||
if (callback) callback(decimated);
|
||||
});
|
||||
} else if (callback) callback(ok);
|
||||
});
|
||||
};
|
||||
|
||||
rename(name, newname, callback) {
|
||||
if (!this.files[name]) return callback({error: 'no such file'});
|
||||
if (!newname || newname == name) return callback({error: 'invalid new name'});
|
||||
let done = 0;
|
||||
let error = [];
|
||||
let store = this;
|
||||
function complete(ok, err) {
|
||||
if (err) error.push(err);
|
||||
if (++done === 2) {
|
||||
store.files[newname] = store.files[name];
|
||||
delete store.files[name];
|
||||
saveFileList(store);
|
||||
store.db.remove(`fdec-${name}`);
|
||||
store.db.remove(`file-${name}`);
|
||||
callback(error.length ? {error} : {});
|
||||
}
|
||||
}
|
||||
store.db.get(`fdec-${name}`, (vertices) => {
|
||||
if (!vertices) return complete(false, 'no decimation');
|
||||
store.db.put(`fdec-${newname}`, vertices, complete);
|
||||
});
|
||||
store.db.get(`file-${name}`, (vertices) => {
|
||||
if (!vertices) return complete(false, 'no raw file');
|
||||
store.db.put(`file-${newname}`, vertices, complete);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {String} name
|
||||
* @param {Function} callback
|
||||
*/
|
||||
getFile(name, callback) {
|
||||
let store = this,
|
||||
rec = store.files[name];
|
||||
if (rec && rec.deferred) {
|
||||
if (store.deferredHandler) return store.deferredHandler(rec.deferred, name, callback);
|
||||
return callback();
|
||||
}
|
||||
this.db.get('fdec-'+name, function(vertices) {
|
||||
if (vertices) {
|
||||
callback(vertices);
|
||||
} else {
|
||||
store.db.get('file-'+name, function(vertices) {
|
||||
if (vertices) {
|
||||
store.decimate(vertices, function(decimated) {
|
||||
store.db.put('fdec-'+name, decimated);
|
||||
callback(vertices);
|
||||
});
|
||||
} else {
|
||||
return callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {String} name
|
||||
* @param {Function} callback
|
||||
*/
|
||||
deleteFile(name, callback) {
|
||||
let store = this;
|
||||
if (store.files[name]) {
|
||||
delete store.files[name];
|
||||
store.db.remove('fdec-'+name);
|
||||
store.db.remove('file-'+name, function(ok) {
|
||||
saveFileList(store);
|
||||
if (callback) callback(ok);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (callback) callback(false);
|
||||
};
|
||||
}
|
||||
|
||||
function saveFileList(store) {
|
||||
store.db.put('files', store.files);
|
||||
|
|
@ -66,125 +184,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
DP.decimate = function(vertices, callback) {
|
||||
if (vertices.length < 500000) return callback(vertices);
|
||||
KIRI.work.decimate(vertices, function(reply) {
|
||||
callback(reply);
|
||||
});
|
||||
};
|
||||
|
||||
DP.setDeferredHandler = function(handler) {
|
||||
this.deferredHandler = handler;
|
||||
};
|
||||
|
||||
DP.putDeferred = function(name, mark) {
|
||||
// triggers refresh callback
|
||||
this.files[name] = {
|
||||
deferred: mark
|
||||
};
|
||||
saveFileList(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {String} name
|
||||
* @param {Float32Array} vertices
|
||||
* @param {Function} [callback]
|
||||
*/
|
||||
DP.putFile = function(name, vertices, callback) {
|
||||
let store = this;
|
||||
store.db.put('file-'+name, vertices, function(ok) {
|
||||
if (ok) {
|
||||
store.files[name] = {
|
||||
vertices: vertices.length/3,
|
||||
updated: new Date().getTime()
|
||||
};
|
||||
saveFileList(store);
|
||||
if (store.autodec) {
|
||||
store.decimate(vertices, function(decimated) {
|
||||
store.db.put('fdec-'+name, decimated);
|
||||
if (callback) callback(decimated);
|
||||
});
|
||||
} else if (callback) callback(ok);
|
||||
} else if (callback) callback(ok);
|
||||
});
|
||||
};
|
||||
|
||||
DP.rename = function(name, newname, callback) {
|
||||
if (!this.files[name]) return callback({error: 'no such file'});
|
||||
if (!newname || newname == name) return callback({error: 'invalid new name'});
|
||||
let done = 0;
|
||||
let error = [];
|
||||
let store = this;
|
||||
function complete(ok, err) {
|
||||
if (err) error.push(err);
|
||||
if (++done === 2) {
|
||||
store.files[newname] = store.files[name];
|
||||
delete store.files[name];
|
||||
saveFileList(store);
|
||||
store.db.remove(`fdec-${name}`);
|
||||
store.db.remove(`file-${name}`);
|
||||
callback(error.length ? {error} : {});
|
||||
}
|
||||
}
|
||||
store.db.get(`fdec-${name}`, (vertices) => {
|
||||
if (!vertices) return complete(false, 'no decimation');
|
||||
store.db.put(`fdec-${newname}`, vertices, complete);
|
||||
});
|
||||
store.db.get(`file-${name}`, (vertices) => {
|
||||
if (!vertices) return complete(false, 'no raw file');
|
||||
store.db.put(`file-${newname}`, vertices, complete);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {String} name
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DP.getFile = function(name, callback) {
|
||||
let store = this,
|
||||
rec = store.files[name];
|
||||
if (rec && rec.deferred) {
|
||||
if (store.deferredHandler) return store.deferredHandler(rec.deferred, name, callback);
|
||||
return callback();
|
||||
}
|
||||
if (!this.autodec) {
|
||||
store.db.get('file-'+name, callback);
|
||||
return;
|
||||
}
|
||||
this.db.get('fdec-'+name, function(vertices) {
|
||||
if (vertices) {
|
||||
callback(vertices);
|
||||
} else {
|
||||
store.db.get('file-'+name, function(vertices) {
|
||||
if (vertices) {
|
||||
store.decimate(vertices, function(decimated) {
|
||||
store.db.put('fdec-'+name, decimated);
|
||||
callback(vertices);
|
||||
});
|
||||
} else {
|
||||
return callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {String} name
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DP.deleteFile = function(name, callback) {
|
||||
let store = this;
|
||||
if (store.files[name]) {
|
||||
delete store.files[name];
|
||||
store.db.remove('fdec-'+name);
|
||||
store.db.remove('file-'+name, function(ok) {
|
||||
saveFileList(store);
|
||||
if (callback) callback(ok);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (callback) callback(false);
|
||||
KIRI.openCatalog = function(motodb, options) {
|
||||
return new Catalog(motodb, options);
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -85,6 +85,25 @@
|
|||
platform.update_size();
|
||||
}
|
||||
|
||||
function detailSave() {
|
||||
let level = UI.detail.options[UI.detail.selectedIndex];
|
||||
if (level) {
|
||||
level = level.value;
|
||||
let rez = BASE.config.clipperClean;
|
||||
switch (level) {
|
||||
case 'best': rez = 50; break;
|
||||
case 'good': rez = BASE.config.clipperClean; break;
|
||||
case 'fair': rez = 500; break;
|
||||
case 'poor': rez = 1000; break;
|
||||
}
|
||||
KIRI.client.config({
|
||||
base: { clipperClean: rez }
|
||||
});
|
||||
settings().controller.detail = level;
|
||||
API.conf.save();
|
||||
}
|
||||
}
|
||||
|
||||
function booleanSave() {
|
||||
let control = settings().controller;
|
||||
let isDark = control.dark;
|
||||
|
|
@ -101,12 +120,16 @@
|
|||
control.exportOcto = UI.exportOcto.checked;
|
||||
control.exportGhost = UI.exportGhost.checked;
|
||||
control.exportLocal = UI.exportLocal.checked;
|
||||
control.decimate = UI.decimate.checked;
|
||||
SPACE.view.setZoom(control.reverseZoom, control.zoomSpeed);
|
||||
platform.layout();
|
||||
platform.update_stock();
|
||||
API.conf.save();
|
||||
API.mode.set_expert(control.expert);
|
||||
API.platform.update_size();
|
||||
API.catalog.setOptions({
|
||||
maxpass: control.decimate ? 10 : 0
|
||||
});
|
||||
UC.setHoverPop(control.hoverPop);
|
||||
}
|
||||
|
||||
|
|
@ -1349,10 +1372,15 @@
|
|||
freeLayout: UC.newBoolean(LANG.op_free_s, booleanSave, {title:LANG.op_free_l}),
|
||||
units: UC.newSelect(LANG.op_unit_s, {title: LANG.op_unit_l, action:unitsSave}, "units"),
|
||||
|
||||
export: UC.newGroup(LANG.xp_menu, $('prefs-out'), {inline: true}),
|
||||
export: UC.newGroup(LANG.xp_menu, $('prefs-xpo'), {inline: true}),
|
||||
exportOcto: UC.newBoolean(`OctoPrint`, booleanSave),
|
||||
exportGhost: UC.newBoolean(`Grid:Host`, booleanSave),
|
||||
exportLocal: UC.newBoolean(`Grid:Local`, booleanSave),
|
||||
|
||||
parts: UC.newGroup(LANG.pt_menu, $('prefs-prt'), {inline: true}),
|
||||
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}),
|
||||
|
||||
prefadd: UC.checkpoint($('prefs-add')),
|
||||
|
||||
process: UC.newGroup(LANG.sl_menu, $('settings'), {modes:FDM_LASER}),
|
||||
|
|
@ -1895,6 +1923,8 @@
|
|||
UI.autoLayout.checked = control.autoLayout;
|
||||
UI.alignTop.checked = control.alignTop;
|
||||
UI.reverseZoom.checked = control.reverseZoom;
|
||||
UI.decimate.checked = control.decimate;
|
||||
detailSave();
|
||||
|
||||
// load script extensions
|
||||
if (SETUP.s) SETUP.s.forEach(function(lib) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
(function () {
|
||||
|
||||
let iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent),
|
||||
autoDecimate = true,
|
||||
// ---------------
|
||||
MOTO = moto,
|
||||
KIRI = self.kiri,
|
||||
|
|
@ -24,7 +23,7 @@
|
|||
ODB = KIRI.odb = new MOTO.Storage(SETUP.d ? SETUP.d[0] : 'kiri'),
|
||||
SPACE = KIRI.space = MOTO.Space,
|
||||
WIDGETS = KIRI.widgets = [],
|
||||
CATALOG = KIRI.catalog = KIRI.openCatalog(ODB,autoDecimate),
|
||||
CATALOG = KIRI.catalog = KIRI.openCatalog(ODB),
|
||||
STATS = new Stats(SDB),
|
||||
SEED = 'kiri-seed',
|
||||
// ---------------
|
||||
|
|
@ -149,6 +148,12 @@
|
|||
{ name: "none" },
|
||||
{ name: "x axis" },
|
||||
{ name: "y axis" }
|
||||
],
|
||||
detail: [
|
||||
{ name: "best" },
|
||||
{ name: "good" },
|
||||
{ name: "fair" },
|
||||
{ name: "poor" },
|
||||
]
|
||||
};
|
||||
|
||||
|
|
@ -1444,17 +1449,20 @@
|
|||
let source = uie.parentNode.getAttribute('source'),
|
||||
list = settings[source] || lists[source],
|
||||
chosen = null;
|
||||
if (list) list.forEach(function(tool, index) {
|
||||
let id = tool.id || tool.name;
|
||||
if (list) list.forEach(function(el, index) {
|
||||
let id = el.id || el.name;
|
||||
let ev = el.value || id;
|
||||
if (val == id) {
|
||||
chosen = index;
|
||||
}
|
||||
let opt = DOC.createElement('option');
|
||||
opt.appendChild(DOC.createTextNode(tool.name));
|
||||
opt.setAttribute('value', id);
|
||||
opt.appendChild(DOC.createTextNode(el.name));
|
||||
opt.setAttribute('value', ev);
|
||||
uie.appendChild(opt);
|
||||
});
|
||||
if (chosen) uie.selectedIndex = chosen;
|
||||
if (chosen) {
|
||||
uie.selectedIndex = chosen;
|
||||
}
|
||||
} else if (typ === 'textarea') {
|
||||
if (Array.isArray(val)) {
|
||||
uie.value = val.join('\n');
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
UTIL = BASE.util,
|
||||
POLY = BASE.polygons,
|
||||
MATH = Math,
|
||||
SQRT = MATH.sqrt,
|
||||
PRO = Widget.prototype,
|
||||
time = UTIL.time,
|
||||
solid_opacity = 1.0;
|
||||
|
|
@ -190,104 +189,6 @@
|
|||
KIRI.odb.remove('ws-save-'+id, ondone);
|
||||
};
|
||||
|
||||
/**
|
||||
* converts a geometry point array into a kiri point array
|
||||
* with auto-decimation
|
||||
*
|
||||
* @param {Float32Array} array
|
||||
* @param {boolean} [decimate]
|
||||
* @returns {Array}
|
||||
*/
|
||||
Widget.verticesToPoints = function(array,decimate) {
|
||||
let parr = new Array(array.length / 3),
|
||||
i = 0,
|
||||
j = 0,
|
||||
t = time(),
|
||||
hash = {},
|
||||
unique = 0,
|
||||
passes = 0,
|
||||
points,
|
||||
oldpoints = parr.length,
|
||||
newpoints;
|
||||
// replace point objects with their equivalents
|
||||
while (i < array.length) {
|
||||
let p = BASE.newPoint(array[i++], array[i++], array[i++]),
|
||||
k = p.key,
|
||||
m = hash[k];
|
||||
if (!m) {
|
||||
m = p;
|
||||
hash[k] = p;
|
||||
unique++;
|
||||
}
|
||||
parr[j++] = m;
|
||||
}
|
||||
// decimate until all point spacing > precision_decimate
|
||||
while (parr.length > BASE.config.decimate_threshold && decimate && BASE.config.precision_decimate > 0.0) {
|
||||
let lines = [], line, dec = 0;
|
||||
for (i=0; i<oldpoints; ) {
|
||||
let p1 = parr[i++],
|
||||
p2 = parr[i++],
|
||||
p3 = parr[i++];
|
||||
lines.push( {p1:p1, p2:p2, d:SQRT(p1.distToSq3D(p2))} );
|
||||
lines.push( {p1:p1, p2:p3, d:SQRT(p1.distToSq3D(p3))} );
|
||||
lines.push( {p1:p2, p2:p3, d:SQRT(p2.distToSq3D(p3))} );
|
||||
}
|
||||
// sort by ascending line length
|
||||
lines.sort(function(a,b) {
|
||||
return a.d - b.d
|
||||
});
|
||||
// create offset mid-points
|
||||
for (i=0; i<lines.length; i++) {
|
||||
line = lines[i];
|
||||
if (line.d >= BASE.config.precision_decimate) break;
|
||||
if (line.p1.op || line.p2.op) continue;
|
||||
// todo skip dropping lines where either point is a "sharp" on 3 vectors
|
||||
line.p1.op = line.p2.op = line.p1.midPointTo3D(line.p2);
|
||||
dec++;
|
||||
}
|
||||
// exit if nothing to decimate
|
||||
if (dec === 0) break;
|
||||
passes++;
|
||||
// create new facets
|
||||
points = new Array(oldpoints);
|
||||
newpoints = 0;
|
||||
for (i=0; i<oldpoints; ) {
|
||||
let p1 = parr[i++],
|
||||
p2 = parr[i++],
|
||||
p3 = parr[i++];
|
||||
// drop facets with two offset points
|
||||
if (p1.op && p1.op === p2.op) continue;
|
||||
if (p1.op && p1.op === p3.op) continue;
|
||||
if (p2.op && p2.op === p3.op) continue;
|
||||
// otherwise emit altered facet
|
||||
points[newpoints++] = p1.op || p1;
|
||||
points[newpoints++] = p2.op || p2;
|
||||
points[newpoints++] = p3.op || p3;
|
||||
}
|
||||
parr = points.slice(0,newpoints);
|
||||
oldpoints = newpoints;
|
||||
}
|
||||
if (passes) DBUG.log({
|
||||
before: array.length / 3,
|
||||
after: parr.length,
|
||||
unique: unique,
|
||||
decimations: passes,
|
||||
time: (time() - t)
|
||||
});
|
||||
return parr;
|
||||
};
|
||||
|
||||
Widget.pointsToVertices = function(points) {
|
||||
let vertices = new Float32Array(points.length * 3),
|
||||
i = 0, vi = 0;
|
||||
while (i < points.length) {
|
||||
vertices[vi++] = points[i].x;
|
||||
vertices[vi++] = points[i].y;
|
||||
vertices[vi++] = points[i++].z;
|
||||
}
|
||||
return vertices;
|
||||
};
|
||||
|
||||
/** ******************************************************************
|
||||
* Widget Prototype Functions
|
||||
******************************************************************* */
|
||||
|
|
@ -601,7 +502,9 @@
|
|||
PRO.getPoints = function() {
|
||||
if (!this.points) {
|
||||
// convert and cache points from geometry vertices
|
||||
this.points = Widget.verticesToPoints(this.getGeoVertices());
|
||||
this.points = BASE.verticesToPoints(this.getGeoVertices(), {
|
||||
maxpass: 0 // disable decimation
|
||||
});
|
||||
}
|
||||
return this.points;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
let BASE = self.base,
|
||||
KIRI = self.kiri,
|
||||
UTIL = BASE.util,
|
||||
Widget = KIRI.Widget,
|
||||
time = UTIL.time,
|
||||
current = self.worker = {
|
||||
print: null,
|
||||
|
|
@ -27,9 +26,10 @@ KIRI.server =
|
|||
KIRI.worker = {
|
||||
cache: cache,
|
||||
|
||||
decimate: function(vertices, send) {
|
||||
decimate: function(data, send) {
|
||||
let { vertices, options } = data;
|
||||
vertices = new Float32Array(vertices),
|
||||
vertices = Widget.pointsToVertices(Widget.verticesToPoints(vertices, true));
|
||||
vertices = BASE.pointsToVertices(BASE.verticesToPoints(vertices, options));
|
||||
send.done(vertices);
|
||||
},
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ KIRI.worker = {
|
|||
vertices = new Float32Array(data.vertices),
|
||||
position = data.position,
|
||||
tracking = data.tracking,
|
||||
points = Widget.verticesToPoints(vertices),
|
||||
points = BASE.verticesToPoints(vertices, { maxpass: 0 }),
|
||||
state = data.state || {},
|
||||
rotation = state.rotation,
|
||||
centerz = state.centerz,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
let terms = {
|
||||
COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved",
|
||||
LICENSE: "See the license.md file included with the source distribution",
|
||||
VERSION: "2.3.D1"
|
||||
VERSION: "2.3.D2"
|
||||
};
|
||||
|
||||
if (typeof(module) === 'object') {
|
||||
|
|
|
|||
|
|
@ -100,5 +100,8 @@
|
|||
}
|
||||
|
||||
CAM.Tool = Tool;
|
||||
CAM.getToolDiameter = function(settings, id) {
|
||||
return new CAM.Tool(settings, id).fluteDiameter();
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -308,7 +308,9 @@
|
|||
<div class="t-pad2"></div>
|
||||
<div id="prefs-lay" class="f-col"></div>
|
||||
<div class="t-pad2"></div>
|
||||
<div id="prefs-out" class="f-col"></div>
|
||||
<div id="prefs-xpo" class="f-col"></div>
|
||||
<div class="t-pad2"></div>
|
||||
<div id="prefs-prt" class="f-col"></div>
|
||||
<div class="t-pad2"></div>
|
||||
<div id="prefs-add" class="f-col"></div>
|
||||
<div class="t-pad2"></div>
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ kiri.lang['en-us'] = {
|
|||
ws_cler: "Clear",
|
||||
|
||||
// OPTIONS
|
||||
op_menu: "preferences",
|
||||
op_menu: "interface",
|
||||
op_xprt_s: "expert",
|
||||
op_xprt_l: "enable expert options",
|
||||
op_hopo_s: "hover pop",
|
||||
|
|
@ -140,6 +140,12 @@ kiri.lang['en-us'] = {
|
|||
|
||||
lo_menu: "layout",
|
||||
|
||||
pt_menu: "parts",
|
||||
pt_deci_s: "decimate",
|
||||
pt_deci_l: "enable or disable\npoint decimation\nfor faster slicing",
|
||||
pt_qual_s: "quality",
|
||||
pt_qual_l: "level of detail\nto retain on\nimported part",
|
||||
|
||||
xp_menu: "exports",
|
||||
|
||||
// LAYERS pop-menu
|
||||
|
|
|
|||
Loading…
Reference in a new issue