Permalink
Cannot retrieve contributors at this time
Fetching contributors…

// ----------------------------------------------------------------------------- | |
// Variables | |
// ----------------------------------------------------------------------------- | |
//window.localStorage.clear(); | |
var settings = new SLAcer.Settings({ | |
file: { | |
panel: { | |
collapsed: false, | |
position : 0 | |
} | |
}, | |
slicer: { | |
layers: { | |
height: 100 // μm | |
}, | |
light: { | |
on : 1000, | |
off: 500 | |
}, | |
zip: true, | |
svg: false, | |
png: true, | |
speed: false, | |
speedDelay: 10, // ms | |
panel: { | |
collapsed: false, | |
position : 1 | |
}, | |
lifting: { | |
speed : 50, // mm/min | |
height: 3, // mm | |
} | |
}, | |
mesh: { | |
panel: { | |
collapsed: false, | |
position : 2 | |
} | |
}, | |
transform: { | |
panel: { | |
collapsed: false, | |
position : 3 | |
}, | |
mirror: false | |
}, | |
buildVolume: { | |
size : { x: 100, y: 100, z: 100 }, // mm | |
unit : 'mm', // mm or in | |
color : 0xcccccc, | |
opacity : 0.1, | |
panel : { | |
collapsed: false, | |
position : 4 | |
} | |
}, | |
resin: { | |
density : 1.1, // g/cm3 | |
price : 50, // $ | |
panel : { | |
collapsed: false, | |
position : 5 | |
} | |
}, | |
screen: { | |
width : window.screen.width, | |
height : window.screen.height, | |
diagonal : { size: 22, unit: 'in' }, | |
panel : { | |
collapsed: false, | |
position : 6 | |
} | |
}, | |
colors: { | |
mesh : '#eb0984', | |
slice: '#88ee11', | |
panel: { | |
collapsed: false, | |
position : 7 | |
} | |
}, | |
viewer3d: { | |
color: 0xffffff | |
} | |
}); | |
// ----------------------------------------------------------------------------- | |
// Error handler | |
// ----------------------------------------------------------------------------- | |
function errorHandler(error) { | |
console.error(error); | |
} | |
// ----------------------------------------------------------------------------- | |
// Slicer | |
// ----------------------------------------------------------------------------- | |
var slicer = new SLAcer.Slicer(); | |
var shapes, slices; | |
function removeShapes() { | |
if (shapes && shapes.length) { | |
for (var i = 0, il = shapes.length; i < il; i++) { | |
viewer3d.removeObject(shapes[i]); | |
} | |
} | |
} | |
function removeSlices() { | |
if (slices && slices.length) { | |
for (var i = 0, il = slices.length; i < il; i++) { | |
viewer2d.removeObject(slices[i]); | |
} | |
} | |
sliceImage('none'); | |
} | |
function hexToDec(hex) { | |
return parseInt(hex.toString().replace('#', ''), 16); | |
} | |
function sliceImage(dataURL) { | |
settings.set('slice.dataURL', dataURL || 'none'); | |
} | |
function getSlice(layerNumber) { | |
// remove old shapes | |
removeSlices(); | |
removeShapes(); | |
// ... | |
$slicerLayerValue.html(layerNumber); | |
if (layerNumber < 1) { | |
viewer2d.render(); | |
viewer3d.render(); | |
return; | |
} | |
if (transformations.update) { | |
throw 'transformations not applyed...'; | |
} | |
// get position | |
layerHeight = settings.get('slicer.layers.height') / 1000; | |
zPosition = layerNumber * layerHeight; | |
// get faces | |
var faces = slicer.getFaces(zPosition); | |
//console.log('layer number:', layerNumber); | |
//console.log('z position :', zPosition); | |
// get new shapes list | |
shapes = faces.meshes; | |
zPosition -= viewer3d.buildVolume.size.z / 2; | |
// slices | |
slices = []; | |
var slice, shape; | |
var sliceColor = hexToDec(settings.get('colors.slice')); | |
// add new shapes | |
for (var i = 0, il = shapes.length; i < il; i++) { | |
shape = shapes[i]; | |
slice = shape.clone(); | |
slice.material = slice.material.clone(); | |
slice.material.color.setHex(0xffffff); | |
viewer2d.addObject(slice); | |
slices.push(slice); | |
shape.material.color.setHex(sliceColor); | |
shape.material.depthTest = false; | |
shape.position.z = zPosition; | |
viewer3d.scene.add(shape); | |
} | |
// render 3D view | |
viewer3d.render(); | |
// render 2D view | |
viewer2d.screenshot(function(dataURL) { | |
sliceImage(dataURL); | |
if (PNGExport) { | |
var fileName = layerNumber + '.png'; | |
var imgData = dataURL.substr(dataURL.indexOf(',') + 1); | |
zipFolder.file(fileName, imgData, { base64: true }); | |
} | |
}); | |
// SVG export | |
if (SVGExport) { | |
// offset for positive coords | |
var xOffset = Math.abs(slicer.mesh.geometry.boundingBox.min.x); | |
var yOffset = Math.abs(slicer.mesh.geometry.boundingBox.min.y); | |
function SVGPath(actions, dir) { | |
var path = []; | |
var area = 0; | |
var a, b, c, x, y; | |
if (dir != 'ccw') { | |
dir = 'cw'; | |
} | |
actions.push({ action: 'lineTo', args: actions[0].args }); | |
for (var i = 0, il = actions.length; i < il; i++) { | |
a = actions[i]; | |
if (i > 0) { | |
b = actions[i - 1]; | |
area += a.args[0] * b.args[1]; | |
area -= b.args[0] * a.args[1]; | |
} | |
c = a.action == 'moveTo' ? 'M' : 'L'; | |
x = (a.args[0] + xOffset); | |
y = (a.args[1] + yOffset); | |
path.push(c + x + ' ' + y); | |
} | |
if (dir == 'cw' && area < 0 || dir == 'ccw' && area > 0) { | |
path = path.reverse(); | |
path[0] = path[0].replace(/^L/, 'M'); | |
path[actions.length - 1] = path[actions.length - 1].replace(/^M/, 'L'); | |
} | |
return path.join(' ') + ' Z '; | |
} | |
// svg start | |
var size = slicer.mesh.getSize(); | |
var svg = '<svg version="1.1" width="' + size.x + '" height="' + size.y + '" baseProfile="full" xmlns="http://www.w3.org/2000/svg">\n'; | |
// svg background | |
svg += '<rect x="0" y="0" width="100%" height="100%" style="fill:black; stroke:none"/>\n'; | |
// fix coordinate system (rotate 180°) | |
svg += '<g transform="translate(0, ' + size.y + ') scale(1, -1)">\n'; | |
// draw main paths | |
var actions; | |
for (var i = 0, il = faces.shapes.length; i < il; i++) { | |
actions = faces.shapes[i].actions; | |
// path start | |
svg += '<path d="'; | |
// main paths | |
svg += SVGPath(actions, 'cw'); | |
// holes paths | |
var holes = faces.shapes[i].holes; | |
for (var j = 0, jl = holes.length; j < jl; j++) { | |
svg += SVGPath(holes[j].actions, 'ccw'); | |
} | |
// path end | |
svg += '" style="fill:white; stroke:none" />\n'; | |
} | |
// svg end | |
svg += '</g>'; | |
svg += '</svg>'; | |
// add svg file to zip | |
zipFolder.file(layerNumber + '.svg', svg); | |
} | |
} | |
// ----------------------------------------------------------------------------- | |
// 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'); | |
// Viewer 2D | |
var viewer2dWin = null; | |
var $openViewer2D = $('#open-viewer-2d'); | |
var viewer2d = new SLAcer.Viewer2D({ | |
target : null, // off-screen | |
color : 0x000000, | |
buildPlate : { | |
size : settings.get('buildVolume.size'), | |
unit : settings.get('buildVolume.unit'), | |
color : 0x000000, | |
opacity: 0 // hide build plate | |
}, | |
size: settings.get('screen') | |
}); | |
$openViewer2D.click(function(e) { | |
if (viewer2dWin == null || viewer2dWin.closed) { | |
var screen = settings.get('screen'); | |
var size = 'width=' + screen.width + ', height=' + screen.height; | |
var opts = 'menubar=0, toolbar=0, location=0, directories=0, personalbar=0, status=0, resizable=1, dependent=0' | |
viewer2dWin = window.open('viewer2d.html', 'SLAcerViewer2D', size + ', ' + opts); | |
$(viewer2dWin).on('beforeunload', function(e) { | |
viewer2dWin = null; | |
}) | |
.load(function(e) { | |
getSlice($sliderInput.slider('getValue')); | |
}); | |
} | |
else { | |
viewer2dWin.focus(); | |
} | |
return false; | |
}); | |
// Slider | |
var $sliderInput = $('#slider input'); | |
$sliderInput.slider({ reversed : true }).on('change', function(e) { | |
getSlice(e.value.newValue); | |
}); | |
var $sliderElement = $('#slider .slider'); | |
var $sliderMaxValue = $('#slider .max'); | |
function updateSliderUI() { | |
var layersHeight = settings.get('slicer.layers.height') / 1000; | |
var layersNumber = Math.floor(slicer.mesh.getSize().z / layersHeight); | |
$sliderInput.slider('setAttribute', 'max', layersNumber); | |
$sliderMaxValue.html(layersNumber); | |
$slicerLayersValue.html(layersNumber); | |
} | |
// 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'); | |
var $fileInput = $fileBody.find('#file-input'); | |
var loadedFile = null; | |
$fileInput.on('change', function(e) { | |
resetTransformValues(); | |
loadedFile = e.target.files[0]; | |
loader.loadFile(loadedFile); | |
}); | |
// 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 $meshCost = $meshBody.find('#mesh-cost'); | |
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() { | |
var mesh = slicer.mesh; | |
var size = mesh.getSize(); | |
var unit = settings.get('buildVolume.unit'); | |
updateSliderUI(); | |
$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)); | |
var volume = parseInt(mesh.getVolume() / 1000); // cm3/ml | |
var weight = (volume * settings.get('resin.density')).toFixed(2); // g | |
var cost = volume * settings.get('resin.price') / 1000; | |
$meshFaces.html(mesh.geometry.faces.length); | |
$meshVolume.html(volume); | |
$meshWeight.html(weight); | |
$meshCost.html(cost); | |
} | |
// Slicer panel | |
var $slicerBody = initPanel('slicer'); | |
var $slicerLayerHeight = $slicerBody.find('#slicer-layers-height'); | |
var $slicerLayersValue = $slicerBody.find('#slicer-layers-value'); | |
var $slicerLayerValue = $slicerBody.find('#slicer-layer-value'); | |
var $slicerLightOff = $slicerBody.find('#slicer-light-off'); | |
var $slicerLightOn = $slicerBody.find('#slicer-light-on'); | |
var $slicerLiftingSpeed = $slicerBody.find('#slicer-lifting-speed'); | |
var $slicerLiftingHeight = $slicerBody.find('#slicer-lifting-height'); | |
var $slicerExportPNG = $slicerBody.find('#slicer-image-extension-png'); | |
var $slicerExportSVG = $slicerBody.find('#slicer-image-extension-svg'); | |
var $slicerSpeedYes = $slicerBody.find('#slicer-speed-yes'); | |
var $slicerSpeedNo = $slicerBody.find('#slicer-speed-no'); | |
var $slicerSpeedDelay = $slicerBody.find('#slicer-speed-delay'); | |
var $slicerMakeZipYes = $slicerBody.find('#slicer-make-zip-yes'); | |
var $slicerMakeZipNo = $slicerBody.find('#slicer-make-zip-no'); | |
var $sliceButton = $sidebar.find('#slice-button'); | |
var $abortButton = $sidebar.find('#abort-button'); | |
var $zipButton = $sidebar.find('#zip-button'); | |
function updateSlicerUI() { | |
var slicer = settings.get('slicer'); | |
$slicerSpeedDelay.val(slicer.speedDelay); | |
$slicerLayerHeight.val(slicer.layers.height); | |
$slicerLightOff.val(slicer.light.off); | |
$slicerLightOn.val(slicer.light.on); | |
$slicerLiftingSpeed.val(slicer.lifting.speed); | |
$slicerLiftingHeight.val(slicer.lifting.height); | |
} | |
function updateSlicerSettings() { | |
settings.set('slicer.layers.height', $slicerLayerHeight.val()); | |
settings.set('slicer.light.off', $slicerLightOff.val()); | |
settings.set('slicer.light.on', $slicerLightOn.val()); | |
settings.set('slicer.lifting.speed', $slicerLiftingSpeed.val()); | |
settings.set('slicer.lifting.height', $slicerLiftingHeight.val()); | |
settings.set('slicer.png', $slicerExportPNG[0].checked); | |
settings.set('slicer.svg', $slicerExportSVG[0].checked); | |
settings.set('slicer.zip', $slicerMakeZipYes[0].checked); | |
settings.set('slicer.speed', $slicerSpeedYes[0].checked); | |
settings.set('slicer.speedDelay', $slicerSpeedDelay.val()); | |
getSlice($sliderInput.slider('getValue')); | |
updateSliderUI(); | |
} | |
var sliceInterval; | |
var expectedSliceInterval; | |
var currentSliceNumber; | |
var slicesNumber; | |
var zipFile; | |
var zipFolder; | |
var SVGExport; | |
var PNGExport; | |
var layerHeight; | |
var zPosition; | |
var exposureTime; | |
var liftingSpeed; | |
var liftingHeight; | |
var liftingTime; | |
var estimatedTime; | |
function slice() { | |
currentSliceNumber++; | |
if (currentSliceNumber > slicesNumber) { | |
return endSlicing(); | |
} | |
getSlice(currentSliceNumber); | |
$sliderInput.slider('setValue', currentSliceNumber); | |
var time = Date.now(); | |
var diff = time - expectedSliceInterval; | |
!settings.get('slicer.speed') && viewer2dWin && setTimeout(function() { | |
sliceImage('none'); | |
}, settings.get('slicer.light.on')); | |
expectedSliceInterval += sliceInterval; | |
setTimeout(slice, Math.max(0, sliceInterval - diff)); | |
} | |
function endSlicing() { | |
sliceImage('none'); | |
$sidebar.find('input, button').prop('disabled', false); | |
$sliderInput.slider('enable'); | |
$abortButton.addClass('hidden'); | |
$sliceButton.removeClass('hidden'); | |
$zipButton.prop('disabled', !zipFile); | |
} | |
function startSlicing() { | |
var times = settings.get('slicer.light'); | |
if (settings.get('slicer.speed')) { | |
sliceInterval = parseInt(settings.get('slicer.speedDelay')); | |
} | |
else { | |
sliceInterval = parseInt(times.on) + parseInt(times.off); | |
} | |
expectedSliceInterval = Date.now() + sliceInterval; | |
slicesNumber = parseInt($slicerLayersValue.html()); | |
currentSliceNumber = 0; | |
zipFile = null; | |
zipFolder = null; | |
SVGExport = null; | |
PNGExport = null; | |
if (settings.get('slicer.zip')) { | |
zipFile = new JSZip(); | |
zipFolder = zipFile.folder('slices'); | |
zipFile.file("README.txt", 'Generated by SLAcer.js\r\nhttp://lautr3k.github.io/SLAcer.js/\r\n'); | |
zipFile.file("slacer.json", JSON.stringify({ | |
imageExtension: settings.get('slicer.png') ? 'png' : 'svg', | |
imageDirectory: 'slices', | |
screenWidth : settings.get('screen.width'), | |
screenHeight : settings.get('screen.height'), | |
screenSize : settings.get('screen.diagonal.size'), | |
screenUnit : settings.get('screen.diagonal.unit'), | |
layersNumber : slicesNumber, | |
layersHeight : settings.get('slicer.layers.height') / 1000, // mm | |
exposureTime : parseInt(settings.get('slicer.light.on')), // ms | |
liftingSpeed : parseInt(settings.get('slicer.lifting.speed')), // mm/min | |
liftingHeight : parseInt(settings.get('slicer.lifting.height')) // mm | |
}, null, 2)); | |
SVGExport = settings.get('slicer.svg'); | |
PNGExport = settings.get('slicer.png'); | |
} | |
slicesNumber && slice(); | |
} | |
$zipButton.on('click', function(e) { | |
if (zipFile) { | |
var name = 'SLAcer'; | |
if (loadedFile && loadedFile.name) { | |
name = loadedFile.name; | |
} | |
saveAs(zipFile.generate({type: 'blob'}), name + '.zip'); | |
} | |
}); | |
$sliceButton.on('click', function(e) { | |
$sidebar.find('input, button').prop('disabled', true); | |
$('.panel-heading button').prop('disabled', false); | |
$openViewer2D.prop('disabled', false); | |
$sliderInput.slider('disable'); | |
$abortButton.prop('disabled', false); | |
$abortButton.removeClass('hidden'); | |
$sliceButton.addClass('hidden'); | |
startSlicing(); | |
}); | |
$abortButton.on('click', function(e) { | |
currentSliceNumber = slicesNumber + 1; | |
endSlicing(); | |
}); | |
$('#slicer-image-extension-' + (settings.get('slicer.png') ? 'png' : 'svg')).prop('checked', true); | |
$('#slicer-make-zip-' + (settings.get('slicer.zip') ? 'yes' : 'no')).prop('checked', true); | |
$('#slicer-speed-' + (settings.get('slicer.speed') ? 'yes' : 'no')).prop('checked', true); | |
$('#slicer input').on('input, change', updateSlicerSettings); | |
updateSlicerUI(); | |
// 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); | |
updateBuildVolumeSizeStep(); | |
} | |
function updateBuildVolumeSizeStep() { | |
var step = (settings.get('buildVolume.unit') == 'in') ? 0.01 : 1; | |
$buildVolumeX.prop('step', step); | |
$buildVolumeY.prop('step', step); | |
$buildVolumeZ.prop('step', step); | |
} | |
function updateBuildVolumeSettings() { | |
var unit = $('#build-volume input[type=radio]:checked').val(); | |
if (unit != settings.get('buildVolume.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 | |
}); | |
viewer3d.setBuildVolume(settings.get('buildVolume')); | |
slicer.mesh && viewer3d.dropObject(slicer.mesh); | |
viewer3d.render(); | |
size && updateMeshInfoUI(); | |
updateBuildVolumeSizeStep(); | |
getSlice($sliderInput.slider('getValue')); | |
} | |
$('#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()); | |
updateMeshInfoUI(); | |
} | |
$('#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'); | |
var $screenDotPitch = $screenBody.find('#screen-dot-pitch'); | |
function updateScreenUI() { | |
var screen = settings.get('screen'); | |
$screenWidth.val(screen.width); | |
$screenHeight.val(screen.height); | |
$screenDiagonalSize.val(screen.diagonal.size); | |
$screenDotPitch.html(viewer2d.dotPitch.toFixed(2)); | |
viewer2d.setScreenResolution(screen); | |
} | |
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 | |
} | |
}); | |
viewer2d.setScreenResolution(settings.get('screen')); | |
$screenDotPitch.html(viewer2d.dotPitch.toFixed(2)); | |
getSlice($sliderInput.slider('getValue')); | |
} | |
$('#screen-diagonal-unit-' + settings.get('screen.diagonal.unit')).prop('checked', true); | |
$('#screen input[type=radio]').on('change', updateScreenSettings); | |
$('#screen input').on('input', updateScreenSettings); | |
updateScreenUI(); | |
// Colors | |
var $colorsBody = initPanel('colors'); | |
var $meshColor = $colorsBody.find('#mesh-color'); | |
var $sliceColor = $colorsBody.find('#slice-color'); | |
function updateColorsUI() { | |
var colors = settings.get('colors'); | |
$meshColor.val(colors.mesh); | |
$sliceColor.val(colors.slice); | |
} | |
updateColorsUI(); | |
$meshColor.colorpicker({ format: 'hex' }); | |
$sliceColor.colorpicker({ format: 'hex' }); | |
$meshColor.colorpicker().on('changeColor.colorpicker', function(e) { | |
if (slicer.mesh && slicer.mesh.material) { | |
var hexString = e.color.toHex(); | |
var hexInteger = hexToDec(hexString); | |
settings.set('colors.mesh', hexString); | |
slicer.mesh.material.color.setHex(hexInteger); | |
viewer3d.render(); | |
} | |
}); | |
$sliceColor.colorpicker().on('changeColor.colorpicker', function(e) { | |
if (shapes && shapes.length) { | |
var hexString = e.color.toHex(); | |
var hexInteger = hexToDec(hexString); | |
settings.set('colors.slice', hexString); | |
for (var i = 0, il = shapes.length; i < il; i++) { | |
shapes[i].material.color.setHex(hexInteger); | |
} | |
viewer3d.render(); | |
} | |
}); | |
// Alert | |
var $alertPanel = $('#alert'); | |
var $alertMessage = $alertPanel.find('.message'); | |
// Transform | |
var $transformBody = initPanel('transform'); | |
var $transformAction = $transformBody.find('#transform-action'); | |
var $transformUniform = $transformBody.find('#transform-uniform'); | |
var $transformX = $transformBody.find('#transform-x'); | |
var $transformY = $transformBody.find('#transform-y'); | |
var $transformZ = $transformBody.find('#transform-z'); | |
var $transformButtons = $transformBody.find('button'); | |
var $transformMirrorYes = $transformBody.find('#transform-mirror-yes'); | |
var $transformMirrorNo = $transformBody.find('#transform-mirror-no'); | |
var transformAction, transformations; | |
function resetTransformValues() { | |
transformAction = 'scale'; | |
transformations = { | |
scale : { x:1, y:1 , z:1 }, | |
rotate : { x:0, y:0 , z:0 }, | |
translate: { x:0, y:0 , z:0 } | |
}; | |
updateTransformAction(); | |
} | |
function updateTransformAction() { | |
transformAction = $transformAction.val(); | |
var axis = transformations[transformAction]; | |
var min, max, step; | |
if (transformAction == 'scale') { | |
min = 0.01; | |
max = 999; | |
step = 0.01; | |
} | |
else if (transformAction == 'rotate') { | |
min = 0; | |
max = 360; | |
step = 1; | |
} | |
else { | |
min = -9999; | |
max = 9999; | |
step = 1; | |
} | |
$transformUniform.toggleClass('hidden', transformAction != 'scale'); | |
$transformZ.parent().toggleClass('hidden', transformAction == 'translate'); | |
$transformX.prop('min', min); | |
$transformY.prop('min', min); | |
$transformZ.prop('min', min); | |
$transformX.prop('max', max); | |
$transformY.prop('max', max); | |
$transformZ.prop('max', max); | |
$transformX.prop('step', step); | |
$transformY.prop('step', step); | |
$transformZ.prop('step', step); | |
$transformX.val(axis.x); | |
$transformY.val(axis.y); | |
$transformZ.val(axis.z); | |
} | |
function updateTransformValues() { | |
var current = transformations[transformAction]; | |
var uniform = $('#transform-uniform input[type=radio]:checked').val() == 'yes'; | |
var input = { | |
x: parseFloat($transformX.val()), | |
y: parseFloat($transformY.val()), | |
z: parseFloat($transformZ.val()) | |
}; | |
input.x = isNaN(input.x) ? current.x : input.x; | |
input.y = isNaN(input.y) ? current.y : input.y; | |
input.z = isNaN(input.z) ? current.z : input.z; | |
$transformX.val(input.x); | |
$transformY.val(input.y); | |
$transformZ.val(input.z); | |
if (transformAction == 'scale') { | |
if (uniform) { | |
if (input.x != current.x) { | |
var ratio = current.x / input.x; | |
input.y = (current.y / ratio).toFixed(2); | |
input.z = (current.z / ratio).toFixed(2); | |
$transformY.val(input.y); | |
$transformZ.val(input.z); | |
} | |
else if (input.y != current.y) { | |
var ratio = current.y / input.y; | |
input.x = (current.x / ratio).toFixed(2); | |
input.z = (current.z / ratio).toFixed(2); | |
$transformX.val(input.x); | |
$transformZ.val(input.z); | |
} | |
else if (input.z != current.z) { | |
var ratio = current.z / input.z; | |
input.x = (current.x / ratio).toFixed(2); | |
input.y = (current.y / ratio).toFixed(2); | |
$transformX.val(input.x); | |
$transformY.val(input.y); | |
} | |
} | |
input.x <= 0 && (input.x = 1); | |
input.y <= 0 && (input.y = 1); | |
input.z <= 0 && (input.z = 1); | |
slicer.mesh.geometry.scale( | |
input.x / current.x, | |
input.y / current.y, | |
input.z / current.z | |
); | |
} | |
else if (transformAction == 'rotate') { | |
var deg = Math.PI / 180; | |
var offsets = { | |
x: input.x - current.x, | |
y: input.y - current.y, | |
z: input.z - current.z | |
}; | |
slicer.mesh.geometry.rotateX(offsets.x * deg); | |
slicer.mesh.geometry.rotateY(offsets.y * deg); | |
slicer.mesh.geometry.rotateZ(offsets.z * deg); | |
} | |
else { | |
var offsets = { | |
x: input.x - current.x, | |
y: input.y - current.y | |
}; | |
slicer.mesh.geometry.translate(offsets.x, offsets.y, 0); | |
} | |
current.x = input.x; | |
current.y = input.y; | |
current.z = input.z; | |
if (transformAction != 'translate') { | |
loadGeometry(slicer.mesh.geometry, false); | |
// stay at current position | |
var current = transformations['translate']; | |
slicer.mesh.geometry.translate(current.x, current.y, current.z); | |
} | |
getSlice($sliderInput.slider('getValue')); | |
} | |
$transformButtons.on('click', function(e) { | |
var $this = $(this); | |
var axis = $this.data('axis'); | |
var action = $this.data('action'); | |
var value = transformations[transformAction][axis]; | |
var $target = $transformBody.find('#transform-' + axis); | |
$target.val(value + (action == '+' ? 1 : -1)); | |
updateTransformValues(); | |
}); | |
function flipGeometry() { | |
settings.set('transform.mirror', $transformMirrorYes[0].checked); | |
loadGeometry(slicer.mesh.geometry, true); | |
getSlice($sliderInput.slider('getValue')); | |
} | |
$transformMirrorYes.on('change', flipGeometry); | |
$transformMirrorNo.on('change', flipGeometry); | |
$('#transform-mirror-' + (settings.get('transform.mirror') ? 'yes' : 'no')).prop('checked', true); | |
$('#transform select').on('change', updateTransformAction); | |
$('#transform input').on('change', updateTransformValues); | |
resetTransformValues(); | |
// 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 | |
// Load an geometry | |
function loadGeometry(geometry, mirror) { | |
try { | |
// remove old mesh and plane | |
slicer.mesh && viewer3d.removeObject(slicer.mesh); | |
// remove old shapes | |
removeShapes(); | |
// flip geometry | |
if (mirror) { | |
geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1)); | |
} | |
// load new mesh in slicer | |
slicer.loadMesh(new SLAcer.Mesh(geometry, new THREE.MeshPhongMaterial({ | |
color: hexToDec(settings.get('colors.mesh')), side: THREE.DoubleSide | |
}))); | |
// add new mesh and render view | |
viewer3d.addObject(slicer.mesh); | |
viewer3d.render(); | |
// update mesh info | |
updateMeshInfoUI(); | |
// get first slice | |
//getSlice(1); | |
} | |
catch(e) { | |
errorHandler(e); | |
} | |
} | |
// Haaaaaaaaaaaaaaaaaa!!!! | |
function ultraMegaDirtyFix() { | |
$transformBody.find('#transform-x').val(1.1); | |
$transformBody.find('#transform-y').val(1.1); | |
$transformBody.find('#transform-z').val(1.1); | |
updateTransformValues(); | |
$transformBody.find('#transform-x').val(1); | |
$transformBody.find('#transform-y').val(1); | |
$transformBody.find('#transform-z').val(1); | |
updateTransformValues(); | |
} | |
// On Geometry loaded | |
loader.onGeometry = function(geometry) { | |
resetTransformValues(); | |
loadGeometry(geometry, settings.get('transform.mirror')); | |
ultraMegaDirtyFix(); | |
}; | |
// On loading error | |
loader.onError = errorHandler; | |
// ----------------------------------------------------------------------------- | |
// load example | |
// ----------------------------------------------------------------------------- | |
// example STL file | |
//var stl = 'stl/Octocat-v2.stl'; | |
var stl = 'stl/StressTest.stl'; | |
//var stl = 'stl/SLAcer.stl'; | |
// File url | |
// var url = 'http://' + window.location.hostname + window.location.pathname + stl; | |
var url = window.location.href + 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(); |