use super::{
impl_server_render_resource, IAnimatedServerObject, IServerObject, IServerRenderResource, ServerCompositor,
ServerRenderResource,
};
use crate::media::{FillRule, GeometryBuilder};
use crate::platform::{IGeometryImpl, IPlatformRenderInterface};
use crate::rendering::composition::generated::{
ServerCompositionEllipseGeometryHooks, ServerCompositionEllipseGeometryProps, ServerCompositionGeometryHooks,
ServerCompositionGeometryProps, ServerCompositionLineGeometryHooks, ServerCompositionLineGeometryProps,
ServerCompositionPathGeometryHooks, ServerCompositionPathGeometryProps, ServerCompositionRectangleGeometryHooks,
ServerCompositionRectangleGeometryProps, ServerCompositionRoundedRectangleGeometryHooks,
ServerCompositionRoundedRectangleGeometryProps,
};
use crate::rendering::composition::transport::BatchStreamReader;
use crate::{Point, Rect};
use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::rc::{Rc, Weak};
use std::sync::Arc;
use std::time::Duration;
pub trait IServerCompositionGeometry {
fn platform_geometry(&self) -> Option<Arc<dyn IGeometryImpl>>;
fn geometry_props(&self) -> &ServerCompositionGeometryProps;
}
pub fn server_geometry_of(object: &dyn IServerObject) -> Option<&dyn IServerCompositionGeometry> {
let any = object.as_any();
if let Some(geometry) = any.downcast_ref::<ServerCompositionRectangleGeometry>() {
return Some(geometry);
}
if let Some(geometry) = any.downcast_ref::<ServerCompositionRoundedRectangleGeometry>() {
return Some(geometry);
}
if let Some(geometry) = any.downcast_ref::<ServerCompositionEllipseGeometry>() {
return Some(geometry);
}
if let Some(geometry) = any.downcast_ref::<ServerCompositionLineGeometry>() {
return Some(geometry);
}
if let Some(geometry) = any.downcast_ref::<ServerCompositionPathGeometry>() {
return Some(geometry);
}
None
}
pub fn platform_geometry_of(object: &Rc<dyn IServerObject>) -> Option<Arc<dyn IGeometryImpl>> {
server_geometry_of(&**object)?.platform_geometry()
}
fn widen(value: f32) -> f64 {
value.to_string().parse().unwrap_or(value as f64)
}
pub fn trim_geometry(
geometry: Arc<dyn IGeometryImpl>,
trim_start: f32,
trim_end: f32,
trim_offset: f32,
render_interface: &dyn IPlatformRenderInterface,
) -> Option<Arc<dyn IGeometryImpl>> {
let mut start = widen(trim_start).clamp(0.0, 1.0);
let mut end = widen(trim_end).clamp(0.0, 1.0);
if start > end {
std::mem::swap(&mut start, &mut end);
}
if start <= 0.0 && end >= 1.0 {
return Some(geometry);
}
if end - start <= 0.0 {
return None;
}
let length = geometry.contour_length();
if length.is_nan() || length <= 0.0 {
return Some(geometry);
}
let from = (start + widen(trim_offset)).rem_euclid(1.0);
let to = from + (end - start);
let segment = |a: f64, b: f64| geometry.try_get_segment(a * length, b * length, true);
let trimmed = if to <= 1.0 {
segment(from, to)
} else {
match (segment(from, 1.0), segment(0.0, to - 1.0)) {
(Some(first), Some(second)) => {
Some(render_interface.create_geometry_group(FillRule::NonZero, &[first, second]))
}
_ => None,
}
};
Some(trimmed.unwrap_or(geometry))
}
#[derive(Default)]
struct GeometryCache {
value: RefCell<Option<Option<Arc<dyn IGeometryImpl>>>>,
}
impl GeometryCache {
fn clear(&self) {
*self.value.borrow_mut() = None;
}
fn get_or_build(
&self,
compositor: Option<Rc<ServerCompositor>>,
props: &ServerCompositionGeometryProps,
build: impl FnOnce(&dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>>,
) -> Option<Arc<dyn IGeometryImpl>> {
if let Some(value) = &*self.value.borrow() {
return value.clone();
}
let compositor = compositor?;
let built = compositor.render_interface().with_platform_render_interface(|render_interface| {
build(render_interface).and_then(|geometry| {
trim_geometry(geometry, props.trim_start(), props.trim_end(), props.trim_offset(), render_interface)
})
})?;
*self.value.borrow_mut() = Some(built.clone());
built
}
}
macro_rules! server_geometry {
($(#[$meta:meta])* $name:ident, $props:ident, $hooks:ident) => {
$(#[$meta])*
pub struct $name {
base: ServerRenderResource,
props: $props,
cache: GeometryCache,
}
impl $name {
pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<$name> {
let geometry = Rc::new_cyclic(|this: &Weak<$name>| $name {
base: ServerRenderResource::new(
compositor,
this.clone() as Weak<dyn IAnimatedServerObject>,
this.clone() as Weak<dyn IServerRenderResource>,
),
props: $props::new(),
cache: GeometryCache::default(),
});
geometry.base.object().activate();
geometry
}
pub fn props(&self) -> &$props {
&self.props
}
pub fn is_disposed(&self) -> bool {
self.base.is_disposed()
}
}
impl_server_render_resource!($name, base, composition_property: $props::get_composition_property);
impl ServerCompositionGeometryHooks for $name {}
impl $hooks for $name {}
impl IServerCompositionGeometry for $name {
fn platform_geometry(&self) -> Option<Arc<dyn IGeometryImpl>> {
self.cache.get_or_build(self.base.compositor(), self.props.base(), |render_interface| {
self.build(render_interface)
})
}
fn geometry_props(&self) -> &ServerCompositionGeometryProps {
self.props.base()
}
}
impl IServerObject for $name {
fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
self.props.deserialize_changes_core(self, reader, committed_at);
}
fn values_invalidated(&self) {
self.cache.clear();
self.base.core().invalidated(self);
}
fn dispose(&self) {
self.cache.clear();
self.base.core().dispose();
}
fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
Some(self)
}
fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
Some(self)
}
fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
self.props.find_props(type_id)
}
fn as_any(&self) -> &dyn Any {
self
}
fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
self
}
}
};
}
server_geometry!(
ServerCompositionRectangleGeometry,
ServerCompositionRectangleGeometryProps,
ServerCompositionRectangleGeometryHooks
);
impl ServerCompositionRectangleGeometry {
pub fn rect(&self) -> Rect {
let (offset, size) = (self.props.offset(), self.props.size());
Rect::new(offset.x, offset.y, size.x.max(0.0), size.y.max(0.0))
}
fn build(&self, render_interface: &dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>> {
Some(render_interface.create_rectangle_geometry(self.rect()))
}
}
server_geometry!(
ServerCompositionRoundedRectangleGeometry,
ServerCompositionRoundedRectangleGeometryProps,
ServerCompositionRoundedRectangleGeometryHooks
);
impl ServerCompositionRoundedRectangleGeometry {
pub fn rect(&self) -> Rect {
let (offset, size) = (self.props.offset(), self.props.size());
Rect::new(offset.x, offset.y, size.x.max(0.0), size.y.max(0.0))
}
pub fn radii(&self) -> (f64, f64) {
let rect = self.rect();
let radius = self.props.corner_radius();
(radius.x.clamp(0.0, rect.width / 2.0), radius.y.clamp(0.0, rect.height / 2.0))
}
fn build(&self, render_interface: &dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>> {
let rect = self.rect();
let (radius_x, radius_y) = self.radii();
if radius_x <= 0.0 || radius_y <= 0.0 {
return Some(render_interface.create_rectangle_geometry(rect));
}
let mut builder = render_interface.create_stream_geometry_builder(None);
GeometryBuilder::draw_rounded_corners_rectangle(&mut *builder, rect, radius_x, radius_y);
let geometry = builder.to_geometry();
builder.dispose();
Some(geometry)
}
}
server_geometry!(
ServerCompositionEllipseGeometry,
ServerCompositionEllipseGeometryProps,
ServerCompositionEllipseGeometryHooks
);
impl ServerCompositionEllipseGeometry {
pub fn rect(&self) -> Rect {
let (center, radius) = (self.props.center(), self.props.radius());
let (radius_x, radius_y) = (radius.x.abs(), radius.y.abs());
Rect::new(center.x - radius_x, center.y - radius_y, radius_x * 2.0, radius_y * 2.0)
}
fn build(&self, render_interface: &dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>> {
Some(render_interface.create_ellipse_geometry(self.rect()))
}
}
server_geometry!(
ServerCompositionLineGeometry,
ServerCompositionLineGeometryProps,
ServerCompositionLineGeometryHooks
);
impl ServerCompositionLineGeometry {
fn build(&self, render_interface: &dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>> {
let (start, end) = (self.props.start(), self.props.end());
Some(render_interface.create_line_geometry(Point::new(start.x, start.y), Point::new(end.x, end.y)))
}
}
server_geometry!(
ServerCompositionPathGeometry,
ServerCompositionPathGeometryProps,
ServerCompositionPathGeometryHooks
);
impl ServerCompositionPathGeometry {
fn build(&self, _render_interface: &dyn IPlatformRenderInterface) -> Option<Arc<dyn IGeometryImpl>> {
self.props.path().map(|path| path.geometry().clone())
}
}