-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
3D milling: added new 3D computer test
- the minkowski pass is now space-aware, we should be able to give it sparce geometry to select the computation points
- Loading branch information
Showing
5 changed files
with
308 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
"use strict"; | ||
define(['RSVP', 'THREE', 'cnc/cam/3D/modelProjector', 'cnc/cam/3D/minkowskiComputer', | ||
'cnc/cam/3D/toolProfile', 'cnc/cam/toolpath', 'cnc/app/task', 'cnc/util', 'require' | ||
], | ||
function (RSVP, THREE, ModelProjector, MinkowskiComputer, toolProfile, tp, Task, util, require) { | ||
RSVP.on('error', function (reason) { | ||
console.assert(false, reason); | ||
if (reason.stack) | ||
console.assert(false, reason.stack); | ||
}); | ||
|
||
|
||
function ToolPathComputer() { | ||
} | ||
|
||
ToolPathComputer.prototype = { | ||
computeHeightField: function (geometry, stepover, tool, leaveStock, angle, startRatio, stopRatio, renderer) { | ||
|
||
function work(task, resolve, reject) { | ||
if (angle == null) | ||
angle = 0; | ||
var modelStage = new ModelProjector(); | ||
modelStage.setGeometry(geometry.clone()); | ||
modelStage.setAngle(angle); | ||
if (!renderer) | ||
renderer = new THREE.WebGLRenderer({ | ||
antialias: false, | ||
alpha: false, | ||
precision: 'highp', | ||
autoClear: false, | ||
preserveDrawingBuffer: true | ||
}); | ||
var toolSamples = 10; | ||
var sampleRate = toolSamples / (tool.diameter / 2 + leaveStock); | ||
var profile = toolProfile.createTool(tool, toolSamples, modelStage.zRatio, leaveStock); | ||
var bbox = modelStage.modelBbox.clone(); | ||
|
||
function toPow2(value) { | ||
return Math.sign(value) * Math.pow(2, Math.ceil(Math.log2(Math.abs(value)))); | ||
} | ||
|
||
var globalWidthP = toPow2(bbox.size().x * sampleRate); | ||
var globalHeightP = toPow2(bbox.size().y * sampleRate); | ||
|
||
var centerXP = globalWidthP / 2; | ||
var centerYP = globalHeightP / 2; | ||
var globalWidthMM = globalWidthP / sampleRate; | ||
var globalHeightMM = globalHeightP / sampleRate; | ||
|
||
var expansion = new THREE.Vector3(globalWidthMM - bbox.size().x, globalHeightMM - bbox.size().y, 0); | ||
bbox.expandByVector(expansion); | ||
|
||
var minXP = 0; | ||
var minYP = 0; | ||
|
||
var tileSideP = 512; | ||
var tileXCount = Math.ceil(globalWidthP / tileSideP); | ||
var tileYCount = Math.ceil(globalHeightP / tileSideP); | ||
var minkowskiTileXP = tileSideP; | ||
var minkowskiTileYP = tileSideP; | ||
var modelTileXP = minkowskiTileXP + 2 * toolSamples; | ||
var modelTileYP = minkowskiTileYP + 2 * toolSamples; | ||
var tileSelector = function (i, j) { | ||
return j >= tileYCount * startRatio && j <= tileYCount * stopRatio; | ||
}; | ||
var resultBufferWidth = tileXCount * minkowskiTileXP; | ||
var resultBufferHeight = tileYCount * minkowskiTileYP; | ||
|
||
|
||
var minkowskiPass = new MinkowskiComputer(bbox.min.x, bbox.max.x, bbox.min.y, bbox.max.y); | ||
renderer.autoClear = false; | ||
var sequence = []; | ||
for (var j = 0; j < tileYCount; j++) | ||
for (var i = 0; i < tileXCount; i++) | ||
if (tileSelector(i, j)) | ||
sequence.push([i, j]); | ||
var rotationMatrix = new THREE.Matrix4().makeRotationZ(angle * Math.PI / 180); | ||
var scaleMatrix = new THREE.Matrix4().makeScale(1 / sampleRate, 1 / sampleRate, 1); | ||
var translationMatrix = new THREE.Matrix4().makeTranslation(minXP / sampleRate, minYP / sampleRate, 0); | ||
var transformMatrix = new THREE.Matrix4().multiply(rotationMatrix).multiply(translationMatrix).multiply(scaleMatrix); | ||
modelStage.pushZInverseProjOn(transformMatrix); | ||
var resultBuffer = new Float32Array(resultBufferWidth * resultBufferHeight); | ||
var resultHeightField = new HeightField(resultBuffer, bbox, resultBufferWidth, resultBufferHeight, | ||
transformMatrix, startRatio, stopRatio, leaveStock + bbox.min.z); | ||
|
||
var modelBuffer = modelStage.createRenderBuffer(modelTileXP, modelTileYP); | ||
var worker = new Worker(require.toUrl('worker.js')); | ||
var gl = renderer.getContext(); | ||
var minkowskiBuffer = new THREE.WebGLRenderTarget(minkowskiTileXP, minkowskiTileYP, { | ||
stencilBuffer: false, generateMipmaps: false, | ||
texture: new THREE.Texture(null, THREE.Texture.DEFAULT_MAPPING, THREE.ClampToEdgeWrapping, | ||
THREE.ClampToEdgeWrapping, THREE.LinearFilter, THREE.LinearFilter, THREE.RGBFormat, | ||
THREE.FloatType) | ||
}); | ||
minkowskiBuffer.texture.generateMipmaps = false; | ||
renderer.setRenderTarget(minkowskiBuffer); | ||
var isFloatReadPixelSupported = gl.getParameter(gl['IMPLEMENTATION_COLOR_READ_FORMAT']) == gl.RGB | ||
&& gl.getParameter(gl['IMPLEMENTATION_COLOR_READ_TYPE']) == gl.FLOAT; | ||
renderer.setRenderTarget(null); | ||
|
||
function readBuffer(x, y, renderTarget) { | ||
var texture = renderTarget.texture; | ||
var colorChannels = texture.format == THREE.RGBFormat ? [3, gl.RGB] : [4, gl.RGBA]; | ||
var type = texture.type == THREE.FloatType ? [Float32Array, gl.FLOAT, 1] : [Uint8Array, gl.UNSIGNED_BYTE, 1 / 255]; | ||
var resultTileBuffer = new type[0](colorChannels[0] * renderTarget.width * renderTarget.height); | ||
renderer.setRenderTarget(renderTarget); | ||
gl.readPixels(0, 0, renderTarget.width, renderTarget.height, colorChannels[1], type[1], resultTileBuffer); | ||
renderer.setRenderTarget(null); | ||
var tileShiftX = (renderTarget.width - minkowskiTileXP) / 2; | ||
var tileShiftY = (renderTarget.height - minkowskiTileYP) / 2; | ||
var left = tileShiftX + x * minkowskiTileXP; | ||
var top = tileShiftY + y * minkowskiTileYP; | ||
//by keeping this loop in the main thread, I think we are leaving some time for the GPU to breathe. | ||
for (var j = 0; j < minkowskiTileYP; j++) | ||
for (var i = 0; i < minkowskiTileXP; i++) { | ||
if (top + j < resultBufferHeight && i + left < resultBufferWidth) { | ||
var pixIndex = ((tileShiftY + j) * renderTarget.width + i + tileShiftX) * colorChannels[0]; | ||
resultBuffer[(top + j) * resultBufferWidth + i + left] = resultTileBuffer[pixIndex] * type[2]; | ||
} | ||
} | ||
} | ||
|
||
var outputFloats = isFloatReadPixelSupported; | ||
//compensate because the model tile has a margin of 1 tool radius around it | ||
var terrainRatio = new THREE.Vector2(minkowskiTileXP / modelBuffer.width, minkowskiTileYP / modelBuffer.height); | ||
var terrainTranslation = new THREE.Vector2(toolSamples / modelBuffer.width, toolSamples / modelBuffer.height); | ||
minkowskiPass.setParams(profile, new THREE.Vector2(toolSamples / modelBuffer.width, | ||
toolSamples / modelBuffer.height), null, outputFloats, terrainRatio, terrainTranslation, modelBuffer.depthTexture); | ||
|
||
function setCameraPixCenter(x, y) { | ||
var xOffset = bbox.center().x; | ||
var yOffset = bbox.center().y; | ||
modelStage.setCamera(xOffset + (x - modelTileXP / 2 - centerXP) / sampleRate, xOffset + (x + modelTileXP / 2 - centerXP) / sampleRate, | ||
yOffset + (y - modelTileYP / 2 - centerXP) / sampleRate, yOffset + (y + modelTileYP / 2 - centerYP) / sampleRate); | ||
minkowskiPass.setCamera(xOffset + (x - minkowskiTileXP / 2 - centerXP) / sampleRate, xOffset + (x + minkowskiTileXP / 2 - centerXP) / sampleRate, | ||
yOffset + (y - minkowskiTileYP / 2 - centerXP) / sampleRate, yOffset + (y + minkowskiTileYP / 2 - centerYP) / sampleRate); | ||
} | ||
|
||
function setModelTilePos(x, y) { | ||
setCameraPixCenter(minXP + (x + 0.5) * minkowskiTileXP, minYP + (y + 0.5) * minkowskiTileXP); | ||
} | ||
|
||
var percentage = null; | ||
|
||
renderer.autoClear = false; | ||
var sequenceIndex = 0; | ||
|
||
function drawTile() { | ||
console.log('drawTile'); | ||
if (task.get('isPaused')) | ||
return; | ||
if (sequenceIndex < sequence.length) { | ||
percentage = Math.round(sequenceIndex / sequence.length * 25) * 4; | ||
var x = sequence[sequenceIndex][0]; | ||
var y = sequence[sequenceIndex][1]; | ||
setModelTilePos(x, y); | ||
modelStage.render(renderer, modelBuffer); | ||
minkowskiPass.render(renderer, minkowskiBuffer); | ||
readBuffer(x, y, minkowskiBuffer); | ||
//setTimeout is not throttled in workers | ||
$(worker).one('message', drawTile); | ||
worker.postMessage({operation: 'ping'}); | ||
sequenceIndex++; | ||
} else { | ||
worker.terminate(); | ||
console.timeEnd('computation'); | ||
resolve(resultHeightField); | ||
} | ||
} | ||
|
||
console.time('computation'); | ||
task.resumeWork = drawTile; | ||
task.cancelWork = function () { | ||
worker.terminate(); | ||
}; | ||
drawTile(); | ||
} | ||
|
||
return Task.create({work: work}); | ||
} | ||
}; | ||
|
||
function HeightField(data, modelbbox, samplesX, samplesY, bufferToWorldMatrix, startRatio, stopRatio, noModelValue) { | ||
this.data = data; | ||
this.modelbbox = modelbbox; | ||
this.samplesX = samplesX; | ||
this.samplesY = samplesY; | ||
this.bufferToWorldMatrix = bufferToWorldMatrix; | ||
this.startRatio = startRatio; | ||
this.stopRatio = stopRatio; | ||
this.noModelValue = noModelValue + (modelbbox.max.z - modelbbox.min.z) * 0.000001; | ||
} | ||
|
||
HeightField.prototype = { | ||
getPoint: function (ijVector) { | ||
ijVector.setX(Math.min(ijVector.x, this.samplesX - 1)); | ||
ijVector.setY(Math.min(ijVector.y, this.samplesY - 1)); | ||
ijVector.setZ(this.data[ijVector.y * this.samplesX + ijVector.x]); | ||
return ijVector.applyMatrix4(this.bufferToWorldMatrix); | ||
} | ||
}; | ||
|
||
return { | ||
ToolPathComputer: ToolPathComputer | ||
}; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
"use strict"; | ||
define(['THREE', 'shader!spatial_vUv.vert', 'shader!minkowski.frag'], function (THREE, minkowskiVert, minkowskiFrag) { | ||
function MinkowskiComputer(minXMM, maxXMM, minYMM, maxYMM) { | ||
if (this == null) | ||
throw "this is a constructor, you forgot 'new'"; | ||
var geometry = new THREE.PlaneBufferGeometry(maxXMM - minXMM, maxYMM - minYMM); | ||
geometry.translate((minXMM + maxXMM) / 2, (minYMM + maxYMM) / 2, 0); | ||
this.material = new THREE.ShaderMaterial({ | ||
side: THREE.DoubleSide, | ||
depthTest: true, | ||
linewidth: 10, | ||
uniforms: { | ||
aabb: {type: 'v4', value: new THREE.Vector4(minXMM, minYMM, maxXMM, maxYMM)}, | ||
modelHeight: {type: 't'}, | ||
toolProfile: {type: 't', value: null}, | ||
toolToPartRatio: {type: 'v2', value: null}, | ||
terrainRatio: {type: 'v2', value: null}, | ||
terrainTranslation: {type: 'v2', value: null}, | ||
minZ: {type: 'f', value: -Infinity} | ||
}, | ||
defines: { | ||
radialSamples: null | ||
}, | ||
vertexShader: minkowskiVert, | ||
fragmentShader: minkowskiFrag | ||
}); | ||
this.object = new THREE.Mesh(geometry, this.material); | ||
this.camera = new THREE.OrthographicCamera(minXMM, maxXMM, maxYMM, minYMM, 0, 2); | ||
this.scene = new THREE.Scene(); | ||
this.scene.add(this.object); | ||
this.scene.add(this.camera); | ||
} | ||
|
||
MinkowskiComputer.prototype = { | ||
setParams: function (toolProfile, toolToPartRatio, minZ, outputFloats, terrainRatio, terrainTranslation, modelHeightBuffer) { | ||
var pixelsOnRadius = toolProfile.length; | ||
var uniforms = this.material.uniforms; | ||
uniforms.toolProfile.value = new THREE.DataTexture(new Float32Array(toolProfile), pixelsOnRadius, 1, | ||
THREE.LuminanceFormat, THREE.FloatType, THREE.Texture.DEFAULT_MAPPING, THREE.ClampToEdgeWrapping, | ||
THREE.ClampToEdgeWrapping, THREE.LinearFilter, THREE.LinearFilter); | ||
uniforms.toolProfile.value.generateMipmaps = false; | ||
uniforms.toolProfile.value.needsUpdate = true; | ||
uniforms.toolToPartRatio.value = toolToPartRatio; | ||
uniforms.minZ.value = minZ != null ? minZ : -Infinity; | ||
uniforms.terrainRatio.value = terrainRatio; | ||
uniforms.terrainTranslation.value = terrainTranslation; | ||
uniforms.modelHeight.value = modelHeightBuffer; | ||
this.material.defines.radialSamples = pixelsOnRadius; | ||
if (outputFloats) | ||
this.material.defines.OUTPUT_FLOATS = true; | ||
this.material.needsUpdate = true; | ||
}, | ||
setCamera: function (left, right, bottom, top) { | ||
this.camera.up.set(0, 1, 0); | ||
this.camera.lookAt(this.camera.position.clone().sub(new THREE.Vector3(0, 0, 1))); | ||
this.camera.bottom = bottom; | ||
this.camera.top = top; | ||
this.camera.left = left; | ||
this.camera.right = right; | ||
this.camera.updateProjectionMatrix(); | ||
this.camera.updateMatrixWorld(); | ||
}, | ||
render: function (renderer, outputBuffer) { | ||
renderer.render(this.scene, this.camera, outputBuffer, true); | ||
} | ||
}; | ||
return MinkowskiComputer; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
//will map the vUv so that 0,0 is the min of the bounding box and 1,1 is the max | ||
//expects the axis aligned XY bounding box min in xy, max in zw | ||
|
||
uniform vec4 aabb; | ||
varying vec2 vUv; | ||
void main() { | ||
vUv = (position.xy - aabb.xy) / (aabb.zw - aabb.xy); | ||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); | ||
} |
Oops, something went wrong.