31 Commits

Author SHA1 Message Date
omrips fbb7f5c389 Add support for MeshLab colored-STL method 2021-10-17 12:07:21 +03:00
omrips e14585dff0 Add files via upload
Add support for MeshLab colored-STL method
2021-10-17 12:06:38 +03:00
omrips 35626740ae Update README.md 2021-08-07 11:31:29 +03:00
omrips 8ee9296a7f 3mf "<build>" tag bug fix
3mf "<build>" tag bug fix
2021-08-03 15:27:48 +03:00
omrips 35f2e54652 Add files via upload
Fixed an issue with "<build>" tag on 3mf file
2021-08-03 15:26:30 +03:00
omrips 46b76d7606 3mf support, bug fix 2021-07-06 16:51:52 +03:00
omrips a8d9c4c019 3mf support, bug fix 2021-07-06 16:51:16 +03:00
omrips 2ba4c979cc Add files via upload 2021-02-14 12:15:59 +02:00
omrips c7f11e4a2f Add files via upload 2021-02-14 12:15:39 +02:00
omrips 6fbc2cc36a Add files via upload
//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
2021-02-09 18:12:03 +02:00
omrips 8e88aec93f Add files via upload 2021-02-09 18:11:02 +02:00
omrips 843ece22e1 Merge pull request #10 from antho1404/patch-1
Switch to fetch to retrieve files
2021-02-03 16:21:06 +02:00
antho1404 733d981b81 download compatibility with fetch and xhr 2021-01-29 10:28:59 +07:00
Anthony e0143c9f59 Switch to fetch to retrieve files 2021-01-28 10:17:30 +07:00
omrips 649d10d8f9 on_mouse_click event
add 'click_type parameter'
2020-12-07 15:28:47 +02:00
omrips e4b97cb853 Add files via upload 2020-12-07 15:28:08 +02:00
omrips 58ebbf5aad Delete stl_viewer.js 2020-12-07 15:27:50 +02:00
omrips 4a72cbdd37 on_mouse_click event
add 'click_type' parameter
2020-12-07 15:27:13 +02:00
omrips 07be41a6b3 Add files via upload 2020-11-21 12:02:58 +02:00
omrips 174b37527a Add files via upload 2020-11-21 12:02:20 +02:00
omrips f29305e5e7 Add files via upload 2020-11-21 12:01:51 +02:00
omrips b87c4e78f8 Add files via upload
New version - fix bugs, 'set_grid'
2020-11-21 12:01:20 +02:00
omrips 9ab79bd47a Add files via upload
Fix issues with rotation at 0 angle
2020-10-09 12:03:48 +03:00
omrips 69780f953d Add files via upload 2020-10-09 12:02:33 +03:00
omrips 6a5b545a83 Add files via upload 2020-08-12 16:12:41 +03:00
omrips b9c0bd9435 Add files via upload
1. fix issue with colored STL on vsb
2. make all vsb ids -1
3. fixed an issue with mesh cloning
2020-08-12 16:10:37 +03:00
omrips 5d9daa6dcd Add files via upload 2020-07-30 11:35:02 +03:00
omrips 4d46da020f Add files via upload
takes 'filename' property for local file, if no 'file.name' property is available. usefull for blob files
2020-07-30 11:34:33 +03:00
omrips 7df7673429 Add files via upload
takes 'filename' property for local files, if no file.name property is avilable
2020-07-30 11:33:08 +03:00
omrips a8d0fb71e7 Add files via upload 2020-07-27 12:49:13 +03:00
omrips 5859bff3bf Add files via upload
Add units support
2020-07-27 12:48:37 +03:00
9 changed files with 846 additions and 1897 deletions
+16
View File
@@ -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/
+2 -2
View File
@@ -1,2 +1,2 @@
//=========== Stl Viewer v1.10, by Omri Rips, Viewstl.com, July 2020 ; 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){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.setRequestHeader("Content-type","application/x-www-form-urlencoded"),e.responseType="arraybuffer",e.send(null)}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,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)}});
+2 -2
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -873
View File
File diff suppressed because one or more lines are too long
+1 -873
View File
File diff suppressed because one or more lines are too long
+65 -9
View File
@@ -1,7 +1,8 @@
//1.10
//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,9 +28,12 @@ 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;
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
local_file=e.data.data.local_file?e.data.data.local_file:null;
}
else if (e.data.data.filename)
@@ -44,6 +49,7 @@ self.addEventListener("message", function(e)
model_id=e.data.data.id?e.data.data.id:-1;
get_progress=e.data.get_progress?e.data.get_progress:false;
break;
case MSG_LOAD:
@@ -74,13 +80,26 @@ self.addEventListener("message", function(e)
function isNumeric(n)
{
return !isNaN(parseFloat(n)) && isFinite(n);
}
}
function send_error(s)
{
postMessage({msg_type:MSG_ERROR, data:s});
}
function download_from_local(filename)
{
//if ((fetch)&&(async_supported()))
//{
// download_from_local_fetch(filename);
//}
//else
//{
download_from_local_xhr(filename);
//}
}
function download_from_local_xhr(filename)
{
var xhr = new XMLHttpRequest();
@@ -108,12 +127,36 @@ function download_from_local(filename)
}
xhr.open("GET", filename, true);
xhr.open("GET", filename, true);
//xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = "arraybuffer";
xhr.send(null);
}
/*
async function download_from_local_fetch(filename)
{
const response = await fetch(filename);
const reader = response.body.getReader();
const total = Number(response.headers.get('content-length'));
const chunksAll = new Uint8Array(total);
let position = 0
while (true)
{
const { done, value } = await reader.read()
if (done) break;
if (!value) continue;
chunksAll.set(value, position);
position += value.length;
if (get_progress)
{
postMessage({ msg_type: MSG_LOAD_IN_PROGRESS, id: model_id, loaded: position, total: total });
}
}
after_file_load(chunksAll.buffer)
}
*/
function after_file_load(s)
{
var vf_data;
@@ -123,16 +166,29 @@ function after_file_load(s)
send_error("no data");
return;
}
}
try
{
{
vf_data=parse_3d_file(filename, s, after_file_parse, jszip_path);
}
catch(err)
{
{
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
View File
@@ -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=[];
+337 -102
View File
@@ -1,5 +1,24 @@
//1.10
//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 => 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
//New in 1.12 => cal vol/area bug
//New in 1.12 => remove edges where model is removed
//New in 1.11 => set grid
//New in 1.10 => revoke dataURLs
//New in 1.10 => fix issue with rotation at 0 angle
//New in 1.10 => fix issue with colored STL on vsb
//New in 1.10 => make all vsb ids -1
//New in 1.10 => fixed issue with mesh cloning
//New in 1.10 => mm/inch settings
//New in 1.10 => Option to trigger 'no model' click event - 'send_no_model_click_event'
//New in 1.10 => Scale always 1 for vsb file, ro avoid double scalling
//New in 1.10 => define default path for loading THREE JS files by script path (and not by html page path) - thanks venkyr!
@@ -7,7 +26,6 @@
//New in 1.10 => fix rotation issues
//New in 1.09 => get_camera_state - get camera's info
//New in 1.09 => set_camera_state - set camera
//New in 1.09 => Returns 'orig_filename' optional parameter at 'get_model_info'
//New in 1.09 => 'get_vsj' - returns object current stands as json, files not included
//New in 1.09 => 'download_vsj' - download json descriptor of current scene
@@ -40,13 +58,14 @@ function StlViewer(parent_element_obj, options)
if (!_this.options) return def;
if (_this.options[opt_id]===false) return false;
return _this.options[opt_id]?_this.options[opt_id]:def;
}
this.canvas_width="100%";
this.canvas_height="100%";
this.bg_color="transparent";
this.models_to_add=null; //at start
this.models_to_add=[]; //at start
this.models=new Array();
this.models_count=0;
this.models_ref=new Array(); //models with index - direct reference from id(comes from user) to model array (above)
@@ -65,6 +84,7 @@ function StlViewer(parent_element_obj, options)
this.camerax=0;
this.cameray=0;
this.cameraz=0;
this.camera_state=null;
this.auto_rotate=false;
this.mouse_zoom=true;
@@ -72,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;
@@ -81,6 +103,11 @@ function StlViewer(parent_element_obj, options)
this.pre_loaded_vsj=null; //VSJ file content, waiting to be loaded (used when loading VSB)
this.zip_load_count=-1; //Zip files waiting to be loaded to memory (used when loading VSB)
this.send_no_model_click_event=false; //will trigger click event even if no model was clicked (just parent element was clicked)
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)
{
@@ -89,6 +116,7 @@ function StlViewer(parent_element_obj, options)
if (_this.onmousedown_callback)
{
_this.parent_element.addEventListener('mousedown', _this.onmousedown);
_this.parent_element.addEventListener('dblclick', _this.onmousedown);
_this.parent_element.addEventListener('touchstart', _this.onmousedown);
}
}
@@ -111,7 +139,7 @@ function StlViewer(parent_element_obj, options)
{
_this.canvas_width=_this.get_opt("width",_this.canvas_width);
_this.canvas_height=_this.get_opt("height",_this.canvas_height);
_this.bg_color=_this.get_opt("bgcolor",_this.bg_color);
_this.bg_color=_this.get_opt("bg_color",_this.bg_color);
_this.models_to_add=_this.get_opt("models",_this.models_to_add);
_this.model_loaded_callback=_this.get_opt("model_loaded_callback",_this.model_loaded_callback);
_this.all_loaded_callback=_this.get_opt("all_loaded_callback",_this.all_loaded_callback);
@@ -126,10 +154,13 @@ 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);
_this.controls_type=_this.get_opt("controls", _this.controls_type);
_this.grid=_this.get_opt("grid",_this.grid?true:false); //now it is boolean, later it will be grid helper object
if (_this.zoom>=0)
_this.cameraz=_this.zoom;
else
@@ -170,7 +201,7 @@ function StlViewer(parent_element_obj, options)
_this.set_geo_minmax(model);
_this.recalc_dims(model);
model.color=model.mesh.material.color.getHexString();
model.color='#'+model.mesh.material.color.getHexString();
_this.scene.add(model.mesh);
_this.model_loaded(model.id);
@@ -193,11 +224,12 @@ 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();
var material=new THREE.MeshLambertMaterial({color:0x909090, overdraw: 1, wireframe: false, vertexColors: model.color?THREE.NoColors:THREE.FaceColors}); //if model color is set, ignores face colors set on the STL file itself (if any)
//var material=new THREE.MeshLambertMaterial({color:0x909090, overdraw: 1, wireframe: false, vertexColors: model.color?THREE.NoColors:THREE.FaceColors}); //if model color is set, ignores face colors set on the STL file itself (if any)
var material=new THREE.MeshLambertMaterial({color:0x909090, wireframe: false, vertexColors: model.color?THREE.NoColors:THREE.FaceColors}); //if model color is set, ignores face colors set on the STL file itself (if any)
if (!_this.is_ie) material.side = THREE.DoubleSide;
if (!model.display) model.display="flat";
_this.set_material_display(model.display, material, geo); //shading (aka display)
@@ -239,7 +271,8 @@ function StlViewer(parent_element_obj, options)
var blob_to_load=null;
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];
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)});
//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), jszip_path:_this.jszip_path});
model_worker.postMessage({msg_type:_this.MSG2WORKER_LOAD});
}
@@ -258,6 +291,7 @@ function StlViewer(parent_element_obj, options)
_this.camera_state=null; //it is one time thingy (next bunch of models will have to set camera state again)
_this.set_light();
_this.set_grid(_this.grid?true:false);
_this.load_session++; //from now on it is a new loading session
@@ -268,6 +302,26 @@ function StlViewer(parent_element_obj, options)
}
}
this.set_grid=function(b, size, divisions)
{
if (_this.grid) _this.scene.remove(_this.grid);
_this.grid=null;
if (b)
{
if (!size) size=Math.max(Math.abs(_this.maxx),Math.abs(_this.minx))*2.5;
if (size<=0)
{
var height = isNaN(window.innerHeight) ? window.clientHeight : window.innerHeight;
var width = isNaN(window.innerWidth) ? window.clientWidth : window.innerWidth;
size=Math.min(height, width)*0.8;
}
if (!divisions) divisions=10;
_this.grid = new THREE.GridHelper(size,divisions);
_this.scene.add(_this.grid);
}
}
this.remove_model = function(model_id)
{
if (_this.models_ref[model_id]===undefined) return _this.model_error("remove_model - id not found: "+model_id);
@@ -275,6 +329,8 @@ function StlViewer(parent_element_obj, options)
var model=_this.models[_this.models_ref[model_id]];
if (!model) return;
_this.set_or_update_geo_edges (model, false);
delete _this.models[_this.models_ref[model_id]];
delete _this.models_ref[model_id];
delete _this.loaded_models_arr[model_id];
@@ -297,14 +353,43 @@ function StlViewer(parent_element_obj, options)
{
if (zoom) _this.zoom=zoom;
if ((_this.zoom_done)&&(!force_zoom)&&(_this.zoom>=0)) //don't do zoom for more than once
//if ((_this.zoom_done)&&(!force_zoom)&&(_this.zoom>=0)) //don't do zoom for more than once
if ((_this.zoom_done)&&(!force_zoom)) //don't do zoom for more than once
return;
_this.zoom_done=true;
var max_dim = Math.max(_this.maxx*2, _this.maxy*2, _this.maxz);
_this.camera.position.set(_this.camera.position.x,_this.camera.position.y,_this.zoom>=0?_this.zoom:(max_dim*1.2*Math.max(1,_this.camera.aspect/2))); //-1 = auto zoom
//var max_dim = Math.max(Math.abs(_this.maxx-_this.minx), Math.abs(_this.maxy-_this.miny), Math.abs(_this.maxz-_this.minz));
var cameraZ=_this.zoom;
if (_this.zoom<0) cameraZ=_this.calc_z_for_auto_zoom(); //-1 = auto zoom
_this.camera.position.set(_this.camera.position.x,_this.camera.position.y,cameraZ);
// set the far plane of the camera so that it easily encompasses the whole object
const minZ = _this.minz;
const cameraToFarEdge = ( minZ < 0 ) ? -minZ + cameraZ : cameraZ - minZ;
_this.camera.far = Math.max(cameraToFarEdge * 3000, _this.camera.far);
_this.camera.updateProjectionMatrix();
//console.log(_this.camera.far);
}
this.calc_z_for_auto_zoom=function (offset)
{
offset = offset || 1.01;
const boundingBox = new THREE.Box3(new THREE.Vector3( _this.minx, _this.miny, _this.minz ), new THREE.Vector3( _this.maxx, _this.maxy, _this.maxz ));
var size = new THREE.Vector3();
boundingBox.getSize(size);
const fov = _this.camera.fov * ( Math.PI / 180 );
const fovh = 2*Math.atan(Math.tan(fov/2) * _this.camera.aspect);
let dx = size.z / 2 + Math.abs( size.x / 2 / Math.tan( fovh / 2 ) );
let dy = size.z / 2 + Math.abs( size.y / 2 / Math.tan( fov / 2 ) );
let cameraZ = Math.max(dx, dy);
cameraZ *= offset;
return cameraZ;
}
//position, up and target vectors (each 3 coors vector) described camera's position
this.get_camera_state=function()
@@ -377,12 +462,14 @@ function StlViewer(parent_element_obj, options)
this.stop_auto_zoom=function ()
{
_this.zoom=_this.camera.position.z;
_this.zoom_done=true;
}
this.set_camera=function (x,y,z)
{
if (y) _this.zoom=y;
_this.camera.position.set(!_this.is_empty(x)?x:_this.camera.position.x,!_this.is_empty(y)?y:_this.camera.position.y,_this.zoom>=0?_this.zoom:Math.max(_this.maxx*3, _this.maxy*3, _this.maxz*3.5));
//_this.camera.position.set(!_this.is_empty(x)?x:_this.camera.position.x,!_this.is_empty(y)?y:_this.camera.position.y,_this.zoom>=0?_this.zoom:Math.max(_this.maxx*3, _this.maxy*3, _this.maxz*3.5));
_this.camera.position.set(!_this.is_empty(x)?x:_this.camera.position.x,!_this.is_empty(y)?y:_this.camera.position.y,!_this.is_empty(z)?z:_this.camera.position.z);
}
this.set_auto_zoom=function()
@@ -451,6 +538,10 @@ function StlViewer(parent_element_obj, options)
//set model custome properties
this.set_model_custom_props = function (model)
{
//units
model.units=model.units?model.units:'mm';
_this.set_model_units(model.id, model.units, true);
//position
model.x=model.x?model.x:0;
model.y=model.y?model.y:0;
@@ -491,7 +582,7 @@ function StlViewer(parent_element_obj, options)
_this.set_or_update_geo_edges (model, true);
//opacity
if (model.opacity)
if (typeof model.opacity !== 'undefined')
this.set_material_opacity(model.mesh.material, model.opacity);
//animation
@@ -499,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);
@@ -523,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)
@@ -578,6 +676,9 @@ function StlViewer(parent_element_obj, options)
if (!model) return;
if (!model.mesh) return;
if (color.length<6) return;
if (color.charAt(0)!='#') color='#'+color;
model.color=color;
_this.update_mesh_color(model.mesh, color, color?false:model.colors);
@@ -618,6 +719,8 @@ function StlViewer(parent_element_obj, options)
this.renderer.setClearColor(0x000000, 0);
else
this.renderer.setClearColor(bg_color, 1);
_this.bg_color=bg_color;
}
this.set_display = function(model_id, display)
@@ -637,10 +740,11 @@ function StlViewer(parent_element_obj, options)
this.set_opacity = function(model_id, opacity)
{
if (_this.models_ref[model_id]===undefined) return _this.model_error("set_display - id not found: "+model_id);
var model=_this.models[_this.models_ref[model_id]];
if (!model) return;
model.opacity=opacity;
this.set_material_opacity(model.mesh.material, opacity);
}
@@ -664,32 +768,40 @@ function StlViewer(parent_element_obj, options)
event.stopPropagation();
event.preventDefault();
//reset click_type: 1=left click, 3=right click, 11=double click, 20=touch
var click_type=event.which;
switch (event.type)
{
case 'touchstart':
click_type=20;
var touch = event.touches[0] || event.changedTouches[0];
_this.mouse.x = ( (touch.pageX-_this.parent_element.offsetLeft) / _this.parent_element.clientWidth ) * 2 - 1;
_this.mouse.y = - ( (touch.pageY-_this.parent_element.offsetTop) / _this.parent_element.clientHeight ) * 2 + 1;
break;
case 'dblclick': //double-click
click_type=11;
default: //click
_this.mouse.x = ( (event.clientX-_this.parent_element.offsetLeft) / _this.parent_element.clientWidth ) * 2 - 1;
_this.mouse.y = - ( (event.clientY-_this.parent_element.offsetTop) / _this.parent_element.clientHeight ) * 2 + 1;
}
_this.raycaster.setFromCamera( _this.mouse, _this.camera );
var intersects = _this.raycaster.intersectObjects( _this.scene.children );
if (intersects.length>0)
{
if (intersects[0].object.model_id===undefined) return;
if (_this.onmousedown_callback)
{
_this.onmousedown_callback(intersects[0].object.model_id, event, intersects[0].distance);
_this.onmousedown_callback(intersects[0].object.model_id, event, intersects[0].distance, click_type);
}
}
else if (_this.send_no_model_click_event)
_this.onmousedown_callback(null, event, 0);
_this.onmousedown_callback(null, event, 0, click_type);
}
//will return if value is empty (null/undefined etc.) and not zero (which is valid)
@@ -698,6 +810,42 @@ function StlViewer(parent_element_obj, options)
return (!a && a !== 0);
}
this.set_model_units = function(model_id, units, scale)
{
if (_this.models_ref[model_id]===undefined) return _this.model_error("set_model_units - id not found: "+model_id);
var model=_this.models[_this.models_ref[model_id]];
if (!model) return;
if (!model.mesh) return;
var scale_factor=1;
switch (units)
{
case 'mm':
if (scale) if (model.units=='inch') scale_factor=1/25.4;
model.units='mm';
break;
case 'inch':
if (scale) if (model.units=='mm') scale_factor=25.4;
model.units='inch';
break;
default:
return _this.model_error("set_model_units - invalid unit "+units);
}
if (scale_factor!=1)
{
_this.set_scale(model.id,
model.scalex*scale_factor,
model.scaley*scale_factor,
model.scalez*scale_factor,
true //keep the current scale, just change the geometry
);
}
}
this.set_position = function(model_id, x,y,z)
{
if (_this.models_ref[model_id]===undefined) return _this.model_error("set_position - id not found: "+model_id);
@@ -729,7 +877,8 @@ function StlViewer(parent_element_obj, options)
case "smooth":
material.wireframe=false;
material.shading=THREE.SmoothShading;
//material.shading=THREE.SmoothShading;
material.flatShading=false;
if (geo)
{
geo.mergeVertices();
@@ -739,7 +888,8 @@ function StlViewer(parent_element_obj, options)
case "flat":
material.wireframe=false;
material.shading=THREE.FlatShading;
//material.shading=THREE.FlatShading;
material.flatShading=true;
if (geo)
geo.computeFlatVertexNormals();
break;
@@ -757,19 +907,19 @@ function StlViewer(parent_element_obj, options)
var c=add_to_current?1:0; //add or set angle
if (axis_x_angel)
if (axis_x_angel!==undefined)
{
model.rotationx=axis_x_angel+model.mesh.rotation.x*c;
model.mesh.rotation.x=model.rotationx;
}
if (axis_y_angel)
if (axis_y_angel!==undefined)
{
model.rotationy=axis_y_angel+model.mesh.rotation.y*c;
model.mesh.rotation.y=model.rotationy;
}
if (axis_z_angel)
if (axis_z_angel!==undefined)
{
model.rotationz=axis_z_angel+model.mesh.rotation.z*c;
model.mesh.rotation.z=model.rotationz;
@@ -795,6 +945,7 @@ function StlViewer(parent_element_obj, options)
return filename.split(/[\\/]/).pop();
}
this.get_model_filename=function(model, no_null, basename, skip_url)
{
var fn=null;
@@ -841,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
}
}
@@ -908,7 +1059,7 @@ function StlViewer(parent_element_obj, options)
return _this.status;
}
this.calc_volume_and_area=function(geo)
this.calc_volume_and_area=function(geo, factor)
{
var x1,x2,x3,y1,y2,y3,z1,z2,z3,i;
var len=geo.faces.length;
@@ -918,15 +1069,15 @@ function StlViewer(parent_element_obj, options)
for (i=0;i<len;i++)
{
x1=geo.vertices[geo.faces[i].a].x;
y1=geo.vertices[geo.faces[i].a].y;
z1=geo.vertices[geo.faces[i].a].z;
x2=geo.vertices[geo.faces[i].b].x;
y2=geo.vertices[geo.faces[i].b].y;
z2=geo.vertices[geo.faces[i].b].z;
x3=geo.vertices[geo.faces[i].c].x;
y3=geo.vertices[geo.faces[i].c].y;
z3=geo.vertices[geo.faces[i].c].z;
x1=geo.vertices[geo.faces[i].a].x*factor;
y1=geo.vertices[geo.faces[i].a].y*factor;
z1=geo.vertices[geo.faces[i].a].z*factor;
x2=geo.vertices[geo.faces[i].b].x*factor;
y2=geo.vertices[geo.faces[i].b].y*factor;
z2=geo.vertices[geo.faces[i].b].z*factor;
x3=geo.vertices[geo.faces[i].c].x*factor;
y3=geo.vertices[geo.faces[i].c].y*factor;
z3=geo.vertices[geo.faces[i].c].z*factor;
totalVolume +=
(-x3 * y2 * z1 +
@@ -936,9 +1087,9 @@ function StlViewer(parent_element_obj, options)
x2 * y1 * z3 +
x1 * y2 * z3);
a=geo.vertices[geo.faces[i].a].distanceTo(geo.vertices[geo.faces[i].b]);
b=geo.vertices[geo.faces[i].b].distanceTo(geo.vertices[geo.faces[i].c]);
c=geo.vertices[geo.faces[i].c].distanceTo(geo.vertices[geo.faces[i].a]);
a=geo.vertices[geo.faces[i].a].distanceTo(geo.vertices[geo.faces[i].b])*factor;
b=geo.vertices[geo.faces[i].b].distanceTo(geo.vertices[geo.faces[i].c])*factor;
c=geo.vertices[geo.faces[i].c].distanceTo(geo.vertices[geo.faces[i].a])*factor;
s=(a+b+c)/2;
totalArea+=Math.sqrt(s*(s-a)*(s-b)*(s-c));
}
@@ -956,8 +1107,8 @@ function StlViewer(parent_element_obj, options)
if (!model.mesh) return null;
if (!model.mesh.geometry) return null;
var vol_and_area=model.mesh.geometry?_this.calc_volume_and_area(model.mesh.geometry):[0,0,0];
return {name:model.filename?model.filename:(model.local_file?model.local_file.name:""), orig_filename:model.orig_filename?model.orig_filename:null, position:{x:model.x, y:model.y, z:model.z}, dims:{x:model.mesh.geometry.maxx-model.mesh.geometry.minx, y:model.mesh.geometry.maxy-model.mesh.geometry.miny, z:model.mesh.geometry.maxz-model.mesh.geometry.minz}, rotation:{x:model.mesh.rotation.x,y:model.mesh.rotation.y,z:model.mesh.rotation.z}, display:model.display?model.display:null, color:model.color?model.color:null, scale:{x:model.scalex,y:model.scaley,z:model.scalez}, volume:vol_and_area[0], area:vol_and_area[1], triangles:vol_and_area[2]};
var vol_and_area=model.mesh.geometry?_this.calc_volume_and_area(model.mesh.geometry, model.units=='inch'?1/25.4:1):[0,0,0];
return {name:model.filename?model.filename:(model.local_file?model.local_file.name:""), orig_filename:model.orig_filename?model.orig_filename:null, position:{x:model.x, y:model.y, z:model.z}, dims:{x:model.mesh.geometry.maxx-model.mesh.geometry.minx, y:model.mesh.geometry.maxy-model.mesh.geometry.miny, z:model.mesh.geometry.maxz-model.mesh.geometry.minz}, rotation:{x:model.mesh.rotation.x,y:model.mesh.rotation.y,z:model.mesh.rotation.z}, display:model.display?model.display:null, color:model.color?model.color:null, scale:{x:model.scalex,y:model.scaley,z:model.scalez}, volume:vol_and_area[0], area:vol_and_area[1], triangles:vol_and_area[2], units:model.units, opacity:model.opacity!==undefined?model.opacity:1};
}
this.get_vsb = function()
@@ -980,18 +1131,20 @@ function StlViewer(parent_element_obj, options)
//get object info in json format
var pos=_this.camera.position;
var data={canvas_height:_this.canvas_height, bg_color:_this.bg_color, camera_state:_this.get_camera_state(), auto_rotate:_this.auto_rotate, mouse_zoom:_this.mouse_zoom, auto_resize:_this.auto_resize, center_models:_this.center_models};
if (_this.grid) data['grid']=1;
data['models']=[];
Object.keys(_this.models_ref).forEach(function(key)
{
var model=_this.models[_this.models_ref[key]];
var info={id:model.id};
var info={id:for_vsb?-1:model.id};
if (for_vsb)
{
//console.log(model);
var curr_filename=_this.get_model_filename(model, true, true, true);
if (curr_filename) info['filename']=curr_filename;
//var curr_filename=_this.get_model_filename(model, true, true, true);
//if (curr_filename) info['filename']=curr_filename;
info['filename']=model.id+'.stl';
}
else
{
@@ -999,11 +1152,13 @@ function StlViewer(parent_element_obj, options)
if (curr_filename) info['filename']=curr_filename;
if (model.local_file) info['local_file']=model.local_file;
}
if (model.x) info['x']=model.x;
if (model.y) info['y']=model.y;
if (model.z) info['z']=model.z;
if (model.display) info['display']=model.display;
if (model.color) info['color']=model.color;
if (!((model.colors)&&(model.color=='#ffffff'))) if (model.color) info['color']=model.color; //if model is colored stl and the color is #FFFFFF, skip color property (cause this is the defauly for colored STL)
if (model.units) info['units']=model.units;
if (model.rotationx) info['rotationx']=model.rotationx;
if (model.rotationy) info['rotationy']=model.rotationy;
if (model.rotationz) info['rotationz']=model.rotationz;
@@ -1048,6 +1203,7 @@ function StlViewer(parent_element_obj, options)
link.download = download_name+'.vsj';
link.click();
URL.revokeObjectURL(link.href);
}
this.load_vsj = function(filename)
@@ -1208,11 +1364,12 @@ function StlViewer(parent_element_obj, options)
this.json_without_nulls=function(arr)
{
return JSON.stringify(arr).split(",null").join("");
return JSON.stringify(arr).split(",null").join("").split("null,").join("");
}
this.download_vsb = function(filename)
this.get_vsb_blob = function()
{
//makes vsb and returns it as a blob
var zip=null;
try
{
@@ -1234,31 +1391,33 @@ function StlViewer(parent_element_obj, options)
zip.file(curr_filename, vsb.files[key].bin);
});
zip.generateAsync({type:"blob"})
.then(function(content)
{
var blob = new Blob([content], {type: "application/zip"});
var link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
var download_name=filename?filename:"1";
var p=download_name.toLowerCase().indexOf('.vsb');
if (p>=0) download_name=download_name.substring( 0, p );
if (download_name.length<1) download_name='1';
if (window.navigator.msSaveOrOpenBlob)
{
//only for IE
window.navigator.msSaveBlob(blob, download_name+'.vsb');
return;
}
return zip.generateAsync({type:"blob"});
}
this.download_vsb = function(filename)
{
_this.get_vsb_blob(filename).then(function(content)
{
var blob = new Blob([content], {type: "application/zip"});
link.download = download_name+'.vsb';
link.click();
});
return;
var link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
var download_name=filename?filename:"1";
var p=download_name.toLowerCase().indexOf('.vsb');
if (p>=0) download_name=download_name.substring( 0, p );
if (download_name.length<1) download_name='1';
if (window.navigator.msSaveOrOpenBlob)
{
//only for IE
window.navigator.msSaveBlob(blob, download_name+'.vsb');
return;
}
link.download = download_name+'.vsb';
link.click();
URL.revokeObjectURL(link.href);
});
}
this.load_vsb = function(filename)
@@ -1295,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);
};
@@ -1350,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);
@@ -1371,7 +1523,7 @@ function StlViewer(parent_element_obj, options)
link.href = window.URL.createObjectURL(blob);
//var download_name=filename?filename:(model.filename?model.filename:(model.local_file?model.local_file.name:"1"));
var download_name=_this.get_model_filename(model,true,true,true);
console.log(download_name);
var p=download_name.toLowerCase().indexOf('.stl');
if (p>=0) download_name=download_name.substring( 0, p );
if (download_name.length<1) download_name='1';
@@ -1385,6 +1537,7 @@ function StlViewer(parent_element_obj, options)
link.download = download_name+'.stl';
link.click();
URL.revokeObjectURL(link.href);
}
this.get_model_mesh = function(model_id)
@@ -1395,7 +1548,10 @@ function StlViewer(parent_element_obj, options)
if (!model) return;
if (!model.mesh) return;
return model.mesh.clone();
var mesh=model.mesh.clone();
mesh.geometry=model.mesh.geometry.clone();
mesh.material=model.mesh.material.clone();
return mesh;
}
@@ -1435,28 +1591,32 @@ function StlViewer(parent_element_obj, options)
this.do_resize = function()
{
if (!_this.parent_element) return;
var r=_this.parent_element.getBoundingClientRect();
//var r=_this.parent_element.children[0].getBoundingClientRect();
var rsize_width=r.width;
var rsize_height=r.height;
_this.camera.aspect = rsize_width / rsize_height;
_this.camera.updateProjectionMatrix();
_this.renderer.setSize(rsize_width-5, rsize_height-5);
//_this.renderer.setSize(rsize_width-5, rsize_height-5);
_this.renderer.setSize(rsize_width, rsize_height);
}
this.animation=new Array();
this.animate = function()
{
if (_this.killsign) return;
Object.keys(_this.animation).forEach(function(key)
{
if (!(_this.models_ref[key]===undefined))
_this.do_model_animation(_this.models[_this.models_ref[key]]);
});
requestAnimationFrame(_this.animate);
_this.renderer.render(_this.scene, _this.camera);
//console.log(_this.camera.position);
requestAnimationFrame(_this.animate);
if (_this.renderer)
_this.renderer.render(_this.scene, _this.camera);
if (_this.controls)
_this.controls.update();
@@ -1687,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);
@@ -1759,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;
@@ -1783,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);
}
}
@@ -1891,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]});
@@ -1906,25 +2072,41 @@ function StlViewer(parent_element_obj, options)
this.clean = function()
{
_this.models=null;_this.models=[];
_this.models_count=0;
_this.models_ref=null;_this.models_ref=[];
_this.max_model_id=0;
_this.load_status=null;_this.load_status=[];
_this.load_session=0;
_this.loaded_models_arr=null;_this.loaded_models_arr=[];
_this.animation=null;_this.animation=[];
_this.models_to_add=null;_this.models_to_add=[];
_this.options.models=null;
if (!_this.scene) return;
var scene=_this.scene;
i=scene.children.length;
while (i--)
{
if (scene.children[i].type==='Mesh')
{
scene.children[i].geometry.dispose();
scene.children[i].material.dispose();
scene.remove(scene.children[i]);
}
}
_this.camera.position.set(_this.camerax,_this.cameray,_this.cameraz);
//remove edges for each model
Object.keys(_this.models_ref).forEach(function(key)
{
_this.set_or_update_geo_edges (_this.models[_this.models_ref[key]], false);
});
_this.models=new Array();
_this.models_count=0;
_this.models_ref=new Array();
_this.max_model_id=0;
_this.load_status=new Array();
_this.load_session=0;
_this.loaded_models_arr=new Array();
_this.animation=new Array();
//_this.camera.position.set(_this.camerax,_this.cameray,_this.cameraz);
_this.renderer.renderLists.dispose();
}
this.reset_parent_element=function(parent_element_obj)
@@ -1951,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)
@@ -1970,13 +2155,63 @@ function StlViewer(parent_element_obj, options)
}
_this.options=data;
_this.set_options();
if (_this.ready)
_this.init();
}
this.dispose = function()
{
//clean object's references (and let brower's GC do it work)
_this.clean();
_this.killsign=true; //not to render anymore
if (_this.renderer) //one last render (to clean it all)
_this.renderer.render(_this.scene, _this.camera);
if (_this.controls)
_this.controls.update();
_this.animate=null;
_this.animation=null;
_this.error=null;
_this.options=null;
_this.parent_element=null;
_this.models_to_add=null;
_this.models=null;
_this.models_ref=null;
_this.model_loaded_callback=null;
_this.all_loaded_callback=null;
_this.load_error_callback=null;
_this.loading_progress_callback=null;
_this.load_status=null;
_this.loaded_models_arr=null;
_this.onmousedown_callback=null;
_this.camera_state=null;
_this.ready_callback=null;
_this.on_model_drop=null;
_this.pre_loaded_ab_files=null;
_this.pre_loaded_vsj=null;
_this.grid=null;
_this.WORLD_X_VECTOR=null;
_this.WORLD_Y_VECTOR=null;
_this.WORLD_Z_VECTOR=null;
_this.edges_material=null;
_this.raycaster=null;
_this.mouse =null;
_this.renderer = null;
_this.scene = null;
_this.camera = null;
_this.ambientLight = null;
_this.directionalLight = null;
_this.pointLight = null;
_this.controls = null;
}
//constructor
_this.set_options();