forked from mirror/viewstl
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbb7f5c389 | |||
| e14585dff0 | |||
| 35626740ae | |||
| 8ee9296a7f | |||
| 35f2e54652 | |||
| 46b76d7606 | |||
| a8d9c4c019 | |||
| 2ba4c979cc | |||
| c7f11e4a2f |
@@ -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
@@ -1,2 +1,2 @@
|
||||
//=========== Stl Viewer v1.12, by Omri Rips, Viewstl.com, February 2021 ; admin@viewstl.com ===========
|
||||
importScripts("parser.min.js"),MSG_DATA=0,MSG_LOAD=1,MSG_ERROR=2,MSG_STL_LOADED=3,MSG_LOAD_IN_PROGRESS=4;var filename=null,local_file=null,load_from_blob_or_ab=null,x=0,y=0,z=0,model_id=-1,get_progress=!1;function isNumeric(a){return!isNaN(parseFloat(a))&&isFinite(a)}function send_error(a){postMessage({msg_type:MSG_ERROR,data:a})}function download_from_local(a){fetch?download_from_local_fetch(a):download_from_local_xhr(a)}function download_from_local_xhr(a){var e=new XMLHttpRequest;get_progress&&(e.onprogress=function(a){postMessage({msg_type:MSG_LOAD_IN_PROGRESS,id:model_id,loaded:a.loaded,total:a.total})}),e.onreadystatechange=function(a){4==e.readyState&&200==e.status&&after_file_load(e.response)},e.open("GET",a,!0),e.responseType="arraybuffer",e.send(null)}async function download_from_local_fetch(a){const e=await fetch(a),o=e.body.getReader(),r=Number(e.headers.get("content-length")),t=new Uint8Array(r);let l=0;for(;;){const{done:a,value:e}=await o.read();if(a)break;e&&(t.set(e,l),l+=e.length,get_progress&&postMessage({msg_type:MSG_LOAD_IN_PROGRESS,id:model_id,loaded:l,total:r}))}after_file_load(t.buffer)}function after_file_load(a){var e;if(a){try{e=parse_3d_file(filename,a)}catch(a){e="Error parsing the file"}"string"!=typeof e?postMessage({msg_type:MSG_STL_LOADED,vertices:e.vertices,faces:e.faces,colors:e.colors}):send_error(e)}else send_error("no data")}function read_file(a){var e=new FileReader;e.onerror=function(a){var e="";switch(a.target.error.code){case a.target.error.NOT_FOUND_ERR:e="File not found";break;case a.target.error.NOT_READABLE_ERR:e="Can't read file - too large?";break;case a.target.error.ABORT_ERR:e="Read operation aborted";break;case a.target.error.SECURITY_ERR:e="File is locked";break;case a.target.error.ENCODING_ERR:e="File too large";break;default:e="Error reading file"}send_error(e)},e.onprogress=function(a){postMessage({msg_type:MSG_LOAD_IN_PROGRESS,id:model_id,loaded:a.loaded,total:a.total})},e.onload=function(a){after_file_load(a.target.result)},e.readAsArrayBuffer(a)}self.addEventListener("message",function(a){switch(a.data.msg_type){case MSG_DATA:if(!a.data.data){send_error("no data");break}if(!a.data.data.filename&&!a.data.data.local_file){send_error("no file");break}a.data.data.local_file?(filename=a.data.data.local_file.name?a.data.data.local_file.name:a.data.data.filename,local_file=a.data.data.local_file?a.data.data.local_file:null):a.data.data.filename&&(filename=a.data.data.filename),a.data.data.x&&isNumeric(a.data.data.x)&&(x=a.data.data.x),a.data.data.y&&isNumeric(a.data.data.y)&&(y=a.data.data.y),a.data.data.y&&isNumeric(a.data.data.z)&&(z=a.data.data.z),load_from_blob_or_ab=null,a.data.load_from_blob_or_ab&&(load_from_blob_or_ab=a.data.load_from_blob_or_ab),model_id=a.data.data.id?a.data.data.id:-1,get_progress=!!a.data.get_progress&&a.data.get_progress;break;case MSG_LOAD:load_from_blob_or_ab?load_from_blob_or_ab instanceof ArrayBuffer?after_file_load(load_from_blob_or_ab):read_file(load_from_blob_or_ab):local_file?read_file(local_file):filename&&download_from_local(filename);break;default:console.log("invalid msg: "+a.data.msg_type)}});
|
||||
//=========== Stl Viewer v1.13, by Omri Rips, Viewstl.com, July 2021 ; admin@viewstl.com ===========
|
||||
importScripts("parser.min.js"),MSG_DATA=0,MSG_LOAD=1,MSG_ERROR=2,MSG_STL_LOADED=3,MSG_LOAD_IN_PROGRESS=4;var filename=null,local_file=null,load_from_blob_or_ab=null,x=0,y=0,z=0,model_id=-1,get_progress=!1,jszip_path="jszip.min.js";function isNumeric(a){return!isNaN(parseFloat(a))&&isFinite(a)}function send_error(a){postMessage({msg_type:MSG_ERROR,data:a})}function download_from_local(a){download_from_local_xhr(a)}function download_from_local_xhr(a){var e=new XMLHttpRequest;get_progress&&(e.onprogress=function(a){postMessage({msg_type:MSG_LOAD_IN_PROGRESS,id:model_id,loaded:a.loaded,total:a.total})}),e.onreadystatechange=function(a){4==e.readyState&&200==e.status&&after_file_load(e.response)},e.open("GET",a,!0),e.responseType="arraybuffer",e.send(null)}function after_file_load(a){if(a)try{parse_3d_file(filename,a,after_file_parse,jszip_path)}catch(a){send_error("Error parsing the file")}else send_error("no data")}function after_file_parse(a){"string"!=typeof a?postMessage({msg_type:MSG_STL_LOADED,vertices:a.vertices,faces:a.faces,colors:a.colors}):send_error(a)}function read_file(a){var e=new FileReader;e.onerror=function(a){var e="";switch(a.target.error.code){case a.target.error.NOT_FOUND_ERR:e="File not found";break;case a.target.error.NOT_READABLE_ERR:e="Can't read file - too large?";break;case a.target.error.ABORT_ERR:e="Read operation aborted";break;case a.target.error.SECURITY_ERR:e="File is locked";break;case a.target.error.ENCODING_ERR:e="File too large";break;default:e="Error reading file"}send_error(e)},e.onprogress=function(a){postMessage({msg_type:MSG_LOAD_IN_PROGRESS,id:model_id,loaded:a.loaded,total:a.total})},e.onload=function(a){after_file_load(a.target.result)},e.readAsArrayBuffer(a)}self.addEventListener("message",function(a){switch(a.data.msg_type){case MSG_DATA:if(!a.data.data){send_error("no data");break}if(!a.data.data.filename&&!a.data.data.local_file){send_error("no file");break}a.data.jszip_path&&(jszip_path=a.data.jszip_path),a.data.data.local_file?(filename=a.data.data.local_file.name?a.data.data.local_file.name:a.data.data.filename,local_file=a.data.data.local_file?a.data.data.local_file:null):a.data.data.filename&&(filename=a.data.data.filename),a.data.data.x&&isNumeric(a.data.data.x)&&(x=a.data.data.x),a.data.data.y&&isNumeric(a.data.data.y)&&(y=a.data.data.y),a.data.data.y&&isNumeric(a.data.data.z)&&(z=a.data.data.z),load_from_blob_or_ab=null,a.data.load_from_blob_or_ab&&(load_from_blob_or_ab=a.data.load_from_blob_or_ab),model_id=a.data.data.id?a.data.data.id:-1,get_progress=!!a.data.get_progress&&a.data.get_progress;break;case MSG_LOAD:load_from_blob_or_ab?load_from_blob_or_ab instanceof ArrayBuffer?after_file_load(load_from_blob_or_ab):read_file(load_from_blob_or_ab):local_file?read_file(local_file):filename&&download_from_local(filename);break;default:console.log("invalid msg: "+a.data.msg_type)}});
|
||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
+33
-13
@@ -1,7 +1,8 @@
|
||||
//1.12
|
||||
//1.13
|
||||
//load STL file info geometry and returns it
|
||||
|
||||
importScripts("parser.min.js");
|
||||
//importScripts("parser.min.js");
|
||||
importScripts("parser.js");
|
||||
|
||||
MSG_DATA=0;
|
||||
MSG_LOAD=1;
|
||||
@@ -17,6 +18,7 @@ var y=0;
|
||||
var z=0;
|
||||
var model_id=-1;
|
||||
var get_progress=false;
|
||||
var jszip_path='jszip.min.js';
|
||||
|
||||
self.addEventListener("message", function(e)
|
||||
{
|
||||
@@ -26,6 +28,9 @@ self.addEventListener("message", function(e)
|
||||
if (!e.data.data) {send_error("no data");break;}
|
||||
if ((!e.data.data.filename)&&(!e.data.data.local_file)) {send_error("no file");break;}
|
||||
|
||||
if (e.data.jszip_path)
|
||||
jszip_path=e.data.jszip_path;
|
||||
|
||||
if (e.data.data.local_file)
|
||||
{
|
||||
filename=e.data.data.local_file.name?e.data.data.local_file.name:e.data.data.filename; //filename property is usefull for blob files, 10x Fraser
|
||||
@@ -84,14 +89,14 @@ function send_error(s)
|
||||
|
||||
function download_from_local(filename)
|
||||
{
|
||||
if (fetch)
|
||||
{
|
||||
download_from_local_fetch(filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if ((fetch)&&(async_supported()))
|
||||
//{
|
||||
// download_from_local_fetch(filename);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
download_from_local_xhr(filename);
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
function download_from_local_xhr(filename)
|
||||
@@ -128,6 +133,7 @@ function download_from_local_xhr(filename)
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
/*
|
||||
async function download_from_local_fetch(filename)
|
||||
{
|
||||
const response = await fetch(filename);
|
||||
@@ -149,6 +155,7 @@ async function download_from_local_fetch(filename)
|
||||
}
|
||||
after_file_load(chunksAll.buffer)
|
||||
}
|
||||
*/
|
||||
|
||||
function after_file_load(s)
|
||||
{
|
||||
@@ -159,16 +166,29 @@ function after_file_load(s)
|
||||
send_error("no data");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
vf_data=parse_3d_file(filename, s);
|
||||
vf_data=parse_3d_file(filename, s, after_file_parse, jszip_path);
|
||||
}
|
||||
catch(err)
|
||||
{
|
||||
vf_data="Error parsing the file";
|
||||
send_error("Error parsing the file");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
if (typeof vf_data === 'string')
|
||||
{
|
||||
send_error(vf_data);
|
||||
return;
|
||||
}
|
||||
|
||||
postMessage({msg_type:MSG_STL_LOADED, vertices:vf_data.vertices, faces:vf_data.faces, colors:vf_data.colors});
|
||||
*/
|
||||
}
|
||||
|
||||
function after_file_parse(vf_data)
|
||||
{
|
||||
if (typeof vf_data === 'string')
|
||||
{
|
||||
send_error(vf_data);
|
||||
|
||||
+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=[];
|
||||
|
||||
+43
-23
@@ -1,9 +1,12 @@
|
||||
//1.12
|
||||
//1.13
|
||||
//**********************************************************
|
||||
//New in 1.13 => add default color for faces (relevant for colored model)
|
||||
//New in 1.13 => 3mf format support
|
||||
//New in 1.13 => enlarge camera.far value
|
||||
//New in 1.13 => fixed bug in units changing (do not change scale properties)
|
||||
//New in 1.12 => improve auto_zoom algorithm by Michal Jirku (https://wejn.org/2020/12/cracking-the-threejs-object-fitting-nut/)
|
||||
//New in 1.12 => "dispose" function
|
||||
//New in 1.12 => fixed memory leak on "clean" function (thanks to Anthony https://github.com/antho1404)
|
||||
//New in 1.12 => use 'fetch' instead for xhr when possible (thanks to Anthony https://github.com/antho1404)
|
||||
//New in 1.12 => added get_vsb_blob (returns vsb file as binary)
|
||||
//New in 1.12 => set viewstl to take all of the container (and not 5px margin)
|
||||
//New in 1.12 => fixed bug in json_without_nulls function
|
||||
@@ -89,6 +92,8 @@ function StlViewer(parent_element_obj, options)
|
||||
this.load_three_files=_this.get_opt("load_three_files", stl_viewer_script_path);
|
||||
this.ready=(typeof THREE != 'undefined');
|
||||
this.ready_callback=null;
|
||||
this.jszip_path=null;
|
||||
this.jszip_utils_path=null;
|
||||
this.auto_resize=true;
|
||||
this.on_model_drop=null;
|
||||
this.center_models=true;
|
||||
@@ -101,6 +106,8 @@ function StlViewer(parent_element_obj, options)
|
||||
this.grid=null; //draw grid over scene
|
||||
|
||||
this.killsign=false; //use by 'dispose', stl_viewer instqance will be unusable after setting this to true
|
||||
|
||||
this.default_face_color="#909090"; //in case of a colored model, RGB in hex
|
||||
|
||||
this.set_on_model_mousedown = function (callback)
|
||||
{
|
||||
@@ -147,6 +154,8 @@ function StlViewer(parent_element_obj, options)
|
||||
_this.auto_rotate=_this.get_opt("auto_rotate",_this.auto_rotate);
|
||||
_this.mouse_zoom=_this.get_opt("mouse_zoom",_this.mouse_zoom);
|
||||
_this.ready_callback=_this.get_opt("ready_callback",null);
|
||||
_this.jszip_path=_this.get_opt("jszip_path",null);
|
||||
_this.jszip_utils_path=_this.get_opt("jszip_utils_path",null);
|
||||
_this.auto_resize=_this.get_opt("auto_resize",_this.auto_resize);
|
||||
_this.on_model_drop=_this.get_opt("on_model_drop",_this.on_model_drop);
|
||||
_this.center_models=_this.get_opt("center_models",_this.center_models);
|
||||
@@ -215,7 +224,7 @@ function StlViewer(parent_element_obj, options)
|
||||
{
|
||||
case _this.MSGFROMWORKER_STL_LOADED:
|
||||
model.colors=e.data.colors;
|
||||
var geo=_this.vf_to_geo(e.data.vertices, e.data.faces, e.data.colors?e.data.colors:false);
|
||||
var geo=_this.vf_to_geo(e.data.vertices, e.data.faces, e.data.colors?e.data.colors:false, model.color);
|
||||
if (geo)
|
||||
{
|
||||
//if (!geo.boundingBox) geo.computeBoundingBox();
|
||||
@@ -263,7 +272,7 @@ function StlViewer(parent_element_obj, options)
|
||||
if (_this.pre_loaded_ab_files) if (model.filename) if (_this.pre_loaded_ab_files[model.filename]) blob_to_load=_this.pre_loaded_ab_files[model.filename];
|
||||
|
||||
//console.log ('blob to load', blob_to_load, _this.pre_loaded_ab_files, _this.pre_loaded_ab_files[model.filename]);
|
||||
model_worker.postMessage({msg_type:_this.MSG2WORKER_DATA, data:model, load_from_blob_or_ab:blob_to_load, get_progress:(_this.loading_progress_callback!=null)});
|
||||
model_worker.postMessage({msg_type:_this.MSG2WORKER_DATA, data:model, load_from_blob_or_ab:blob_to_load, get_progress:(_this.loading_progress_callback!=null), jszip_path:_this.jszip_path});
|
||||
model_worker.postMessage({msg_type:_this.MSG2WORKER_LOAD});
|
||||
|
||||
}
|
||||
@@ -359,8 +368,9 @@ function StlViewer(parent_element_obj, options)
|
||||
const minZ = _this.minz;
|
||||
const cameraToFarEdge = ( minZ < 0 ) ? -minZ + cameraZ : cameraZ - minZ;
|
||||
|
||||
_this.camera.far = cameraToFarEdge * 3;
|
||||
_this.camera.far = Math.max(cameraToFarEdge * 3000, _this.camera.far);
|
||||
_this.camera.updateProjectionMatrix();
|
||||
//console.log(_this.camera.far);
|
||||
|
||||
}
|
||||
|
||||
@@ -580,7 +590,7 @@ function StlViewer(parent_element_obj, options)
|
||||
_this.animation[model.id]=1;
|
||||
}
|
||||
|
||||
this.set_scale = function(model_id, scalex, scaley, scalez)
|
||||
this.set_scale = function(model_id, scalex, scaley, scalez, keep_prev_scale)
|
||||
{
|
||||
if (_this.models_ref[model_id]===undefined) return _this.model_error("set_scale - id not found: "+model_id);
|
||||
|
||||
@@ -604,6 +614,13 @@ function StlViewer(parent_element_obj, options)
|
||||
_this.set_or_update_geo_edges (model, true, true);
|
||||
|
||||
//console.log(model.scalex+"/"+model.scaley+"/"+model.scalez);
|
||||
|
||||
if (keep_prev_scale) //keep the previous scale (just change the geometry)
|
||||
{
|
||||
model.scalex=prev_scalex;
|
||||
model.scaley=prev_scaley;
|
||||
model.scalez=prev_scalez;
|
||||
}
|
||||
}
|
||||
|
||||
this.scale_geo = function(model,scalex,scaley,scalez)
|
||||
@@ -823,7 +840,8 @@ function StlViewer(parent_element_obj, options)
|
||||
_this.set_scale(model.id,
|
||||
model.scalex*scale_factor,
|
||||
model.scaley*scale_factor,
|
||||
model.scalez*scale_factor
|
||||
model.scalez*scale_factor,
|
||||
true //keep the current scale, just change the geometry
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -974,7 +992,7 @@ function StlViewer(parent_element_obj, options)
|
||||
|
||||
case 'vsb':
|
||||
return _this.load_vsb(new_model.local_file?new_model.local_file:model_filename);
|
||||
|
||||
|
||||
//default: assumed as a regular model (STL etc.) - do nothing, continue below
|
||||
}
|
||||
}
|
||||
@@ -1436,6 +1454,7 @@ function StlViewer(parent_element_obj, options)
|
||||
|
||||
reader.onload = function(e)
|
||||
{
|
||||
//return after_read_func(e.target.result, filename);
|
||||
return after_read_func(e.target.result);
|
||||
};
|
||||
|
||||
@@ -1491,14 +1510,6 @@ function StlViewer(parent_element_obj, options)
|
||||
});
|
||||
}
|
||||
|
||||
//load arraybuffer to memory (for later loading)
|
||||
this.load_bin_to_ab_file=function(url, ab_data)
|
||||
{
|
||||
if (!_this.pre_loaded_ab_files) _this.pre_loaded_ab_files=[];
|
||||
_this.pre_loaded_ab_files[url]=ab_data;
|
||||
_this.add_model({id:-1, filename:url});
|
||||
}
|
||||
|
||||
this.download_model = function(model_id, filename)
|
||||
{
|
||||
if (_this.models_ref[model_id]===undefined) return _this.model_error("download_model - id not found: "+model_id);
|
||||
@@ -1836,7 +1847,7 @@ function StlViewer(parent_element_obj, options)
|
||||
_this.scene = new THREE.Scene();
|
||||
_this.is_webgl=webgl_Detector.webgl;
|
||||
_this.renderer = _this.is_webgl ? new THREE.WebGLRenderer({preserveDrawingBuffer:true, alpha:true}): new THREE.CanvasRenderer({alpha:true});
|
||||
_this.camera = new THREE.PerspectiveCamera(45, 1, 0.1, 10000);
|
||||
_this.camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100000);
|
||||
_this.parent_element.appendChild(_this.renderer.domElement);
|
||||
_this.scene.add(_this.camera);
|
||||
|
||||
@@ -1908,7 +1919,7 @@ function StlViewer(parent_element_obj, options)
|
||||
|
||||
}
|
||||
|
||||
this.vf_to_geo = function (vertices, faces, colors)
|
||||
this.vf_to_geo = function (vertices, faces, colors, default_face_color)
|
||||
{
|
||||
if (!vertices) return null;
|
||||
if (!faces) return null;
|
||||
@@ -1932,7 +1943,13 @@ function StlViewer(parent_element_obj, options)
|
||||
for (i=0;i<len;i++)
|
||||
{
|
||||
var face=new THREE.Face3(faces[i][0],faces[i][1],faces[i][2]);
|
||||
face.color.setRGB ( faces[i][3], faces[i][4], faces[i][5] );
|
||||
if (typeof faces[i][3] === "undefined")
|
||||
{
|
||||
if (!default_face_color) default_face_color=_this.default_face_color; //if user didn't define model color, it will take the default value. since model is colored (if we're here) - we have to give each face a color, or it will appear black
|
||||
face.color.setRGB ( parseInt(_this.default_face_color.substr(1,2),16)/255, parseInt(_this.default_face_color.substr(3,2),16)/255, parseInt(_this.default_face_color.substr(5,2),16)/255);
|
||||
}
|
||||
else
|
||||
face.color.setRGB ( faces[i][3], faces[i][4], faces[i][5] );
|
||||
geo_faces.push(face);
|
||||
}
|
||||
}
|
||||
@@ -2040,7 +2057,7 @@ function StlViewer(parent_element_obj, options)
|
||||
case 'vsb':
|
||||
_this.load_vsb(files[i]);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
//assumed as a regular model (STL etc.)
|
||||
dropped_models.push({id:-1, local_file:files[i]});
|
||||
@@ -2116,9 +2133,12 @@ function StlViewer(parent_element_obj, options)
|
||||
{
|
||||
if (typeof _this.load_three_files != "string") _this.load_three_files="";
|
||||
_this.scripts_loader=new ScriptsLoader();
|
||||
//_this.scripts_loader.load_scripts(new Array(path+"three.min.js", path+"webgl_detector.js", path+"Projector.js", path+"CanvasRenderer.js", path+"OrbitControls.js"), _this.external_files_loaded);
|
||||
//_this.scripts_loader.load_scripts(new Array(path+"three.min.js", path+"webgl_detector.js", path+"Projector.js", path+"CanvasRenderer.js", path+"TrackballControls.js"), _this.external_files_loaded);
|
||||
_this.scripts_loader.load_scripts(new Array(path+"three.min.js", path+"webgl_detector.js", path+"Projector.js", path+"CanvasRenderer.js", path+(_this.controls_type==0?"OrbitControls.js":"TrackballControls.js")), _this.external_files_loaded);
|
||||
var scripts_to_load=[path+"three.min.js", path+"webgl_detector.js", path+"Projector.js", path+"CanvasRenderer.js", path+(_this.controls_type==0?"OrbitControls.js":"TrackballControls.js")];
|
||||
|
||||
if (_this.jszip_path) scripts_to_load.push(_this.jszip_path); //need it to handle vsb and 3mf formats
|
||||
if (_this.jszip_utils_path) scripts_to_load.push(_this.jszip_utils_path); //need it to handle vsb format
|
||||
|
||||
_this.scripts_loader.load_scripts(scripts_to_load, _this.external_files_loaded);
|
||||
}
|
||||
|
||||
this.init_by_json = function(json_str)
|
||||
|
||||
Reference in New Issue
Block a user