blob: 05a7f909686213693c2ae5111fc6649933337000 [file] [log] [blame]
// 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)
}
}