| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use std::{ |
| cell::{Cell, Ref, RefCell}, |
| collections::{HashMap, VecDeque}, |
| iter, |
| rc::Rc, |
| }; |
| |
| use crate::{ |
| animation::{LinearAnimation, StateMachine}, |
| component::Component, |
| component_dirt::ComponentDirt, |
| container_component::ContainerComponent, |
| core::{Core, CoreContext, Object, ObjectRef, OnAdded, Property}, |
| dependency_sorter::DependencySorter, |
| draw_target::{DrawTarget, DrawTargetPlacement}, |
| drawable::Drawable, |
| math::{self, Aabb, Mat}, |
| option_cell::OptionCell, |
| renderer::Renderer, |
| shapes::{CommandPath, CommandPathBuilder, ShapePaintContainer}, |
| status_code::StatusCode, |
| }; |
| |
| #[derive(Clone, Debug)] |
| pub struct ObjectsIter<T: Core> { |
| objects: Rc<RefCell<Vec<Object<T>>>>, |
| head: usize, |
| tail: usize, |
| } |
| |
| impl<T: Core> ObjectsIter<T> { |
| fn new(objects: Rc<RefCell<Vec<Object<T>>>>) -> Self { |
| let len = objects.borrow().len(); |
| Self { objects, head: 0, tail: len } |
| } |
| } |
| |
| impl<T: Core> Iterator for ObjectsIter<T> { |
| type Item = Object<T>; |
| |
| #[inline] |
| fn next(&mut self) -> Option<Self::Item> { |
| if self.head == self.tail { |
| return None; |
| } |
| |
| let result = self.objects.borrow().iter().nth(self.head).cloned(); |
| |
| if result.is_some() { |
| self.head += 1; |
| } |
| |
| result |
| } |
| |
| #[inline] |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| let len = self.tail - self.head; |
| (len, Some(len)) |
| } |
| |
| #[inline] |
| fn count(self) -> usize { |
| self.tail - self.head |
| } |
| |
| #[inline] |
| fn nth(&mut self, n: usize) -> Option<Self::Item> { |
| self.objects.borrow().iter().nth(self.head + n).cloned() |
| } |
| } |
| |
| impl<T: Core> DoubleEndedIterator for ObjectsIter<T> { |
| #[inline] |
| fn next_back(&mut self) -> Option<Self::Item> { |
| if self.head == self.tail { |
| return None; |
| } |
| |
| let result = self.objects.borrow().iter().nth(self.tail - 1).cloned(); |
| |
| if result.is_some() { |
| self.tail -= 1; |
| } |
| |
| result |
| } |
| |
| #[inline] |
| fn nth_back(&mut self, n: usize) -> Option<Self::Item> { |
| if n >= self.tail { |
| return None; |
| } |
| |
| self.objects.borrow().iter().nth(self.tail - 1 - n).cloned() |
| } |
| } |
| |
| impl<T: Core> ExactSizeIterator for ObjectsIter<T> { |
| #[inline] |
| fn len(&self) -> usize { |
| self.tail - self.head |
| } |
| } |
| |
| #[derive(Debug, Default)] |
| struct ArtboardInner { |
| objects: RefCell<Vec<Object>>, |
| animations: Rc<RefCell<Vec<Object<LinearAnimation>>>>, |
| state_machines: Rc<RefCell<Vec<Object<StateMachine>>>>, |
| dependency_order: RefCell<VecDeque<Object<Component>>>, |
| drawables: RefCell<Vec<Object<Drawable>>>, |
| draw_targets: RefCell<Vec<Object<DrawTarget>>>, |
| dirt_depth: Cell<usize>, |
| background_path: RefCell<Option<CommandPath>>, |
| clip_path: RefCell<Option<CommandPath>>, |
| first_drawable: OptionCell<Object<Drawable>>, |
| root: OptionCell<Rc<dyn Core>>, |
| } |
| |
| fn objects_of<T: Core>(objects: &[Object]) -> impl Iterator<Item = Object<T>> + '_ { |
| objects.iter().cloned().filter_map(|object| object.try_cast()) |
| } |
| |
| impl ArtboardInner { |
| pub fn initialize(&self) -> StatusCode { |
| for object in self.objects.borrow().iter() { |
| let code = object.as_ref().on_added_dirty(self); |
| if code != StatusCode::Ok { |
| return code; |
| } |
| } |
| |
| for object in self.animations.borrow().iter() { |
| let code = object.as_ref().on_added_dirty(self); |
| if code != StatusCode::Ok { |
| return code; |
| } |
| } |
| |
| for object in self.state_machines.borrow().iter() { |
| let code = object.as_ref().on_added_dirty(self); |
| if code != StatusCode::Ok { |
| return code; |
| } |
| } |
| |
| let mut component_draw_rules = HashMap::new(); |
| |
| for object in self.objects.borrow().iter() { |
| let code = object.as_ref().on_added_clean(self); |
| if code != StatusCode::Ok { |
| return code; |
| } |
| |
| if let Some(draw_rules) = object.try_cast() { |
| if let Some(component) = self |
| .resolve(draw_rules.cast::<Component>().as_ref().parent_id() as usize) |
| .and_then(|core| core.try_cast()) |
| { |
| component_draw_rules.insert(component, draw_rules); |
| } else { |
| return StatusCode::MissingObject; |
| } |
| } |
| } |
| |
| for object in self.animations.borrow().iter() { |
| let code = object.as_ref().on_added_clean(self); |
| if code != StatusCode::Ok { |
| return code; |
| } |
| } |
| |
| for object in self.state_machines.borrow().iter() { |
| let code = object.as_ref().on_added_clean(self); |
| if code != StatusCode::Ok { |
| return code; |
| } |
| } |
| |
| for component in objects_of::<Component>(self.objects.borrow().as_slice()) { |
| component.as_ref().build_dependencies(); |
| } |
| |
| for drawable in objects_of::<Drawable>(self.objects.borrow().as_slice()) { |
| self.drawables.borrow_mut().push(drawable.clone()); |
| |
| let parents = iter::once(drawable.cast::<ContainerComponent>()) |
| .chain(drawable.cast::<Component>().as_ref().parents()); |
| |
| for parent in parents { |
| if let Some(draw_rules) = component_draw_rules.get(&parent) { |
| drawable.as_ref().flattened_draw_rules.set(Some(draw_rules.clone())); |
| break; |
| } |
| } |
| } |
| |
| self.sort_dependencies(); |
| |
| let root: Rc<dyn Core> = Rc::new(DrawTarget::default()); |
| self.root.set(Some(Rc::clone(&root))); |
| let root = Object::new(&root); |
| |
| for draw_target in objects_of::<DrawTarget>(self.objects.borrow().as_slice()) { |
| root.cast::<Component>().as_ref().push_dependent(draw_target.cast()); |
| |
| if let Some(dependent_rules) = draw_target |
| .as_ref() |
| .drawable() |
| .expect("DrawTarget has no Drawable set") |
| .as_ref() |
| .flattened_draw_rules |
| .get() |
| { |
| for dependent_target in objects_of::<DrawTarget>(self.objects.borrow().as_slice()) { |
| let dependent_target = dependent_target.cast::<Component>(); |
| let dependent_target = dependent_target.as_ref(); |
| if let Some(parent) = dependent_target.parent() { |
| if parent.ptr_eq(&dependent_rules) { |
| dependent_target.push_dependent(draw_target.cast()); |
| } |
| } |
| } |
| } |
| } |
| |
| let mut sorter = DependencySorter::default(); |
| let mut draw_target_order = VecDeque::new(); |
| |
| sorter.sort(root, &mut draw_target_order); |
| |
| self.draw_targets |
| .borrow_mut() |
| .extend(draw_target_order.into_iter().map(|component| component.cast())); |
| |
| StatusCode::Ok |
| } |
| |
| pub fn sort_draw_order(&self) { |
| for target in &*self.draw_targets.borrow() { |
| let target = target.as_ref(); |
| target.first.set(None); |
| target.last.set(None); |
| } |
| |
| self.first_drawable.set(None); |
| let mut last_drawable = None; |
| |
| for drawable in &*self.drawables.borrow() { |
| let drawable_ref = drawable.as_ref(); |
| if let Some(target) = drawable_ref |
| .flattened_draw_rules |
| .get() |
| .and_then(|rules| rules.as_ref().active_target()) |
| { |
| let target = target.as_ref(); |
| if let (Some(_), Some(last)) = (target.first.get(), target.last.get()) { |
| last.as_ref().next.set(Some(drawable.clone())); |
| drawable_ref.prev.set(Some(last)); |
| target.last.set(Some(drawable.clone())); |
| } else { |
| target.first.set(Some(drawable.clone())); |
| target.last.set(Some(drawable.clone())); |
| drawable_ref.prev.set(None); |
| } |
| drawable_ref.next.set(None); |
| } else { |
| drawable_ref.prev.set(last_drawable.clone()); |
| drawable_ref.next.set(None); |
| |
| if let Some(ref last_drawable_ref) = last_drawable { |
| last_drawable_ref.as_ref().next.set(Some(drawable.clone())); |
| last_drawable = Some(drawable.clone()); |
| } else { |
| last_drawable = Some(drawable.clone()); |
| self.first_drawable.set(Some(drawable.clone())); |
| } |
| } |
| } |
| |
| for rule in &*self.draw_targets.borrow() { |
| let rule = rule.as_ref(); |
| if let (Some(ref rule_first), Some(ref rule_last)) = (rule.first.get(), rule.last.get()) |
| { |
| let rule_last_ref = rule_last.as_ref(); |
| if let Some(ref target_drawable) = rule.drawable() { |
| let target_drawable_ref = target_drawable.as_ref(); |
| match rule.placement() { |
| DrawTargetPlacement::Before => { |
| if let Some(prev) = target_drawable_ref.prev.get() { |
| prev.as_ref().next.set(Some(rule_first.clone())); |
| rule_first.as_ref().prev.set(Some(prev)); |
| } |
| |
| if Some(target_drawable) == self.first_drawable.get().as_ref() { |
| self.first_drawable.set(Some(rule_first.clone())); |
| } |
| |
| target_drawable_ref.prev.set(Some(rule_last.clone())); |
| rule_last_ref.next.set(Some(target_drawable.clone())); |
| } |
| DrawTargetPlacement::After => { |
| if let Some(next) = target_drawable_ref.next.get() { |
| next.as_ref().prev.set(Some(rule_last.clone())); |
| rule_last_ref.next.set(target_drawable_ref.next.get()); |
| } |
| |
| if Some(target_drawable) == last_drawable.as_ref() { |
| last_drawable = Some(rule_last.clone()); |
| } |
| |
| target_drawable_ref.next.set(Some(rule_first.clone())); |
| rule_last_ref.prev.set(Some(target_drawable.clone())); |
| } |
| } |
| } |
| } |
| } |
| |
| self.first_drawable.set(last_drawable); |
| } |
| |
| fn component(&self) -> Object<Component> { |
| self.objects.borrow()[0].try_cast().expect("first object in Artboard must be itself") |
| } |
| |
| fn sort_dependencies(&self) { |
| let mut sorter = DependencySorter::default(); |
| |
| let component = self.component(); |
| sorter.sort(component.clone(), &mut *self.dependency_order.borrow_mut()); |
| |
| for (component, graph_order) in self.dependency_order.borrow().iter().zip(0..) { |
| component.as_ref().graph_order.set(graph_order); |
| } |
| |
| component.as_ref().dirt.set(component.as_ref().dirt.get() | ComponentDirt::COMPONENTS); |
| } |
| |
| pub fn push_object(&self, object: Object) { |
| self.objects.borrow_mut().push(object); |
| } |
| |
| pub fn push_animation(&self, animation: Object<LinearAnimation>) { |
| self.animations.borrow_mut().push(animation); |
| } |
| |
| pub fn push_state_machine(&self, state_machine: Object<StateMachine>) { |
| self.state_machines.borrow_mut().push(state_machine); |
| } |
| |
| pub fn on_component_dirty(&self, component: &Component) { |
| let this_component = self.component(); |
| let this_component = this_component.as_ref(); |
| this_component.dirt.set(this_component.dirt.get() | ComponentDirt::COMPONENTS); |
| |
| self.dirt_depth.set(self.dirt_depth.get().min(component.graph_order.get())); |
| } |
| |
| pub fn update_components(&self, component: ObjectRef<'_, Component>) -> bool { |
| let mut step = 0; |
| while component.has_dirt(ComponentDirt::COMPONENTS) && step < 100 { |
| for (i, component) in self.dependency_order.borrow().iter().enumerate() { |
| self.dirt_depth.set(i); |
| |
| let component = component.as_ref(); |
| let dirt = component.dirt.get(); |
| if dirt.is_empty() { |
| continue; |
| } |
| |
| component.dirt.set(ComponentDirt::empty()); |
| component.update(dirt); |
| |
| if self.dirt_depth.get() < i { |
| break; |
| } |
| } |
| |
| step += 1; |
| } |
| |
| false |
| } |
| } |
| |
| #[derive(Debug, Default)] |
| pub struct Artboard { |
| container_component: ContainerComponent, |
| shape_paint_container: ShapePaintContainer, |
| width: Property<f32>, |
| height: Property<f32>, |
| x: Property<f32>, |
| y: Property<f32>, |
| origin_x: Property<f32>, |
| origin_y: Property<f32>, |
| inner: ArtboardInner, |
| } |
| |
| impl ObjectRef<'_, Artboard> { |
| pub fn width(&self) -> f32 { |
| self.width.get() |
| } |
| |
| pub fn set_width(&self, width: f32) { |
| self.width.set(width); |
| } |
| |
| pub fn height(&self) -> f32 { |
| self.height.get() |
| } |
| |
| pub fn set_height(&self, height: f32) { |
| self.height.set(height); |
| } |
| |
| pub fn x(&self) -> f32 { |
| self.x.get() |
| } |
| |
| pub fn set_x(&self, x: f32) { |
| self.x.set(x); |
| } |
| |
| pub fn y(&self) -> f32 { |
| self.y.get() |
| } |
| |
| pub fn set_y(&self, y: f32) { |
| self.y.set(y); |
| } |
| |
| pub fn origin_x(&self) -> f32 { |
| self.origin_x.get() |
| } |
| |
| pub fn set_origin_x(&self, origin_x: f32) { |
| self.origin_x.set(origin_x); |
| } |
| |
| pub fn origin_y(&self) -> f32 { |
| self.origin_y.get() |
| } |
| |
| pub fn set_origin_y(&self, origin_y: f32) { |
| self.origin_y.set(origin_y); |
| } |
| } |
| |
| impl ObjectRef<'_, Artboard> { |
| pub fn initialize(&self) -> StatusCode { |
| self.inner.initialize() |
| } |
| |
| pub fn push_object(&self, object: Object) { |
| self.inner.push_object(object); |
| } |
| |
| pub(crate) fn objects(&self) -> Ref<'_, [Object]> { |
| Ref::map(self.inner.objects.borrow(), |objects| objects.as_slice()) |
| } |
| |
| pub fn push_animation(&self, animation: Object<LinearAnimation>) { |
| self.inner.push_animation(animation); |
| } |
| |
| pub fn push_state_machine(&self, state_machine: Object<StateMachine>) { |
| self.inner.push_state_machine(state_machine); |
| } |
| |
| pub fn on_component_dirty(&self, component: &Component) { |
| self.inner.on_component_dirty(component); |
| } |
| |
| fn as_component(&self) -> ObjectRef<'_, Component> { |
| self.cast() |
| } |
| |
| pub fn on_dirty(&self, _dirt: ComponentDirt) { |
| let dirt = &self.as_component().dirt; |
| dirt.set(dirt.get() | ComponentDirt::COMPONENTS); |
| } |
| |
| pub fn update(&self, value: ComponentDirt) { |
| if Component::value_has_dirt(value, ComponentDirt::DRAW_ORDER) { |
| self.inner.sort_draw_order(); |
| } |
| |
| if Component::value_has_dirt(value, ComponentDirt::PATH) { |
| let mut builder = CommandPathBuilder::new(); |
| |
| builder.rect(math::Vec::new(0.0, 0.0), math::Vec::new(self.width(), self.height())); |
| |
| *self.inner.clip_path.borrow_mut() = Some(builder.build()); |
| |
| let mut builder = CommandPathBuilder::new(); |
| |
| builder.rect( |
| math::Vec::new(-self.width() * self.origin_x(), -self.height() * self.origin_y()), |
| math::Vec::new(self.width(), self.height()), |
| ); |
| |
| *self.inner.background_path.borrow_mut() = Some(builder.build()); |
| } |
| } |
| |
| pub fn update_components(&self) -> bool { |
| let component = self.as_component(); |
| if component.has_dirt(ComponentDirt::COMPONENTS) { |
| return self.inner.update_components(component); |
| } |
| |
| false |
| } |
| |
| pub fn advance(&self, _elapsed_seconds: f32) -> bool { |
| self.update_components() |
| } |
| |
| pub fn draw(&self, renderer: &mut impl Renderer, transform: Mat) { |
| let mut artboard_transform = Mat { |
| translate_x: self.width() * self.origin_x(), |
| translate_y: self.height() * self.origin_y(), |
| ..Default::default() |
| }; |
| |
| artboard_transform = artboard_transform * transform; |
| |
| for shape_paint in self.cast::<ShapePaintContainer>().shape_paints() { |
| shape_paint.as_ref().draw( |
| renderer, |
| self.inner |
| .background_path |
| .borrow() |
| .as_ref() |
| .expect("background_path should already be set in Artboard"), |
| artboard_transform, |
| ); |
| } |
| |
| let drawables = iter::successors(self.inner.first_drawable.get(), |drawable| { |
| drawable.as_ref().prev.get() |
| }); |
| |
| for drawable in drawables { |
| drawable.as_ref().draw(renderer, artboard_transform); |
| } |
| } |
| |
| pub fn bounds(&self) -> Aabb { |
| Aabb::new(0.0, 0.0, self.width(), self.height()) |
| } |
| |
| pub fn animations(&self) -> ObjectsIter<LinearAnimation> { |
| ObjectsIter::new(self.inner.animations.clone()) |
| } |
| |
| pub fn state_machines(&self) -> ObjectsIter<StateMachine> { |
| ObjectsIter::new(self.inner.state_machines.clone()) |
| } |
| } |
| |
| impl Core for Artboard { |
| parent_types![ |
| (container_component, ContainerComponent), |
| (shape_paint_container, ShapePaintContainer), |
| ]; |
| |
| properties![ |
| (7, width, set_width), |
| (8, height, set_height), |
| (9, x, set_x), |
| (10, y, set_y), |
| (11, origin_x, set_origin_x), |
| (12, origin_y, set_origin_y), |
| container_component, |
| ]; |
| } |
| |
| impl OnAdded for ObjectRef<'_, Artboard> { |
| on_added!(ContainerComponent); |
| } |
| |
| impl CoreContext for ArtboardInner { |
| fn resolve(&self, id: usize) -> Option<Object> { |
| self.objects.borrow().get(id).cloned() |
| } |
| } |
| |
| impl CoreContext for Artboard { |
| fn resolve(&self, id: usize) -> Option<Object> { |
| self.inner.resolve(id) |
| } |
| } |