GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
docs: name the family "relief primitives", not "bevels"
"Bevel" was doing two jobs: naming one specific prim — a filled rounded rect
plus a lit roll on its lip — and standing in loosely for the whole lit-surface
family in prose. The second job never fit: a groove, a concave fillet and a
sphere are not bevels in any sense.
"Relief" is what the rest of the stack already says — layout::control_relief
gates the family, the config node is `relief`, cce-files keys its flat-path
mirror RELIEF_RECESSED/RAISED/INSET, and four call sites already said "relief
prims". This makes that the documented name and puts the definition on the
Prim enum, including the split that flat-path hosts depend on: plates carry
their own fill, carves emit shading only over what is already painted.
Deliberately NOT renamed, because they are the shared lit EDGE TREATMENT
rather than the shapes — which is the distinction that makes the family name
worth having:
- Prim::Bevel and PaintCtx::bevel: one specific member, correctly named.
- bevel_width / bevel_depth / bevel_shader / bevel_profile: parameters of
the roll every relief primitive is shaded with. They are also config
registry keys read by string and public API used by eight sibling crates,
so renaming them is a DE-wide change with a config-compat story, not a
terminology pass.
- "banded bevels", "rounded rim bevels", "it bevels like the params plate":
all genuinely about bevelling.
Co-Authored-By: Claude <[email protected]>
CLAUDE.md | 4 +++-
docs/rfc-core-rebuild.md | 2 +-
src/backend/window_runner.rs | 4 ++--
src/layout.rs | 8 +++++---
src/scene/paint.rs | 24 ++++++++++++++++++++++++
src/widget/input/checkbox.rs | 2 +-
src/widget/mod.rs | 2 +-
7 files changed, 37 insertions(+), 9 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index 8c17b46..7623f51 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -22,7 +22,9 @@ set outright.
The wgpu path is retired; cosmic-text used to be reached through **glyphon**, which is gone
too — every `glyphon::` item used here was a cosmic-text re-export, and dropping it takes
wgpu out of the build. There is no HTML/DOM — the UI is GPU primitives (quads, rounded rects with
- per-corner radii, vectors with caps, arcs, circles, bevels). Tessellators live in
+ per-corner radii, vectors with caps, arcs, circles, and the **relief primitives** — the
+ lit-surface family: bevels, plates, recesses, bosses, ridges, fillets, grooves; see the
+ `Prim` enum doc in `src/scene/paint.rs`). Tessellators live in
`backend/window_runner.rs` and are re-exported through `src/engine.rs`.
- It is **both a library and a binary.** `src/lib.rs` is the toolkit; `src/main.rs` is
`DemoApp`, the reference `Application` — a small widget gallery on the Phase 6 target
diff --git a/docs/rfc-core-rebuild.md b/docs/rfc-core-rebuild.md
index 9b1b2c7..cbdd267 100644
--- a/docs/rfc-core-rebuild.md
+++ b/docs/rfc-core-rebuild.md
@@ -50,7 +50,7 @@ methods spanning layout, paint, hit-testing, clipboard, tree expand/collapse, an
long-lived, geometry is re-emitted each frame into one shared vertex buffer and one shader
pipeline. We are not throwing this out.
- **The primitive tessellators** (rounded rects with per-corner radii, vectors with caps,
- arcs, circles, bevels) are solid and reusable as-is.
+ arcs, circles, relief primitives) are solid and reusable as-is.
---
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 350acc6..77592ad 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -1065,7 +1065,7 @@ const DARK_GAIN: f32 = 3.0;
/// A shading value (already scaled by `bevel_depth`) as the two overlay passes: the lit
/// pass is translucent white, the shadow pass translucent black. Painting the
-/// *modulation* instead of a resolved surface color is what lets bevels compose — a step
+/// *modulation* instead of a resolved surface color is what lets relief primitives compose — a step
/// crossing a rim shades the rim's gradient instead of stamping a flat band over it, a
/// lip on a translucent plate no longer doubles its opacity, and a recess needs no
/// knowledge of the surface color it carves.
@@ -1299,7 +1299,7 @@ const FACE_RATIO: f32 = 0.35;
/// under directional light is never uniform, and a perfectly flat fill makes the eye
/// read the (much smaller) edge shading as frame decoration rather than shape.
///
-/// Emitted as the same two-pass white/black overlays as the bevels (see
+/// Emitted as the same two-pass white/black overlays as the relief primitives (see
/// [`overlay_light`]/[`overlay_dark`]): fixed RGB per pass, per-corner alphas clamped at
/// the terminator, bilinear across the quad. The quad is square — its corners poke past
/// a rounded plate's arcs — but the compositor clips the window surface to the same
diff --git a/src/layout.rs b/src/layout.rs
index c6a43e0..6343def 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -1587,9 +1587,11 @@ pub fn corner_span_factor() -> f32 {
}
}
-/// Whether plates/bevels/recesses render through shader2d's per-pixel SDF-lit
-/// plate branch (the default) or the legacy banded vertex shading. `bevel_shader 0`
-/// in config flips back to the old look for A/B comparison.
+/// Whether the relief primitives (see `scene::paint::Prim`) render through
+/// shader2d's per-pixel SDF-lit branch (the default) or the legacy banded vertex
+/// shading. `bevel_shader 0` in config flips back to the old look for A/B
+/// comparison — the key keeps the bevel name because it selects how the shared
+/// lit EDGE is computed, not which shapes exist.
pub fn bevel_shader() -> bool {
lazy_init_style_registry();
get_style_registry().read().unwrap().get_float("bevel_shader").map(|v| v != 0.0).unwrap_or(true)
diff --git a/src/scene/paint.rs b/src/scene/paint.rs
index d730bb3..12d52c2 100644
--- a/src/scene/paint.rs
+++ b/src/scene/paint.rs
@@ -33,6 +33,30 @@ pub enum Cap {
/// `CornerRadii` order.
pub type Radii = (f32, f32, f32, f32);
+/// The **relief primitives** are the members of this enum that describe a lit
+/// surface rather than a flat fill: [`Prim::Bevel`], [`Prim::Plate`],
+/// [`Prim::Recess`], [`Prim::Boss`], [`Prim::Ridge`], [`Prim::ConcaveFillet`],
+/// [`Prim::Groove`] and [`Prim::Sphere`]. They share one lighting model — the
+/// DE's light vector, roll width and profile, per-pixel through shader2d's
+/// SDF branch (see `crate::layout::bevel_shader`) — and split in two:
+///
+/// - **plates** carry their own fill: `Bevel`, `Plate`. Shader mode 1.
+/// - **carves** emit shading ONLY, no fill, over whatever is already painted
+/// beneath: `Recess`, `Boss`, `Ridge`, `ConcaveFillet`, `Groove`. Modes 2-4
+/// and 6-8. (`Sphere`, mode 5, is neither — a lit ball under the same model.)
+///
+/// That split is load-bearing for flat-path hosts, which need one list for the
+/// faces and another for the edges drawn over them (cce-files' `rects` vs
+/// `reliefs`).
+///
+/// Call the family **relief primitives**, not "bevel primitives": `Bevel` is one
+/// specific member — a filled rounded rect plus a lit roll on its lip — and a
+/// groove, a fillet or a sphere is not a bevel in any sense. "Relief" is also
+/// what the rest of the stack already says: `layout::control_relief` gates the
+/// whole family, and the config node is `relief`. The name **bevel** is reserved
+/// for two things: the `Bevel` prim, and the shared *edge treatment* every
+/// relief primitive is shaded with (`bevel_width`, `bevel_depth`,
+/// `bevel_shader`, `bevel_profile` — the lit roll, not the shape).
#[derive(Clone, Debug, PartialEq)]
pub enum Prim {
Quad { rect: Rect, color: [f32; 4] },
diff --git a/src/widget/input/checkbox.rs b/src/widget/input/checkbox.rs
index fd3efca..60c77c2 100644
--- a/src/widget/input/checkbox.rs
+++ b/src/widget/input/checkbox.rs
@@ -330,7 +330,7 @@ impl Toggle {
}
/// A rocker face's UNIFORM lighting overlay, evaluated under the SAME DE
- /// light the bevels answer to: `light_source_position` through the plate
+ /// light the relief primitives answer to: `light_source_position` through the plate
/// model (shader2d's `plate_shade` — ambient floor, diffuse off the
/// normal, expressed relative to the flat face). The rocker reads as a
/// bent plate: the state half tilts OUT toward the viewer, the other IN,
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index aa5a99c..43458e4 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -371,7 +371,7 @@ pub trait WidgetHost {
/// Emit this widget's OWN primitives (non-recursive) into the single paint pass (Phase 3).
/// The default composes the pieces the legacy recursive `all_*` emit for one node: rounded
/// background, plain/decoration quads, circles, and own text. Widgets with richer painting
- /// (borders, bevels, arcs, vectors, SVGs) can override. Recursion into children and clipping
+ /// (borders, relief primitives, arcs, vectors, SVGs) can override. Recursion into children and clipping
/// are handled by the paint walk (`scene::painter`), not here.
fn paint_self(&self, ui: &UiContext, ctx: &mut crate::scene::paint::PaintCtx) {
use crate::scene::layout::Rect;