add belt mode for support painting
This commit is contained in:
parent
c5e4bac1cb
commit
eb660aa20b
2 changed files with 94 additions and 4 deletions
|
|
@ -17,6 +17,11 @@
|
||||||
* support regions. For multi-material coloring or other uses, set
|
* support regions. For multi-material coloring or other uses, set
|
||||||
* options.onlyDownwardFacing = false to paint all surfaces.
|
* options.onlyDownwardFacing = false to paint all surfaces.
|
||||||
*
|
*
|
||||||
|
* BELT PRINTER MODE:
|
||||||
|
* For belt printers, surfaces need support when facing "down the belt slope" rather
|
||||||
|
* than straight down. Set options.beltMode = true and provide options.beltAngle (in radians)
|
||||||
|
* to paint surfaces that face down the angled belt direction.
|
||||||
|
*
|
||||||
* PAINT POINT DATA STRUCTURE:
|
* PAINT POINT DATA STRUCTURE:
|
||||||
* ---------------------------
|
* ---------------------------
|
||||||
* Paint points must be an array of objects with this structure:
|
* Paint points must be an array of objects with this structure:
|
||||||
|
|
@ -79,6 +84,9 @@ import { THREE } from '../../ext/three.js';
|
||||||
* @param {THREE.Color} [paintColor] - Color for painted regions (default: light blue)
|
* @param {THREE.Color} [paintColor] - Color for painted regions (default: light blue)
|
||||||
* @param {Object} [options] - Optional configuration
|
* @param {Object} [options] - Optional configuration
|
||||||
* @param {boolean} [options.onlyDownwardFacing=true] - Only paint on downward-facing surfaces (z-normal < 0)
|
* @param {boolean} [options.onlyDownwardFacing=true] - Only paint on downward-facing surfaces (z-normal < 0)
|
||||||
|
* @param {boolean} [options.beltMode=false] - Enable belt printer mode (paints surfaces facing down belt slope)
|
||||||
|
* @param {number} [options.beltAngle=0] - Belt angle in radians (only used if beltMode is true)
|
||||||
|
* @param {number} [options.supportAngle=45] - Support angle in degrees (cone around down direction)
|
||||||
* @returns {THREE.Material} The modified material
|
* @returns {THREE.Material} The modified material
|
||||||
*/
|
*/
|
||||||
function addPaintOverlaySimple(material, paintPoints, paintColor = new THREE.Color(0x4488ff), options = {}) {
|
function addPaintOverlaySimple(material, paintPoints, paintColor = new THREE.Color(0x4488ff), options = {}) {
|
||||||
|
|
@ -89,6 +97,10 @@ function addPaintOverlaySimple(material, paintPoints, paintColor = new THREE.Col
|
||||||
|
|
||||||
// Default: only paint downward-facing surfaces (for support painting)
|
// Default: only paint downward-facing surfaces (for support painting)
|
||||||
const onlyDownwardFacing = options.onlyDownwardFacing !== undefined ? options.onlyDownwardFacing : true;
|
const onlyDownwardFacing = options.onlyDownwardFacing !== undefined ? options.onlyDownwardFacing : true;
|
||||||
|
const beltMode = options.beltMode || false;
|
||||||
|
const beltAngle = options.beltAngle || 0;
|
||||||
|
const supportAngle = options.supportAngle || 45;
|
||||||
|
const supportThreshold = Math.cos((90 - supportAngle) * Math.PI / 180);
|
||||||
|
|
||||||
// Store initial paint points reference
|
// Store initial paint points reference
|
||||||
material.userData.paintPoints = paintPoints;
|
material.userData.paintPoints = paintPoints;
|
||||||
|
|
@ -119,6 +131,9 @@ function addPaintOverlaySimple(material, paintPoints, paintColor = new THREE.Col
|
||||||
shader.uniforms.paintCount = { value: validPoints.length };
|
shader.uniforms.paintCount = { value: validPoints.length };
|
||||||
shader.uniforms.paintColor = { value: paintColor };
|
shader.uniforms.paintColor = { value: paintColor };
|
||||||
shader.uniforms.onlyDownwardFacing = { value: onlyDownwardFacing };
|
shader.uniforms.onlyDownwardFacing = { value: onlyDownwardFacing };
|
||||||
|
shader.uniforms.beltMode = { value: beltMode };
|
||||||
|
shader.uniforms.beltAngle = { value: beltAngle };
|
||||||
|
shader.uniforms.supportThreshold = { value: supportThreshold };
|
||||||
|
|
||||||
// Pass model position from vertex shader to fragment shader
|
// Pass model position from vertex shader to fragment shader
|
||||||
// Paint points are in widget/model space, so we compare against transformed (model space)
|
// Paint points are in widget/model space, so we compare against transformed (model space)
|
||||||
|
|
@ -143,6 +158,9 @@ function addPaintOverlaySimple(material, paintPoints, paintColor = new THREE.Col
|
||||||
uniform int paintCount;
|
uniform int paintCount;
|
||||||
uniform vec3 paintColor;
|
uniform vec3 paintColor;
|
||||||
uniform bool onlyDownwardFacing;
|
uniform bool onlyDownwardFacing;
|
||||||
|
uniform bool beltMode;
|
||||||
|
uniform float beltAngle;
|
||||||
|
uniform float supportThreshold;
|
||||||
` + shader.fragmentShader;
|
` + shader.fragmentShader;
|
||||||
|
|
||||||
shader.fragmentShader = shader.fragmentShader.replace(
|
shader.fragmentShader = shader.fragmentShader.replace(
|
||||||
|
|
@ -156,7 +174,20 @@ function addPaintOverlaySimple(material, paintPoints, paintColor = new THREE.Col
|
||||||
vec3 worldNormal = normalize(cross(posDx, posDy));
|
vec3 worldNormal = normalize(cross(posDx, posDy));
|
||||||
|
|
||||||
// Only paint downward-facing surfaces if option is enabled
|
// Only paint downward-facing surfaces if option is enabled
|
||||||
bool shouldPaint = !onlyDownwardFacing || worldNormal.z < -0.001;
|
bool shouldPaint;
|
||||||
|
if (beltMode) {
|
||||||
|
// Belt mode: mesh is rotated, check if normal points toward world down
|
||||||
|
// World down (0,0,-1) appears as (0, -sin(θ), -cos(θ)) in rotated mesh space
|
||||||
|
// Dot product: normal · (0, -sin(θ), -cos(θ)) = -sin(θ)*ny - cos(θ)*nz
|
||||||
|
float sinAngle = sin(beltAngle);
|
||||||
|
float cosAngle = cos(beltAngle);
|
||||||
|
float rotatedZ = worldNormal.y * sinAngle + worldNormal.z * cosAngle;
|
||||||
|
// Check if within support angle cone from down direction (rotatedZ < -threshold)
|
||||||
|
shouldPaint = !onlyDownwardFacing || rotatedZ < -supportThreshold;
|
||||||
|
} else {
|
||||||
|
// Normal mode: check if normal points downward within support angle cone
|
||||||
|
shouldPaint = !onlyDownwardFacing || worldNormal.z < -supportThreshold;
|
||||||
|
}
|
||||||
if (shouldPaint) {
|
if (shouldPaint) {
|
||||||
// Check if this fragment is within any paint sphere
|
// Check if this fragment is within any paint sphere
|
||||||
bool painted = false;
|
bool painted = false;
|
||||||
|
|
@ -206,6 +237,9 @@ function addPaintOverlaySimple(material, paintPoints, paintColor = new THREE.Col
|
||||||
* @param {THREE.Color} [paintColor] - Color for painted regions (default: light blue)
|
* @param {THREE.Color} [paintColor] - Color for painted regions (default: light blue)
|
||||||
* @param {Object} [options] - Optional configuration
|
* @param {Object} [options] - Optional configuration
|
||||||
* @param {boolean} [options.onlyDownwardFacing=true] - Only paint on downward-facing surfaces (z-normal < 0)
|
* @param {boolean} [options.onlyDownwardFacing=true] - Only paint on downward-facing surfaces (z-normal < 0)
|
||||||
|
* @param {boolean} [options.beltMode=false] - Enable belt printer mode (paints surfaces facing down belt slope)
|
||||||
|
* @param {number} [options.beltAngle=0] - Belt angle in radians (only used if beltMode is true)
|
||||||
|
* @param {number} [options.supportAngle=45] - Support angle in degrees (cone around down direction)
|
||||||
* @returns {THREE.Material} The modified material
|
* @returns {THREE.Material} The modified material
|
||||||
*/
|
*/
|
||||||
function addPaintOverlayTexture(material, paintPoints, paintColor = new THREE.Color(0x4488ff), options = {}) {
|
function addPaintOverlayTexture(material, paintPoints, paintColor = new THREE.Color(0x4488ff), options = {}) {
|
||||||
|
|
@ -216,6 +250,10 @@ function addPaintOverlayTexture(material, paintPoints, paintColor = new THREE.Co
|
||||||
|
|
||||||
// Default: only paint downward-facing surfaces (for support painting)
|
// Default: only paint downward-facing surfaces (for support painting)
|
||||||
const onlyDownwardFacing = options.onlyDownwardFacing !== undefined ? options.onlyDownwardFacing : true;
|
const onlyDownwardFacing = options.onlyDownwardFacing !== undefined ? options.onlyDownwardFacing : true;
|
||||||
|
const beltMode = options.beltMode || false;
|
||||||
|
const beltAngle = options.beltAngle || 0;
|
||||||
|
const supportAngle = options.supportAngle || 45;
|
||||||
|
const supportThreshold = Math.cos((90 - supportAngle) * Math.PI / 180);
|
||||||
|
|
||||||
// Store initial paint points reference
|
// Store initial paint points reference
|
||||||
material.userData.paintPoints = paintPoints;
|
material.userData.paintPoints = paintPoints;
|
||||||
|
|
@ -264,6 +302,9 @@ function addPaintOverlayTexture(material, paintPoints, paintColor = new THREE.Co
|
||||||
shader.uniforms.paintTexSize = { value: new THREE.Vector2(texWidth, texHeight) };
|
shader.uniforms.paintTexSize = { value: new THREE.Vector2(texWidth, texHeight) };
|
||||||
shader.uniforms.paintColor = { value: paintColor };
|
shader.uniforms.paintColor = { value: paintColor };
|
||||||
shader.uniforms.onlyDownwardFacing = { value: onlyDownwardFacing };
|
shader.uniforms.onlyDownwardFacing = { value: onlyDownwardFacing };
|
||||||
|
shader.uniforms.beltMode = { value: beltMode };
|
||||||
|
shader.uniforms.beltAngle = { value: beltAngle };
|
||||||
|
shader.uniforms.supportThreshold = { value: supportThreshold };
|
||||||
|
|
||||||
// Pass model position from vertex shader to fragment shader
|
// Pass model position from vertex shader to fragment shader
|
||||||
// Paint points are in widget/model space, so we compare against transformed (model space)
|
// Paint points are in widget/model space, so we compare against transformed (model space)
|
||||||
|
|
@ -289,6 +330,9 @@ function addPaintOverlayTexture(material, paintPoints, paintColor = new THREE.Co
|
||||||
uniform vec2 paintTexSize;
|
uniform vec2 paintTexSize;
|
||||||
uniform vec3 paintColor;
|
uniform vec3 paintColor;
|
||||||
uniform bool onlyDownwardFacing;
|
uniform bool onlyDownwardFacing;
|
||||||
|
uniform bool beltMode;
|
||||||
|
uniform float beltAngle;
|
||||||
|
uniform float supportThreshold;
|
||||||
` + shader.fragmentShader;
|
` + shader.fragmentShader;
|
||||||
|
|
||||||
shader.fragmentShader = shader.fragmentShader.replace(
|
shader.fragmentShader = shader.fragmentShader.replace(
|
||||||
|
|
@ -302,7 +346,20 @@ function addPaintOverlayTexture(material, paintPoints, paintColor = new THREE.Co
|
||||||
vec3 worldNormal = normalize(cross(posDx, posDy));
|
vec3 worldNormal = normalize(cross(posDx, posDy));
|
||||||
|
|
||||||
// Only paint downward-facing surfaces if option is enabled
|
// Only paint downward-facing surfaces if option is enabled
|
||||||
bool shouldPaint = !onlyDownwardFacing || worldNormal.z < -0.001;
|
bool shouldPaint;
|
||||||
|
if (beltMode) {
|
||||||
|
// Belt mode: mesh is rotated, check if normal points toward world down
|
||||||
|
// World down (0,0,-1) appears as (0, -sin(θ), -cos(θ)) in rotated mesh space
|
||||||
|
// Dot product: normal · (0, -sin(θ), -cos(θ)) = -sin(θ)*ny - cos(θ)*nz
|
||||||
|
float sinAngle = sin(beltAngle);
|
||||||
|
float cosAngle = cos(beltAngle);
|
||||||
|
float rotatedZ = worldNormal.y * sinAngle + worldNormal.z * cosAngle;
|
||||||
|
// Check if within support angle cone from down direction (rotatedZ < -threshold)
|
||||||
|
shouldPaint = !onlyDownwardFacing || rotatedZ < -supportThreshold;
|
||||||
|
} else {
|
||||||
|
// Normal mode: check if normal points downward within support angle cone
|
||||||
|
shouldPaint = !onlyDownwardFacing || worldNormal.z < -supportThreshold;
|
||||||
|
}
|
||||||
if (shouldPaint) {
|
if (shouldPaint) {
|
||||||
bool painted = false;
|
bool painted = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,30 @@ function supportStart({ remove } = { remove: false }) {
|
||||||
return api.widgets.meshes();
|
return api.widgets.meshes();
|
||||||
};
|
};
|
||||||
api.feature.on_mouse_drag = ({ int }) => {
|
api.feature.on_mouse_drag = ({ int }) => {
|
||||||
if (int?.length && int[0].face.normal.z < 0) {
|
// Calculate if surface needs support based on belt mode or normal mode
|
||||||
|
let needsSupport = false;
|
||||||
|
if (int?.length) {
|
||||||
|
const settings = api.conf.get();
|
||||||
|
const normal = int[0].face.normal;
|
||||||
|
|
||||||
|
// Get support angle threshold (how steep before needing support)
|
||||||
|
const supportAngle = settings.process.sliceSupportAngle || 45;
|
||||||
|
const supportThreshold = Math.cos((90 - supportAngle) * Math.PI / 180);
|
||||||
|
|
||||||
|
if (settings.device.bedBelt) {
|
||||||
|
// Belt mode: check if surface will face downward after belt rotation
|
||||||
|
// After rotating by angle θ around X: nz' = ny*sin(θ) + nz*cos(θ)
|
||||||
|
// We want nz' < -threshold (within cone of support angle from down)
|
||||||
|
const angle = (settings.process.sliceAngle || 45) * Math.PI / 180;
|
||||||
|
const rotatedZ = normal.y * Math.sin(angle) + normal.z * Math.cos(angle);
|
||||||
|
needsSupport = rotatedZ < -supportThreshold;
|
||||||
|
} else {
|
||||||
|
// Normal mode: check if normal points downward within support angle cone
|
||||||
|
needsSupport = normal.z < -supportThreshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsSupport) {
|
||||||
supportUpdate(int[0].point, down.widget, remove);
|
supportUpdate(int[0].point, down.widget, remove);
|
||||||
}
|
}
|
||||||
return [ down.widget.mesh ];
|
return [ down.widget.mesh ];
|
||||||
|
|
@ -279,6 +302,12 @@ function supportStart({ remove } = { remove: false }) {
|
||||||
const scheme = colorSchemeRegistry.getScheme(mode, theme);
|
const scheme = colorSchemeRegistry.getScheme(mode, theme);
|
||||||
const paintColor = scheme.operations?.paint?.overlay ?? 0x4488ff;
|
const paintColor = scheme.operations?.paint?.overlay ?? 0x4488ff;
|
||||||
|
|
||||||
|
// Get belt configuration for paint constraint
|
||||||
|
const settings = api.conf.get();
|
||||||
|
const isBelt = settings.device.bedBelt;
|
||||||
|
const beltAngle = isBelt ? (settings.process.sliceAngle || 45) : 0;
|
||||||
|
const supportAngle = settings.process.sliceSupportAngle || 45;
|
||||||
|
|
||||||
api.widgets.each(w => {
|
api.widgets.each(w => {
|
||||||
// Use pushVisualState to track the paint operation
|
// Use pushVisualState to track the paint operation
|
||||||
w.pushVisualState('paint', {
|
w.pushVisualState('paint', {
|
||||||
|
|
@ -295,7 +324,11 @@ function supportStart({ remove } = { remove: false }) {
|
||||||
if (!w.anno.paint) {
|
if (!w.anno.paint) {
|
||||||
w.anno.paint = [];
|
w.anno.paint = [];
|
||||||
}
|
}
|
||||||
addPaintOverlayTexture(mat, w.anno.paint, new THREE.Color(paintColor));
|
addPaintOverlayTexture(mat, w.anno.paint, new THREE.Color(paintColor), {
|
||||||
|
beltMode: isBelt,
|
||||||
|
beltAngle: beltAngle * Math.PI / 180, // Convert to radians
|
||||||
|
supportAngle: settings.process.sliceSupportAngle || 45
|
||||||
|
});
|
||||||
})
|
})
|
||||||
api.space.update();
|
api.space.update();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue