ferroui_base/rendering/composition/server/
server_composition_geometry.rs1use super::{
9 impl_server_render_resource, IAnimatedServerObject, IServerObject, IServerRenderResource, ServerCompositor,
10 ServerRenderResource,
11};
12use crate::media::{FillRule, GeometryBuilder};
13use crate::platform::{IGeometryImpl, IPlatformRenderInterface};
14use crate::rendering::composition::generated::{
15 ServerCompositionEllipseGeometryHooks, ServerCompositionEllipseGeometryProps, ServerCompositionGeometryHooks,
16 ServerCompositionGeometryProps, ServerCompositionLineGeometryHooks, ServerCompositionLineGeometryProps,
17 ServerCompositionPathGeometryHooks, ServerCompositionPathGeometryProps, ServerCompositionRectangleGeometryHooks,
18 ServerCompositionRectangleGeometryProps, ServerCompositionRoundedRectangleGeometryHooks,
19 ServerCompositionRoundedRectangleGeometryProps,
20};
21use crate::rendering::composition::transport::BatchStreamReader;
22use crate::{Point, Rect};
23use std::any::{Any, TypeId};
24use std::cell::RefCell;
25use std::rc::{Rc, Weak};
26use std::sync::Arc;
27use std::time::Duration;
28
29pub trait IServerCompositionGeometry {
31 fn platform_geometry(&self) -> Option<Arc<dyn IGeometryImpl>>;
36
37 fn geometry_props(&self) -> &ServerCompositionGeometryProps;
39}
40
41pub fn server_geometry_of(object: &dyn IServerObject) -> Option<&dyn IServerCompositionGeometry> {
43 let any = object.as_any();
44 if let Some(geometry) = any.downcast_ref::<ServerCompositionRectangleGeometry>() {
45 return Some(geometry);
46 }
47 if let Some(geometry) = any.downcast_ref::<ServerCompositionRoundedRectangleGeometry>() {
48 return Some(geometry);
49 }
50 if let Some(geometry) = any.downcast_ref::<ServerCompositionEllipseGeometry>() {
51 return Some(geometry);
52 }
53 if let Some(geometry) = any.downcast_ref::<ServerCompositionLineGeometry>() {
54 return Some(geometry);
55 }
56 if let Some(geometry) = any.downcast_ref::<ServerCompositionPathGeometry>() {
57 return Some(geometry);
58 }
59 None
60}
61
62pub fn platform_geometry_of(object: &Rc<dyn IServerObject>) -> Option<Arc<dyn IGeometryImpl>> {
65 server_geometry_of(&**object)?.platform_geometry()
66}
67
68fn widen(value: f32) -> f64 {
81 value.to_string().parse().unwrap_or(value as f64)
82}
83
84pub fn trim_geometry(
85 geometry: Arc<dyn IGeometryImpl>,
86 trim_start: f32,
87 trim_end: f32,
88 trim_offset: f32,
89 render_interface: &dyn IPlatformRenderInterface,
90) -> Option<Arc<dyn IGeometryImpl>> {
91 let mut start = widen(trim_start).clamp(0.0, 1.0);
92 let mut end = widen(trim_end).clamp(0.0, 1.0);
93 if start > end {
94 std::mem::swap(&mut start, &mut end);
95 }
96 if start <= 0.0 && end >= 1.0 {
97 return Some(geometry);
98 }
99 if end - start <= 0.0 {
100 return None;
101 }
102 let length = geometry.contour_length();
103 if length.is_nan() || length <= 0.0 {
104 return Some(geometry);
105 }
106 let from = (start + widen(trim_offset)).rem_euclid(1.0);
107 let to = from + (end - start);
108 let segment = |a: f64, b: f64| geometry.try_get_segment(a * length, b * length, true);
109 let trimmed = if to <= 1.0 {
110 segment(from, to)
111 } else {
112 match (segment(from, 1.0), segment(0.0, to - 1.0)) {
113 (Some(first), Some(second)) => {
114 Some(render_interface.create_geometry_group(FillRule::NonZero, &[first, second]))
115 }
116 _ => None,
117 }
118 };
119 Some(trimmed.unwrap_or(geometry))
120}
121
122#[derive(Default)]
124struct GeometryCache {
125 value: RefCell<Option<Option<Arc<dyn IGeometryImpl>>>>,
127}
128
129impl GeometryCache {
130 fn clear(&self) {
131 *self.value.borrow_mut() = None;
132 }
133
134 fn get_or_build(
135 &self,
136 compositor: Option<Rc<ServerCompositor>>,
137 props: &ServerCompositionGeometryProps,
138 build: impl FnOnce(&dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>>,
139 ) -> Option<Arc<dyn IGeometryImpl>> {
140 if let Some(value) = &*self.value.borrow() {
141 return value.clone();
142 }
143 let compositor = compositor?;
144 let built = compositor.render_interface().with_platform_render_interface(|render_interface| {
147 build(render_interface).and_then(|geometry| {
148 trim_geometry(geometry, props.trim_start(), props.trim_end(), props.trim_offset(), render_interface)
149 })
150 })?;
151 *self.value.borrow_mut() = Some(built.clone());
152 built
153 }
154}
155
156macro_rules! server_geometry {
160 ($(#[$meta:meta])* $name:ident, $props:ident, $hooks:ident) => {
161 $(#[$meta])*
162 pub struct $name {
163 base: ServerRenderResource,
164 props: $props,
165 cache: GeometryCache,
166 }
167
168 impl $name {
169 pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<$name> {
172 let geometry = Rc::new_cyclic(|this: &Weak<$name>| $name {
173 base: ServerRenderResource::new(
174 compositor,
175 this.clone() as Weak<dyn IAnimatedServerObject>,
176 this.clone() as Weak<dyn IServerRenderResource>,
177 ),
178 props: $props::new(),
179 cache: GeometryCache::default(),
180 });
181 geometry.base.object().activate();
182 geometry
183 }
184
185 pub fn props(&self) -> &$props {
187 &self.props
188 }
189
190 pub fn is_disposed(&self) -> bool {
191 self.base.is_disposed()
192 }
193 }
194
195 impl_server_render_resource!($name, base, composition_property: $props::get_composition_property);
196
197 impl ServerCompositionGeometryHooks for $name {}
198
199 impl $hooks for $name {}
200
201 impl IServerCompositionGeometry for $name {
202 fn platform_geometry(&self) -> Option<Arc<dyn IGeometryImpl>> {
203 self.cache.get_or_build(self.base.compositor(), self.props.base(), |render_interface| {
204 self.build(render_interface)
205 })
206 }
207
208 fn geometry_props(&self) -> &ServerCompositionGeometryProps {
209 self.props.base()
210 }
211 }
212
213 impl IServerObject for $name {
214 fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
215 self.props.deserialize_changes_core(self, reader, committed_at);
216 }
217
218 fn values_invalidated(&self) {
221 self.cache.clear();
222 self.base.core().invalidated(self);
223 }
224
225 fn dispose(&self) {
226 self.cache.clear();
227 self.base.core().dispose();
228 }
229
230 fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
231 Some(self)
232 }
233
234 fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
235 Some(self)
236 }
237
238 fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
239 self.props.find_props(type_id)
240 }
241
242 fn as_any(&self) -> &dyn Any {
243 self
244 }
245
246 fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
247 self
248 }
249 }
250 };
251}
252
253server_geometry!(
254 ServerCompositionRectangleGeometry,
257 ServerCompositionRectangleGeometryProps,
258 ServerCompositionRectangleGeometryHooks
259);
260
261impl ServerCompositionRectangleGeometry {
262 pub fn rect(&self) -> Rect {
264 let (offset, size) = (self.props.offset(), self.props.size());
265 Rect::new(offset.x, offset.y, size.x.max(0.0), size.y.max(0.0))
266 }
267
268 fn build(&self, render_interface: &dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>> {
269 Some(render_interface.create_rectangle_geometry(self.rect()))
270 }
271}
272
273server_geometry!(
274 ServerCompositionRoundedRectangleGeometry,
277 ServerCompositionRoundedRectangleGeometryProps,
278 ServerCompositionRoundedRectangleGeometryHooks
279);
280
281impl ServerCompositionRoundedRectangleGeometry {
282 pub fn rect(&self) -> Rect {
284 let (offset, size) = (self.props.offset(), self.props.size());
285 Rect::new(offset.x, offset.y, size.x.max(0.0), size.y.max(0.0))
286 }
287
288 pub fn radii(&self) -> (f64, f64) {
290 let rect = self.rect();
291 let radius = self.props.corner_radius();
292 (radius.x.clamp(0.0, rect.width / 2.0), radius.y.clamp(0.0, rect.height / 2.0))
293 }
294
295 fn build(&self, render_interface: &dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>> {
296 let rect = self.rect();
297 let (radius_x, radius_y) = self.radii();
298 if radius_x <= 0.0 || radius_y <= 0.0 {
299 return Some(render_interface.create_rectangle_geometry(rect));
300 }
301 let mut builder = render_interface.create_stream_geometry_builder(None);
302 GeometryBuilder::draw_rounded_corners_rectangle(&mut *builder, rect, radius_x, radius_y);
303 let geometry = builder.to_geometry();
304 builder.dispose();
305 Some(geometry)
306 }
307}
308
309server_geometry!(
310 ServerCompositionEllipseGeometry,
313 ServerCompositionEllipseGeometryProps,
314 ServerCompositionEllipseGeometryHooks
315);
316
317impl ServerCompositionEllipseGeometry {
318 pub fn rect(&self) -> Rect {
320 let (center, radius) = (self.props.center(), self.props.radius());
321 let (radius_x, radius_y) = (radius.x.abs(), radius.y.abs());
322 Rect::new(center.x - radius_x, center.y - radius_y, radius_x * 2.0, radius_y * 2.0)
323 }
324
325 fn build(&self, render_interface: &dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>> {
326 Some(render_interface.create_ellipse_geometry(self.rect()))
327 }
328}
329
330server_geometry!(
331 ServerCompositionLineGeometry,
334 ServerCompositionLineGeometryProps,
335 ServerCompositionLineGeometryHooks
336);
337
338impl ServerCompositionLineGeometry {
339 fn build(&self, render_interface: &dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>> {
340 let (start, end) = (self.props.start(), self.props.end());
341 Some(render_interface.create_line_geometry(Point::new(start.x, start.y), Point::new(end.x, end.y)))
342 }
343}
344
345server_geometry!(
346 ServerCompositionPathGeometry,
349 ServerCompositionPathGeometryProps,
350 ServerCompositionPathGeometryHooks
351);
352
353impl ServerCompositionPathGeometry {
354 fn build(&self, _render_interface: &dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>> {
355 self.props.path().map(|path| path.geometry().clone())
356 }
357}