add load_url to platform api which handles STL, OBJ, 3MF

This commit is contained in:
Stewart Allen 2021-09-03 17:58:02 -04:00
commit 7fb36b91a6
6 changed files with 123 additions and 8 deletions

1
app.js
View file

@ -248,6 +248,7 @@ const script = {
"moto/load-3mf",
"moto/load-obj",
"moto/load-stl",
"moto/load-url",
"moto/db",
"kiri/ui",
"kiri/lang",

View file

@ -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;

View file

@ -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)) {

View file

@ -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;
}
})();

View file

@ -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
View 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
};
})();