lumen_engine/node/vector/
shape.rs1use crate::gpu::{
2 BoundFrame, CompiledOutput, FrameBindContext, FrameBinding, GpuCompileNode, GpuFrameBindNode,
3};
4use crate::node::{NodeId, NodeProperty, PortRef};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, lumen_macros::NodeEnum)]
7#[repr(i64)]
8pub enum ShapeGeometryKind {
9 Rectangle = 0,
10 Ellipse = 1,
11 Polygon = 2,
12}
13
14impl ShapeGeometryKind {
15 pub fn from_int(value: i64) -> Self {
16 match value {
17 1 => Self::Ellipse,
18 2 => Self::Polygon,
19 _ => Self::Rectangle,
20 }
21 }
22}
23
24#[derive(Debug, Clone, lumen_macros::Node)]
26#[node(kind = "shape", name = "Shape", category = "vector")]
27pub struct Shape {
28 pub id: NodeId,
29 #[property(kind = "enum", enum_type = ShapeGeometryKind)]
31 pub geometry_kind: NodeProperty,
32 #[property(kind = "int", min = 1, step = 1)]
34 pub width: NodeProperty,
35 #[property(kind = "int", min = 1, step = 1)]
37 pub height: NodeProperty,
38 #[property(kind = "float", min = 0, step = 1)]
40 pub border_radius: NodeProperty,
41 #[property(
43 kind = "string",
44 name = "Polygon points",
45 format = "point_list",
46 multiline,
47 recommended_rows = 3
48 )]
49 pub polygon_points: NodeProperty,
50 #[property(kind = "vec2")]
52 pub position: NodeProperty,
53 #[property(kind = "bool")]
55 pub fill_enabled: NodeProperty,
56 #[property(kind = "color")]
58 pub fill_color: NodeProperty,
59 #[property(kind = "bool")]
61 pub stroke_enabled: NodeProperty,
62 #[property(kind = "color")]
64 pub stroke_color: NodeProperty,
65 #[property(kind = "float", min = 0, step = 0.5)]
67 pub stroke_width: NodeProperty,
68}
69
70impl Default for Shape {
71 fn default() -> Self {
72 Self {
73 id: NodeId::new(0),
74 geometry_kind: NodeProperty::Int(ShapeGeometryKind::Rectangle as i64),
75 width: NodeProperty::Int(1),
76 height: NodeProperty::Int(1),
77 border_radius: NodeProperty::Float(0.0),
78 polygon_points: NodeProperty::String(String::new()),
79 position: NodeProperty::Vec2((0.0, 0.0)),
80 fill_enabled: NodeProperty::Bool(true),
81 fill_color: NodeProperty::Color([255, 255, 255, 255]),
82 stroke_enabled: NodeProperty::Bool(false),
83 stroke_color: NodeProperty::Color([0, 0, 0, 255]),
84 stroke_width: NodeProperty::Float(1.0),
85 }
86 }
87}
88
89impl GpuCompileNode for Shape {
90 fn compile_gpu(
91 &self,
92 ctx: &mut crate::gpu::CompileContext<'_>,
93 port: &PortRef,
94 ) -> crate::Result<CompiledOutput> {
95 crate::node::vector::renderer::VectorRenderer::new(ctx).compile_shape(self, port)
96 }
97}
98
99impl GpuFrameBindNode for Shape {
100 fn bind_gpu_frame(
101 &self,
102 ctx: &FrameBindContext<'_>,
103 binding: &FrameBinding,
104 bound: &mut BoundFrame,
105 ) -> crate::Result<()> {
106 let FrameBinding::Shape {
107 node_id,
108 geometry_kind,
109 width,
110 height,
111 border_radius,
112 position,
113 fill_enabled,
114 fill_color,
115 stroke_enabled,
116 stroke_color,
117 stroke_width,
118 buffer,
119 } = binding
120 else {
121 return Ok(());
122 };
123 let (x, y) = position.resolve_vec2(
124 *node_id,
125 "position",
126 &ctx.expr_context(*node_id, "position"),
127 )?;
128 let fill = fill_color.resolve_color(
129 *node_id,
130 "fill_color",
131 &ctx.expr_context(*node_id, "fill_color"),
132 )?;
133 let stroke = stroke_color.resolve_color(
134 *node_id,
135 "stroke_color",
136 &ctx.expr_context(*node_id, "stroke_color"),
137 )?;
138 let mut flags = 0;
139 if fill_enabled.resolve_bool(
140 *node_id,
141 "fill_enabled",
142 &ctx.expr_context(*node_id, "fill_enabled"),
143 )? {
144 flags |= 1;
145 }
146 if stroke_enabled.resolve_bool(
147 *node_id,
148 "stroke_enabled",
149 &ctx.expr_context(*node_id, "stroke_enabled"),
150 )? {
151 flags |= 2;
152 }
153 let params = super::renderer::ShapeParams {
154 fill_color: rgba8_to_f32(fill),
155 stroke_color: rgba8_to_f32(stroke),
156 position: [x as f32, y as f32],
157 size: [
158 width
159 .resolve_int(*node_id, "width", &ctx.expr_context(*node_id, "width"))?
160 .max(1) as f32,
161 height
162 .resolve_int(*node_id, "height", &ctx.expr_context(*node_id, "height"))?
163 .max(1) as f32,
164 ],
165 border_radius: border_radius.resolve_float(
166 *node_id,
167 "border_radius",
168 &ctx.expr_context(*node_id, "border_radius"),
169 )? as f32,
170 stroke_width: stroke_width.resolve_float(
171 *node_id,
172 "stroke_width",
173 &ctx.expr_context(*node_id, "stroke_width"),
174 )? as f32,
175 geometry_kind: ShapeGeometryKind::from_int(geometry_kind.resolve_int(
176 *node_id,
177 "geometry_kind",
178 &ctx.expr_context(*node_id, "geometry_kind"),
179 )?) as u32,
180 flags,
181 };
182 bound.write_buffer(*buffer, 0, bytemuck::bytes_of(¶ms));
183 Ok(())
184 }
185}
186
187fn rgba8_to_f32(color: [u8; 4]) -> [f32; 4] {
188 [
189 f32::from(color[0]) / 255.0,
190 f32::from(color[1]) / 255.0,
191 f32::from(color[2]) / 255.0,
192 f32::from(color[3]) / 255.0,
193 ]
194}