/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2025 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
use std::ops::Range;
use crate::{
error::{Error, Result},
expr::{SExprMachine, SExprVal},
gfx::{Dimension, Point, Rectangle},
scene::SceneNode as SceneNode3,
};
use super::{PropertyAtomicGuard, PropertyPtr, Role};
#[derive(Clone)]
pub struct PropertyBool {
prop: PropertyPtr,
role: Role,
idx: usize,
}
impl PropertyBool {
pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str, idx: usize) -> Result {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
// Test if it works
let _ = prop.get_bool(idx)?;
Ok(Self { prop, role, idx })
}
pub fn get(&self) -> bool {
self.prop.get_bool(self.idx).unwrap()
}
pub fn set(&self, atom: &mut PropertyAtomicGuard, val: bool) {
self.prop().set_bool(atom, self.role, self.idx, val).unwrap()
}
#[inline]
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
#[derive(Clone)]
pub struct PropertyUint32 {
prop: PropertyPtr,
role: Role,
idx: usize,
}
impl PropertyUint32 {
pub fn from(prop: PropertyPtr, role: Role, idx: usize) -> Result {
// Test if it works
let _ = prop.get_u32(idx)?;
Ok(Self { prop, role, idx })
}
pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str, idx: usize) -> Result {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
// Test if it works
let _ = prop.get_u32(idx)?;
Ok(Self { prop, role, idx })
}
pub fn get(&self) -> u32 {
self.prop.get_u32(self.idx).unwrap()
}
pub fn set(&self, atom: &mut PropertyAtomicGuard, val: u32) {
self.prop().set_u32(atom, self.role, self.idx, val).unwrap()
}
#[inline]
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
#[derive(Clone)]
pub struct PropertyFloat32 {
prop: PropertyPtr,
role: Role,
idx: usize,
}
impl PropertyFloat32 {
pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str, idx: usize) -> Result {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
// Test if it works
let _ = prop.get_f32(idx)?;
Ok(Self { prop, role, idx })
}
pub fn get(&self) -> f32 {
self.prop.get_f32(self.idx).unwrap()
}
pub fn set(&self, atom: &mut PropertyAtomicGuard, val: f32) {
self.prop().set_f32(atom, self.role, self.idx, val).unwrap()
}
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
#[derive(Clone)]
pub struct PropertyStr {
prop: PropertyPtr,
role: Role,
idx: usize,
}
impl PropertyStr {
pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str, idx: usize) -> Result {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
// Test if it works
let _ = prop.get_str(idx)?;
Ok(Self { prop, role, idx })
}
pub fn get(&self) -> String {
self.prop.get_str(self.idx).unwrap()
}
pub fn set>(&self, atom: &mut PropertyAtomicGuard, val: S) {
self.prop().set_str(atom, self.role, self.idx, val.into()).unwrap()
}
#[inline]
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
#[derive(Clone)]
pub struct PropertyColor {
prop: PropertyPtr,
role: Role,
}
impl PropertyColor {
pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str) -> Result {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
if !prop.is_bounded() || prop.get_len() != 4 {
return Err(Error::PropertyWrongLen)
}
// Test if it works
let _ = prop.get_f32(0)?;
Ok(Self { prop, role })
}
pub fn get(&self) -> [f32; 4] {
[
self.prop.get_f32(0).unwrap(),
self.prop.get_f32(1).unwrap(),
self.prop.get_f32(2).unwrap(),
self.prop.get_f32(3).unwrap(),
]
}
pub fn set(&self, atom: &mut PropertyAtomicGuard, val: [f32; 4]) {
self.prop().set_f32(atom, self.role, 0, val[0]).unwrap();
self.prop().set_f32(atom, self.role, 1, val[1]).unwrap();
self.prop().set_f32(atom, self.role, 2, val[2]).unwrap();
self.prop().set_f32(atom, self.role, 3, val[3]).unwrap();
}
#[inline]
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
#[derive(Clone)]
pub struct PropertyDimension {
prop: PropertyPtr,
role: Role,
}
impl PropertyDimension {
pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str) -> Result {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
if !prop.is_bounded() || prop.get_len() != 2 {
return Err(Error::PropertyWrongLen)
}
// Test if it works
let _ = prop.get_f32(0)?;
Ok(Self { prop, role })
}
pub fn get(&self) -> Dimension {
[self.prop.get_f32(0).unwrap(), self.prop.get_f32(1).unwrap()].into()
}
pub fn set(&self, atom: &mut PropertyAtomicGuard, dim: Dimension) {
self.prop().set_f32(atom, self.role, 0, dim.w).unwrap();
self.prop().set_f32(atom, self.role, 1, dim.h).unwrap();
}
#[inline]
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
#[derive(Clone)]
pub struct PropertyPoint {
prop: PropertyPtr,
role: Role,
}
impl PropertyPoint {
pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str) -> Result {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
if !prop.is_bounded() || prop.get_len() != 2 {
return Err(Error::PropertyWrongLen)
}
// Test if it works
let _ = prop.get_f32(0)?;
Ok(Self { prop, role })
}
pub fn get(&self) -> Point {
[self.prop.get_f32(0).unwrap(), self.prop.get_f32(1).unwrap()].into()
}
pub fn set(&self, atom: &mut PropertyAtomicGuard, pos: Point) {
self.prop().set_f32(atom, self.role, 0, pos.x).unwrap();
self.prop().set_f32(atom, self.role, 1, pos.y).unwrap();
}
#[inline]
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
#[derive(Clone)]
pub struct PropertyRect {
prop: PropertyPtr,
role: Role,
}
impl PropertyRect {
pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str) -> Result {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
if !prop.is_bounded() || prop.get_len() != 4 {
return Err(Error::PropertyWrongLen)
}
// Test if it works
let _ = prop.get_f32(0)?;
Ok(Self { prop, role })
}
pub fn eval(&self, parent_rect: &Rectangle) -> Result<()> {
self.eval_with(
(0..4).collect(),
vec![("w".to_string(), parent_rect.w), ("h".to_string(), parent_rect.h)],
)
}
pub fn eval_with(&self, range: Vec, extras: Vec<(String, f32)>) -> Result<()> {
let mut globals = vec![];
for dep in self.prop.get_depends() {
let Some(prop) = dep.prop.upgrade() else { return Err(Error::PropertyNotFound) };
let value = prop.get_f32(dep.i)?;
globals.push((dep.local_name, SExprVal::Float32(value)));
}
for (name, val) in extras {
globals.push((name, SExprVal::Float32(val)));
}
//debug!(target: "prop::wrap", "PropertyRect::eval() [globals = {globals:?}]");
let mut changes = vec![];
for i in range {
if !self.prop.is_expr(i)? {
continue
}
let expr = self.prop.get_expr(i).unwrap();
let mut machine = SExprMachine { globals: globals.clone(), stmts: &expr };
let v = machine.call()?.as_f32()?;
changes.push((i, v));
}
self.prop.set_cache_f32_multi(self.role, changes).unwrap();
Ok(())
}
pub fn get(&self) -> Rectangle {
Rectangle::from_array([
self.prop.get_f32(0).unwrap(),
self.prop.get_f32(1).unwrap(),
self.prop.get_f32(2).unwrap(),
self.prop.get_f32(3).unwrap(),
])
}
pub fn get_opt(&self) -> Option {
Some(Rectangle::from_array([
self.prop.get_f32(0).ok()?,
self.prop.get_f32(1).ok()?,
self.prop.get_f32(2).ok()?,
self.prop.get_f32(3).ok()?,
]))
}
pub fn set(&self, atom: &mut PropertyAtomicGuard, rect: &Rectangle) {
self.prop().set_f32(atom, self.role, 0, rect.x).unwrap();
self.prop().set_f32(atom, self.role, 1, rect.y).unwrap();
self.prop().set_f32(atom, self.role, 2, rect.y).unwrap();
self.prop().set_f32(atom, self.role, 3, rect.y).unwrap();
}
#[inline]
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
fn is_f32_or_has_cached(&self, i: usize) -> bool {
if self.prop.is_expr(i).unwrap() {
if self.prop.get_cached(i).unwrap().is_null() {
return false
}
}
true
}
pub fn has_cached(&self) -> bool {
self.is_f32_or_has_cached(0) &&
self.is_f32_or_has_cached(1) &&
self.is_f32_or_has_cached(2) &&
self.is_f32_or_has_cached(3)
}
}