forked from mirror/viewstl
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbb7f5c389 | |||
| e14585dff0 | |||
| 35626740ae | |||
| 8ee9296a7f | |||
| 35f2e54652 |
@@ -22,4 +22,20 @@ Create a new instance of Stl Viewer (simplest initiation - read and view STL fil
|
||||
var stl_viewer=new StlViewer(document.getElementById("stl_cont"), { models: [ {id:0, filename:"mystl.stl"} ] });
|
||||
```
|
||||
|
||||
Dependency on JSZip library:
|
||||
============================
|
||||
When dealing with 3mf/vsb files you must use JSZip library - https://stuk.github.io/jszip/
|
||||
- this library is not included here, you must upload it to your server and supply jszip_path and jszip_utils_path parameters:
|
||||
```
|
||||
var stl_viewer=new StlViewer
|
||||
(
|
||||
document.getElementById("stl_cont"),
|
||||
{
|
||||
....
|
||||
jszip_path:"/<path_to_jszip>/jszip.min.js",
|
||||
jszip_utils_path:"/<path_to_jszip>/jszip-utils.min.js"
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
more at https://www.viewstl.com/plugin/
|
||||
|
||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
+420
-34
@@ -1,24 +1,36 @@
|
||||
//1.10
|
||||
function parse_3d_file(filename, s)
|
||||
//1.13.1
|
||||
//1.13.1 support for MagicLab colored-STL method
|
||||
|
||||
function parse_3d_file(filename, s, callback, jszip_path)
|
||||
{
|
||||
//determine type of file
|
||||
//console.log(filename.split('.').pop().toLowerCase());
|
||||
//switch (filename.split('.').pop().toLowerCase())
|
||||
var res=null;
|
||||
switch (filename.split('.').pop().split('?')[0].toLowerCase())
|
||||
{
|
||||
case "stl":
|
||||
return parse_stl_bin(s);
|
||||
res=parse_stl_bin(s);
|
||||
break;
|
||||
|
||||
case "obj":
|
||||
return parse_obj(s);
|
||||
res=parse_obj(s);
|
||||
break;
|
||||
|
||||
case "vf":
|
||||
return parse_vf(arrayBufferToString(s));
|
||||
res=parse_vf(arrayBufferToString(s));
|
||||
break;
|
||||
|
||||
case "3mf":
|
||||
parse_3mf(s, callback, jszip_path); //async function
|
||||
return;
|
||||
|
||||
default:
|
||||
return parse_stl_bin(s);
|
||||
res=parse_stl_bin(s);
|
||||
//return "Unknown file type";
|
||||
}
|
||||
|
||||
if (callback) callback(res);
|
||||
}
|
||||
|
||||
function arrayBufferToString(buffer,onSuccess,onFail)
|
||||
@@ -118,18 +130,25 @@ function parse_stl_bin(s)
|
||||
var vertexIndex;
|
||||
var f1,f2,f3;
|
||||
var v1,v2,v3;
|
||||
var color_bit=0;
|
||||
var color_method_mt=false; //face colors are encoded Materialise Magics method (othereise it can be Meshlab method)
|
||||
|
||||
if (!s) return null;
|
||||
|
||||
//see if this is colored STL
|
||||
var cpos=arrayBufferToString(s.slice(0,80)).toLowerCase().indexOf("color");
|
||||
//cpos=true;
|
||||
|
||||
var fdata = new DataView(s, 0);
|
||||
var only_default_color=true;
|
||||
var have_face_colors=false;
|
||||
var def_red_color=-1;
|
||||
var def_green_color=-1;
|
||||
var def_blue_color=-1;
|
||||
|
||||
if (cpos>-1)
|
||||
{
|
||||
//there is a color, get the default color
|
||||
//there is a color (Materialise Magics format), get the default color
|
||||
color_method_mt=true;
|
||||
def_red_color=(fdata.getUint8 (cpos+6,true)) / 31;
|
||||
def_green_color=(fdata.getUint8 (cpos+7,true)) / 31;
|
||||
def_blue_color=(fdata.getUint8 (cpos+8,true)) / 31;
|
||||
@@ -201,49 +220,59 @@ function parse_stl_bin(s)
|
||||
}
|
||||
v3=vertexIndex;
|
||||
|
||||
if (cpos>-1)
|
||||
//color data (if any)
|
||||
pos+=12;
|
||||
face_color=fdata.getUint16(pos,true);
|
||||
//color_bit=color_method_mt?1:(face_color & 1); //0000000000000001 => 1=have face color, 0=nope
|
||||
color_bit=color_method_mt?1:((face_color & 32768)>>15); //1000000000000000 => 1=have face color, 0=nope
|
||||
//console.log('color_bit', color_bit, face_color);
|
||||
|
||||
if (color_bit)
|
||||
{
|
||||
pos+=12;
|
||||
|
||||
//get 2 bytes of color (if any)
|
||||
face_color=fdata.getUint16(pos,true);
|
||||
|
||||
if ((face_color==32768)||(face_color==65535))
|
||||
if (color_method_mt)
|
||||
{
|
||||
//default color
|
||||
color_red=def_red_color;
|
||||
color_green=def_green_color;
|
||||
color_blue=def_blue_color;
|
||||
if ((face_color==32768)||(face_color==65535))
|
||||
{
|
||||
//default color
|
||||
color_red=def_red_color;
|
||||
color_green=def_green_color;
|
||||
color_blue=def_blue_color;
|
||||
}
|
||||
else
|
||||
{
|
||||
have_face_colors=true;
|
||||
color_red=((face_color & 31)/31); //0000000000011111
|
||||
color_green=(((face_color & 992)>>5)/31); //0000001111100000
|
||||
color_blue=(((face_color & 31744)>>10)/31); //0111110000000000
|
||||
|
||||
//the rgb are saved in values from 0 to 31 ... for us, we want it to be 0 to 1 - hence the 31)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
only_default_color=false;
|
||||
color_red=((face_color & 31)/31); //0000000000011111
|
||||
//meshlab color format
|
||||
have_face_colors=true;
|
||||
color_blue=((face_color & 31)/31); //0000000000011111
|
||||
color_green=(((face_color & 992)>>5)/31); //0000001111100000
|
||||
color_blue=(((face_color & 31744)>>10)/31); //0111110000000000
|
||||
|
||||
//the rgb are saved in values from 0 to 31 ... for us, we want it to be 0 to 1 - hence the 31)
|
||||
color_red=(((face_color & 31744)>>10)/31); //0111110000000000
|
||||
}
|
||||
|
||||
//faces.push(new THREE.Face3(v1,v2,v3,1,new THREE.Color("rgb("+color_red+","+color_green+","+color_blue+")")));
|
||||
faces.push(new Array(v1,v2,v3,color_red, color_green,color_blue ));
|
||||
|
||||
pos+=2;
|
||||
|
||||
faces.push(new Array(v1,v2,v3, color_red, color_green, color_blue));
|
||||
}
|
||||
else
|
||||
{
|
||||
//no color
|
||||
//no color for face
|
||||
//faces.push(new THREE.Face3(v1,v2,v3));
|
||||
faces.push(new Array(v1,v2,v3));
|
||||
pos+=14;
|
||||
|
||||
}
|
||||
pos+=2;
|
||||
|
||||
}
|
||||
|
||||
vert_hash=null;
|
||||
|
||||
//console.log("CPOS: "+cpos+" only default: "+only_default_color);
|
||||
|
||||
return ({vertices:vertices, faces:faces, colors:((cpos>-1)&&(!only_default_color))});
|
||||
return ({vertices:vertices, faces:faces, colors:have_face_colors});
|
||||
}
|
||||
catch(err)
|
||||
{
|
||||
@@ -281,6 +310,363 @@ function parse_vf(s)
|
||||
|
||||
}
|
||||
|
||||
//returns if JSZip lib is loaded - if so, returns an instance, otherwise tries to load the lib
|
||||
function init_zip(skip_load_script, jszip_path)
|
||||
{
|
||||
var zip=null;
|
||||
try
|
||||
{
|
||||
zip = new JSZip();
|
||||
}
|
||||
catch(err)
|
||||
{
|
||||
if (skip_load_script) console.log('JSZip is missing', err.message);
|
||||
zip=null;
|
||||
}
|
||||
|
||||
if (!zip)
|
||||
{
|
||||
if (!skip_load_script)
|
||||
{
|
||||
importScripts(jszip_path);
|
||||
return init_zip(true, jszip_path); //tries again
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return zip;
|
||||
}
|
||||
|
||||
function parse_3mf(s, callback, jszip_path)
|
||||
{
|
||||
var file_txt=arrayBufferToString(s.slice(0,5));
|
||||
if (file_txt=='<?xml')
|
||||
return parse_3mf_from_txt(arrayBufferToString(s), callback);
|
||||
|
||||
var zip=init_zip(false, jszip_path);
|
||||
if (!zip) return false;
|
||||
|
||||
var found=false;
|
||||
zip.loadAsync(s).then(function ()
|
||||
{
|
||||
var zkeys=Object.keys(zip.files);
|
||||
var i=zkeys.length;
|
||||
while (i--)
|
||||
{
|
||||
if (zip.files[zkeys[i]].name=="3D/3dmodel.model")
|
||||
{
|
||||
found=true;
|
||||
zip.files[zkeys[i]].async('text').then(function (fileData)
|
||||
{
|
||||
return parse_3mf_from_txt(fileData, callback); //'return' because our work in this loop is done
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
callback ("3D/3dmodel.model in 3mf file not found");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function parse_3mf_from_txt(s, callback)
|
||||
{
|
||||
var vertices=[];
|
||||
var faces=[];
|
||||
|
||||
var vertices_for_build=[];
|
||||
var faces_for_build=[];
|
||||
|
||||
var have_colors=false;
|
||||
|
||||
var vertex_pattern = /vertex\s+.*(x|y|z)\s*=\s*([0-9,\.\"\+\-e]+)\s+.*(x|y|z)\s*=\s*([0-9,\.\"\+\-e]+)\s+.*(x|y|z)\s*=\s*([0-9,\.\"\+\-e]+)\s*/i;
|
||||
var face_pattern = /triangle\s+.*(v1|v2|v3)\s*=\s*([0-9\"]+)\s+.*(v1|v2|v3)\s*=\s*([0-9\"]+)\s+.*(v1|v2|v3)\s*=\s*([0-9\"]+)\s*(?:pid=([0-9\"]+)\s+)?(?:p[1|2|3]=([0-9\"]+)\s)?\s*/i;
|
||||
var res_pattern = /(?:m:\S+|basematerials)\s+id=([0-9\"]+)\s*/i;
|
||||
var res_color_pattern=/(?:m:(\S+)|base)\s+.*color=([0-9A-F\"\#]+)\s*/i;
|
||||
//var object_pattern = /<object\s+.*type=model.*/i;
|
||||
var object_pattern = /<object\s+/i;
|
||||
var component_pattern = /<component\s+.*objectid=([0-9\"]+)/i;
|
||||
var item_transform_pattern = /item\s+.*objectid=([0-9\"]+)\s+.*transform=(([0-9\".e-]+\s+){12})/i;
|
||||
var resources={};
|
||||
var objects={};
|
||||
var curr_object=null;
|
||||
var curr_rid=0; //current resource id
|
||||
var vcounter=0; //vertices counter
|
||||
var fcounter=0; //faces counter
|
||||
var build_open_pattern=/<build/i;
|
||||
var build_close_pattern=/<\/build/i;
|
||||
var build_item_pattern=/<item\s+.*objectid=([0-9\"]+)/i;
|
||||
var lines = s.split(/[\r\n]+/g);
|
||||
if (lines.length<5)
|
||||
lines = s.split(/(?=<)/g); //files without new line, can happen
|
||||
var build_stage=false;
|
||||
|
||||
for ( var i = 0; i < lines.length; i ++ )
|
||||
{
|
||||
var line = lines[ i ];
|
||||
line = line.replace(/"/g, '');
|
||||
//console.log(line);
|
||||
|
||||
var res=vertex_pattern.exec( line );
|
||||
if ( res )
|
||||
{
|
||||
var v={x:0,y:0,z:0};
|
||||
v[res[1]]=res[2];
|
||||
v[res[3]]=res[4];
|
||||
v[res[5]]=res[6];
|
||||
vertices.push([v.x, v.y, v.z]);
|
||||
vcounter++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var res=face_pattern.exec( line );
|
||||
if ( res )
|
||||
{
|
||||
var f={v1:0,v2:0,v3:0};
|
||||
var face_color=null;
|
||||
var v_start_index=curr_object?curr_object.v_start_index:0;
|
||||
|
||||
f[res[1]]=parseInt(res[2])+v_start_index;
|
||||
f[res[3]]=parseInt(res[4])+v_start_index;
|
||||
f[res[5]]=parseInt(res[6])+v_start_index;
|
||||
|
||||
//maybe a color from face the itself??
|
||||
if (typeof res[7] !== "undefined")
|
||||
if (resources[res[7]])
|
||||
if (typeof res[8] !== "undefined")
|
||||
if (resources[res[7]])
|
||||
if (resources[res[7]].color)
|
||||
if (resources[res[7]].color[res[8]])
|
||||
face_color=resources[res[7]].color[res[8]].substr(1);
|
||||
|
||||
//maybe a color from the object??
|
||||
if ((!face_color)&&(curr_object))
|
||||
{
|
||||
if (typeof curr_object.pid !== "undefined")
|
||||
if (typeof curr_object.pindex !== "undefined")
|
||||
if (resources[curr_object.pid])
|
||||
if (resources[curr_object.pid].color)
|
||||
if (resources[curr_object.pid].color[curr_object.pindex])
|
||||
face_color=resources[curr_object.pid].color[curr_object.pindex].substr(1);
|
||||
}
|
||||
|
||||
if (face_color)
|
||||
{
|
||||
have_colors=true;
|
||||
face_color={red:parseInt(face_color.substr(0,2),16)/255, green:parseInt(face_color.substr(2,2),16)/255, blue:parseInt(face_color.substr(4,2),16)/255};
|
||||
faces.push([f.v1, f.v2, f.v3, face_color.red, face_color.green, face_color.blue]);
|
||||
}
|
||||
else
|
||||
faces.push([f.v1, f.v2, f.v3]);
|
||||
|
||||
fcounter++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var res=res_pattern.exec( line );
|
||||
if ( res )
|
||||
{
|
||||
curr_rid=res[1];
|
||||
if (!resources[curr_rid]) resources[curr_rid]={};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var res=component_pattern.exec( line );
|
||||
if ( res )
|
||||
{
|
||||
//console.log('component patterns: ',JSON.stringify(objects[res[1]]), res[1]);
|
||||
if (!curr_object) continue;
|
||||
if (!objects[res[1]]) continue;
|
||||
|
||||
//add new vertices to current vertices array
|
||||
//console.log('old vertices', JSON.stringify(vertices));
|
||||
vertices=vertices.concat(JSON.parse(JSON.stringify(vertices.slice(objects[res[1]].v_start_index, objects[res[1]].v_end_index+1)))); //the JSON is for a deep-copy, array of arrays can be tricky to just slice
|
||||
|
||||
//has transform?
|
||||
var tres=/transform=(([0-9\".e-]+\s+){12})/i.exec( line );
|
||||
if (tres)
|
||||
{
|
||||
var tsplit=tres[1].trim().split(/[ ,]+/);
|
||||
if (tsplit.length==12)
|
||||
{
|
||||
var to_iv=vertices.length-1;
|
||||
var from_iv=to_iv-(objects[res[1]].v_end_index-objects[res[1]].v_start_index);
|
||||
transform_vertices(vertices, from_iv, to_iv, tsplit);
|
||||
}
|
||||
}
|
||||
|
||||
//console.log('new vertices', JSON.stringify(vertices));
|
||||
|
||||
//add new faces to current faces array
|
||||
var new_faces=JSON.parse(JSON.stringify(faces.slice(objects[res[1]].f_start_index, objects[res[1]].f_end_index+1))); //the JSON is for a deep-copy, array of arrays can be tricky to just slice
|
||||
var vgap=vertices.length-objects[res[1]].v_end_index-1;
|
||||
var findex=new_faces.length;
|
||||
while (findex--)
|
||||
{
|
||||
new_faces[findex][0]+=vgap;
|
||||
new_faces[findex][1]+=vgap;
|
||||
new_faces[findex][2]+=vgap;
|
||||
}
|
||||
|
||||
//console.log('old faces', JSON.stringify(faces));
|
||||
faces=faces.concat(new_faces);
|
||||
//console.log('new faces', JSON.stringify(faces));
|
||||
|
||||
vcounter=vertices.length;
|
||||
fcounter=faces.length;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var res=object_pattern.exec( line );
|
||||
if ( res )
|
||||
{
|
||||
var res=/id=([0-9\"]+)/i.exec( line );if (!res) continue;
|
||||
if (curr_object) curr_object.v_end_index=vcounter-1;
|
||||
if (curr_object) curr_object.f_end_index=fcounter-1;
|
||||
|
||||
var new_object={};
|
||||
new_object.id=res[1];
|
||||
new_object.v_start_index=vcounter; //ref to the complete vertices array
|
||||
new_object.f_start_index=fcounter; //ref to the complete faces array
|
||||
|
||||
var res=/pid=([0-9\"]+)/i.exec( line );
|
||||
if (res) new_object.pid=parseInt(res[1]);
|
||||
|
||||
var res=/pindex=([0-9\"]+)/i.exec( line );
|
||||
if (res) new_object.pindex=parseInt(res[1]);
|
||||
|
||||
//console.log(JSON.stringify(curr_object));
|
||||
//var old_id=curr_object?curr_object.id:null;
|
||||
curr_object=new_object; //just changing reference
|
||||
//console.log(old_id?JSON.stringify(objects[old_id]):'NA');
|
||||
objects[curr_object.id]=curr_object;
|
||||
//console.log(curr_object.id, objects[curr_object.id]);
|
||||
|
||||
//console.log ('********* new object', JSON.stringify(curr_object));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var res=res_color_pattern.exec( line );
|
||||
if ( res )
|
||||
{
|
||||
if (typeof res[1] === "undefined") res[1]="color"; //this is color of basematerials peoperty
|
||||
if (!resources[curr_rid]) {console.log('warning: no source id for '+res[1]);continue;}
|
||||
if (!resources[curr_rid][res[1]]) resources[curr_rid][res[1]]=[];
|
||||
|
||||
resources[curr_rid][res[1]].push(res[2]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (build_open_pattern.exec( line ))
|
||||
{
|
||||
build_stage=true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (build_close_pattern.exec( line ))
|
||||
{
|
||||
build_stage=false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!build_stage) continue; //if not in build stage, we finished checking this row
|
||||
|
||||
var res=build_item_pattern.exec(line);
|
||||
if (res)
|
||||
{
|
||||
//console.log('build', res[1], JSON.stringify(objects));
|
||||
|
||||
//if (res[1]==128) continue; //*** REMOVE ME
|
||||
|
||||
if (!objects[res[1]]) continue;
|
||||
|
||||
var end_inx=(typeof objects[res[1]].v_end_index === "undefined")?(vcounter-1):objects[res[1]].v_end_index;
|
||||
|
||||
var res2=item_transform_pattern.exec(line);
|
||||
if ( res2 )
|
||||
{
|
||||
var tsplit=res2[2].trim().split(/[ ,]+/);
|
||||
if (tsplit.length==12)
|
||||
transform_vertices(vertices, objects[res2[1]].v_start_index, end_inx, tsplit);
|
||||
}
|
||||
|
||||
|
||||
vertices_for_build=vertices_for_build.concat(vertices.slice(objects[res[1]].v_start_index, end_inx+1));
|
||||
var vgap=vertices_for_build.length-end_inx-1;
|
||||
//console.log('vgap', vgap);
|
||||
|
||||
|
||||
var end_inx=(typeof objects[res[1]].f_end_index === "undefined")?(fcounter-1):objects[res[1]].f_end_index;
|
||||
//console.log('bulding from faces',objects[res[1]].f_start_index, end_inx);
|
||||
var new_faces=JSON.parse(JSON.stringify(faces.slice(objects[res[1]].f_start_index, end_inx+1))); //need a deep copy, so we won't change the old faces array
|
||||
var findex=new_faces.length;
|
||||
while (findex--)
|
||||
{
|
||||
new_faces[findex][0]+=vgap;
|
||||
new_faces[findex][1]+=vgap;
|
||||
new_faces[findex][2]+=vgap;
|
||||
}
|
||||
faces_for_build=faces_for_build.concat(new_faces);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//console.log('vertices: ', JSON.stringify(vertices));
|
||||
//console.log('vertices for build: ', JSON.stringify(vertices_for_build));
|
||||
//console.log('faces: ', JSON.stringify(faces));
|
||||
//console.log('faces for build: ', JSON.stringify(faces_for_build));
|
||||
//console.log('resources: ', JSON.stringify(resources));
|
||||
//console.log('objects: ', JSON.stringify(objects));
|
||||
//return;
|
||||
|
||||
callback({vertices:vertices_for_build, faces:faces_for_build, colors:have_colors});
|
||||
|
||||
}
|
||||
|
||||
function transform_vertices(vertices, from_iv, to_iv, tsplit) //transform vertices by matrix, used by parse_3mf_from_txt
|
||||
{
|
||||
var tvals=[[],[],[],[]];
|
||||
for (var itval=0;itval<3;itval++) tvals[0][itval]=parseFloat(tsplit[itval]);tvals[0].push(0);
|
||||
for (var itval=3;itval<6;itval++) tvals[1][itval-3]=parseFloat(tsplit[itval]);tvals[1].push(0);
|
||||
for (var itval=6;itval<9;itval++) tvals[2][itval-6]=parseFloat(tsplit[itval]);tvals[2].push(0);
|
||||
for (var itval=9;itval<12;itval++) tvals[3][itval-9]=parseFloat(tsplit[itval]);tvals[3].push(1);
|
||||
//console.log('transform', tvals);
|
||||
|
||||
for (var iv=from_iv;iv<=to_iv;iv++)
|
||||
{
|
||||
//console.log('before mul', JSON.stringify(vertices[iv]));
|
||||
var transform=matrix_multiply([vertices[iv].concat(1)], tvals);
|
||||
vertices[iv]=[transform[0][0],transform[0][1],transform[0][2]];
|
||||
//console.log('after mul', JSON.stringify(vertices[iv]));
|
||||
}
|
||||
}
|
||||
|
||||
function matrix_multiply(a, b)
|
||||
{
|
||||
console.log()
|
||||
var aNumRows = a.length, aNumCols = a[0].length,
|
||||
bNumRows = b.length, bNumCols = b[0].length,
|
||||
m = new Array(aNumRows); // initialize array of rows
|
||||
for (var r = 0; r < aNumRows; ++r) {
|
||||
m[r] = new Array(bNumCols); // initialize the current row
|
||||
for (var c = 0; c < bNumCols; ++c) {
|
||||
m[r][c] = 0; // initialize the current cell
|
||||
for (var i = 0; i < aNumCols; ++i) {
|
||||
m[r][c] += a[r][i] * b[i][c];
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
function geo_to_vf(geo)
|
||||
{
|
||||
var vertices=[];
|
||||
|
||||
Reference in New Issue
Block a user