add load_url to platform api which handles STL, OBJ, 3MF
This commit is contained in:
parent
655b92e2a0
commit
7fb36b91a6
6 changed files with 123 additions and 8 deletions
1
app.js
1
app.js
|
|
@ -248,6 +248,7 @@ const script = {
|
|||
"moto/load-3mf",
|
||||
"moto/load-obj",
|
||||
"moto/load-stl",
|
||||
"moto/load-url",
|
||||
"moto/db",
|
||||
"kiri/ui",
|
||||
"kiri/lang",
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
|||
layout: platformLayout,
|
||||
load: platformLoad,
|
||||
load_stl: platformLoadSTL,
|
||||
load_url: platformLoadURL,
|
||||
deselect: platformDeselect,
|
||||
select: platformSelect,
|
||||
select_all: platformSelectAll,
|
||||
|
|
@ -1874,6 +1875,20 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
|||
}, formdata, scale);
|
||||
}
|
||||
|
||||
function platformLoadURL(url, group) {
|
||||
platform.group();
|
||||
MOTO.URL.load(url).then((objects) => {
|
||||
for (let object of objects) {
|
||||
let widget = newWidget(undefined, group).loadVertices(object.mesh);
|
||||
widget.meta.file = object.file;
|
||||
platform.add(widget);
|
||||
}
|
||||
platform.group_done();
|
||||
}).catch(error => {
|
||||
API.show.alert(error);
|
||||
});
|
||||
}
|
||||
|
||||
function platformComputeMaxZ() {
|
||||
topZ = 0;
|
||||
forAllWidgets(function(widget) {
|
||||
|
|
@ -2501,7 +2516,7 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
|||
}
|
||||
}
|
||||
else if (is3mf) {
|
||||
MOTO.TMF.parse(e.target.result).then(models => {
|
||||
MOTO.TMF.parseAsync(e.target.result).then(models => {
|
||||
console.log({models});
|
||||
for (let model of models) {
|
||||
let name = e.target.file.name;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
if (self.moto.OBJ) return;
|
||||
|
||||
self.moto.TMF = {
|
||||
parse : parse
|
||||
parseAsync
|
||||
};
|
||||
|
||||
function query(node, path, fn) {
|
||||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
function loadModel(doc) {
|
||||
let models = [];
|
||||
|
||||
function emitModel(model, faces) {
|
||||
if (faces && faces.length) {
|
||||
models.push({name: model, faces});
|
||||
|
|
@ -82,7 +81,7 @@
|
|||
* @param {Object} data binary file
|
||||
* @returns {Array} vertex face array
|
||||
*/
|
||||
function parse(data) {
|
||||
function parseAsync(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
JSZip.loadAsync(data).then(zip => {
|
||||
for (let [key,value] of Object.entries(zip.files)) {
|
||||
|
|
|
|||
|
|
@ -6,19 +6,27 @@
|
|||
if (self.moto.OBJ) return;
|
||||
|
||||
self.moto.OBJ = {
|
||||
parse : parse
|
||||
parse,
|
||||
parseAsync
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {String} text
|
||||
* @returns {Array} vertex face array
|
||||
*/
|
||||
|
||||
function parseAsync(text) {
|
||||
return new Promise((resolve,reject) => {
|
||||
resolve(parse(text));
|
||||
});
|
||||
}
|
||||
|
||||
function parse(text) {
|
||||
|
||||
let lines = text.split('\n').map(l => l.trim());
|
||||
let verts = [];
|
||||
let faces = [];
|
||||
let obj = [ faces ];
|
||||
let objs = [ faces ];
|
||||
|
||||
for (let line of lines) {
|
||||
let toks = line.split(' ');
|
||||
|
|
@ -35,7 +43,7 @@
|
|||
break;
|
||||
case 'g':
|
||||
if (faces.length) {
|
||||
obj.push(faces = []);
|
||||
objs.push(faces = []);
|
||||
}
|
||||
if (toks[0]) {
|
||||
faces.name = toks[0];
|
||||
|
|
@ -44,7 +52,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
return objs;
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -107,6 +107,11 @@
|
|||
return bin;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} data
|
||||
* @param {number} scale
|
||||
* @returns vertex array
|
||||
*/
|
||||
SP.parse = function(data, scale) {
|
||||
let binData = this.convertToBinary(data);
|
||||
|
||||
|
|
@ -124,6 +129,12 @@
|
|||
: this.parseASCII(this.convertToString(data), scale);
|
||||
};
|
||||
|
||||
SP.parseAsync = function(data, scale) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(this.parse(data, scale));
|
||||
});
|
||||
};
|
||||
|
||||
SP.parseBinary = function(data, scale = 1) {
|
||||
let reader = new DataView(data),
|
||||
faces = reader.getUint32 (80, true),
|
||||
|
|
|
|||
81
src/moto/load-url.js
Normal file
81
src/moto/load-url.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
'use strict';
|
||||
|
||||
(function() {
|
||||
if (!self.moto) self.moto = {};
|
||||
if (self.moto.URL) return;
|
||||
|
||||
const CDH = 'Content-Disposition';
|
||||
|
||||
function load(url, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
let file = (((url.split('?')[0]).split('#')[0]).split('/')).pop();
|
||||
let ext = file.split('.').pop().toLowerCase();
|
||||
let deftype = ext === "obj" ? "text" : "arraybuffer";
|
||||
let datatype = options.datatype || deftype;
|
||||
let formdata = options.formdata;
|
||||
|
||||
function onloaded(event) {
|
||||
if (event.target.status === 200 || event.target.status === 0) {
|
||||
if (xhr.getAllResponseHeaders().indexOf(CDH) > 0) {
|
||||
// attempt to extract filename from content disposition
|
||||
let fname = xhr.getResponseHeader(CDH)
|
||||
.split(';').map(v => v.trim()).filter(v => {
|
||||
return v.indexOf('filename=') === 0;
|
||||
}).map(v => {
|
||||
return v.substring(10,v.length-1);
|
||||
})[0];
|
||||
if (fname) {
|
||||
file = fname;
|
||||
ext = file.split('.').pop().toLowerCase();
|
||||
}
|
||||
}
|
||||
let data = event.target.response || event.target.responseText;
|
||||
// return raw data if indicated
|
||||
if (options.parse === false) {
|
||||
return resolve(data, ext);
|
||||
}
|
||||
switch (ext) {
|
||||
case "stl":
|
||||
resolve([{
|
||||
mesh: new moto.STL().parse(data), file
|
||||
}]);
|
||||
break;
|
||||
case "obj":
|
||||
resolve(moto.OBJ.parse(data).map(m => { return {mesh: m.toFloat32(), file} }));
|
||||
break;
|
||||
case "3mf":
|
||||
moto.TMF.parseAsync(data).then((meshes) => {
|
||||
resolve(meshes.map(m => {
|
||||
return { mesh: m.faces.toFloat32(), file }
|
||||
}));
|
||||
});
|
||||
break;
|
||||
default:
|
||||
reject(`unknown file type: "${ext}" from ${url}`);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
reject(event.target.statusText);
|
||||
}
|
||||
}
|
||||
|
||||
xhr.addEventListener('load', onloaded, false);
|
||||
xhr.addEventListener('progress', function (event) { }, false);
|
||||
xhr.addEventListener('error', function () { }, false);
|
||||
|
||||
if (xhr.overrideMimeType) {
|
||||
xhr.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
}
|
||||
|
||||
xhr.open(formdata ? 'POST' : 'GET', url, true);
|
||||
xhr.responseType = datatype;
|
||||
xhr.send(formdata);
|
||||
});
|
||||
};
|
||||
|
||||
self.moto.URL = {
|
||||
load
|
||||
};
|
||||
|
||||
})();
|
||||
Loading…
Reference in a new issue