graphic design tool
git clone https://git.lucas.co/cce-designer.git
feat: Guides Point Marker Color
The meta Point Markers overlay's color joins its size in the Guides node —
a hex color param (the Grid Color pattern) driving the previously
hardcoded tint through State::meta_marker_color, linearized at vertex
build; a change re-collects the overlays immediately. Existing metas and
saved Guides nodes gain the param through the usual ensure migration.
src/app.rs | 4 ++++
src/main.rs | 16 +++++++++++++---
src/project.rs | 9 +++++++++
src/render.rs | 10 ++++++----
4 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/src/app.rs b/src/app.rs
index d962c90..c88fc47 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1096,6 +1096,9 @@ pub struct State {
/// World-unit radius of the meta "Point Markers" overlay — the Guides
/// subnet's "Point Marker Size" control (stored there in thousandths).
pub meta_marker_size: f32,
+ /// sRGB color of the meta "Point Markers" overlay — the Guides subnet's
+ /// "Point Marker Color" control (stored there as hex, like Grid Color).
+ pub meta_marker_color: [f32; 3],
/// The raster scene's model-view-projection and the viewport pane rect in
/// LOGICAL px, cached at staging so the 2D pass can project 3D overlays.
pub last_scene_mvp: Option<Mat4>,
@@ -3278,6 +3281,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
meta_normal_verts: Vec::new(),
meta_normal_count: 0,
meta_marker_size: 0.02,
+ meta_marker_color: [0.85, 0.85, 1.0],
last_scene_mvp: None,
last_scene_view_rect: (0.0, 0.0, 0.0, 0.0),
last_viewport_rt_mode: false,
diff --git a/src/main.rs b/src/main.rs
index d1b6acd..55106ad 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -523,9 +523,16 @@ mod tests {
.expect("Guides has Point Marker Size");
assert_eq!(p.default, "20", "default = 0.02 world units");
p.default = "50".to_string();
+ let c = guides.params.iter_mut().find(|p| p.name == "Point Marker Color")
+ .expect("Guides has Point Marker Color");
+ assert_eq!(c.param_type, "color");
+ c.default = "#ff8000".to_string();
}
state.apply_settings_from_menubar_subnets();
assert!((state.meta_marker_size - 0.05).abs() < 1e-6);
+ assert!((state.meta_marker_color[0] - 1.0).abs() < 0.01);
+ assert!((state.meta_marker_color[1] - 0.5).abs() < 0.01);
+ assert!((state.meta_marker_color[2] - 0.0).abs() < 0.01);
}
/// An old save carries Main/View/Guides/Render at the root with the user's
@@ -1235,7 +1242,7 @@ mod tests {
// Overlays: nothing while the prefs are off…
let mut cache = crate::geometry::SimCache::default();
let (markers, labels, wires, normals) = crate::render::collect_meta_overlays(
- &root, 0.02, &mut crate::geometry::EvalSim::new(0, 0, &mut cache));
+ &root, 0.02, [1.0, 0.5, 0.0], &mut crate::geometry::EvalSim::new(0, 0, &mut cache));
assert!(markers.is_empty() && labels.is_empty() && wires.is_empty() && normals.is_empty());
// …all four overlays for the flagged sphere: 240 marker verts per
@@ -1250,10 +1257,13 @@ mod tests {
assert!(crate::app::meta_pref(&root.children[0], "Wireframe"));
let mut cache = crate::geometry::SimCache::default();
let (markers, labels, wires, normals) = crate::render::collect_meta_overlays(
- &root, 0.02, &mut crate::geometry::EvalSim::new(0, 0, &mut cache));
+ &root, 0.02, [1.0, 0.5, 0.0], &mut crate::geometry::EvalSim::new(0, 0, &mut cache));
assert!(!labels.is_empty() && labels.len() < 16 * 24 * 6);
assert_eq!(markers.len(), labels.len() * 240);
assert!(labels.iter().any(|(_, i)| *i > 0));
+ // The marker color parameter flows into the vertices (linearized).
+ let expect = cce_ui::colors::to_linear_rgb([1.0, 0.5, 0.0]);
+ assert!(markers.iter().all(|v| v.color == expect));
assert_eq!(wires.len(), (16 * 24 * 6 / 3) * 6);
// Normals: one whisker per distinct point, pointing OUT of the
// sphere (center (0, 0.55, 0)) — this pins the winding/negation
@@ -1275,7 +1285,7 @@ mod tests {
root.children[0].geometry_visible = false;
let mut cache = crate::geometry::SimCache::default();
let (markers, labels, wires, normals) = crate::render::collect_meta_overlays(
- &root, 0.02, &mut crate::geometry::EvalSim::new(0, 0, &mut cache));
+ &root, 0.02, [1.0, 0.5, 0.0], &mut crate::geometry::EvalSim::new(0, 0, &mut cache));
assert!(markers.is_empty() && labels.is_empty() && wires.is_empty() && normals.is_empty());
}
diff --git a/src/project.rs b/src/project.rs
index 5453216..1ed1862 100644
--- a/src/project.rs
+++ b/src/project.rs
@@ -691,6 +691,8 @@ impl State {
// (the Grid Thickness convention): 20 = 0.02 world units.
let marker_size_seed = ((self.meta_marker_size * 1000.0).round() as i32).to_string();
ensure_param(guides_node, "Point Marker Size", "spinbox", &marker_size_seed, &[], Some(5.0), Some(100.0), Some(1.0));
+ let marker_color_seed = color_to_hex(self.meta_marker_color);
+ ensure_param(guides_node, "Point Marker Color", "color", &marker_color_seed, &[], None, None, None);
for p in guides_node.params.iter_mut() {
match p.name.as_str() {
"Show Grid Guide" => set_toggle(p, vp_show_grid),
@@ -842,6 +844,13 @@ impl State {
self.rebuild_scene_geometry();
}
}
+ "Point Marker Color" => if let Some(col) = hex_to_color(&p.default) {
+ if col != self.meta_marker_color {
+ self.meta_marker_color = col;
+ // Baked into the marker verts, like the radius.
+ self.rebuild_scene_geometry();
+ }
+ }
_ => {}
}
}
diff --git a/src/render.rs b/src/render.rs
index c2c5ff0..548f66e 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -858,7 +858,7 @@ impl State {
let mut sim_cache = std::mem::take(&mut self.sim_cache);
let (markers, labels, wires, normals) = {
let mut sim = crate::geometry::EvalSim::new(frame, start, &mut sim_cache);
- collect_meta_overlays(&self.fs_root, self.meta_marker_size, &mut sim)
+ collect_meta_overlays(&self.fs_root, self.meta_marker_size, self.meta_marker_color, &mut sim)
};
self.sim_cache = sim_cache;
self.meta_marker_verts = markers;
@@ -899,6 +899,7 @@ impl State {
pub(crate) fn collect_meta_overlays(
root: &FsNode,
point_size: f32,
+ marker_color: [f32; 3],
sim: &mut crate::geometry::EvalSim,
) -> (
Vec<crate::geometry::Vertex3D>,
@@ -915,6 +916,7 @@ pub(crate) fn collect_meta_overlays(
node: &FsNode,
parent_visible: bool,
point_size: f32,
+ marker_color: [f32; 3],
markers: &mut Vec<crate::geometry::Vertex3D>,
labels: &mut Vec<([f32; 3], u32)>,
wires: &mut Vec<crate::geometry::Vertex3D>,
@@ -956,7 +958,7 @@ pub(crate) fn collect_meta_overlays(
markers.extend(crate::geometry::points_vertices(
&src,
point_size,
- cce_ui::colors::to_linear_rgb([0.85, 0.85, 1.0]),
+ cce_ui::colors::to_linear_rgb(marker_color),
));
}
if want_normals {
@@ -1016,11 +1018,11 @@ pub(crate) fn collect_meta_overlays(
}
}
for c in &node.children {
- visit(root, c, is_visible, point_size, markers, labels, wires, normals, sim);
+ visit(root, c, is_visible, point_size, marker_color, markers, labels, wires, normals, sim);
}
}
for c in &root.children {
- visit(root, c, true, point_size, &mut markers, &mut labels, &mut wires, &mut normals, sim);
+ visit(root, c, true, point_size, marker_color, &mut markers, &mut labels, &mut wires, &mut normals, sim);
}
// A dense mesh can label tens of thousands of points; the text pass is
// per-frame, so cap it rather than melt the frame rate.