// ----------------------------------------------------------------------------- // Variables // ----------------------------------------------------------------------------- //window.localStorage.clear(); var settings = new SLAcer.Settings({ file: { panel: { collapsed: false, position : 0 } }, mesh: { color: 0x333333, panel: { collapsed: false, position : 1 } }, slicer: { layers: { height: 100 // μm }, panel: { collapsed: false, position : 2 } }, buildVolume: { size : { x: 100, y: 100, z: 100 }, // mm unit : 'mm', // mm or in color : 0xcccccc, opacity : 0.1, panel : { collapsed: false, position : 3 } }, resin: { density : 1.1, // g/cm3 price : 50, // $ panel : { collapsed: false, position : 4 } }, screen: { width : 1680, height : 1050, diagonal : { size: 22, unit: 'in' }, panel : { collapsed: false, position : 5 } }, viewer3d: { color: 0xffffff } }); // ----------------------------------------------------------------------------- // Error handler // ----------------------------------------------------------------------------- function errorHandler(error) { console.error(error); } // ----------------------------------------------------------------------------- // Slicer // ----------------------------------------------------------------------------- var slicer = new SLAcer.Slicer(); var shapes; function removeShapes() { if (shapes && shapes.length) { for (var i = 0, il = shapes.length; i < il; i++) { viewer3d.removeObject(shapes[i]); } } } function slice(layerNumber) { // remove old shapes removeShapes(); if (layerNumber < 1) { viewer3d.render(); return; } // get slice var layerHeight = settings.get('slicer.layers.height') / 1000; var zPosition = layerNumber * layerHeight; var slice = slicer.getFaces(zPosition); //console.log('layer number:', layerNumber); //console.log('z position :', zPosition); // get new shapes list shapes = slice.shapes; zPosition -= settings.get('buildVolume.size.z') / 2; // add new shapes for (var i = 0, il = shapes.length; i < il; i++) { shapes[i].position.z = zPosition; shapes[i].material.depthTest = false; viewer3d.scene.add(shapes[i]); } // render viewer3d.render(); } // ----------------------------------------------------------------------------- // UI // ----------------------------------------------------------------------------- // Main container var $main = $('#main'); // Viewer 3D var $viewer3d = $('#viewer3d'); var viewer3d = new SLAcer.Viewer3D({ color : settings.get('viewer3d.color'), buildVolume: settings.get('buildVolume'), target : $viewer3d[0] }); // Triangulation algorithm //THREE.Triangulation.setTimer(true); THREE.Triangulation.setLibrary('earcut'); //THREE.Triangulation.setLibrary('libtess'); //THREE.Triangulation.setLibrary('poly2tri'); // Slider var $sliderInput = $('#slider input'); $sliderInput.slider({ reversed : true }).on('change', function(e) { slice(e.value.newValue); }); var $sliderElement = $('#slider .slider'); var $sliderMinValue = $('#slider .min'); var $sliderMaxValue = $('#slider .max'); // Sidebar var $sidebar = $('#sidebar'); var $panels = $sidebar.find('.panel'); $sidebar.sortable({ axis : 'y', handle : '.panel-heading', cancel : '.panel-toggle', placeholder: 'panel-placeholder', forcePlaceholderSize: true, // update panel position stop: function(e, ui) { $sidebar.find('.panel').each(function(i, element) { settings.set(_.camelCase(element.id) + '.panel.position', i); }); } }); // Sort panels var panels = []; var panel; _.forEach(settings.settings, function(item, namespace) { if (item && item.panel) { panels[item.panel.position] = $('#' + _.kebabCase(namespace)); } }); for (var i in panels) { $sidebar.append(panels[i]); } // Init panel function initPanel(name) { var id = _.kebabCase(name); var name = _.camelCase(name); var $body = $('#' + id + '-body'); $body.on('hidden.bs.collapse', function () { settings.set(name + '.panel.collapsed', true); }); $body.on('shown.bs.collapse', function () { settings.set(name + '.panel.collapsed', false); }); if (settings.get(name + '.panel.collapsed')) { $body.collapse('hide'); } return $body; } // Unit converter function parseUnit(value, unit) { return parseFloat(unit == 'in' ? (value / 25.4) : (value * 25.4)); } // File panel var $fileBody = initPanel('file'); // Mesh panel var $meshBody = initPanel('mesh'); var $meshFaces = $meshBody.find('#mesh-faces'); var $meshVolume = $meshBody.find('#mesh-volume'); var $meshWeight = $meshBody.find('#mesh-weight'); var $meshSizeX = $meshBody.find('#mesh-size-x'); var $meshSizeY = $meshBody.find('#mesh-size-y'); var $meshSizeZ = $meshBody.find('#mesh-size-z'); var $meshSizeUnit = $meshBody.find('.mesh-size-unit'); function updateMeshInfoUI(mesh) { var size = mesh.getSize(); var unit = settings.get('buildVolume.unit'); var layersHeight = settings.get('slicer.layers.height') / 1000; var layersNumber = Math.ceil(size.z / layersHeight); $sliderInput.slider('setAttribute', 'max', layersNumber); $sliderMaxValue.html(layersNumber); $meshSizeUnit.html(unit); if (unit == 'in') { size.x = parseUnit(size.x, 'in'); size.y = parseUnit(size.y, 'in'); size.z = parseUnit(size.z, 'in'); } $meshSizeX.html(size.x.toFixed(2)); $meshSizeY.html(size.y.toFixed(2)); $meshSizeZ.html(size.z.toFixed(2)); $meshFaces.html(mesh.geometry.faces.length); $meshVolume.html(parseInt(mesh.getVolume() / 1000)); // cm3/ml $meshWeight.html(0); } // Slicer panel var $slicerBody = initPanel('slicer'); // Build volume panel var $buildVolumeBody = initPanel('buildVolume'); var $buildVolumeX = $buildVolumeBody.find('#build-volume-x'); var $buildVolumeY = $buildVolumeBody.find('#build-volume-y'); var $buildVolumeZ = $buildVolumeBody.find('#build-volume-z'); function updateBuildVolumeUI() { var buildVolume = settings.get('buildVolume'); $buildVolumeX.val(buildVolume.size.x); $buildVolumeY.val(buildVolume.size.y); $buildVolumeZ.val(buildVolume.size.z); } function updateBuildVolumeSettings() { var unit = $('#build-volume input[type=radio]:checked').val(); if (unit != settings.get('buildVolume.diagonal.unit')) { var size = settings.get('buildVolume.size'); $buildVolumeX.val(parseUnit(size.x, unit)); $buildVolumeY.val(parseUnit(size.y, unit)); $buildVolumeZ.val(parseUnit(size.z, unit)); } settings.set('buildVolume', { size: { x: $buildVolumeX.val(), y: $buildVolumeY.val(), z: $buildVolumeZ.val() }, unit: unit }); if (size) { updateMeshInfoUI(slicer.mesh); } } $('#build-volume-unit-' + settings.get('buildVolume.unit')).prop('checked', true); $('#build-volume input[type=radio]').on('change', updateBuildVolumeSettings); $('#build-volume input').on('input', updateBuildVolumeSettings); updateBuildVolumeUI(); // Resin panel var $resinBody = initPanel('resin'); var $resinPrice = $resinBody.find('#resin-price'); var $resinDensity = $resinBody.find('#resin-density'); function updateResinUI() { var resin = settings.get('resin'); $resinDensity.val(resin.density); $resinPrice.val(resin.price); } function updateResinSettings() { settings.set('resin.price' , $resinPrice.val()); settings.set('resin.density', $resinDensity.val()); } $('#resin input').on('input', updateResinSettings); updateResinUI(); // Screen var $screenBody = initPanel('screen'); var $screenWidth = $screenBody.find('#screen-width'); var $screenHeight = $screenBody.find('#screen-height'); var $screenDiagonalSize = $screenBody.find('#screen-diagonal-size'); function updateScreenUI() { var screen = settings.get('screen'); $screenWidth.val(screen.width); $screenHeight.val(screen.height); $screenDiagonalSize.val(screen.diagonal.size); } function updateScreenSettings() { var unit = $('#screen input[type=radio]:checked').val(); if (unit != settings.get('screen.diagonal.unit')) { $screenDiagonalSize.val( parseUnit(settings.get('screen.diagonal.size'), unit) ); } settings.set('screen', { width : $screenWidth.val(), height : $screenHeight.val(), diagonal: { size: $screenDiagonalSize.val(), unit: unit } }); } $('#screen-diagonal-unit-' + settings.get('screen.diagonal.unit')).prop('checked', true); $('#screen input[type=radio]').on('change', updateScreenSettings); $('#screen input').on('input', updateScreenSettings); updateScreenUI(); // UI resize function resizeUI() { var width = $main.width(); var height = $main.height(); $sliderElement.height(height - 80); viewer3d.setSize({ width : width, height: height }); viewer3d.render(); } $(window).resize(resizeUI); resizeUI(); // ----------------------------------------------------------------------------- // STL loader // ----------------------------------------------------------------------------- // Loader instance var loader = new MeshesJS.STLLoader($main[0]); // drop target // On Geometry loaded loader.onGeometry = function(geometry) { try { // remove old mesh and plane slicer.mesh && viewer3d.removeObject(slicer.mesh); // remove old shapes removeShapes(); // load new mesh in slicer slicer.loadMesh(new SLAcer.Mesh(geometry, new THREE.MeshPhongMaterial({ color: settings.get('mesh.color') }))); // add new mesh and render view viewer3d.addObject(slicer.mesh); viewer3d.render(); // update mesh info updateMeshInfoUI(slicer.mesh); // get first slice //slice(1); } catch(e) { errorHandler(e); } }; // On loading error loader.onError = errorHandler; // ----------------------------------------------------------------------------- // load example // ----------------------------------------------------------------------------- // example STL file //var stl = 'stl/Octocat-v2.stl'; var stl = 'stl/StressTest.stl'; // File url var url = 'http://' + window.location.hostname + window.location.pathname + stl; // Create http request object var xmlhttp = new XMLHttpRequest(); // Get the file contents xmlhttp.open("GET", url); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == XMLHttpRequest.DONE) { if(xmlhttp.status == 200){ loader.loadString(xmlhttp.responseText); }else{ errorHandler('xmlhttp: ' + xmlhttp.statusText); } } } xmlhttp.send();