graphic design tool
git clone https://git.lucas.co/cce-designer.git
feat: Plane node width/length + per-axis cell counts
Size splits into Width (X) and Length (Z) — slider:0:4 so the readout
range covers the values instead of display-clamping at the default 0-2
— plus Columns/Rows spinboxes (chi, clamped 1-128 in the kernel: 128^2
cells stays under the 200k vertex budget) controlling the grid
resolution per axis.
Co-Authored-By: Claude Fable 5 <[email protected]>
nodes/plane.json | 27 ++++++++++++++++++++++++---
src/main.rs | 28 +++++++++++++++++-----------
2 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/nodes/plane.json b/nodes/plane.json
index 9abd095..f5d068c 100644
--- a/nodes/plane.json
+++ b/nodes/plane.json
@@ -5,9 +5,30 @@
"outputs": 1,
"params": [
{
- "name": "Size",
+ "name": "Width",
"default": "1.0",
- "type": "slider"
+ "type": "slider:0:4"
+ },
+ {
+ "name": "Length",
+ "default": "1.0",
+ "type": "slider:0:4"
+ },
+ {
+ "name": "Columns",
+ "default": "16",
+ "type": "spinbox",
+ "min": 1,
+ "max": 128,
+ "step": 1
+ },
+ {
+ "name": "Rows",
+ "default": "16",
+ "type": "spinbox",
+ "min": 1,
+ "max": 128,
+ "step": 1
}
],
"children": [
@@ -17,7 +38,7 @@
"params": [
{
"name": "Code",
- "default": "__kernel void process(__global const float* in_pos, __global const float* in_col, int in_count, __global float* out_pos, __global float* out_col, __global int* out_count, int max_vertices) {\n int id = get_global_id(0);\n if (id == 0) {\n float size = chf(\"Size\", 1.0f);\n float center_x = 0.0f;\n float center_y = 0.0f;\n float center_z = 0.0f;\n int divs = 16;\n float half_size = size * 0.5f;\n float step = size / (float)divs;\n float inv = size > 0.0f ? 1.0f / size : 0.0f;\n int count = 0;\n for (int i = 0; i < divs; i++) {\n float x0 = -half_size + (float)i * step;\n float x1 = x0 + step;\n for (int j = 0; j < divs; j++) {\n float z0 = -half_size + (float)j * step;\n float z1 = z0 + step;\n float px[6] = {x0, x1, x1, x0, x0, x1};\n float pz[6] = {z0, z1, z0, z0, z1, z1};\n for (int v = 0; v < 6; v++) {\n int idx = count++;\n if (idx < max_vertices) {\n out_pos[idx * 3 + 0] = center_x + px[v];\n out_pos[idx * 3 + 1] = center_y;\n out_pos[idx * 3 + 2] = center_z + pz[v];\n float fx = (px[v] + half_size) * inv;\n float fz = (pz[v] + half_size) * inv;\n out_col[idx * 3 + 0] = 0.35f + fx * 0.35f;\n out_col[idx * 3 + 1] = 0.45f + fz * 0.35f;\n out_col[idx * 3 + 2] = 0.85f;\n }\n }\n }\n }\n *out_count = count;\n }\n}"
+ "default": "__kernel void process(__global const float* in_pos, __global const float* in_col, int in_count, __global float* out_pos, __global float* out_col, __global int* out_count, int max_vertices) {\n int id = get_global_id(0);\n if (id == 0) {\n float size_x = chf(\"Width\", 1.0f);\n float size_z = chf(\"Length\", 1.0f);\n int cols = chi(\"Columns\", 16);\n int rows = chi(\"Rows\", 16);\n if (cols < 1) cols = 1;\n if (cols > 128) cols = 128;\n if (rows < 1) rows = 1;\n if (rows > 128) rows = 128;\n float center_x = 0.0f;\n float center_y = 0.0f;\n float center_z = 0.0f;\n float half_x = size_x * 0.5f;\n float half_z = size_z * 0.5f;\n float step_x = size_x / (float)cols;\n float step_z = size_z / (float)rows;\n float inv_x = size_x > 0.0f ? 1.0f / size_x : 0.0f;\n float inv_z = size_z > 0.0f ? 1.0f / size_z : 0.0f;\n int count = 0;\n for (int i = 0; i < cols; i++) {\n float x0 = -half_x + (float)i * step_x;\n float x1 = x0 + step_x;\n for (int j = 0; j < rows; j++) {\n float z0 = -half_z + (float)j * step_z;\n float z1 = z0 + step_z;\n float px[6] = {x0, x1, x1, x0, x0, x1};\n float pz[6] = {z0, z1, z0, z0, z1, z1};\n for (int v = 0; v < 6; v++) {\n int idx = count++;\n if (idx < max_vertices) {\n out_pos[idx * 3 + 0] = center_x + px[v];\n out_pos[idx * 3 + 1] = center_y;\n out_pos[idx * 3 + 2] = center_z + pz[v];\n float fx = (px[v] + half_x) * inv_x;\n float fz = (pz[v] + half_z) * inv_z;\n out_col[idx * 3 + 0] = 0.35f + fx * 0.35f;\n out_col[idx * 3 + 1] = 0.45f + fz * 0.35f;\n out_col[idx * 3 + 2] = 0.85f;\n }\n }\n }\n }\n *out_count = count;\n }\n}"
}
],
"position": [
diff --git a/src/main.rs b/src/main.rs
index 4f7449b..fe24536 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -244,14 +244,14 @@ mod tests {
let output1 = plane_template.children.iter().find(|c| c.name == "output1").unwrap();
assert_eq!(output1.node_type, "output");
- let generate = |size: Option<&str>, id: &str| {
+ let generate = |overrides: &[(&str, &str)], id: &str| {
let mut inst = plane_template.clone();
inst.id = id.to_string();
for child in &mut inst.children {
child.id = format!("{}_{}", inst.id, child.name);
}
- if let Some(size) = size {
- inst.params.iter_mut().find(|p| p.name == "Size").unwrap().default = size.to_string();
+ for (name, value) in overrides {
+ inst.params.iter_mut().find(|p| p.name == *name).unwrap().default = value.to_string();
}
let root = FsNode {
id: "root".to_string(),
@@ -276,9 +276,9 @@ mod tests {
geom
};
- // Default Size 1.0: a 16x16 grid of two-triangle cells, flat at y = 0,
- // spanning [-0.5, 0.5] on X and Z.
- let geom = generate(None, "plane_inst");
+ // Defaults (Width/Length 1.0, Columns/Rows 16): a 16x16 grid of
+ // two-triangle cells, flat at y = 0, spanning [-0.5, 0.5] on X and Z.
+ let geom = generate(&[], "plane_inst");
assert_eq!(geom.vertices.len(), 16 * 16 * 6);
let mut max_x: f32 = 0.0;
let mut max_z: f32 = 0.0;
@@ -287,13 +287,19 @@ mod tests {
max_x = max_x.max(v.pos[0].abs());
max_z = max_z.max(v.pos[2].abs());
}
- assert!((max_x - 0.5).abs() < 0.01, "Expected half-size 0.5 on X, got {}", max_x);
- assert!((max_z - 0.5).abs() < 0.01, "Expected half-size 0.5 on Z, got {}", max_z);
+ assert!((max_x - 0.5).abs() < 0.01, "Expected half-width 0.5 on X, got {}", max_x);
+ assert!((max_z - 0.5).abs() < 0.01, "Expected half-length 0.5 on Z, got {}", max_z);
- // Size override 2.0 spans [-1, 1].
- let geom_2 = generate(Some("2.0"), "plane_inst_2");
+ // Width and Length size their axes independently.
+ let geom_2 = generate(&[("Width", "2.0"), ("Length", "3.0")], "plane_inst_2");
let max_x_2 = geom_2.vertices.iter().map(|v| v.pos[0].abs()).fold(0.0f32, f32::max);
- assert!((max_x_2 - 1.0).abs() < 0.01, "Expected half-size 1.0 on X, got {}", max_x_2);
+ let max_z_2 = geom_2.vertices.iter().map(|v| v.pos[2].abs()).fold(0.0f32, f32::max);
+ assert!((max_x_2 - 1.0).abs() < 0.01, "Expected half-width 1.0 on X, got {}", max_x_2);
+ assert!((max_z_2 - 1.5).abs() < 0.01, "Expected half-length 1.5 on Z, got {}", max_z_2);
+
+ // Columns/Rows control the cell counts per axis.
+ let geom_3 = generate(&[("Columns", "4"), ("Rows", "8")], "plane_inst_3");
+ assert_eq!(geom_3.vertices.len(), 4 * 8 * 6);
}
#[test]