From 4f6561a4aba29e7e7248f8b35c9357ff1d34dabd Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 13 Feb 2025 22:04:45 -0500 Subject: [PATCH 001/149] start fixing svg --- murrelet_common/src/transform.rs | 10 ++++ murrelet_draw/src/curve_drawer.rs | 10 ++++ murrelet_livecode/src/unitcells.rs | 12 ++-- murrelet_svg/src/svg.rs | 93 ++++++++++++++++++++++-------- 4 files changed, 96 insertions(+), 29 deletions(-) diff --git a/murrelet_common/src/transform.rs b/murrelet_common/src/transform.rs index 44024cf..4bc7d03 100644 --- a/murrelet_common/src/transform.rs +++ b/murrelet_common/src/transform.rs @@ -187,6 +187,16 @@ impl SimpleTransform2d { pub fn translate(v: Vec2) -> Self { Self(vec![SimpleTransform2dStep::Translate(v)]) } + + pub fn to_mat3(&self) -> Mat3 { + self.0 + .iter() + .fold(Mat3::IDENTITY, |acc, el| el.transform() * acc) + } + + pub fn to_mat4(&self) -> Mat4 { + mat4_from_mat3_transform(self.to_mat3()) + } } impl TransformVec2 for SimpleTransform2d { diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 4f08659..396442c 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -74,6 +74,16 @@ impl CurveDrawer { pub fn noop() -> Self { Self::new(vec![], false) } + + pub fn first_point(&self) -> Vec2 { + let first_command = self.segments().first().unwrap(); + first_command.first_point() + } + + pub fn last_point(&self) -> Vec2 { + let last_command = self.segments().last().unwrap(); + last_command.last_point() + } } #[derive(Debug, Clone, Livecode, Lerpable)] diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index 30a48ac..a73c324 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -604,12 +604,14 @@ impl IntoExprWorldContext for UnitCellContext { fn as_expr_world_context_values(&self) -> ExprWorldContextValues { let mut ctx_vals = self.ctx.as_expr_world_context_values(); - let loc = self - .detail - .transform_with_skew(&vec![vec2(-50.0, -50.0), vec2(50.0, 50.0)]); + let loc = self.detail.transform_with_skew(&vec![ + vec2(-50.0, -50.0), + vec2(50.0, -50.0), + vec2(50.0, 50.0), + ]); let locs = loc.into_iter_vec2().collect_vec(); - let width = locs[1].x - locs[0].x; - let height = locs[1].y - locs[0].y; + let width = locs[1].distance(locs[0]); + let height = locs[1].distance(locs[2]); ctx_vals.set_val("u_width", LivecodeValue::float(width)); ctx_vals.set_val("u_height", LivecodeValue::float(height)); diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 9cb94f9..5a572a8 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -76,18 +76,24 @@ impl ToSvgMatrix for Mat4 { } } +// for a type that means something, e.g. "style fro a shape" trait AddSvgAttributes { fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path; } -impl AddSvgAttributes for StyledPathSvgFill { - fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path { - p.set("fill", format!("url(#P{})", self.hash())) +// for component types, e.g. "color" +trait GetSvgAttributes { + fn get_svg_attributes(&self) -> String; +} + +impl GetSvgAttributes for StyledPathSvgFill { + fn get_svg_attributes(&self) -> String { + format!("url(#P{})", self.hash()) } } -impl AddSvgAttributes for MurreletColor { - fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path { +impl GetSvgAttributes for MurreletColor { + fn get_svg_attributes(&self) -> String { let [r, g, b, a] = self.into_rgba_components(); let fill = format!( "rgba({} {} {} / {})", @@ -96,22 +102,34 @@ impl AddSvgAttributes for MurreletColor { (b * 255.0) as i32, a ); - p.set("fill", fill) + fill } } -impl AddSvgAttributes for RGBandANewtype { - fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path { - self.color().add_svg_attributes(p) +impl GetSvgAttributes for RGBandANewtype { + fn get_svg_attributes(&self) -> String { + self.color().get_svg_attributes() } } impl AddSvgAttributes for MurreletColorStyle { fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path { + let mut p = p; + match self { + MurreletColorStyle::Color(c) => p = p.set("fill", c.get_svg_attributes()), + MurreletColorStyle::RgbaFill(c) => p = p.set("fill", c.get_svg_attributes()), + MurreletColorStyle::SvgFill(c) => p = p.set("fill", c.get_svg_attributes()), + } + p + } +} + +impl GetSvgAttributes for MurreletColorStyle { + fn get_svg_attributes(&self) -> String { match self { - MurreletColorStyle::Color(c) => c.add_svg_attributes(p), - MurreletColorStyle::RgbaFill(c) => c.add_svg_attributes(p), - MurreletColorStyle::SvgFill(c) => c.add_svg_attributes(p), + MurreletColorStyle::Color(c) => c.get_svg_attributes(), + MurreletColorStyle::RgbaFill(c) => c.get_svg_attributes(), + MurreletColorStyle::SvgFill(c) => c.get_svg_attributes(), } } } @@ -119,29 +137,56 @@ impl AddSvgAttributes for MurreletColorStyle { impl AddSvgAttributes for MurreletPath { fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path { let mut p = p; - if let Some(t) = self.transform() { - p = p.set("transform", t.to_svg_matrix()); + let svg_str = self.get_svg_attributes(); + if !svg_str.is_empty() { + p = p.set("transform", svg_str); } p } } +impl GetSvgAttributes for MurreletPath { + fn get_svg_attributes(&self) -> String { + if let Some(t) = self.transform() { + t.to_svg_matrix() + } else { + "".to_string() + } + } +} impl AddSvgAttributes for MurreletStyle { fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path { let mut p = p; - if self.stroke_weight > 0.0 { - p = p.set("stroke-width", self.stroke_weight / 10.0); - } - if self.filled { - p = self.color.add_svg_attributes(p); + match self.drawing_plan() { + murrelet_draw::draw::MurreletDrawPlan::Shader(fill) => { + p = p.set("fill", fill.get_svg_attributes()) + } + murrelet_draw::draw::MurreletDrawPlan::DebugPoints(_) => unimplemented!(), + murrelet_draw::draw::MurreletDrawPlan::FilledClosed => { + p = p.set("fill", self.color.get_svg_attributes()); + + if self.stroke_weight > 0.0 { + p = p.set("stroke-width", self.stroke_weight / 10.0); + p = p.set("stroke", self.stroke_color.get_svg_attributes()); + } + } + murrelet_draw::draw::MurreletDrawPlan::Outline => { + p = p.set("fill", "none"); - if self.stroke_weight > 0.0 { - p = p.set("stroke", "black"); + if self.stroke_weight > 0.0 { + p = p.set("stroke-width", self.stroke_weight / 10.0); + p = p.set("stroke", self.color.get_svg_attributes()); + } + } + murrelet_draw::draw::MurreletDrawPlan::Line => { + p = p.set("fill", "none"); + + if self.stroke_weight > 0.0 { + p = p.set("stroke-width", self.stroke_weight / 10.0); + p = p.set("stroke", self.color.get_svg_attributes()); + } } - } else { - p = p.set("fill", "none"); - p = p.set("stroke", "black"); } p From 87de6cc7c0d4ca9ebe054980b535b14f08d57ba5 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 16 Feb 2025 13:32:19 -0500 Subject: [PATCH 002/149] more svg, and some commented out code to squash unitcells into vectors --- murrelet_common/src/transform.rs | 4 + murrelet_draw/src/lib.rs | 1 + murrelet_draw/src/style.rs | 10 +- murrelet_livecode/src/types.rs | 111 +++++++++++- .../src/derive_livecode.rs | 1 + murrelet_perform/src/perform.rs | 34 ++-- murrelet_svg/src/svg.rs | 165 +++++++++++------- 7 files changed, 247 insertions(+), 79 deletions(-) diff --git a/murrelet_common/src/transform.rs b/murrelet_common/src/transform.rs index 4bc7d03..ca1e98f 100644 --- a/murrelet_common/src/transform.rs +++ b/murrelet_common/src/transform.rs @@ -108,6 +108,10 @@ impl SimpleTransform2dStep { Self::Translate(v) } + pub fn rotate_pi(angle_pi: A) -> Self { + Self::Rotate(Vec2::ZERO, angle_pi.as_angle_pi()) + } + pub fn scale_both(v: f32) -> Self { Self::Scale(Vec2::ONE * v) } diff --git a/murrelet_draw/src/lib.rs b/murrelet_draw/src/lib.rs index 0c4b246..6efc860 100644 --- a/murrelet_draw/src/lib.rs +++ b/murrelet_draw/src/lib.rs @@ -6,3 +6,4 @@ pub mod newtypes; pub mod sequencers; pub mod style; pub mod transform2d; +pub mod svg; diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 87a7f14..483bf3d 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -1,5 +1,5 @@ #![allow(dead_code)] -use crate::{curve_drawer::CurveDrawer, draw::*, transform2d::*}; +use crate::{curve_drawer::CurveDrawer, draw::*, svg::TransformedSvgShape, transform2d::*}; use glam::*; use lerpable::Lerpable; use md5::{Digest, Md5}; @@ -463,6 +463,7 @@ impl MurreletCurve { pub enum MurreletPath { Polyline(Polyline), Curve(MurreletCurve), + Svg(TransformedSvgShape) } impl MurreletPath { pub fn polyline(path: F) -> Self { @@ -480,13 +481,15 @@ impl MurreletPath { t: Mat4::IDENTITY, }, MurreletPath::Curve(c) => c.clone(), + MurreletPath::Svg(_) => todo!(), } } pub fn transform_with(&self, t: &T) -> Self { match self { MurreletPath::Polyline(x) => MurreletPath::Polyline(x.transform_with(t)), - MurreletPath::Curve(_) => todo!(), // i'm not sure how i want to handle this yet + MurreletPath::Curve(_) => todo!(), + MurreletPath::Svg(_) => todo!(), // i'm not sure how i want to handle this yet } } @@ -494,6 +497,7 @@ impl MurreletPath { match self { MurreletPath::Polyline(_) => self.transform_with(&t), MurreletPath::Curve(c) => MurreletPath::Curve(c.transform_with_mat4_after(t)), + MurreletPath::Svg(c) => MurreletPath::Svg(c.transform_with_mat4_after(t)), } } @@ -501,6 +505,7 @@ impl MurreletPath { match self { MurreletPath::Polyline(_) => self.transform_with(&t), MurreletPath::Curve(c) => MurreletPath::Curve(c.transform_with_mat4_before(t)), + MurreletPath::Svg(c) => MurreletPath::Svg(c.transform_with_mat4_before(t)), } } @@ -508,6 +513,7 @@ impl MurreletPath { match self { MurreletPath::Polyline(_) => None, MurreletPath::Curve(c) => Some(c.t), + MurreletPath::Svg(c) => Some(c.t), } } } diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index d278f0e..958fcaf 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{collections::HashSet, fmt::Debug, marker::PhantomData}; use evalexpr::{build_operator_tree, EvalexprError, HashMapContext, Node}; use itertools::Itertools; @@ -9,9 +9,10 @@ use serde_yaml::Location; use crate::{ expr::IntoExprWorldContext, + lazy::IsLazy, livecode::{GetLivecodeIdentifiers, LivecodeFromWorld}, state::LivecodeWorldState, - unitcells::UnitCellExprWorldContext, + unitcells::{TmpUnitCells, UnitCellCreator, UnitCellExprWorldContext, UnitCells}, }; #[derive(Debug)] @@ -121,7 +122,12 @@ pub struct ControlVecElementRepeat { what: Vec, } -impl GetLivecodeIdentifiers for ControlVecElement { +// impl GetLivecodeIdentifiers for ControlVecElement +impl GetLivecodeIdentifiers for ControlVecElement +where + Source: Clone + Debug + GetLivecodeIdentifiers, + // Sequencer: UnitCellCreator + GetLivecodeIdentifiers, +{ fn variable_identifiers(&self) -> Vec { match self { ControlVecElement::Single(c) => c.variable_identifiers(), @@ -132,6 +138,15 @@ impl GetLivecodeIdentifiers for ControlVecElement .collect::>() .into_iter() .collect_vec(), + // ControlVecElement::UnitCell(c) => vec![ + // c.node.variable_identifiers(), + // c.sequencer.variable_identifiers(), + // ] + // .concat() + // .into_iter() + // .collect::>() + // .into_iter() + // .collect_vec(), } } @@ -145,6 +160,15 @@ impl GetLivecodeIdentifiers for ControlVecElement .collect::>() .into_iter() .collect_vec(), + // ControlVecElement::UnitCell(c) => vec![ + // c.node.function_identifiers(), + // c.sequencer.function_identifiers(), + // ] + // .concat() + // .into_iter() + // .collect::>() + // .into_iter() + // .collect_vec(), } } } @@ -176,14 +200,76 @@ impl ControlVecElementRepeat { } } +// #[derive(Debug, Clone)] +// #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +// #[cfg_attr( +// feature = "schemars", +// schemars(bound = "Source: schemars::JsonSchema, ControlSequencer: schemars::JsonSchema") +// )] +// pub struct VecUnitCell +// where +// Source: Clone + Debug + Default, +// Sequencer: UnitCellCreator, +// ControlSequencer: LivecodeFromWorld, + +// { +// sequencer: ControlSequencer, +// ctx: AdditionalContextNode, +// prefix: String, +// node: Source, +// #[serde(skip)] +// #[cfg_attr(feature = "schemars", schemars(skip))] +// _marker: PhantomData, +// } +// impl VecUnitCell +// where +// Source: Clone + Debug + Default, +// Sequencer: UnitCellCreator + Clone, +// ControlSequencer: LivecodeFromWorld, +// { +// fn eval_and_expand_vec( +// &self, +// w: &LivecodeWorldState, +// ) -> Result, LivecodeError> +// where +// Source: Clone + Debug + Default + LivecodeFromWorld, +// Sequencer: UnitCellCreator, +// Target: Default + Clone + Debug + +// { +// let seq = self.sequencer.o(w)?; +// let n: Box> = Box::new(self.node.clone()); +// let t = TmpUnitCells::new( +// seq, +// n, +// Some(self.ctx.clone()), +// &self.prefix, +// ).o(w)?; +// Ok(t.items.into_iter().map(|x| *x.node).collect_vec()) +// } +// } + #[derive(Debug, Clone)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub enum ControlVecElement { +// pub enum ControlVecElement +pub enum ControlVecElement +where + Source: Clone + Debug, + // Sequencer: UnitCellCreator, + // ControlSequencer: LivecodeFromWorld, +{ Single(Source), Repeat(ControlVecElementRepeat), + // UnitCell(VecUnitCell), } -impl ControlVecElement { +// impl ControlVecElement +impl ControlVecElement +where + Source: Clone + Debug, + // Sequencer: UnitCellCreator, + // ControlSequencer: LivecodeFromWorld, +{ pub fn raw(c: Source) -> Self { Self::Single(c) } @@ -195,14 +281,19 @@ impl ControlVecElement { match self { ControlVecElement::Single(c) => Ok(vec![c.o(w)?]), ControlVecElement::Repeat(r) => r.eval_and_expand_vec(w), + // ControlVecElement::UnitCell(c) => c.eval_and_expand_vec(w), } } } // chatgpt +// impl<'de, Sequencer, ControlSequencer, Source> Deserialize<'de> +// for ControlVecElement impl<'de, Source> Deserialize<'de> for ControlVecElement where - Source: Deserialize<'de>, + Source: Deserialize<'de> + Clone + Debug, + // Sequencer: UnitCellCreator, + // ControlSequencer: LivecodeFromWorld, { fn deserialize(deserializer: D) -> Result where @@ -227,6 +318,14 @@ where } } + // match VecUnitCell::deserialize(value.clone()) { + // Ok(repeat) => return Ok(ControlVecElement::Repeat(repeat)), + // Err(e) => { + // // it's gonna fail, so just check what + // errors.push(format!("(repeat {})", e)) + // } + // } + // Both variants failed, return an error with detailed messages Err(serde::de::Error::custom(format!( "ControlVecElement {}", diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 63fc17c..8184c93 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -724,6 +724,7 @@ impl GenFinal for FieldTokensLivecode { .unwrap_or(quote! {""}); let for_world = { + // todo, these look like the same if how_to_control_internal.is_lazy() { quote! {#name: { murrelet_livecode::unitcells::TmpUnitCells::new( diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 27336dc..ea7a882 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -44,6 +44,7 @@ pub struct SvgDrawConfig { frame: u64, target_size: f32, // in mm margin_size: f32, + should_resize: bool, // sorry, something to force it not to resize my shapes on the web! } impl SvgDrawConfig { pub fn new( @@ -58,9 +59,16 @@ impl SvgDrawConfig { target_size, margin_size: 10.0, frame, + should_resize: true, } } + pub fn with_no_resize(&self) -> Self { + let mut c = self.clone(); + c.should_resize = false; + c + } + pub fn full_target_width(&self) -> f32 { self.target_size + 2.0 * self.margin_size } @@ -77,17 +85,21 @@ impl SvgDrawConfig { } pub fn transform_for_size(&self) -> Mat4 { - // okay so we take the width, since that's what looked okay on the screen - let size = self.size(); - let full_target_width = self.full_target_width() * 1.0; - - let translation_to_final = vec3(full_target_width, full_target_width, 0.0); - let s = self.target_size / size; - let scale = vec3(s, s, 1.0); - - // aiming for 100mm by 100mm, going from 0 to 10 - // operations go right to left! - Mat4::from_translation(translation_to_final) * Mat4::from_scale(scale) + if self.should_resize { + // okay so we take the width, since that's what looked okay on the screen + let size = self.size(); + let full_target_width = self.full_target_width() * 1.0; + + let translation_to_final = vec3(full_target_width, full_target_width, 0.0); + let s = self.target_size / size; + let scale = vec3(s, s, 1.0); + + // aiming for 100mm by 100mm, going from 0 to 10 + // operations go right to left! + Mat4::from_translation(translation_to_final) * Mat4::from_scale(scale) + } else { + Mat4::IDENTITY + } } pub fn frame(&self) -> u64 { diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 5a572a8..cedfac5 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -14,9 +14,15 @@ use murrelet_draw::{ draw::{MurreletColorStyle, MurreletStyle}, newtypes::RGBandANewtype, style::{MurreletCurve, MurreletPath, StyledPath, StyledPathSvgFill}, + svg::{SvgShape, TransformedSvgShape}, }; use murrelet_perform::perform::SvgDrawConfig; -use svg::{node::element::path::Data, Document, Node}; +use svg::{ + node::element::{path::Data, Group}, + Document, Node, +}; + +// this area actually follows the spec more closely #[derive(Debug, Clone)] pub struct StyledText { @@ -44,21 +50,79 @@ impl StyledText { } } +// this can take all of our favorite types, and returns the Group that applies the shape's transforms +// and styles +pub trait ToStyledGroup { + fn to_group(&self, style: &MurreletStyle) -> Option; +} + pub trait ToSvgData { - fn to_svg(&self) -> Option; + fn to_svg_data(&self) -> Option; + + fn make_path(&self, style: &MurreletStyle) -> Option { + if let Some(d) = self.to_svg_data() { + let mut d = d; + if style.closed { + d = d.close(); + } + let mut p = svg::node::element::Path::new().set("d", d); + p = style.add_svg_attributes(p); + Some(p) + } else { + None + } + } +} + +// if you implement ToSvgData, we can get the group for you +impl ToStyledGroup for T { + fn to_group(&self, style: &MurreletStyle) -> Option { + if let Some(p) = self.make_path(style) { + let mut g = Group::new(); + g.append(p); + Some(g) + } else { + None + } + } } impl ToSvgData for MurreletCurve { - fn to_svg(&self) -> Option { - self.curve().to_svg() + fn to_svg_data(&self) -> Option { + self.curve().to_svg_data() } } -impl ToSvgData for MurreletPath { - fn to_svg(&self) -> Option { +impl ToStyledGroup for TransformedSvgShape { + fn to_group(&self, style: &MurreletStyle) -> Option { + let mut g = Group::new(); + g = g.set("transform", self.t.to_svg_matrix()); + match &self.shape { + SvgShape::Rect(s) => { + let mut rect = svg::node::element::Rectangle::new() + .set("x", s.x) + .set("y", s.y) + .set("rx", s.rx) + .set("ry", s.ry) + .set("width", s.width) + .set("height", s.height); + + rect = style.add_svg_attributes(rect); + + g.append(rect); + + Some(g) + } + } + } +} + +impl ToStyledGroup for MurreletPath { + fn to_group(&self, style: &MurreletStyle) -> Option { match self { - MurreletPath::Polyline(path) => path.into_iter_vec2().collect_vec().to_svg(), - MurreletPath::Curve(c) => c.to_svg(), + MurreletPath::Polyline(path) => path.into_iter_vec2().collect_vec().to_group(style), + MurreletPath::Curve(c) => c.to_group(style), + MurreletPath::Svg(c) => c.to_group(style), } } } @@ -78,7 +142,7 @@ impl ToSvgMatrix for Mat4 { // for a type that means something, e.g. "style fro a shape" trait AddSvgAttributes { - fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path; + fn add_svg_attributes(&self, p: T) -> T; } // for component types, e.g. "color" @@ -96,7 +160,7 @@ impl GetSvgAttributes for MurreletColor { fn get_svg_attributes(&self) -> String { let [r, g, b, a] = self.into_rgba_components(); let fill = format!( - "rgba({} {} {} / {})", + "rgba({}, {}, {}, {})", (r * 255.0) as i32, (g * 255.0) as i32, (b * 255.0) as i32, @@ -113,12 +177,12 @@ impl GetSvgAttributes for RGBandANewtype { } impl AddSvgAttributes for MurreletColorStyle { - fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path { + fn add_svg_attributes(&self, p: T) -> T { let mut p = p; match self { - MurreletColorStyle::Color(c) => p = p.set("fill", c.get_svg_attributes()), - MurreletColorStyle::RgbaFill(c) => p = p.set("fill", c.get_svg_attributes()), - MurreletColorStyle::SvgFill(c) => p = p.set("fill", c.get_svg_attributes()), + MurreletColorStyle::Color(c) => p.assign("fill", c.get_svg_attributes()), + MurreletColorStyle::RgbaFill(c) => p.assign("fill", c.get_svg_attributes()), + MurreletColorStyle::SvgFill(c) => p.assign("fill", c.get_svg_attributes()), } p } @@ -135,11 +199,11 @@ impl GetSvgAttributes for MurreletColorStyle { } impl AddSvgAttributes for MurreletPath { - fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path { + fn add_svg_attributes(&self, p: T) -> T { let mut p = p; let svg_str = self.get_svg_attributes(); if !svg_str.is_empty() { - p = p.set("transform", svg_str); + p.assign("transform", svg_str); } p } @@ -155,36 +219,36 @@ impl GetSvgAttributes for MurreletPath { } impl AddSvgAttributes for MurreletStyle { - fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path { + fn add_svg_attributes(&self, p: T) -> T { let mut p = p; match self.drawing_plan() { murrelet_draw::draw::MurreletDrawPlan::Shader(fill) => { - p = p.set("fill", fill.get_svg_attributes()) + p.assign("fill", fill.get_svg_attributes()) } murrelet_draw::draw::MurreletDrawPlan::DebugPoints(_) => unimplemented!(), murrelet_draw::draw::MurreletDrawPlan::FilledClosed => { - p = p.set("fill", self.color.get_svg_attributes()); + p.assign("fill", self.color.get_svg_attributes()); if self.stroke_weight > 0.0 { - p = p.set("stroke-width", self.stroke_weight / 10.0); - p = p.set("stroke", self.stroke_color.get_svg_attributes()); + p.assign("stroke-width", self.stroke_weight); + p.assign("stroke", self.stroke_color.get_svg_attributes()); } } murrelet_draw::draw::MurreletDrawPlan::Outline => { - p = p.set("fill", "none"); + p.assign("fill", "none"); if self.stroke_weight > 0.0 { - p = p.set("stroke-width", self.stroke_weight / 10.0); - p = p.set("stroke", self.color.get_svg_attributes()); + p.assign("stroke-width", self.stroke_weight); + p.assign("stroke", self.color.get_svg_attributes()); } } murrelet_draw::draw::MurreletDrawPlan::Line => { - p = p.set("fill", "none"); + p.assign("fill", "none"); if self.stroke_weight > 0.0 { - p = p.set("stroke-width", self.stroke_weight / 10.0); - p = p.set("stroke", self.color.get_svg_attributes()); + p.assign("stroke-width", self.stroke_weight); + p.assign("stroke", self.color.get_svg_attributes()); } } } @@ -194,7 +258,7 @@ impl AddSvgAttributes for MurreletStyle { } impl AddSvgAttributes for StyledPath { - fn add_svg_attributes(&self, p: svg::node::element::Path) -> svg::node::element::Path { + fn add_svg_attributes(&self, p: T) -> T { let mut p = p; p = self.path.add_svg_attributes(p); p = self.style.add_svg_attributes(p); @@ -202,32 +266,12 @@ impl AddSvgAttributes for StyledPath { } } -impl ToSvgData for StyledPath { - fn to_svg(&self) -> Option { - self.path.to_svg() - } -} - pub trait ToSvgPath { - fn make_path(&self) -> Option; + fn make_group(&self) -> Option; fn make_pattern(&self) -> Option<(String, svg::node::element::Pattern)>; } impl ToSvgPath for StyledPath { - fn make_path(&self) -> Option { - if let Some(d) = self.path.to_svg() { - let mut d = d; - if self.style.closed { - d = d.close(); - } - let mut p = svg::node::element::Path::new().set("d", d); - p = self.add_svg_attributes(p); - Some(p) - } else { - None - } - } - fn make_pattern(&self) -> Option<(String, svg::node::element::Pattern)> { if let MurreletColorStyle::SvgFill(f) = self.style.color { // ooookay so the whole purpose of this is to have pattern transform @@ -252,6 +296,10 @@ impl ToSvgPath for StyledPath { None } } + + fn make_group(&self) -> Option { + self.path.to_group(&self.style) + } } impl TransformVec2 for SvgDocCreator { @@ -313,7 +361,7 @@ impl SvgDocCreator { let mut patterns = Vec::new(); for path in layer.paths.iter() { - if let Some(d) = path.make_path() { + if let Some(d) = path.make_group() { // if it's using a svg pattern, we should add that attribute if let Some((key, pattern)) = path.make_pattern() { // the path already will have the id on it, so just make sure it's @@ -615,16 +663,13 @@ impl SvgPathCache { } } -pub trait ToSvg { - fn to_svg(&self) -> Option; +// pub trait ToSvg { +// fn to_svg(&self) -> Option; - fn to_svg_closed(&self) -> Option { - self.to_svg().map(|x| x.close()) - } -} +// } -impl ToSvg for CurveDrawer { - fn to_svg(&self) -> Option { +impl ToSvgData for CurveDrawer { + fn to_svg_data(&self) -> Option { let segments = self.segments(); if segments.is_empty() { return None; @@ -692,8 +737,8 @@ impl ToSvg for CurveDrawer { } // just connect the dots with lines -impl ToSvg for Vec { - fn to_svg(&self) -> Option { +impl ToSvgData for Vec { + fn to_svg_data(&self) -> Option { if self.len() == 0 { return None; } From 9f622fb26ce540e67473ea89bb7623384de54459 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 16 Feb 2025 21:24:26 -0500 Subject: [PATCH 003/149] wip --- murrelet_common/src/assets.rs | 78 +++++++++++++++++++++---- murrelet_common/src/idx.rs | 4 ++ murrelet_gpu/src/device_state.rs | 22 ++++++- murrelet_gpu/src/gpu_macros.rs | 4 ++ murrelet_livecode/src/types.rs | 5 +- murrelet_perform/src/asset_loader.rs | 86 ++++++++++++++++++++++++---- murrelet_perform/src/cli.rs | 60 ++++++++++++++++++- murrelet_perform/src/perform.rs | 22 +++++-- 8 files changed, 245 insertions(+), 36 deletions(-) diff --git a/murrelet_common/src/assets.rs b/murrelet_common/src/assets.rs index c6bf8d7..196bdae 100644 --- a/murrelet_common/src/assets.rs +++ b/murrelet_common/src/assets.rs @@ -13,11 +13,11 @@ use crate::{IsPolyline, Polyline}; // eeh, this is not very good, svg assumptions are all mixed in #[derive(Debug, Clone)] -pub struct Asset { +pub struct VectorAsset { layers: Vec, // to support indexing map: HashMap>, } -impl Asset { +impl VectorAsset { pub fn get_layer(&self, layer: &str) -> Option<&Vec> { self.map.get(layer) } @@ -50,12 +50,14 @@ impl Asset { } } + + #[derive(Debug, Clone)] -pub struct Assets { - filename_to_polyline_layers: HashMap, +pub struct VectorLayersAssetLookup { + filename_to_polyline_layers: HashMap, } -impl Assets { - pub fn new(filename_to_polyline_layers: HashMap) -> Self { +impl VectorLayersAssetLookup { + pub fn new(filename_to_polyline_layers: HashMap) -> Self { Self { filename_to_polyline_layers, } @@ -67,25 +69,77 @@ impl Assets { } } - pub fn new_ref(filename_to_polyline_layers: HashMap) -> AssetsRef { - Arc::new(Self::new(filename_to_polyline_layers)) + pub fn asset_layer(&self, key: &str, layer_idx: usize) -> Option<&Vec> { + let asset = &self.filename_to_polyline_layers[key]; + asset.get_layer_idx(layer_idx) + } + + pub fn layer_for_key(&self, key: &str) -> &[String] { + &self.filename_to_polyline_layers[key].layers + } +} + + +pub trait IsColorType {} + +#[derive(Debug, Clone, Copy)] +struct BlackWhite(bool); +impl IsColorType for BlackWhite {} + +// struct RGBAu8([u8; 4]),; +// struct RGBAf32([f32; 4]); + + +#[derive(Debug, Clone)] +pub enum RasterAsset { + RasterBW(Vec>), +} + +#[derive(Debug, Clone)] +pub struct RasterAssetLookup(HashMap); +impl RasterAssetLookup { + pub fn empty() -> Self { + Self(HashMap::new()) + } + + pub fn insert(&mut self, filename: String, img: RasterAsset) { + self.0.insert(filename, img); + } +} + + +#[derive(Debug, Clone)] + +pub struct Assets { + vectors: VectorLayersAssetLookup, + rasters: RasterAssetLookup, +} +impl Assets { + pub fn new(vectors: VectorLayersAssetLookup, rasters: RasterAssetLookup) -> Self { + Self { vectors, rasters } } pub fn empty_ref() -> AssetsRef { Arc::new(Self::empty()) } - pub fn asset_layer(&self, key: &str, layer_idx: usize) -> Option<&Vec> { - let asset = &self.filename_to_polyline_layers[key]; - asset.get_layer_idx(layer_idx) + pub fn empty() -> Assets { + Self { + vectors: VectorLayersAssetLookup::empty(), + rasters: RasterAssetLookup::empty(), + } } pub fn to_ref(self) -> AssetsRef { Arc::new(self) } + pub fn asset_layer(&self, key: &str, layer_idx: usize) -> Option<&Vec> { + self.vectors.asset_layer(key, layer_idx) + } + pub fn layer_for_key(&self, key: &str) -> &[String] { - &self.filename_to_polyline_layers[key].layers + self.vectors.layer_for_key(key) } } diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 0d6c1c5..fbaacee 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -248,4 +248,8 @@ impl IdxInRange2d { let y = self.j.to_range(range.x, range.y); vec2(x, y) } + + pub fn width(&self) -> f32 { + self.i.total as f32 + } } diff --git a/murrelet_gpu/src/device_state.rs b/murrelet_gpu/src/device_state.rs index 55bb14c..c1fa48a 100644 --- a/murrelet_gpu/src/device_state.rs +++ b/murrelet_gpu/src/device_state.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; #[allow(dead_code)] use std::path::PathBuf; @@ -105,7 +106,13 @@ impl OwnedDeviceState { // borrowing from bevy pub fn align_byte_size(value: u32) -> u32 { - value + (wgpu::COPY_BYTES_PER_ROW_ALIGNMENT - (value % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT)) + + if value % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT != 0 { + value + (wgpu::COPY_BYTES_PER_ROW_ALIGNMENT - (value % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT)) + } else { + value + } + } pub fn check_img_size(path: &PathBuf) -> Result<(Vec, u32, u32), Box> { @@ -123,7 +130,11 @@ fn write_png_to_texture( texture: &wgpu::Texture, ) -> Result<(), Box> { // Load the image + println!("loading file {:?}", path); let img = image::open(path)?; + + println!("img.color() {:?}", img.color()); + let img_rgba = img.to_rgba8(); let (img_width, img_height) = img.dimensions(); @@ -135,6 +146,7 @@ fn write_png_to_texture( println!("img_width {:?}", img_width); println!("img_height {:?}", img_height); + println!("padded_row {:?}", padded_row); println!("buffer_rows {:?}", buffer_rows); // just get the name to name the texture @@ -149,6 +161,14 @@ fn write_png_to_texture( padded_img[start..end].copy_from_slice(data); } + + let mut hist = HashMap::new(); + for value in &padded_img { + *hist.entry(value).or_insert(0) += 1; + } + + println!("hist {:?}", hist); + // buffer for loading the png let buffer = device_state .device() diff --git a/murrelet_gpu/src/gpu_macros.rs b/murrelet_gpu/src/gpu_macros.rs index 72d3fa2..a0372d1 100644 --- a/murrelet_gpu/src/gpu_macros.rs +++ b/murrelet_gpu/src/gpu_macros.rs @@ -1144,3 +1144,7 @@ impl ImageTexture { .update_uniforms(c, [offset.x, offset.y, size.x, size.y]); } } + + + + diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 958fcaf..1ecd5df 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, fmt::Debug, marker::PhantomData}; +use std::{collections::HashSet, fmt::Debug}; use evalexpr::{build_operator_tree, EvalexprError, HashMapContext, Node}; use itertools::Itertools; @@ -9,10 +9,9 @@ use serde_yaml::Location; use crate::{ expr::IntoExprWorldContext, - lazy::IsLazy, livecode::{GetLivecodeIdentifiers, LivecodeFromWorld}, state::LivecodeWorldState, - unitcells::{TmpUnitCells, UnitCellCreator, UnitCellExprWorldContext, UnitCells}, + unitcells::UnitCellExprWorldContext, }; #[derive(Debug)] diff --git a/murrelet_perform/src/asset_loader.rs b/murrelet_perform/src/asset_loader.rs index f55b2b4..53b7dca 100644 --- a/murrelet_perform/src/asset_loader.rs +++ b/murrelet_perform/src/asset_loader.rs @@ -2,12 +2,31 @@ use std::{collections::HashMap, path::Path}; use itertools::Itertools; use lerpable::Lerpable; -use murrelet_common::{Asset, Assets}; +use murrelet_common::{ + Assets, RasterAsset, RasterAssetLookup, VectorAsset, VectorLayersAssetLookup, +}; use murrelet_livecode_derive::Livecode; -pub trait AssetLoader { +pub trait VectorAssetLoader { fn is_match(&self, file_extension: &str) -> bool; - fn load(&self, layers: &[&str], filename: &Path) -> Asset; + fn load(&self, layers: &[&str], filename: &Path) -> VectorAsset; +} + +pub trait RasterAssetLoader { + fn is_match(&self, file_extension: &str) -> bool; + fn load(&self, filename: &Path) -> RasterAsset; +} + +#[derive(Livecode, Lerpable, Clone, Debug)] +pub struct RasterFile { + #[livecode(kind = "none")] + name: String, + // probably will want to add something to normalize the nums coming in... +} +impl RasterFile { + pub fn path(&self) -> &Path { + Path::new(&self.name) + } } #[derive(Livecode, Lerpable, Clone, Debug)] @@ -27,35 +46,56 @@ impl PolylineLayerFile { } pub fn _empty_filenames() -> ControlAssetFilenames { - ControlAssetFilenames { files: vec![] } + ControlAssetFilenames { + vector_files: vec![], + raster_files: vec![], + } } pub fn _empty_filenames_lazy() -> ControlLazyAssetFilenames { - ControlLazyAssetFilenames { files: vec![] } + ControlLazyAssetFilenames { + vector_files: vec![], + raster_files: vec![], + } +} + +pub struct AssetLoaders { + vector: Vec>, + raster: Vec>, +} + +impl AssetLoaders { + pub fn new(vector: Vec>, raster: Vec>) -> Self { + Self { vector, raster } + } } #[derive(Livecode, Lerpable, Clone, Debug)] pub struct AssetFilenames { // hmm, the parsers are all in a different part of the code - files: Vec, + vector_files: Vec, + raster_files: Vec, } impl AssetFilenames { pub fn empty() -> Self { - Self { files: Vec::new() } + Self { + vector_files: Vec::new(), + raster_files: Vec::new(), + } } - pub fn load(&self, load_funcs: &[Box]) -> Assets { + pub fn load_polylines(&self, load_funcs: &AssetLoaders) -> Assets { let mut m = HashMap::new(); - for filename in &self.files { + for filename in &self.vector_files { let path = filename.path(); - println!("loading file {:?}", filename.path()); + println!("loading vector file {:?}", filename.path()); // depending on the filetype... if let Some(ext) = path.extension() { let ext_str = ext.to_str(); - for func in load_funcs { + for func in &load_funcs.vector { if func.is_match(ext_str.unwrap()) { let filename_stem = path .file_stem() @@ -69,6 +109,28 @@ impl AssetFilenames { } } - Assets::new(m) + let polylines = VectorLayersAssetLookup::new(m); + + let mut raster = RasterAssetLookup::empty(); + for filename in &self.raster_files { + let path = filename.path(); + println!("loading raster file {:?}", filename.path()); + + if let Some(ext) = path.extension() { + let ext_str = ext.to_str(); + for func in &load_funcs.raster { + if func.is_match(ext_str.unwrap()) { + let filename_stem = path + .file_stem() + .unwrap_or_default() + .to_string_lossy() + .into_owned(); + raster.insert(filename_stem, func.load(path)); + } + } + } + } + + Assets::new(polylines, raster) } } diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index 2a6da81..b2105ee 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -1,8 +1,51 @@ -use std::path::PathBuf; +use std::{path::PathBuf, str::FromStr}; use clap::Parser; -#[derive(Parser, Debug)] +#[derive(Debug, Clone, Copy)] +pub struct TextureDimensions { + pub width: u32, + pub height: u32, +} + +impl TextureDimensions { + pub fn as_dims(&self) -> [u32; 2] { + [self.width, self.height] + } +} + +impl FromStr for TextureDimensions { + type Err = String; + + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split('x').collect(); + if parts.len() != 2 { + return Err("Dimensions must be in format WIDTHxHEIGHT".to_string()); + } + + let width = parts[0].parse::().map_err(|_| "Invalid width")?; + let height = parts[1].parse::().map_err(|_| "Invalid height")?; + + Ok(TextureDimensions { width, height }) + } +} + +impl ToString for TextureDimensions { + fn to_string(&self) -> String { + format!("{}x{}", self.width, self.height) + } +} + +impl Default for TextureDimensions { + fn default() -> Self { + Self { + width: 1080, + height: 1080, + } + } +} + +#[derive(Parser, Debug, Clone)] #[command(author, version, about, long_about = None, allow_hyphen_values = true)] pub struct BaseConfigArgs { pub config_path: PathBuf, @@ -10,6 +53,19 @@ pub struct BaseConfigArgs { #[arg(long, help = "record video")] pub capture: bool, + #[arg(short, long, default_value_t = Default::default())] + pub resolution: TextureDimensions, // window resolution + #[arg(long, default_value_t = 2, value_parser = clap::value_parser!(u32).range(1..=8))] + pub texture_multiplier: u32, // controls number of pixels the shaders work on + #[arg(trailing_var_arg = true)] pub sketch_args: Vec, } +impl BaseConfigArgs { + pub fn texture_dims(&self) -> TextureDimensions { + TextureDimensions { + width: self.resolution.width * self.texture_multiplier, + height: self.resolution.height * self.texture_multiplier, + } + } +} diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index ea7a882..9e14def 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -20,7 +20,7 @@ use murrelet_livecode::livecode::*; use murrelet_livecode_derive::Livecode; use crate::asset_loader::*; -use crate::cli::BaseConfigArgs; +use crate::cli::{BaseConfigArgs, TextureDimensions}; use crate::reload::*; use clap::Parser; @@ -39,7 +39,8 @@ pub trait ConfCommon: CommonTrait { #[derive(Clone, Debug)] pub struct SvgDrawConfig { - size: f32, + size: f32, // todo, what's the difference between this and texture sizes? + pub resolution: TextureDimensions, capture_path: Option, // if it's missing, we don't save (e.g. web browser) frame: u64, target_size: f32, // in mm @@ -49,6 +50,7 @@ pub struct SvgDrawConfig { impl SvgDrawConfig { pub fn new( size: f32, + resolution: TextureDimensions, capture_path: Option, target_size: f32, frame: u64, @@ -60,6 +62,7 @@ impl SvgDrawConfig { margin_size: 10.0, frame, should_resize: true, + resolution, } } @@ -466,6 +469,7 @@ impl AppConfig { // todo, this is all a little weird (svg save path), i should revisit it.. pub struct LilLiveConfig<'a> { + resolution: TextureDimensions, save_path: Option<&'a PathBuf>, run_id: u64, w: &'a LivecodeWorldState, @@ -490,6 +494,7 @@ pub fn svg_save_path_with_prefix(lil_liveconfig: &LilLiveConfig, prefix: &str) - SvgDrawConfig::new( lil_liveconfig.app_config.width, + lil_liveconfig.resolution, capture_path, lil_liveconfig.app_config.svg.size, lil_liveconfig.w.actual_frame_u64(), @@ -539,7 +544,7 @@ where pub fn new_web( conf: String, livecode_src: LivecodeSrc, - load_funcs: &[Box], + load_funcs: &AssetLoaders, ) -> LivecodeResult> { let controlconfig = ControlConfType::parse(&conf).map_err(|err| { if let Some(error) = err.location() { @@ -555,7 +560,7 @@ where pub fn new( save_path: PathBuf, livecode_src: LivecodeSrc, - load_funcs: &[Box], + load_funcs: &AssetLoaders, ) -> LiveCoder { let controlconfig = ControlConfType::fs_load(); @@ -575,7 +580,7 @@ where controlconfig: ControlConfType, save_path: Option, livecode_src: LivecodeSrc, - load_funcs: &[Box], + load_funcs: &AssetLoaders, maybe_args: Option, ) -> LivecodeResult> { let run_id = run_id(); @@ -605,7 +610,7 @@ where let w = s.world(); let app_conf = s.controlconfig._app_config().o(w)?; - let assets = app_conf.assets.load(load_funcs); + let assets = app_conf.assets.load_polylines(load_funcs); s.assets = assets.to_ref(); // use the object to create a world and generate the configs @@ -680,6 +685,7 @@ where run_id: self.run_id, w: self.world(), app_config: self.app_config(), + resolution: self.maybe_args.as_ref().unwrap().resolution, }) } @@ -936,6 +942,10 @@ where self.world().time().seconds_between_render_times() } + pub fn args(&self) -> Option { + self.maybe_args.clone() + } + pub fn sketch_args(&self) -> Vec { if let Some(args) = &self.maybe_args { args.sketch_args.clone() From 503d4ebfb9df774908320c9b5c359509d8486b50 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 16 Feb 2025 21:24:59 -0500 Subject: [PATCH 004/149] wip --- murrelet_draw/src/svg.rs | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 murrelet_draw/src/svg.rs diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs new file mode 100644 index 0000000..c681993 --- /dev/null +++ b/murrelet_draw/src/svg.rs @@ -0,0 +1,57 @@ +// defines the SVG basic shapes + +use glam::Mat4; +use lerpable::Lerpable; +use murrelet_livecode_derive::Livecode; + + +#[derive(Clone, Debug, Livecode, Lerpable)] +pub struct SvgRect { + #[livecode(serde_default = "0")] + pub x: f32, + #[livecode(serde_default = "0")] + pub y: f32, + #[livecode(serde_default = "0")] + pub rx: f32, // x corner radius + #[livecode(serde_default = "0")] + pub ry: f32, // y corner radius + pub width: f32, + pub height: f32, +} + + + + +#[derive(Clone, Debug, Livecode, Lerpable)] +pub enum SvgShape { + Rect(SvgRect) +} + + +#[derive(Clone, Debug)] +pub struct TransformedSvgShape { + pub shape: SvgShape, + pub t: Mat4, +} +impl TransformedSvgShape { + pub fn from_shape(shape: SvgShape) -> Self { + Self { + shape, + t: Mat4::IDENTITY, + } + } + + pub fn transform_with_mat4_after(&self, t: Mat4) -> Self { + Self { + shape: self.shape.clone(), + t: t * self.t, + } + } + + pub fn transform_with_mat4_before(&self, t: Mat4) -> Self { + Self { + shape: self.shape.clone(), + t: self.t * t, + } + } +} From dcc788173c6d8102fc5466b2d866fc3403c7e32d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 19 Feb 2025 18:51:51 -0500 Subject: [PATCH 005/149] wip --- murrelet_draw/src/svg.rs | 1 - murrelet_gpu/src/graphics_ref.rs | 3 --- .../murrelet_livecode_derive/src/derive_graphics_trait.rs | 3 ++- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index c681993..2163620 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -21,7 +21,6 @@ pub struct SvgRect { - #[derive(Clone, Debug, Livecode, Lerpable)] pub enum SvgShape { Rect(SvgRect) diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index aae66bb..89d0f5a 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -1111,10 +1111,8 @@ impl Graphics { let input_texture_view = input_texture.create_view(&Default::default()); - println!("input {:?}", input_texture_view); let (input_texture_view_other, other_texture_and_desc) = if has_second_texture { let other_texture = Graphics::texture(c.dims(), device, second_format.unwrap()); - println!("other texture view {:?}", &other_texture.texture); ( Some(other_texture.texture.create_view(&Default::default())), Some(other_texture), @@ -1122,7 +1120,6 @@ impl Graphics { } else { (None, None) }; - println!("other input {:?}", input_texture_view_other); let sampler = Graphics::_sampler(device, details); let bind_group_layout = Graphics::_bind_group_layout( diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs index 39ec5fe..acf1ef4 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs @@ -76,6 +76,7 @@ fn parse_graphics( if let (Some(kind), Some(ident)) = (&f.kind, &f.ident) { let kind = GraphicKind::parse(kind); let ident = ident.clone(); + match kind { GraphicKind::Drawer => drawers.push(quote! {v.push(&self.#ident)}), GraphicKind::Pipeline => { @@ -84,7 +85,7 @@ fn parse_graphics( .expect("that's not a function!"); quote! { - if !#should_run_fn(render_in) { + if #should_run_fn(render_in) { v.push(&self.#ident as &dyn GraphicsRenderer); } } From 2b5295da5c354425e6eacba33bdf8170f885d6bf Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 20 Feb 2025 17:16:25 -0500 Subject: [PATCH 006/149] wip --- Cargo.toml | 2 +- murrelet_livecode/src/expr.rs | 4 ++++ murrelet_perform/src/cli.rs | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 675887e..99fa57e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,6 @@ members = [ "examples/murrelet_example", "examples/foolish_guillemot", "tinylivecode" -] +, "murrelet_gui", "murrelet_gui_derive"] resolver = "2" diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index fc470ce..8fcf4a0 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -421,4 +421,8 @@ impl MixedEvalDefs { c.set_val(name, val); c } + + pub fn from_idx(idx: IdxInRange) -> Self { + Self::new_from_expr(ExprWorldContextValues::new_from_idx(idx)) + } } diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index b2105ee..fe1ee77 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -58,6 +58,9 @@ pub struct BaseConfigArgs { #[arg(long, default_value_t = 2, value_parser = clap::value_parser!(u32).range(1..=8))] pub texture_multiplier: u32, // controls number of pixels the shaders work on + #[arg(long)] + pub earlystop: Option, + #[arg(trailing_var_arg = true)] pub sketch_args: Vec, } From 5cb666d8589cbdb2954c38614cb7537ce9a715ea Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 20 Feb 2025 17:16:52 -0500 Subject: [PATCH 007/149] wip --- murrelet_gui/Cargo.toml | 20 +++ murrelet_gui/examples/tests.rs | 86 +++++++++++ murrelet_gui/out | 25 +++ murrelet_gui/src/lib.rs | 77 ++++++++++ murrelet_gui_derive/Cargo.toml | 19 +++ murrelet_gui_derive/src/derive_gui.rs | 178 ++++++++++++++++++++++ murrelet_gui_derive/src/lib.rs | 16 ++ murrelet_gui_derive/src/parser.rs | 209 ++++++++++++++++++++++++++ 8 files changed, 630 insertions(+) create mode 100644 murrelet_gui/Cargo.toml create mode 100644 murrelet_gui/examples/tests.rs create mode 100644 murrelet_gui/out create mode 100644 murrelet_gui/src/lib.rs create mode 100644 murrelet_gui_derive/Cargo.toml create mode 100644 murrelet_gui_derive/src/derive_gui.rs create mode 100644 murrelet_gui_derive/src/lib.rs create mode 100644 murrelet_gui_derive/src/parser.rs diff --git a/murrelet_gui/Cargo.toml b/murrelet_gui/Cargo.toml new file mode 100644 index 0000000..02c1f92 --- /dev/null +++ b/murrelet_gui/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "murrelet_gui" +version = "0.1.0" +edition = "2021" + +[features] +schemars = [ + "murrelet_livecode/schemars", +] + +[dependencies] +murrelet_livecode = { path = "../murrelet_livecode/", default-features = false } +murrelet_gui_derive = { path = "../murrelet_gui_derive/" } + +thiserror = "2.0.11" + + +[[example]] +name = "tests" +path = "examples/tests.rs" diff --git a/murrelet_gui/examples/tests.rs b/murrelet_gui/examples/tests.rs new file mode 100644 index 0000000..7e362de --- /dev/null +++ b/murrelet_gui/examples/tests.rs @@ -0,0 +1,86 @@ +use murrelet_gui::{CanMakeGUI, MurreletGUI, MurreletGUISchema}; + +#[derive(MurreletGUI)] +pub struct BasicTypes { + a_number: f32, + b_number: f32, + something: Vec, + // s: String, +} + +// #[derive(Clone)] +// struct CustomMethod { +// pct: f64, +// } +// impl IsLerpingMethod for CustomMethod { +// fn has_lerp_stepped(&self) -> bool { +// false +// } + +// fn partial_lerp_pct(&self, _i: usize, _total: usize) -> f64 { +// 0.0 +// } + +// fn lerp_pct(&self) -> f64 { +// 0.0 +// } + +// fn with_lerp_pct(&self, pct: f64) -> Self { +// let mut c = self.clone(); +// c.pct = pct; +// c +// } +// } + +// fn custom_method() -> CustomMethod { +// CustomMethod { pct: 0.0 } +// } + +// fn custom_func(this: &f32, other: &f32, _pct: &T) -> f32 { +// *this - *other +// } + +// #[derive(Debug, Clone, MurreletUX)] +// pub struct BasicTypesWithOverrides { +// a_number: f32, +// something: Vec, +// label: String, +// b: HashMap, +// } + +// #[derive(Debug, Clone)] +// struct MurreletUXType(); + +// #[derive(Debug, Clone, MurreletUX)] +// enum EnumTest { +// A, +// B(BasicTypesWithOverrides), +// } + +// #[derive(Debug, Clone)] +// struct SimpleNewtype(f32); +// impl MurreletUX for SimpleNewtype { +// fn lerpify(&self, other: &Self, pct: &T) -> Self { +// if pct.lerp_pct() > 0.25 { +// self.clone() +// } else { +// other.clone() +// } +// } + +// fn lerp_partial(&self, pct: T) -> Self { +// SimpleNewtype(pct.lerp_pct() as f32) +// } +// } + +// #[derive(Debug, Clone, MurreletUX)] + +fn main() { + // let b = BasicTypes{ + // a_number: 1.0, + // b_number: -10.0, + // }; + let test_val = BasicTypes::make_gui(); + + assert_eq!(test_val, MurreletGUISchema::Skip) +} diff --git a/murrelet_gui/out b/murrelet_gui/out new file mode 100644 index 0000000..d7a683f --- /dev/null +++ b/murrelet_gui/out @@ -0,0 +1,25 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +use murrelet_gui_derive::MurreletGUI; +use murrelet_livecode::livecode::ControlF32; +pub struct BasicTypes { + a_number: ControlF32, + b_number: ControlF32, +} +impl murrelet_gui::CanMakeGUI for BasicTypes { + fn make_gui(&self) -> murrelet_gui::MurreletGUI { + murrelet_gui::MurreletGUI::Struct( + <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([ + ("a_number".to_owned(), self.a_number.make_gui()), + ("b_number".to_owned(), self.b_number.make_gui()), + ]), + ), + ) + } +} +fn main() {} diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs new file mode 100644 index 0000000..5608090 --- /dev/null +++ b/murrelet_gui/src/lib.rs @@ -0,0 +1,77 @@ +pub use murrelet_gui_derive::MurreletGUI; +use murrelet_livecode::livecode::ControlF32; +use thiserror::Error; + +// #[derive(Debug, Error)] +// pub enum MurreletGUIErr { +// #[error("An error occurred decoding GUI to livecode: {0}")] +// GUIToLivecode(String), +// } + +// pub type MurreletGUIResult = Result; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ValueGUI { + Bool, + Num, + Name(String), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MurreletEnumValGUI { + Unnamed(String, MurreletGUISchema), + Unit(String), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MurreletGUISchema { + Val(ValueGUI), + NewType(Box), + Struct(Vec<(String, MurreletGUISchema)>), // field val + List(Box), + Enum(Vec), // type, val + Skip, +} +impl MurreletGUISchema { + pub fn new_type(m: MurreletGUISchema) -> Self { + Self::NewType(Box::new(m)) + } + + pub fn as_enum(&self) -> Option<&Vec> { + if let Self::Enum(v) = self { + Some(v) + } else { + None + } + } + + pub fn as_new_type(&self) -> Option<&Box> { + if let Self::NewType(v) = self { + Some(v) + } else { + None + } + } +} + +// this should be on the Control version +pub trait CanMakeGUI: Sized { + fn make_gui() -> MurreletGUISchema; + + // fn gui_to_livecode(&self, gui_val: MurreletGUIResponse) -> MurreletGUIResult; +} + + +impl CanMakeGUI for f32 { + fn make_gui() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Num) + } +} + +impl CanMakeGUI for Vec { + fn make_gui() -> MurreletGUISchema { + + // blargh + MurreletGUISchema::List(Box::new(T::make_gui())) + } +} \ No newline at end of file diff --git a/murrelet_gui_derive/Cargo.toml b/murrelet_gui_derive/Cargo.toml new file mode 100644 index 0000000..665d9ef --- /dev/null +++ b/murrelet_gui_derive/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "murrelet_gui_derive" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[features] +debug_logging = ["log"] + +[dependencies] +syn = "2.0.15" +quote = "1.0.18" +proc-macro2 = "1.0.37" +darling = "0.20.3" + +log = { version = "0.4.25", optional = true } + diff --git a/murrelet_gui_derive/src/derive_gui.rs b/murrelet_gui_derive/src/derive_gui.rs new file mode 100644 index 0000000..2edfa84 --- /dev/null +++ b/murrelet_gui_derive/src/derive_gui.rs @@ -0,0 +1,178 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::*; + +pub(crate) struct FieldTokensGUI { + pub(crate) for_make_gui: TokenStream2, + // pub(crate) for_gui_to_livecode: TokenStream2, +} +impl GenFinal for FieldTokensGUI { + // Something(f32) + fn make_newtype_struct_final( + idents: ParsedFieldIdent, + variants: Vec, + ) -> TokenStream2 { + let name = idents.name; + + let for_make_gui = variants.iter().map(|x| x.for_make_gui.clone()); + // let for_gui_to_livecode = variants.iter().map(|x| x.for_gui_to_livecode.clone()); + + quote! { + impl murrelet_gui::CanMakeGUI for #name { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::NewType(#(#for_make_gui,)*) + } + + // fn gui_to_livecode(&self, gui_val: murrelet_gui::MurreletGUISchema) -> murrelet_gui::MurreletGUISchemaResult { + // if let Some(s) = gui_val.as_new_type() { + // Ok(#name(#(#for_gui_to_livecode,)*)) + // } else { + // Err(murrelet_gui::MurreletGUISchemaErr::GUIToLivecode("newtype not in newtype")) + // } + + // } + } + } + } + + fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2 { + let name = idents.name; + + let for_make_gui = variants.iter().map(|x| x.for_make_gui.clone()); + + // let for_assign_vars = variants.iter().map(|x| x.for_assign_vars.clone()); + // let for_gui_to_livecode = variants.iter().map(|x| x.for_gui_to_livecode.clone()); + + quote! { + impl murrelet_gui::CanMakeGUI for #name { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Struct(vec![#(#for_make_gui,)*]) + } + + // fn gui_to_livecode(&self, ux_val: murrelet_gui::MurreletGUISchema) -> murrelet_gui::MurreletGUISchemaResult { + + // #(#for_assign_vars,)* + + // Ok(#name(#(#for_gui_to_livecode,)*)) + // } + } + } + } + + fn make_enum_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2 { + let name = idents.name; + + let for_make_gui = variants.iter().map(|x| x.for_make_gui.clone()); + // let for_gui_to_livecode = variants.iter().map(|x| x.for_gui_to_livecode.clone()); + + quote! { + impl murrelet_gui::CanMakeGUI for #name { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Enum(vec![#(#for_make_gui,)*]) + } + + // fn gui_to_livecode(&self, gui_val: murrelet_gui::MurreletGUISchema) -> murrelet_gui::MurreletGUISchemaResult { + // if let Some(enum_name_and_val) = gui_val.as_enum() { + // let (enum_name, enum_val) = enum_name_and_val; + // match gui_val { + // #(#for_gui_to_livecode,)* + // } + // } + // } + } + } + } + + fn from_newtype_struct(_idents: StructIdents, _parent_ident: syn::Ident) -> FieldTokensGUI { + let for_make_gui = quote! { + self.0.make_gui() + }; + // let for_gui_to_livecode = quote! { + // self.0.gui_to_livecode(v) + // }; + + FieldTokensGUI { + for_make_gui, + // for_gui_to_livecode, + // for_assign_vars: quote!(), + } + } + + // e.g. TileAxisLocs::V(TileAxisVs) + fn from_unnamed_enum(idents: EnumIdents) -> FieldTokensGUI { + let variant_ident = idents.data.ident; + let name = idents.enum_name; + let variant_ident_str = variant_ident.to_string(); + + let for_make_gui = quote! { (#name::#variant_ident(enum_val) => murrelet_gui::MurreletEnumValGUI::Unnamed(#variant_ident_str.to_string(), enum_val.make_gui())) }; + // let for_gui_to_livecode = quote! { murrelet_gui::MurreletEnumValGUI::Unnamed(#variant_ident_str, enum_val) => #name::#variant_ident(enum_val.gui_to_livecode()) }; + + FieldTokensGUI { + for_make_gui, + // for_gui_to_livecode, + // for_assign_vars: quote!(), + } + } + + // e.g. TileAxis::Diag + fn from_unit_enum(idents: EnumIdents) -> FieldTokensGUI { + let variant_ident = idents.data.ident; + let name = idents.enum_name; + let variant_ident_str = variant_ident.to_string(); + + let for_make_gui = quote! { (#name::#variant_ident => murrelet_gui::MurreletEnumValGUI::Unit(#variant_ident_str.to_owned())) }; + // let for_gui_to_livecode = + // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; + + FieldTokensGUI { + for_make_gui, + // for_gui_to_livecode, + // for_assign_vars: quote!(), + } + } + + // s: String with reference + fn from_name(idents: StructIdents) -> FieldTokensGUI { + let name_reference = idents + .data + .reference + .expect("from name called without a reference!"); + + let for_make_gui = quote! { murrelet_gui::ValueGUI::Name(#name_reference) }; + + // let for_assign_vars + // let for_gui_to_livecode = quote! { murrelet_gui::ValueGUIResponse::Name(name) => name }; + + FieldTokensGUI { + for_make_gui, + // for_gui_to_livecode, + // for_assign_vars: , + } + } + + // skip + fn from_noop_struct(idents: StructIdents) -> FieldTokensGUI { + let field_name = idents.data.ident.unwrap().to_string(); + + let for_make_gui = quote! { (#field_name.to_owned(), murrelet_gui::Skip) }; + // let for_gui_to_livecode = + // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; + + FieldTokensGUI { + for_make_gui, + // for_gui_to_livecode, + } + } + + // f32, Vec2, etc + fn from_type_struct(idents: StructIdents) -> FieldTokensGUI { + let field_name = idents.data.ident.unwrap(); + let field_name_str = field_name.to_string(); + let kind = idents.data.ty; + + let for_make_gui = quote! { (#field_name_str.to_owned(), #kind::make_gui()) }; + + FieldTokensGUI { for_make_gui } + } +} diff --git a/murrelet_gui_derive/src/lib.rs b/murrelet_gui_derive/src/lib.rs new file mode 100644 index 0000000..4d64f30 --- /dev/null +++ b/murrelet_gui_derive/src/lib.rs @@ -0,0 +1,16 @@ +extern crate proc_macro; + +use darling::FromDeriveInput; +use derive_gui::FieldTokensGUI; +use parser::{GenFinal, LivecodeReceiver}; +use proc_macro::TokenStream; + +mod parser; +mod derive_gui; + +#[proc_macro_derive(MurreletGUI, attributes(murrelet_gui))] +pub fn murrelet_livecode_derive_murrelet_gui(input: TokenStream) -> TokenStream { + let ast = syn::parse_macro_input!(input as syn::DeriveInput); + let ast_receiver = LivecodeReceiver::from_derive_input(&ast).unwrap(); + FieldTokensGUI::from_ast(ast_receiver).into() +} diff --git a/murrelet_gui_derive/src/parser.rs b/murrelet_gui_derive/src/parser.rs new file mode 100644 index 0000000..b0b4c38 --- /dev/null +++ b/murrelet_gui_derive/src/parser.rs @@ -0,0 +1,209 @@ +use darling::{ast, FromDeriveInput, FromField, FromVariant}; +use proc_macro2::TokenStream as TokenStream2; + +#[derive(Debug)] +pub(crate) struct ParsedFieldIdent { + pub(crate) name: syn::Ident, +} + +// trait and helpers needed to parse a variety of objects +pub(crate) trait GenFinal +where + Self: Sized, +{ + fn from_newtype_struct(_idents: StructIdents, parent_ident: syn::Ident) -> Self; + fn from_unnamed_enum(idents: EnumIdents) -> Self; + fn from_unit_enum(idents: EnumIdents) -> Self; + fn from_noop_struct(idents: StructIdents) -> Self; + fn from_name(idents: StructIdents) -> Self; + fn from_type_struct(idents: StructIdents) -> Self; + + fn from_ast(ast_receiver: LivecodeReceiver) -> TokenStream2 { + match ast_receiver.data { + ast::Data::Enum(_) => Self::make_enum(&ast_receiver), + ast::Data::Struct(ast::Fields { + style: ast::Style::Tuple, + .. + }) => Self::make_newtype(&ast_receiver), + ast::Data::Struct(_) => Self::make_struct(&ast_receiver), + } + } + + fn make_enum_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; + fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; + fn make_newtype_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; + + fn make_struct(s: &LivecodeReceiver) -> TokenStream2 { + let name = s.ident.clone(); + + #[cfg(feature = "debug_logging")] + log::info!("{}::make_struct {}", Self::classname(), name.to_string()); + + // shouldn't be calling this with something that's not a struct.. + let fields = s.data.clone().take_struct().unwrap(); + + let livecodable_fields = fields + .iter() + .map(|field| { + let idents = StructIdents { + data: field.clone(), + }; + + match field.how_to_control_this() { + HowToControlThis::Skip => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_noop_struct"); + Self::from_noop_struct(idents) + } + HowToControlThis::Name => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_name"); + Self::from_name(idents) + } + + HowToControlThis::GUIType => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_type_struct"); + Self::from_type_struct(idents) + } + } + }) + .collect::>(); + + let idents = ParsedFieldIdent { name: name.clone() }; + + Self::make_struct_final(idents, livecodable_fields) + } + + fn make_enum(e: &LivecodeReceiver) -> TokenStream2 { + let name = e.ident.clone(); + + #[cfg(feature = "debug_logging")] + log::info!("{}::make_enum {}", Self::classname(), name.to_string()); + + let variants = e.data.clone().take_enum().unwrap(); + + // just go through and find ones that wrap around a type, and make sure those types are + let variants = variants + .iter() + .map(|variant| { + let ident = EnumIdents { + enum_name: name.clone(), + data: variant.clone(), + }; + + match variant.fields.style { + ast::Style::Tuple => Self::from_unnamed_enum(ident), + ast::Style::Struct => panic!("enum named fields not supported yet"), + ast::Style::Unit => Self::from_unit_enum(ident), + } + }) + .collect::>(); + + let idents = ParsedFieldIdent { name: name.clone() }; + + Self::make_enum_final(idents, variants) + } + + fn make_newtype(s: &LivecodeReceiver) -> TokenStream2 { + let name = s.ident.clone(); + + #[cfg(feature = "debug_logging")] + log::info!("{}::make_newtype {}", Self::classname(), name.to_string()); + + // shouldn't be calling this with something that's not a struct.. + let fields = s.data.clone().take_struct().unwrap(); + + let livecodable_fields = fields + .iter() + .map(|field| { + let idents = StructIdents { + data: field.clone(), + }; + + match field.how_to_control_this() { + HowToControlThis::GUIType => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_newtype_struct"); + Self::from_newtype_struct(idents, name.clone()) + } + HowToControlThis::Skip => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_newtype_recurse_struct_vec"); + Self::from_noop_struct(idents) + } + HowToControlThis::Name => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_name"); + Self::from_name(idents) + }, + } + }) + .collect::>(); + + let idents = ParsedFieldIdent { name: name.clone() }; + + Self::make_newtype_struct_final(idents, livecodable_fields) + } +} + +#[derive(Debug, FromField, Clone)] +#[darling(attributes(lerpable))] +pub(crate) struct LivecodeFieldReceiver { + pub(crate) ident: Option, + pub(crate) ty: syn::Type, + pub(crate) kind: Option, + pub(crate) reference: Option, +} +impl LivecodeFieldReceiver { + fn how_to_control_this(&self) -> HowToControlThis { + if let Some(kind_val) = &self.kind { + if kind_val == "skip" { + HowToControlThis::Skip + } else if kind_val == "reference" { + HowToControlThis::Name + } else { + panic!("unexpected kind") + } + } else { + HowToControlThis::GUIType + } + } +} + +// for enums +#[derive(Debug, FromVariant, Clone)] +#[darling(attributes(lerpable))] +pub(crate) struct LivecodeVariantReceiver { + pub(crate) ident: syn::Ident, + pub(crate) fields: ast::Fields, +} + +#[derive(Debug, Clone, FromDeriveInput)] +#[darling(attributes(lerpable), supports(any))] +pub(crate) struct LivecodeReceiver { + ident: syn::Ident, + data: ast::Data, +} +impl LivecodeReceiver {} + +// represents an enum +pub(crate) struct EnumIdents { + pub(crate) enum_name: syn::Ident, + pub(crate) data: LivecodeVariantReceiver, +} + +impl EnumIdents {} + +#[derive(Clone, Debug)] +pub struct StructIdents { + pub(crate) data: LivecodeFieldReceiver, +} +impl StructIdents {} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) enum HowToControlThis { + Skip, // just do the default values + GUIType, // build a gui for this type + Name, // a referenced thing, +} From 76e7d8b141d6935bfa7673d1013d5d85f42c3b61 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 20 Feb 2025 18:02:33 -0500 Subject: [PATCH 008/149] finish up initial tests --- murrelet_gui/examples/tests.rs | 125 +++++++++++++------------- murrelet_gui/out | 25 ------ murrelet_gui/src/lib.rs | 35 +++++--- murrelet_gui_derive/src/derive_gui.rs | 42 ++++++--- murrelet_gui_derive/src/parser.rs | 14 +-- 5 files changed, 130 insertions(+), 111 deletions(-) diff --git a/murrelet_gui/examples/tests.rs b/murrelet_gui/examples/tests.rs index 7e362de..9ddbaba 100644 --- a/murrelet_gui/examples/tests.rs +++ b/murrelet_gui/examples/tests.rs @@ -1,72 +1,36 @@ -use murrelet_gui::{CanMakeGUI, MurreletGUI, MurreletGUISchema}; +use std::collections::HashMap; + +use murrelet_gui::{CanMakeGUI, MurreletEnumValGUI, MurreletGUI, MurreletGUISchema, ValueGUI}; #[derive(MurreletGUI)] pub struct BasicTypes { a_number: f32, - b_number: f32, + b_number: usize, + c_number: u64, + d_number: i32, something: Vec, - // s: String, + s: String, + #[murrelet_gui(reference = "test")] + referenced_string: String, } -// #[derive(Clone)] -// struct CustomMethod { -// pct: f64, -// } -// impl IsLerpingMethod for CustomMethod { -// fn has_lerp_stepped(&self) -> bool { -// false -// } - -// fn partial_lerp_pct(&self, _i: usize, _total: usize) -> f64 { -// 0.0 -// } - -// fn lerp_pct(&self) -> f64 { -// 0.0 -// } - -// fn with_lerp_pct(&self, pct: f64) -> Self { -// let mut c = self.clone(); -// c.pct = pct; -// c -// } -// } - -// fn custom_method() -> CustomMethod { -// CustomMethod { pct: 0.0 } -// } - -// fn custom_func(this: &f32, other: &f32, _pct: &T) -> f32 { -// *this - *other -// } - -// #[derive(Debug, Clone, MurreletUX)] -// pub struct BasicTypesWithOverrides { -// a_number: f32, -// something: Vec, -// label: String, -// b: HashMap, -// } - -// #[derive(Debug, Clone)] -// struct MurreletUXType(); +#[derive(MurreletGUI)] +pub struct OverridesAndRecursive { + a_number: f32, + something: Vec, + label: String, + #[murrelet_gui(kind = "skip")] + b: HashMap, +} -// #[derive(Debug, Clone, MurreletUX)] -// enum EnumTest { -// A, -// B(BasicTypesWithOverrides), -// } +#[derive(MurreletGUI)] +enum EnumTest { + A, + B(OverridesAndRecursive), +} -// #[derive(Debug, Clone)] -// struct SimpleNewtype(f32); -// impl MurreletUX for SimpleNewtype { -// fn lerpify(&self, other: &Self, pct: &T) -> Self { -// if pct.lerp_pct() > 0.25 { -// self.clone() -// } else { -// other.clone() -// } -// } +#[derive(MurreletGUI)] +struct SimpleNewtype(f32); // fn lerp_partial(&self, pct: T) -> Self { // SimpleNewtype(pct.lerp_pct() as f32) @@ -82,5 +46,44 @@ fn main() { // }; let test_val = BasicTypes::make_gui(); - assert_eq!(test_val, MurreletGUISchema::Skip) + let basic_types_schema = MurreletGUISchema::Struct(vec![ + ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ( + "something".to_owned(), + MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), + ), + ("s".to_owned(), MurreletGUISchema::Skip), + ( + "referenced_string".to_owned(), + MurreletGUISchema::Val(ValueGUI::Name("test".to_owned())), + ), + ]); + + assert_eq!(test_val, basic_types_schema); + + let test_val = OverridesAndRecursive::make_gui(); + + let overrides_and_recursive_schema = MurreletGUISchema::Struct(vec![ + ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ( + "something".to_owned(), + MurreletGUISchema::list(basic_types_schema), + ), + ("label".to_owned(), MurreletGUISchema::Skip), + ("b".to_owned(), MurreletGUISchema::Skip), + ]); + assert_eq!(test_val, overrides_and_recursive_schema); + + let test_val = EnumTest::make_gui(); + + assert_eq!( + test_val, + MurreletGUISchema::Enum(vec![ + (MurreletEnumValGUI::Unit("A".to_owned())), + (MurreletEnumValGUI::Unnamed("B".to_owned(), overrides_and_recursive_schema)), + ]) + ); } diff --git a/murrelet_gui/out b/murrelet_gui/out index d7a683f..e69de29 100644 --- a/murrelet_gui/out +++ b/murrelet_gui/out @@ -1,25 +0,0 @@ -#![feature(prelude_import)] -#[prelude_import] -use std::prelude::rust_2021::*; -#[macro_use] -extern crate std; -use murrelet_gui_derive::MurreletGUI; -use murrelet_livecode::livecode::ControlF32; -pub struct BasicTypes { - a_number: ControlF32, - b_number: ControlF32, -} -impl murrelet_gui::CanMakeGUI for BasicTypes { - fn make_gui(&self) -> murrelet_gui::MurreletGUI { - murrelet_gui::MurreletGUI::Struct( - <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([ - ("a_number".to_owned(), self.a_number.make_gui()), - ("b_number".to_owned(), self.b_number.make_gui()), - ]), - ), - ) - } -} -fn main() {} diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs index 5608090..30427a8 100644 --- a/murrelet_gui/src/lib.rs +++ b/murrelet_gui/src/lib.rs @@ -1,6 +1,4 @@ pub use murrelet_gui_derive::MurreletGUI; -use murrelet_livecode::livecode::ControlF32; -use thiserror::Error; // #[derive(Debug, Error)] // pub enum MurreletGUIErr { @@ -37,6 +35,10 @@ impl MurreletGUISchema { Self::NewType(Box::new(m)) } + pub fn list(m: MurreletGUISchema) -> Self { + Self::List(Box::new(m)) + } + pub fn as_enum(&self) -> Option<&Vec> { if let Self::Enum(v) = self { Some(v) @@ -57,21 +59,34 @@ impl MurreletGUISchema { // this should be on the Control version pub trait CanMakeGUI: Sized { fn make_gui() -> MurreletGUISchema; +} - // fn gui_to_livecode(&self, gui_val: MurreletGUIResponse) -> MurreletGUIResult; +macro_rules! impl_can_make_gui_for_num { + ($ty:ty) => { + impl CanMakeGUI for $ty { + fn make_gui() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Num) + } + } + }; } +impl_can_make_gui_for_num!(f32); +impl_can_make_gui_for_num!(f64); +impl_can_make_gui_for_num!(u32); +impl_can_make_gui_for_num!(u64); +impl_can_make_gui_for_num!(i32); +impl_can_make_gui_for_num!(i64); +impl_can_make_gui_for_num!(usize); -impl CanMakeGUI for f32 { +impl CanMakeGUI for Vec { fn make_gui() -> MurreletGUISchema { - MurreletGUISchema::Val(ValueGUI::Num) + MurreletGUISchema::List(Box::new(T::make_gui())) } } -impl CanMakeGUI for Vec { +impl CanMakeGUI for String { fn make_gui() -> MurreletGUISchema { - - // blargh - MurreletGUISchema::List(Box::new(T::make_gui())) + MurreletGUISchema::Skip } -} \ No newline at end of file +} diff --git a/murrelet_gui_derive/src/derive_gui.rs b/murrelet_gui_derive/src/derive_gui.rs index 2edfa84..aaf9b86 100644 --- a/murrelet_gui_derive/src/derive_gui.rs +++ b/murrelet_gui_derive/src/derive_gui.rs @@ -21,7 +21,7 @@ impl GenFinal for FieldTokensGUI { quote! { impl murrelet_gui::CanMakeGUI for #name { fn make_gui() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::NewType(#(#for_make_gui,)*) + murrelet_gui::MurreletGUISchema::new_type(#(#for_make_gui,)*) } // fn gui_to_livecode(&self, gui_val: murrelet_gui::MurreletGUISchema) -> murrelet_gui::MurreletGUISchemaResult { @@ -84,9 +84,11 @@ impl GenFinal for FieldTokensGUI { } } - fn from_newtype_struct(_idents: StructIdents, _parent_ident: syn::Ident) -> FieldTokensGUI { + fn from_newtype_struct(idents: StructIdents, _parent_ident: syn::Ident) -> FieldTokensGUI { + let ty = convert_vec_type(&idents.data.ty); + let for_make_gui = quote! { - self.0.make_gui() + #ty::make_gui() }; // let for_gui_to_livecode = quote! { // self.0.gui_to_livecode(v) @@ -102,10 +104,10 @@ impl GenFinal for FieldTokensGUI { // e.g. TileAxisLocs::V(TileAxisVs) fn from_unnamed_enum(idents: EnumIdents) -> FieldTokensGUI { let variant_ident = idents.data.ident; - let name = idents.enum_name; + let ty = convert_vec_type(&idents.data.fields.fields.first().unwrap().ty); let variant_ident_str = variant_ident.to_string(); - let for_make_gui = quote! { (#name::#variant_ident(enum_val) => murrelet_gui::MurreletEnumValGUI::Unnamed(#variant_ident_str.to_string(), enum_val.make_gui())) }; + let for_make_gui = quote! { (murrelet_gui::MurreletEnumValGUI::Unnamed(#variant_ident_str.to_string(), #ty::make_gui())) }; // let for_gui_to_livecode = quote! { murrelet_gui::MurreletEnumValGUI::Unnamed(#variant_ident_str, enum_val) => #name::#variant_ident(enum_val.gui_to_livecode()) }; FieldTokensGUI { @@ -118,10 +120,9 @@ impl GenFinal for FieldTokensGUI { // e.g. TileAxis::Diag fn from_unit_enum(idents: EnumIdents) -> FieldTokensGUI { let variant_ident = idents.data.ident; - let name = idents.enum_name; let variant_ident_str = variant_ident.to_string(); - let for_make_gui = quote! { (#name::#variant_ident => murrelet_gui::MurreletEnumValGUI::Unit(#variant_ident_str.to_owned())) }; + let for_make_gui = quote! { murrelet_gui::MurreletEnumValGUI::Unit(#variant_ident_str.to_owned()) }; // let for_gui_to_livecode = // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; @@ -134,12 +135,14 @@ impl GenFinal for FieldTokensGUI { // s: String with reference fn from_name(idents: StructIdents) -> FieldTokensGUI { + let field_name = idents.data.ident.unwrap().to_string(); + let name_reference = idents .data .reference .expect("from name called without a reference!"); - let for_make_gui = quote! { murrelet_gui::ValueGUI::Name(#name_reference) }; + let for_make_gui = quote! { (#field_name.to_owned(), murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Name(#name_reference.to_owned()))) }; // let for_assign_vars // let for_gui_to_livecode = quote! { murrelet_gui::ValueGUIResponse::Name(name) => name }; @@ -155,7 +158,7 @@ impl GenFinal for FieldTokensGUI { fn from_noop_struct(idents: StructIdents) -> FieldTokensGUI { let field_name = idents.data.ident.unwrap().to_string(); - let for_make_gui = quote! { (#field_name.to_owned(), murrelet_gui::Skip) }; + let for_make_gui = quote! { (#field_name.to_owned(), murrelet_gui::MurreletGUISchema::Skip) }; // let for_gui_to_livecode = // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; @@ -169,10 +172,29 @@ impl GenFinal for FieldTokensGUI { fn from_type_struct(idents: StructIdents) -> FieldTokensGUI { let field_name = idents.data.ident.unwrap(); let field_name_str = field_name.to_string(); - let kind = idents.data.ty; + // to call a static function, we need to + let kind = convert_vec_type(&idents.data.ty); let for_make_gui = quote! { (#field_name_str.to_owned(), #kind::make_gui()) }; FieldTokensGUI { for_make_gui } } } + +// we need to use turbofish to call an associated function +fn convert_vec_type(ty: &syn::Type) -> TokenStream2 { + if let syn::Type::Path(type_path) = ty { + if let Some(last_segment) = type_path.path.segments.last() { + if last_segment.ident == "Vec" { + if let syn::PathArguments::AngleBracketed(angle_bracketed) = &last_segment.arguments + { + if let Some(inner_arg) = angle_bracketed.args.first() { + return quote! { Vec:: < #inner_arg > }; + } + } + } + } + } + + quote! { #ty } +} diff --git a/murrelet_gui_derive/src/parser.rs b/murrelet_gui_derive/src/parser.rs index b0b4c38..4e6c1bc 100644 --- a/murrelet_gui_derive/src/parser.rs +++ b/murrelet_gui_derive/src/parser.rs @@ -17,6 +17,7 @@ where fn from_noop_struct(idents: StructIdents) -> Self; fn from_name(idents: StructIdents) -> Self; fn from_type_struct(idents: StructIdents) -> Self; + // fn from_recurse_struct_vec(idents: StructIdents) -> Self; fn from_ast(ast_receiver: LivecodeReceiver) -> TokenStream2 { match ast_receiver.data { @@ -136,7 +137,7 @@ where #[cfg(feature = "debug_logging")] log::info!("-> from_name"); Self::from_name(idents) - }, + } } }) .collect::>(); @@ -148,7 +149,7 @@ where } #[derive(Debug, FromField, Clone)] -#[darling(attributes(lerpable))] +#[darling(attributes(murrelet_gui))] pub(crate) struct LivecodeFieldReceiver { pub(crate) ident: Option, pub(crate) ty: syn::Type, @@ -165,6 +166,8 @@ impl LivecodeFieldReceiver { } else { panic!("unexpected kind") } + } else if let Some(_) = &self.reference { + HowToControlThis::Name } else { HowToControlThis::GUIType } @@ -173,14 +176,14 @@ impl LivecodeFieldReceiver { // for enums #[derive(Debug, FromVariant, Clone)] -#[darling(attributes(lerpable))] +#[darling(attributes(murrelet_gui))] pub(crate) struct LivecodeVariantReceiver { pub(crate) ident: syn::Ident, pub(crate) fields: ast::Fields, } #[derive(Debug, Clone, FromDeriveInput)] -#[darling(attributes(lerpable), supports(any))] +#[darling(attributes(murrelet_gui), supports(any))] pub(crate) struct LivecodeReceiver { ident: syn::Ident, data: ast::Data, @@ -205,5 +208,6 @@ impl StructIdents {} pub(crate) enum HowToControlThis { Skip, // just do the default values GUIType, // build a gui for this type - Name, // a referenced thing, + // GUIVec, // GUI for a list + Name, // a referenced thing, } From efa332dd728cb4a770762665697b97e1bf167524 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 20 Feb 2025 21:36:19 -0500 Subject: [PATCH 009/149] wip --- examples/foolish_guillemot/Cargo.toml | 18 +++++------ murrelet/Cargo.toml | 7 +++++ murrelet_common/Cargo.toml | 2 ++ murrelet_common/src/color.rs | 7 +++++ murrelet_common/src/lib.rs | 8 +++++ murrelet_draw/Cargo.toml | 2 ++ murrelet_draw/src/compass.rs | 16 +++++----- murrelet_gpu/Cargo.toml | 7 +++++ murrelet_gui/Cargo.toml | 6 +--- murrelet_gui/examples/tests.rs | 9 +++++- murrelet_gui/src/lib.rs | 16 ++++++++-- murrelet_gui_derive/Cargo.toml | 2 +- murrelet_gui_derive/src/derive_gui.rs | 31 +++++++++++++++++-- murrelet_gui_derive/src/parser.rs | 11 ++++++- murrelet_livecode/Cargo.toml | 1 + murrelet_livecode/src/types.rs | 7 +++++ murrelet_livecode_macros/Cargo.toml | 1 + .../murrelet_livecode_derive/Cargo.toml | 2 ++ murrelet_perform/Cargo.toml | 9 ++++++ murrelet_perform/src/asset_loader.rs | 19 +++++++++++- murrelet_perform/src/perform.rs | 9 +++--- murrelet_svg/Cargo.toml | 2 ++ 22 files changed, 157 insertions(+), 35 deletions(-) diff --git a/examples/foolish_guillemot/Cargo.toml b/examples/foolish_guillemot/Cargo.toml index 76ec7fc..bfbc650 100644 --- a/examples/foolish_guillemot/Cargo.toml +++ b/examples/foolish_guillemot/Cargo.toml @@ -23,17 +23,17 @@ itertools = "0.10.5" serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" -murrelet = { version = "0.1.1", path = "../../murrelet/" } -murrelet_common = { version = "0.1.1", path = "../../murrelet_common/" } -murrelet_livecode = { version = "0.1.1", path = "../../murrelet_livecode/" } -murrelet_livecode_macros = { version = "0.1.1", path = "../../murrelet_livecode_macros/" } -murrelet_livecode_derive = { version = "0.1.1", path = "../../murrelet_livecode_macros/murrelet_livecode_derive/" } -murrelet_perform = { version = "0.1.1", path = "../../murrelet_perform/", features = [ +murrelet = { version = "0.1.2", path = "../../murrelet/" } +murrelet_common = { version = "0.1.2", path = "../../murrelet_common/" } +murrelet_livecode = { version = "0.1.2", path = "../../murrelet_livecode/" } +murrelet_livecode_macros = { version = "0.1.2", path = "../../murrelet_livecode_macros/" } +murrelet_livecode_derive = { version = "0.1.2", path = "../../murrelet_livecode_macros/murrelet_livecode_derive/" } +murrelet_perform = { version = "0.1.2", path = "../../murrelet_perform/", features = [ "for_the_web", ] } -murrelet_draw = { version = "0.1.1", path = "../../murrelet_draw/" } -murrelet_svg = { version = "0.1.1", path = "../../murrelet_svg/" } -murrelet_gpu = { version = "0.1.1", path = "../../murrelet_gpu/", features = [ +murrelet_draw = { version = "0.1.2", path = "../../murrelet_draw/" } +murrelet_svg = { version = "0.1.2", path = "../../murrelet_svg/" } +murrelet_gpu = { version = "0.1.2", path = "../../murrelet_gpu/", features = [ "no_nannou", ] } diff --git a/murrelet/Cargo.toml b/murrelet/Cargo.toml index ac60176..096bf9b 100644 --- a/murrelet/Cargo.toml +++ b/murrelet/Cargo.toml @@ -17,6 +17,13 @@ schemars = [ "murrelet_livecode_derive/schemars", "murrelet_perform/schemars", ] +# murrelet_gui = [ +# "murrelet_draw/murrelet_gui", +# "murrelet_livecode/murrelet_gui", +# "murrelet_livecode_macros/murrelet_gui", +# "murrelet_livecode_derive/murrelet_gui", +# "murrelet_perform/murrelet_gui", +# ] [dependencies] glam = "0.28.0" diff --git a/murrelet_common/Cargo.toml b/murrelet_common/Cargo.toml index 92148e5..9ddbd09 100644 --- a/murrelet_common/Cargo.toml +++ b/murrelet_common/Cargo.toml @@ -14,6 +14,8 @@ palette = "0.7.6" rand = "0.8" num-traits = "0.2.19" lerpable = "0.0.2" +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/" } + [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = "0.2" diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index 21df571..68dda59 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -1,6 +1,7 @@ use std::fmt; use lerpable::{IsLerpingMethod, Lerpable}; +use murrelet_gui::CanMakeGUI; use palette::{ rgb::Rgb, FromColor, Hsva, IntoColor, LinSrgb, LinSrgba, RgbHue, Srgb, Srgba, WithAlpha, }; @@ -135,6 +136,12 @@ impl MurreletColor { } } +impl CanMakeGUI for MurreletColor { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Color) + } +} + pub trait MurreletIntoLinSrgba { fn into_murrelet_color(&self) -> MurreletColor; } diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index bb2f92e..690e6ab 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -851,3 +851,11 @@ pub fn lerpify_vec_vec3( lerped.into_iter().map(|v| v.into()).collect_vec() } + +pub fn make_gui_vec2() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Vec2) +} + +pub fn make_gui_vec3() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Vec3) +} \ No newline at end of file diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index 78643bd..0ca9f59 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -9,6 +9,7 @@ license = "AGPL-3.0-or-later" [features] schemars = ["dep:schemars", "murrelet_livecode/schemars", "murrelet_livecode_macros/schemars", "murrelet_livecode_derive/schemars"] +# murrelet_gui = ["dep:murrelet_gui", "murrelet_livecode/murrelet_gui", "murrelet_livecode_macros/murrelet_gui", "murrelet_livecode_derive/murrelet_gui"] [dependencies] murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } @@ -32,3 +33,4 @@ md-5 = "0.10.6" hex = "0.4.3" schemars = { version = "0.8.21", optional = true } +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/" } #, optional = true } diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index 2043a76..24ad6fd 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -2,6 +2,7 @@ use glam::Vec2; use lerpable::Lerpable; use murrelet_common::*; +use murrelet_gui::MurreletGUI; use murrelet_livecode_derive::Livecode; use crate::{ @@ -9,9 +10,8 @@ use crate::{ livecodetypes::anglepi::*, }; -#[derive(Debug, Clone, Copy, Livecode, Lerpable)] +#[derive(Debug, Clone, Copy, Livecode, MurreletGUI, Lerpable)] pub struct CurveStart { - #[lerpable(func = "lerpify_vec2")] loc: Vec2, angle_pi: LivecodeAnglePi, } @@ -29,7 +29,7 @@ fn empty_string() -> String { String::new() } -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct CompassDir { angle_pi: LivecodeAnglePi, #[livecode(serde_default = "false")] @@ -48,7 +48,7 @@ impl CompassDir { } } -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct CompassArc { radius: f32, arc_length: LivecodeAnglePi, @@ -62,20 +62,20 @@ pub struct CompassArc { // pub fn new(radius: f32, arc_length: f32, is_absolute: bool) -> Self { Self { radius, arc_length, is_absolute } } // } -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct CompassLine { length: f32, // how far should we head in the current direction #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] label: String, } -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct CompassRepeat { times: usize, what: Vec, } -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub enum CompassAction { Angle(CompassDir), // abs Arc(CompassArc), @@ -291,7 +291,7 @@ impl InteractiveCompassBuilder { } } -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct MurreletCompass { start: CurveStart, dirs: Vec, diff --git a/murrelet_gpu/Cargo.toml b/murrelet_gpu/Cargo.toml index 9c2e6f7..062f504 100644 --- a/murrelet_gpu/Cargo.toml +++ b/murrelet_gpu/Cargo.toml @@ -17,6 +17,12 @@ schemars = [ "murrelet_livecode/schemars", "murrelet_perform/schemars", ] +# murrelet_gui = [ +# "dep:murrelet_gui", +# "murrelet_draw/murrelet_gui", +# "murrelet_livecode/murrelet_gui", +# "murrelet_perform/murrelet_gui", +# ] [dependencies] wgpu_for_latest = { package = "wgpu", version = "0.20.1", optional = true } @@ -48,3 +54,4 @@ image = "0.25.2" bytemuck = { version = "1.16.1", features = ["derive"] } schemars = { version = "0.8.21", optional = true } +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/" } #, optional = true } diff --git a/murrelet_gui/Cargo.toml b/murrelet_gui/Cargo.toml index 02c1f92..5f80928 100644 --- a/murrelet_gui/Cargo.toml +++ b/murrelet_gui/Cargo.toml @@ -1,15 +1,11 @@ [package] name = "murrelet_gui" -version = "0.1.0" +version = "0.1.2" edition = "2021" [features] -schemars = [ - "murrelet_livecode/schemars", -] [dependencies] -murrelet_livecode = { path = "../murrelet_livecode/", default-features = false } murrelet_gui_derive = { path = "../murrelet_gui_derive/" } thiserror = "2.0.11" diff --git a/murrelet_gui/examples/tests.rs b/murrelet_gui/examples/tests.rs index 9ddbaba..a29bd01 100644 --- a/murrelet_gui/examples/tests.rs +++ b/murrelet_gui/examples/tests.rs @@ -8,16 +8,22 @@ pub struct BasicTypes { b_number: usize, c_number: u64, d_number: i32, + bool: bool, something: Vec, s: String, #[murrelet_gui(reference = "test")] referenced_string: String, } +fn custom_func() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Num) +} + #[derive(MurreletGUI)] pub struct OverridesAndRecursive { a_number: f32, something: Vec, + #[murrelet_gui(func = "custom_func")] label: String, #[murrelet_gui(kind = "skip")] b: HashMap, @@ -51,6 +57,7 @@ fn main() { ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("bool".to_owned(), MurreletGUISchema::Val(ValueGUI::Bool)), ( "something".to_owned(), MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), @@ -72,7 +79,7 @@ fn main() { "something".to_owned(), MurreletGUISchema::list(basic_types_schema), ), - ("label".to_owned(), MurreletGUISchema::Skip), + ("label".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), // make sure it calls the override ("b".to_owned(), MurreletGUISchema::Skip), ]); assert_eq!(test_val, overrides_and_recursive_schema); diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs index 30427a8..108e758 100644 --- a/murrelet_gui/src/lib.rs +++ b/murrelet_gui/src/lib.rs @@ -10,9 +10,13 @@ pub use murrelet_gui_derive::MurreletGUI; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ValueGUI { - Bool, - Num, - Name(String), + Bool, // should be a ControlBool + Num, // should be a ControlF32 + Name(String), // clue for the front-end to sink the strings iwth the same name + Color, // expected to give h s v a + Defs, // make a ctx node + Vec2, + Vec3, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -90,3 +94,9 @@ impl CanMakeGUI for String { MurreletGUISchema::Skip } } + +impl CanMakeGUI for bool { + fn make_gui() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Bool) + } +} diff --git a/murrelet_gui_derive/Cargo.toml b/murrelet_gui_derive/Cargo.toml index 665d9ef..dca223a 100644 --- a/murrelet_gui_derive/Cargo.toml +++ b/murrelet_gui_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "murrelet_gui_derive" -version = "0.1.0" +version = "0.1.2" edition = "2021" [lib] diff --git a/murrelet_gui_derive/src/derive_gui.rs b/murrelet_gui_derive/src/derive_gui.rs index aaf9b86..e5655b2 100644 --- a/murrelet_gui_derive/src/derive_gui.rs +++ b/murrelet_gui_derive/src/derive_gui.rs @@ -84,6 +84,18 @@ impl GenFinal for FieldTokensGUI { } } + fn from_override_enum(func: &str) -> FieldTokensGUI { + let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); + + let for_make_gui = quote! { + #method() + }; + + FieldTokensGUI { + for_make_gui, + } + } + fn from_newtype_struct(idents: StructIdents, _parent_ident: syn::Ident) -> FieldTokensGUI { let ty = convert_vec_type(&idents.data.ty); @@ -122,7 +134,8 @@ impl GenFinal for FieldTokensGUI { let variant_ident = idents.data.ident; let variant_ident_str = variant_ident.to_string(); - let for_make_gui = quote! { murrelet_gui::MurreletEnumValGUI::Unit(#variant_ident_str.to_owned()) }; + let for_make_gui = + quote! { murrelet_gui::MurreletEnumValGUI::Unit(#variant_ident_str.to_owned()) }; // let for_gui_to_livecode = // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; @@ -158,7 +171,8 @@ impl GenFinal for FieldTokensGUI { fn from_noop_struct(idents: StructIdents) -> FieldTokensGUI { let field_name = idents.data.ident.unwrap().to_string(); - let for_make_gui = quote! { (#field_name.to_owned(), murrelet_gui::MurreletGUISchema::Skip) }; + let for_make_gui = + quote! { (#field_name.to_owned(), murrelet_gui::MurreletGUISchema::Skip) }; // let for_gui_to_livecode = // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; @@ -179,6 +193,19 @@ impl GenFinal for FieldTokensGUI { FieldTokensGUI { for_make_gui } } + + fn from_override_struct(idents: StructIdents, func: &str) -> FieldTokensGUI { + let field_name = idents.data.ident.unwrap(); + let field_name_str = field_name.to_string(); + + let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); + + let for_make_gui = quote! { (#field_name_str.to_owned(), #method()) }; + + FieldTokensGUI { + for_make_gui, + } + } } // we need to use turbofish to call an associated function diff --git a/murrelet_gui_derive/src/parser.rs b/murrelet_gui_derive/src/parser.rs index 4e6c1bc..d77607c 100644 --- a/murrelet_gui_derive/src/parser.rs +++ b/murrelet_gui_derive/src/parser.rs @@ -1,6 +1,8 @@ use darling::{ast, FromDeriveInput, FromField, FromVariant}; use proc_macro2::TokenStream as TokenStream2; +use crate::FieldTokensGUI; + #[derive(Debug)] pub(crate) struct ParsedFieldIdent { pub(crate) name: syn::Ident, @@ -29,6 +31,8 @@ where ast::Data::Struct(_) => Self::make_struct(&ast_receiver), } } + fn from_override_struct(idents: StructIdents, func: &str) -> Self; + fn from_override_enum(func: &str) -> Self; fn make_enum_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; @@ -61,12 +65,12 @@ where log::info!("-> from_name"); Self::from_name(idents) } - HowToControlThis::GUIType => { #[cfg(feature = "debug_logging")] log::info!("-> from_type_struct"); Self::from_type_struct(idents) } + HowToControlThis::Override(func) => Self::from_override_struct(idents, &func), } }) .collect::>(); @@ -138,6 +142,7 @@ where log::info!("-> from_name"); Self::from_name(idents) } + HowToControlThis::Override(func) => Self::from_override_enum(&func), } }) .collect::>(); @@ -155,6 +160,7 @@ pub(crate) struct LivecodeFieldReceiver { pub(crate) ty: syn::Type, pub(crate) kind: Option, pub(crate) reference: Option, + pub(crate) func: Option, } impl LivecodeFieldReceiver { fn how_to_control_this(&self) -> HowToControlThis { @@ -168,6 +174,8 @@ impl LivecodeFieldReceiver { } } else if let Some(_) = &self.reference { HowToControlThis::Name + } else if let Some(func) = &self.func { + HowToControlThis::Override(func.to_owned()) } else { HowToControlThis::GUIType } @@ -210,4 +218,5 @@ pub(crate) enum HowToControlThis { GUIType, // build a gui for this type // GUIVec, // GUI for a list Name, // a referenced thing, + Override(String), } diff --git a/murrelet_livecode/Cargo.toml b/murrelet_livecode/Cargo.toml index dc3af12..dc87586 100644 --- a/murrelet_livecode/Cargo.toml +++ b/murrelet_livecode/Cargo.toml @@ -12,6 +12,7 @@ schemars = ["dep:schemars"] [dependencies] murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/" } lerpable = "0.0.2" diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 1ecd5df..e439046 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -4,6 +4,7 @@ use evalexpr::{build_operator_tree, EvalexprError, HashMapContext, Node}; use itertools::Itertools; use lerpable::{step, Lerpable}; use murrelet_common::IdxInRange2d; +use murrelet_gui::CanMakeGUI; use serde::{Deserialize, Deserializer}; use serde_yaml::Location; @@ -70,6 +71,12 @@ impl AdditionalContextNode { } } +impl CanMakeGUI for AdditionalContextNode { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Defs) + } +} + impl Lerpable for AdditionalContextNode { fn lerpify(&self, other: &Self, pct: &T) -> Self { step(self, other, pct) diff --git a/murrelet_livecode_macros/Cargo.toml b/murrelet_livecode_macros/Cargo.toml index 65b34b0..e1407fd 100644 --- a/murrelet_livecode_macros/Cargo.toml +++ b/murrelet_livecode_macros/Cargo.toml @@ -9,6 +9,7 @@ license = "AGPL-3.0-or-later" [features] schemars = ["murrelet_livecode/schemars", "murrelet_livecode_derive/schemars"] +# murrelet_gui = ["murrelet_livecode/murrelet_gui", "murrelet_livecode_derive/murrelet_gui"] [dependencies] diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml index 28924ba..2520bae 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml +++ b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml @@ -12,6 +12,7 @@ proc-macro = true [features] schemars = ["murrelet_livecode/schemars"] +# murrelet_gui = ["murrelet_livecode/murrelet_gui"] [dependencies] syn = "2.0.15" @@ -28,6 +29,7 @@ glam = "0.28.0" palette = "0.7.6" lerpable = "0.0.2" schemars = "0.8.21" +murrelet_gui = { version = "0.1.2", path = "../../murrelet_gui"} [[example]] name = "tests" diff --git a/murrelet_perform/Cargo.toml b/murrelet_perform/Cargo.toml index d9f8916..d309a5b 100644 --- a/murrelet_perform/Cargo.toml +++ b/murrelet_perform/Cargo.toml @@ -16,6 +16,13 @@ schemars = [ "murrelet_livecode_macros/schemars", "murrelet_livecode_derive/schemars", ] +# murrelet_gui = [ +# "dep:murrelet_gui", +# "murrelet_livecode/murrelet_gui", +# "murrelet_draw/murrelet_gui", +# "murrelet_livecode_macros/murrelet_gui", +# "murrelet_livecode_derive/murrelet_gui", +# ] [dependencies] murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } @@ -35,5 +42,7 @@ palette = "0.7.6" anyhow = "1.0.86" schemars = { version = "0.8.21", optional = true } +# murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", optional = true } +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/" } clap = { version = "4.5.23", features = ["derive"] } diff --git a/murrelet_perform/src/asset_loader.rs b/murrelet_perform/src/asset_loader.rs index 53b7dca..15366bb 100644 --- a/murrelet_perform/src/asset_loader.rs +++ b/murrelet_perform/src/asset_loader.rs @@ -5,6 +5,7 @@ use lerpable::Lerpable; use murrelet_common::{ Assets, RasterAsset, RasterAssetLookup, VectorAsset, VectorLayersAssetLookup, }; +use murrelet_gui::CanMakeGUI; use murrelet_livecode_derive::Livecode; pub trait VectorAssetLoader { @@ -65,9 +66,19 @@ pub struct AssetLoaders { } impl AssetLoaders { - pub fn new(vector: Vec>, raster: Vec>) -> Self { + pub fn new( + vector: Vec>, + raster: Vec>, + ) -> Self { Self { vector, raster } } + + pub fn empty() -> AssetLoaders { + Self { + vector: vec![], + raster: vec![], + } + } } #[derive(Livecode, Lerpable, Clone, Debug)] @@ -134,3 +145,9 @@ impl AssetFilenames { Assets::new(polylines, raster) } } + +impl CanMakeGUI for AssetFilenames { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Skip + } +} diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 9e14def..b713418 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -4,6 +4,7 @@ use lerpable::Lerpable; use murrelet_common::{Assets, AssetsRef, LivecodeUsage}; use murrelet_common::{LivecodeSrc, LivecodeSrcUpdateInput, MurreletAppInput}; use murrelet_common::{MurreletColor, TransformVec2}; +use murrelet_gui::MurreletGUI; use murrelet_livecode::lazy::ControlLazyNodeF32; use murrelet_livecode::state::{LivecodeTimingConfig, LivecodeWorldState}; use murrelet_livecode::types::{ @@ -234,7 +235,7 @@ fn _default_svg_save_lazy() -> ControlLazyNodeF32 { // this stuff adjusts how time works, so needs to be split off pretty early #[allow(dead_code)] -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct AppConfigTiming { #[livecode(serde_default = "_default_bpm")] pub bpm: f32, @@ -282,7 +283,7 @@ fn _reset_b_lazy() -> ControlLazyNodeF32 { } #[allow(dead_code)] -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct SvgConfig { #[livecode(serde_default = "_default_svg_size")] pub size: f32, @@ -345,7 +346,7 @@ fn _default_gpu_color_channel_lazy() -> ControlLazyNodeF32 { } #[allow(dead_code)] -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct GpuConfig { #[livecode(serde_default = "_default_gpu_debug_next")] debug_next: bool, @@ -390,7 +391,7 @@ fn _default_should_reset_lazy() -> ControlLazyNodeF32 { } #[allow(dead_code)] -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct AppConfig { #[livecode(serde_default = "_default_should_reset")] pub should_reset: bool, // should reset audio and time, diff --git a/murrelet_svg/Cargo.toml b/murrelet_svg/Cargo.toml index 2f25585..4935834 100644 --- a/murrelet_svg/Cargo.toml +++ b/murrelet_svg/Cargo.toml @@ -9,6 +9,7 @@ license = "AGPL-3.0-or-later" [features] schemars = ["murrelet_perform/schemars", "murrelet_draw/schemars"] +# murrelet_gui = ["murrelet_perform/murrelet_gui", "murrelet_draw/murrelet_gui"] [dependencies] murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } @@ -17,3 +18,4 @@ murrelet_draw = { version = "0.1.2", path = "../murrelet_draw/", default-feature glam = "0.28.0" itertools = "0.10.5" svg = "0.10.0" + From 23bdc7fa86499f94cca5c13346e66ac2b66be0e0 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 28 Feb 2025 23:55:31 -0500 Subject: [PATCH 010/149] gen --- Cargo.toml | 3 +- murrelet_common/Cargo.toml | 2 +- murrelet_common/src/assets.rs | 2 +- murrelet_common/src/color.rs | 27 +- murrelet_common/src/lib.rs | 4 + murrelet_draw/Cargo.toml | 2 +- murrelet_draw/src/compass.rs | 33 ++- murrelet_draw/src/livecodetypes.rs | 14 + murrelet_draw/src/style.rs | 18 +- murrelet_draw/src/svg.rs | 20 +- murrelet_gen/Cargo.toml | 20 ++ murrelet_gen/examples/tests.rs | 97 ++++++ murrelet_gen/out | 36 +++ murrelet_gen/src/lib.rs | 62 ++++ murrelet_gen_derive/Cargo.toml | 19 ++ murrelet_gen_derive/src/derive_gen.rs | 255 ++++++++++++++++ murrelet_gen_derive/src/lib.rs | 16 + murrelet_gen_derive/src/parser.rs | 278 ++++++++++++++++++ murrelet_gpu/Cargo.toml | 2 +- murrelet_gui/Cargo.toml | 5 + murrelet_gui/out | 180 ++++++++++++ murrelet_gui/src/lib.rs | 61 ++-- murrelet_gui_derive/src/derive_gui.rs | 37 ++- murrelet_gui_derive/src/parser.rs | 8 +- murrelet_livecode/Cargo.toml | 3 +- murrelet_livecode/src/lazy.rs | 1 + murrelet_livecode/src/livecode.rs | 36 +-- murrelet_livecode/src/types.rs | 59 ++-- .../murrelet_livecode_derive/Cargo.toml | 2 +- .../murrelet_livecode_derive/src/lib.rs | 8 +- .../murrelet_livecode_derive/src/toplevel.rs | 39 ++- murrelet_perform/Cargo.toml | 3 +- murrelet_perform/src/cli.rs | 8 + murrelet_perform/src/perform.rs | 29 +- murrelet_perform/src/reload.rs | 19 +- murrelet_svg/src/svg.rs | 15 + 36 files changed, 1287 insertions(+), 136 deletions(-) create mode 100644 murrelet_gen/Cargo.toml create mode 100644 murrelet_gen/examples/tests.rs create mode 100644 murrelet_gen/out create mode 100644 murrelet_gen/src/lib.rs create mode 100644 murrelet_gen_derive/Cargo.toml create mode 100644 murrelet_gen_derive/src/derive_gen.rs create mode 100644 murrelet_gen_derive/src/lib.rs create mode 100644 murrelet_gen_derive/src/parser.rs diff --git a/Cargo.toml b/Cargo.toml index 99fa57e..a9118c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "examples/murrelet_example", "examples/foolish_guillemot", "tinylivecode" -, "murrelet_gui", "murrelet_gui_derive"] +, "murrelet_gui", "murrelet_gui_derive" +, "murrelet_gen", "murrelet_gen_derive"] resolver = "2" diff --git a/murrelet_common/Cargo.toml b/murrelet_common/Cargo.toml index 9ddbd09..1e19f81 100644 --- a/murrelet_common/Cargo.toml +++ b/murrelet_common/Cargo.toml @@ -14,7 +14,7 @@ palette = "0.7.6" rand = "0.8" num-traits = "0.2.19" lerpable = "0.0.2" -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/" } +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/murrelet_common/src/assets.rs b/murrelet_common/src/assets.rs index 196bdae..4e7c227 100644 --- a/murrelet_common/src/assets.rs +++ b/murrelet_common/src/assets.rs @@ -83,7 +83,7 @@ impl VectorLayersAssetLookup { pub trait IsColorType {} #[derive(Debug, Clone, Copy)] -struct BlackWhite(bool); +pub struct BlackWhite(bool); impl IsColorType for BlackWhite {} // struct RGBAu8([u8; 4]),; diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index 68dda59..c5c547a 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, ops::{Add, Mul}}; use lerpable::{IsLerpingMethod, Lerpable}; use murrelet_gui::CanMakeGUI; @@ -182,3 +182,28 @@ impl Lerpable for MurreletColor { ) } } + +impl Add for MurreletColor { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + let [h, s, v, a] = self.into_hsva_components(); + let [h2, s2, v2, a2] = other.into_hsva_components(); + MurreletColor::hsva(h + h2, s + s2, v + v2, a + a2) + } +} + +impl Mul for MurreletColor { + type Output = Self; + + fn mul(self, scalar: f32) -> Self::Output { + let [h, s, v, a] = self.into_hsva_components(); + + MurreletColor::hsva( + h * scalar, + s * scalar, + v * scalar, + a * scalar, + ) + } +} \ No newline at end of file diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 690e6ab..0c6d73f 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -856,6 +856,10 @@ pub fn make_gui_vec2() -> murrelet_gui::MurreletGUISchema { murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Vec2) } +pub fn make_gui_vec2_coords() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Coords) +} + pub fn make_gui_vec3() -> murrelet_gui::MurreletGUISchema { murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Vec3) } \ No newline at end of file diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index 0ca9f59..13b8865 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -33,4 +33,4 @@ md-5 = "0.10.6" hex = "0.4.3" schemars = { version = "0.8.21", optional = true } -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/" } #, optional = true } +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index 24ad6fd..ac695c4 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -12,6 +12,8 @@ use crate::{ #[derive(Debug, Clone, Copy, Livecode, MurreletGUI, Lerpable)] pub struct CurveStart { + #[lerpable(func = "lerpify_vec2")] + #[murrelet_gui(func = "make_gui_vec2")] loc: Vec2, angle_pi: LivecodeAnglePi, } @@ -33,8 +35,10 @@ fn empty_string() -> String { pub struct CompassDir { angle_pi: LivecodeAnglePi, #[livecode(serde_default = "false")] + #[murrelet_gui(kind="skip")] is_absolute: bool, #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] + #[murrelet_gui(kind="skip")] label: String, } @@ -53,8 +57,10 @@ pub struct CompassArc { radius: f32, arc_length: LivecodeAnglePi, #[livecode(serde_default = "false")] + #[murrelet_gui(kind="skip")] is_absolute: bool, #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] + #[murrelet_gui(kind="skip")] label: String, } @@ -66,6 +72,7 @@ pub struct CompassArc { pub struct CompassLine { length: f32, // how far should we head in the current direction #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] + #[murrelet_gui(kind="skip")] label: String, } @@ -80,7 +87,7 @@ pub enum CompassAction { Angle(CompassDir), // abs Arc(CompassArc), Line(CompassLine), - Repeat(CompassRepeat), + // Repeat(CompassRepeat), // now this is in the control vec! } impl CompassAction { @@ -122,9 +129,9 @@ impl CompassAction { CompassAction::Line(CompassLine { length, label }) } - pub fn repeat(times: usize, what: Vec) -> CompassAction { - CompassAction::Repeat(CompassRepeat { times, what }) - } + // pub fn repeat(times: usize, what: Vec) -> CompassAction { + // CompassAction::Repeat(CompassRepeat { times, what }) + // } } impl Default for CompassAction { fn default() -> Self { @@ -175,15 +182,15 @@ impl InteractiveCompassBuilder { CompassAction::Arc(x) => { vec![self.add_arc(x)] } - CompassAction::Repeat(x) => { - let mut n = Vec::new(); - for _ in 0..x.times { - for w in &x.what { - n.extend(self.new_segments(w)) - } - } - n - } + // CompassAction::Repeat(x) => { + // let mut n = Vec::new(); + // for _ in 0..x.times { + // for w in &x.what { + // n.extend(self.new_segments(w)) + // } + // } + // n + // } } } diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs index 04752a3..05fe8ee 100644 --- a/murrelet_draw/src/livecodetypes.rs +++ b/murrelet_draw/src/livecodetypes.rs @@ -3,6 +3,7 @@ pub mod anglepi { use lerpable::Lerpable; use murrelet_common::{Angle, AnglePi, IsAngle}; + use murrelet_gui::CanMakeGUI; use murrelet_livecode_derive::Livecode; #[derive(Clone, Copy, Debug, Livecode, Lerpable, Default)] @@ -28,4 +29,17 @@ pub mod anglepi { value._to_angle_pi().as_angle() } } + + impl From for AnglePi { + fn from(value: LivecodeAnglePi) -> Self { + value._to_angle_pi() + } + } + + + impl CanMakeGUI for LivecodeAnglePi { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Angle) + } + } } diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 483bf3d..b77b680 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -4,8 +4,10 @@ use glam::*; use lerpable::Lerpable; use md5::{Digest, Md5}; use murrelet_common::*; +use murrelet_gui::{CanMakeGUI, MurreletGUI}; use murrelet_livecode::{lazy::ControlLazyNodeF32, livecode::ControlF32, types::ControlVecElement}; use murrelet_livecode_derive::Livecode; +use styleconf::StyleConf; fn _black() -> [ControlF32; 4] { [ @@ -34,6 +36,10 @@ pub struct MurreletStyleFilled { pub stroke_color: MurreletColor, } impl MurreletStyleFilled { + pub fn new(color: MurreletColor, stroke_weight: f32, stroke_color: MurreletColor) -> Self { + Self { color, stroke_weight, stroke_color } + } + fn to_style(&self) -> MurreletStyle { MurreletStyle { closed: true, @@ -328,7 +334,7 @@ impl MurreletStyleRGBAPoints { } } -#[derive(Copy, Clone, Debug, Livecode, Lerpable, Default)] +#[derive(Copy, Clone, Debug, Livecode, MurreletGUI, Lerpable, Default)] pub struct MurreletStyleLined { pub color: MurreletColor, // fill color #[livecode(serde_default = "zeros")] @@ -360,8 +366,8 @@ pub mod styleconf { Texture(MurreletStyleFilledSvg), Fill(MurreletStyleFilled), Outline(MurreletStyleOutlined), - Points(MurreletStylePoints), Line(MurreletStyleLined), + Points(MurreletStylePoints), ThickLine, RGBAFill(MurreletStyleRGBAFill), RGBALine(MurreletStyleRGBALine), @@ -419,6 +425,12 @@ pub mod styleconf { } } +impl CanMakeGUI for StyleConf { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Style) + } +} + // this one attaches a transform to the curve. // you can _try_ to apply it using to_curve_maker, but this // will act funny for non-affine @@ -463,7 +475,7 @@ impl MurreletCurve { pub enum MurreletPath { Polyline(Polyline), Curve(MurreletCurve), - Svg(TransformedSvgShape) + Svg(TransformedSvgShape), } impl MurreletPath { pub fn polyline(path: F) -> Self { diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index 2163620..1d6218b 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -2,10 +2,10 @@ use glam::Mat4; use lerpable::Lerpable; +use murrelet_gui::MurreletGUI; use murrelet_livecode_derive::Livecode; - -#[derive(Clone, Debug, Livecode, Lerpable)] +#[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] pub struct SvgRect { #[livecode(serde_default = "0")] pub x: f32, @@ -19,14 +19,22 @@ pub struct SvgRect { pub height: f32, } +#[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] +pub struct SvgCircle { + #[livecode(serde_default = "0")] + pub x: f32, + #[livecode(serde_default = "0")] + pub y: f32, + #[livecode(serde_default = "1")] + pub r: f32, +} - -#[derive(Clone, Debug, Livecode, Lerpable)] +#[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] pub enum SvgShape { - Rect(SvgRect) + Rect(SvgRect), + Circle(SvgCircle), } - #[derive(Clone, Debug)] pub struct TransformedSvgShape { pub shape: SvgShape, diff --git a/murrelet_gen/Cargo.toml b/murrelet_gen/Cargo.toml new file mode 100644 index 0000000..b3602fe --- /dev/null +++ b/murrelet_gen/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "murrelet_gen" +version = "0.1.2" +edition = "2021" + +[features] +default = [] + +[dependencies] +murrelet_gen_derive = { path = "../murrelet_gen_derive/" } + +thiserror = "2.0.11" +serde = { version = "1.0.104", features = ["derive"] } +serde_json = "1.0.48" +rand = "0.8" + + +[[example]] +name = "tests" +path = "examples/tests.rs" diff --git a/murrelet_gen/examples/tests.rs b/murrelet_gen/examples/tests.rs new file mode 100644 index 0000000..199f8ad --- /dev/null +++ b/murrelet_gen/examples/tests.rs @@ -0,0 +1,97 @@ +use murrelet_gen::{CanSampleFromDist, MurreletGen}; + +#[derive(Debug, MurreletGen)] +pub struct BasicTypes { + #[murrelet_gen(method_f32(uniform(start = -10.0, end = 1.0 )))] + a_number: f32, + #[murrelet_gen(method_f32(uniform(start = -10.0, end = 1.0 )))] + b_number: usize, + // c_number: u64, + // d_number: i32, + // bool: bool, + // something: Vec, + // s: String, + // #[murrelet_gui(reference = "test")] + // referenced_string: String, +} + +// fn custom_func() -> MurreletGenSchema { +// MurreletGenSchema::Val(ValueGUI::Num) +// } + +// #[derive(MurreletGen)] +// pub struct OverridesAndRecursive { +// a_number: f32, +// something: Vec, +// #[murrelet_gui(func = "custom_func")] +// label: String, +// #[murrelet_gui(kind = "skip")] +// b: HashMap, +// } + +// #[derive(MurreletGen)] +// enum EnumTest { +// A, +// B(OverridesAndRecursive), +// } + +// #[derive(MurreletGen)] +// struct SimpleNewtype(f32); + +// fn lerp_partial(&self, pct: T) -> Self { +// SimpleNewtype(pct.lerp_pct() as f32) +// } +// } + +// #[derive(Debug, Clone, MurreletUX)] + +fn main() { + let seed = 42; + + let test_val = BasicTypes::gen_from_seed(seed); + + // println!("test_val {:?}", test_val); + panic!("{:?}", test_val) + + // let basic_types_schema = MurreletGenSchema::Struct(vec![ + // ("a_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), + // ("b_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), + // ("c_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), + // ("d_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), + // ("bool".to_owned(), MurreletGenSchema::Val(ValueGUI::Bool)), + // ( + // "something".to_owned(), + // MurreletGenSchema::list(MurreletGenSchema::Val(ValueGUI::Num)), + // ), + // ("s".to_owned(), MurreletGenSchema::Skip), + // ( + // "referenced_string".to_owned(), + // MurreletGenSchema::Val(ValueGUI::Name("test".to_owned())), + // ), + // ]); + + // assert_eq!(test_val, basic_types_schema); + + // let test_val = OverridesAndRecursive::make_gui(); + + // let overrides_and_recursive_schema = MurreletGenSchema::Struct(vec![ + // ("a_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), + // ( + // "something".to_owned(), + // MurreletGenSchema::list(basic_types_schema), + // ), + // ("label".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), // make sure it calls the override + // ("b".to_owned(), MurreletGenSchema::Skip), + // ]); + // assert_eq!(test_val, overrides_and_recursive_schema); + + // let test_val = EnumTest::make_gui(); + + // assert_eq!( + // test_val, + // MurreletGenSchema::Enum(vec![ + // (MurreletEnumValGUI::Unit("A".to_owned())), + // (MurreletEnumValGUI::Unnamed("B".to_owned(), overrides_and_recursive_schema)), + // ]) + // ); +} diff --git a/murrelet_gen/out b/murrelet_gen/out new file mode 100644 index 0000000..d48fcf9 --- /dev/null +++ b/murrelet_gen/out @@ -0,0 +1,36 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +use murrelet_gen::{CanSampleFromDist, MurreletGen}; +pub struct BasicTypes { + #[murrelet_gen(method_f32(uniform(start = -10.0, end = 1.0)))] + a_number: f32, +} +#[automatically_derived] +impl ::core::fmt::Debug for BasicTypes { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "BasicTypes", + "a_number", + &&self.a_number, + ) + } +} +impl murrelet_gen::CanSampleFromDist for BasicTypes { + fn sample_dist(rng: &mut R) -> Self { + Self { + a_number: rng.gen::() * (1.0 - -10.0) + -10.0, + } + } +} +fn main() { + let seed = 42; + let test_val = BasicTypes::gen_from_seed(seed); + { + ::core::panicking::panic_fmt(format_args!("{0:?}", test_val)); + } +} diff --git a/murrelet_gen/src/lib.rs b/murrelet_gen/src/lib.rs new file mode 100644 index 0000000..2afb8e7 --- /dev/null +++ b/murrelet_gen/src/lib.rs @@ -0,0 +1,62 @@ +pub use murrelet_gen_derive::MurreletGen; + +use rand::rngs::StdRng; +use rand::Rng; +use rand::SeedableRng; + +pub trait CanSampleFromDist: Sized { + fn sample_dist(rng: &mut R) -> Self; + + fn gen_from_seed(seed: u64) -> Self { + let mut rng = StdRng::seed_from_u64(seed); + Self::sample_dist(&mut rng) + } +} + +// macro_rules! impl_can_make_gui_for_num { +// ($ty:ty) => { +// impl CanGenerate for $ty { +// fn gen(&self, seed: u64) -> Self; +// } +// }; +// } + +// impl_can_make_gui_for_num!(f32); +// impl_can_make_gui_for_num!(f64); +// impl_can_make_gui_for_num!(u32); +// impl_can_make_gui_for_num!(u64); +// impl_can_make_gui_for_num!(i32); +// impl_can_make_gui_for_num!(i64); +// impl_can_make_gui_for_num!(usize); + +// impl CanMakeGUI for Vec { +// fn make_gui() -> MurreletGUISchema { +// MurreletGUISchema::List(Box::new(T::make_gui())) +// } +// } + +// impl CanMakeGUI for String { +// fn make_gui() -> MurreletGUISchema { +// MurreletGUISchema::Skip +// } +// } + +// impl CanMakeGUI for bool { +// fn make_gui() -> MurreletGUISchema { +// MurreletGUISchema::Val(ValueGUI::Bool) +// } +// } + +// #[cfg(feature = "glam")] +// impl CanMakeGUI for glam::Vec2 { +// fn make_gui() -> MurreletGUISchema { +// MurreletGUISchema::Val(ValueGUI::Vec2) +// } +// } + +// #[cfg(feature = "glam")] +// impl CanMakeGUI for glam::Vec3 { +// fn make_gui() -> MurreletGUISchema { +// MurreletGUISchema::Val(ValueGUI::Vec3) +// } +// } diff --git a/murrelet_gen_derive/Cargo.toml b/murrelet_gen_derive/Cargo.toml new file mode 100644 index 0000000..baa98e8 --- /dev/null +++ b/murrelet_gen_derive/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "murrelet_gen_derive" +version = "0.1.2" +edition = "2021" + +[lib] +proc-macro = true + +[features] +debug_logging = ["log"] + +[dependencies] +syn = "2.0.15" +quote = "1.0.18" +proc-macro2 = "1.0.37" +darling = "0.20.3" + +log = { version = "0.4.25", optional = true } + diff --git a/murrelet_gen_derive/src/derive_gen.rs b/murrelet_gen_derive/src/derive_gen.rs new file mode 100644 index 0000000..b9caae2 --- /dev/null +++ b/murrelet_gen_derive/src/derive_gen.rs @@ -0,0 +1,255 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::*; + +pub(crate) struct FieldTokensGen { + pub(crate) for_make_gen: TokenStream2, +} +impl GenFinal for FieldTokensGen { + // Something(f32) + fn make_newtype_struct_final( + idents: ParsedFieldIdent, + variants: Vec, + ) -> TokenStream2 { + let name = idents.name; + + let for_make_gen = variants.iter().map(|x| x.for_make_gen.clone()); + + quote! { + impl murrelet_gen::CanSampleFromDist for #name { + fn sample_dist(rng: &mut R) -> Self { + Self(#(#for_make_gen,)*) + } + } + } + } + + fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2 { + let name = idents.name; + + let for_make_gen = variants.iter().map(|x| x.for_make_gen.clone()); + + quote! { + impl murrelet_gen::CanSampleFromDist for #name { + fn sample_dist(rng: &mut R) -> Self { + Self { + #(#for_make_gen,)* + } + } + } + } + } + + fn make_enum_final( + idents: ParsedFieldIdent, + variants: Vec, + variant_receiver: &[LivecodeVariantReceiver], + ) -> TokenStream2 { + let name = idents.name; + + let mut cumulative_probabilities = vec![]; + + let mut running_total = 0.0; + for receiver in variant_receiver.iter() { + let weight = receiver.weight; + running_total += weight; + cumulative_probabilities.push(running_total); + } + + // normalize + for i in 0..cumulative_probabilities.len() { + cumulative_probabilities[i] /= running_total; + } + + let mut q = vec![]; + for (i, variant) in variants.iter().enumerate() { + let create_variant = &variant.for_make_gen; + let prob_upper_bound = cumulative_probabilities[i]; + q.push(quote! { + x if x < #prob_upper_bound => #create_variant + }); + } + + quote! { + impl murrelet_gen::CanSampleFromDist for #name { + fn gen(rng: &mut R) -> Self { + // first choose which enum + let enum_rn = rng.gen(); + match enum_rn { + #(#q,)* + } + } + } + } + } + + fn from_override_enum(func: &str) -> FieldTokensGen { + let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); + + let for_make_gen = quote! { + #method() + }; + + FieldTokensGen { for_make_gen } + } + + fn from_newtype_struct(_idents: StructIdents, _parent_ident: syn::Ident) -> FieldTokensGen { + let for_make_gen = quote! { + self.0.sample_dist(rng) + }; + + FieldTokensGen { for_make_gen } + } + + // e.g. TileAxisLocs::V(TileAxisVs) + fn from_unnamed_enum(idents: EnumIdents) -> FieldTokensGen { + let variant_ident = idents.data.ident; + let name = idents.enum_name; + + let for_make_gen = quote! { + quote! { #name::#variant_ident(s.sample_dist()) }; + }; + + FieldTokensGen { for_make_gen } + } + + // e.g. TileAxis::Diag + fn from_unit_enum(idents: EnumIdents) -> FieldTokensGen { + let variant_ident = idents.data.ident; + let variant_ident_str = variant_ident.to_string(); + + let for_make_gen = + quote! { murrelet_gui::MurreletEnumValGUI::Unit(#variant_ident_str.to_owned()) }; + // let for_gui_to_livecode = + // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; + + FieldTokensGen { + for_make_gen, + // for_gui_to_livecode, + // for_assign_vars: quote!(), + } + } + + // skip + fn from_noop_struct(idents: StructIdents) -> FieldTokensGen { + let field_name = idents.data.ident.unwrap().to_string(); + + let for_make_gen = + quote! { v.push((#field_name.to_owned(), murrelet_gui::MurreletGUISchema::Skip)) }; + // let for_gui_to_livecode = + // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; + + FieldTokensGen { + for_make_gen, + // for_gui_to_livecode, + } + } + + // f32, Vec2, etc + fn from_type_struct(idents: StructIdents, htctt: &HowToControlThisType) -> FieldTokensGen { + let field_name = idents.data.ident.unwrap(); + + let for_make_gen = match htctt { + HowToControlThisType::Bool(rand_method_bool) => match rand_method_bool { + RandMethodBool::Binomial { pct } => { + quote! { + #field_name: rng.gen() > #pct + } + } + }, + HowToControlThisType::F32(rand_method_f32) => match rand_method_f32 { + RandMethodF32::Uniform { start, end } => { + quote! { #field_name: rng.gen::() * (#end - #start) + #start } + } + }, + HowToControlThisType::Vec2(rand_method_vec2) => { + match rand_method_vec2 { + RandMethodVec2::UniformGrid { + x, + y, + width, + height, + } => { + quote! { + let width = rng.gen() * #width; + let height = rng.gen() * #height; + + #field_name: vec2(#x, #y) - 0.5 * vec2(#width, #height) + vec2(width, height) + + } + } + RandMethodVec2::Circle { x, y, radius } => { + quote! {{ + let angle = rng.gen() * 2.0 * std::f32::consts::PI; + let dist = rng.gen(); // sqrt it to even out the sampling + #field_name: vec2(#x, #y) + vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() + }} + } + } + } + HowToControlThisType::Vec(rand_method_vec) => match rand_method_vec { + RandMethodVec::Length { min, max } => { + let ty = idents.data.ty; + let inside_type = nested_ident(&ty); + println!("INSIDE TYPE {:?}", inside_type); + + let i = inside_type[2].clone(); + + quote! {{ + let how_many = rng.gen_range(#min..#max) as usize; + + let mut v = vec![]; + for _ in 0..how_many { + v.push(#i::sample_dist(rng)) + } + + #field_name: v + }} + } + }, + }; + + FieldTokensGen { for_make_gen } + } + + fn from_override_struct(idents: StructIdents, func: &str) -> FieldTokensGen { + let field_name = idents.data.ident.unwrap(); + + let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); + + let for_make_gen = quote! { #field_name: #method() }; + + FieldTokensGen { for_make_gen } + } +} + +fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { + match t { + syn::Type::Path(syn::TypePath { path, .. }) => { + let s = path.segments.last().unwrap(); + let main_type = s.ident.clone(); + + acc.push(main_type); + + if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, + .. + }) = s.arguments.clone() + { + if let syn::GenericArgument::Type(other_ty) = args.first().unwrap() { + recursive_ident_from_path(other_ty, acc); + } else { + panic!("recursive ident not implemented yet {:?}", args); + } + } + } + x => panic!("no name for type {:?}", x), + } +} + +fn nested_ident(t: &syn::Type) -> Vec { + let mut acc = vec![]; + recursive_ident_from_path(t, &mut acc); + return acc; +} diff --git a/murrelet_gen_derive/src/lib.rs b/murrelet_gen_derive/src/lib.rs new file mode 100644 index 0000000..64cc315 --- /dev/null +++ b/murrelet_gen_derive/src/lib.rs @@ -0,0 +1,16 @@ +extern crate proc_macro; + +use darling::FromDeriveInput; +use derive_gen::FieldTokensGen; +use parser::{GenFinal, LivecodeReceiver}; +use proc_macro::TokenStream; + +mod parser; +mod derive_gen; + +#[proc_macro_derive(MurreletGen, attributes(murrelet_gen))] +pub fn murrelet_livecode_derive_murrelet_gen(input: TokenStream) -> TokenStream { + let ast = syn::parse_macro_input!(input as syn::DeriveInput); + let ast_receiver = LivecodeReceiver::from_derive_input(&ast).unwrap(); + FieldTokensGen::from_ast(ast_receiver).into() +} diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs new file mode 100644 index 0000000..68c3a0f --- /dev/null +++ b/murrelet_gen_derive/src/parser.rs @@ -0,0 +1,278 @@ +use darling::{ast, FromDeriveInput, FromField, FromMeta, FromVariant}; +use proc_macro2::TokenStream as TokenStream2; + +#[derive(Debug)] +pub(crate) struct ParsedFieldIdent { + pub(crate) name: syn::Ident, +} + +// trait and helpers needed to parse a variety of objects +pub(crate) trait GenFinal +where + Self: Sized, +{ + fn from_newtype_struct(_idents: StructIdents, parent_ident: syn::Ident) -> Self; + fn from_unnamed_enum(idents: EnumIdents) -> Self; + fn from_unit_enum(idents: EnumIdents) -> Self; + fn from_noop_struct(idents: StructIdents) -> Self; + fn from_type_struct( + idents: StructIdents, + how_to_control_this_type: &HowToControlThisType, + ) -> Self; + + fn from_ast(ast_receiver: LivecodeReceiver) -> TokenStream2 { + match ast_receiver.data { + ast::Data::Enum(_) => Self::make_enum(&ast_receiver), + ast::Data::Struct(ast::Fields { + style: ast::Style::Tuple, + .. + }) => Self::make_newtype(&ast_receiver), + ast::Data::Struct(_) => Self::make_struct(&ast_receiver), + } + } + fn from_override_struct(idents: StructIdents, func: &str) -> Self; + fn from_override_enum(func: &str) -> Self; + + fn make_enum_final( + idents: ParsedFieldIdent, + variants: Vec, + variants_receiver: &[LivecodeVariantReceiver], + ) -> TokenStream2; + fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; + fn make_newtype_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; + + fn make_struct(s: &LivecodeReceiver) -> TokenStream2 { + let name = s.ident.clone(); + + #[cfg(feature = "debug_logging")] + log::info!("{}::make_struct {}", Self::classname(), name.to_string()); + + // shouldn't be calling this with something that's not a struct.. + let fields = s.data.clone().take_struct().unwrap(); + + let livecodable_fields = fields + .iter() + .map(|field| { + let idents = StructIdents { + data: field.clone(), + }; + + match field.how_to_control_this() { + HowToControlThis::Override(func) => Self::from_override_struct(idents, &func), + HowToControlThis::Normal => panic!("should have an annotation"), //., + HowToControlThis::Type(how_to_control_this_type) => { + Self::from_type_struct(idents, &how_to_control_this_type) + } + HowToControlThis::Default => todo!(), + } + }) + .collect::>(); + + let idents = ParsedFieldIdent { name: name.clone() }; + + Self::make_struct_final(idents, livecodable_fields) + } + + fn make_enum(e: &LivecodeReceiver) -> TokenStream2 { + let name = e.ident.clone(); + + #[cfg(feature = "debug_logging")] + log::info!("{}::make_enum {}", Self::classname(), name.to_string()); + + let variants_receiver = e.data.clone().take_enum().unwrap(); + + // just go through and find ones that wrap around a type, and make sure those types are + let variants = variants_receiver + .iter() + .map(|variant| { + let ident = EnumIdents { + enum_name: name.clone(), + data: variant.clone(), + }; + + match variant.fields.style { + ast::Style::Tuple => Self::from_unnamed_enum(ident), + ast::Style::Struct => panic!("enum named fields not supported yet"), + ast::Style::Unit => Self::from_unit_enum(ident), + } + }) + .collect::>(); + + let idents = ParsedFieldIdent { name: name.clone() }; + + Self::make_enum_final(idents, variants, &variants_receiver) + } + + fn make_newtype(s: &LivecodeReceiver) -> TokenStream2 { + let name = s.ident.clone(); + + #[cfg(feature = "debug_logging")] + log::info!("{}::make_newtype {}", Self::classname(), name.to_string()); + + // shouldn't be calling this with something that's not a struct.. + let fields = s.data.clone().take_struct().unwrap(); + + let livecodable_fields = fields + .iter() + .map(|field| { + let idents = StructIdents { + data: field.clone(), + }; + + match field.how_to_control_this() { + HowToControlThis::Normal => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_newtype_struct"); + Self::from_newtype_struct(idents, name.clone()) + } + HowToControlThis::Default => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_noop_struct"); + Self::from_noop_struct(idents) + } + HowToControlThis::Override(func) => Self::from_override_enum(&func), + HowToControlThis::Type(_) => panic!("hm, hsouldn't have a type here"), + } + }) + .collect::>(); + + let idents = ParsedFieldIdent { name: name.clone() }; + + Self::make_newtype_struct_final(idents, livecodable_fields) + } +} + +#[derive(Debug, FromField, Clone)] +#[darling(attributes(murrelet_gen))] +pub(crate) struct LivecodeFieldReceiver { + pub(crate) ident: Option, + pub(crate) ty: syn::Type, + #[darling(default, rename = "override")] + pub(crate) override_fn: Option, + #[darling(default)] + pub(crate) method_bool: Option, + #[darling(default)] + pub(crate) method_f32: Option, + #[darling(default)] + pub(crate) method_vec2: Option, + #[darling(default)] + pub(crate) method_vec: Option, +} +impl LivecodeFieldReceiver { + fn how_to_control_this(&self) -> HowToControlThis { + let mut method_counts = 0; + if self.override_fn.is_some() { + method_counts += 1 + }; + if self.method_bool.is_some() { + method_counts += 1 + }; + if self.method_f32.is_some() { + method_counts += 1 + }; + if self.method_vec2.is_some() { + method_counts += 1 + }; + if self.method_vec.is_some() { + method_counts += 1 + }; + + // only one should be + if method_counts > 1 { + panic!("more than one method or override specified!"); + } + + if let Some(override_fn) = &self.override_fn { + match override_fn.as_str() { + "default" => HowToControlThis::Default, + _ => HowToControlThis::Override(override_fn.clone()), + } + } else if let Some(r) = self.method_bool { + HowToControlThis::Type(HowToControlThisType::Bool(r)) + } else if let Some(r) = &self.method_f32 { + HowToControlThis::Type(HowToControlThisType::F32(r.clone())) + } else if let Some(r) = &self.method_vec2 { + HowToControlThis::Type(HowToControlThisType::Vec2(r.clone())) + } else if let Some(r) = self.method_vec { + HowToControlThis::Type(HowToControlThisType::Vec(r)) + } else { + HowToControlThis::Normal + } + } +} + +#[derive(Debug, Copy, Clone, FromMeta)] +pub enum RandMethodBool { + Binomial { + pct: f32, // true + }, +} + +#[derive(Debug, Clone, FromMeta)] +pub enum RandMethodF32 { + Uniform { start: syn::Expr, end: syn::Expr }, +} + +#[derive(Debug, Clone, FromMeta)] +pub enum RandMethodVec2 { + UniformGrid { + x: syn::Expr, + y: syn::Expr, + width: f32, + height: f32, + }, + Circle { + x: syn::Expr, + y: syn::Expr, + radius: f32, + }, +} + +#[derive(Debug, Copy, Clone, FromMeta)] +pub enum RandMethodVec { + Length { min: usize, max: usize }, +} + +// for enums +#[derive(Debug, FromVariant, Clone)] +#[darling(attributes(murrelet_gen))] +pub(crate) struct LivecodeVariantReceiver { + pub(crate) ident: syn::Ident, + pub(crate) fields: ast::Fields, + pub(crate) weight: f32, // either each field needs something +} + +#[derive(Debug, Clone, FromDeriveInput)] +#[darling(attributes(murrelet_gen), supports(any))] +pub(crate) struct LivecodeReceiver { + ident: syn::Ident, + data: ast::Data, +} +impl LivecodeReceiver {} + +// represents an enum +pub(crate) struct EnumIdents { + pub(crate) enum_name: syn::Ident, + pub(crate) data: LivecodeVariantReceiver, +} + +#[derive(Clone, Debug)] +pub struct StructIdents { + pub(crate) data: LivecodeFieldReceiver, +} + +#[derive(Clone, Debug)] +pub(crate) enum HowToControlThis { + Normal, + Type(HowToControlThisType), + Default, // just do the default values + Override(String), +} + +#[derive(Clone, Debug)] +pub(crate) enum HowToControlThisType { + Bool(RandMethodBool), + F32(RandMethodF32), + Vec2(RandMethodVec2), + Vec(RandMethodVec), +} diff --git a/murrelet_gpu/Cargo.toml b/murrelet_gpu/Cargo.toml index 062f504..42eb1e9 100644 --- a/murrelet_gpu/Cargo.toml +++ b/murrelet_gpu/Cargo.toml @@ -54,4 +54,4 @@ image = "0.25.2" bytemuck = { version = "1.16.1", features = ["derive"] } schemars = { version = "0.8.21", optional = true } -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/" } #, optional = true } +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } \ No newline at end of file diff --git a/murrelet_gui/Cargo.toml b/murrelet_gui/Cargo.toml index 5f80928..b77436b 100644 --- a/murrelet_gui/Cargo.toml +++ b/murrelet_gui/Cargo.toml @@ -4,12 +4,17 @@ version = "0.1.2" edition = "2021" [features] +default = [] +glam = ["dep:glam"] [dependencies] murrelet_gui_derive = { path = "../murrelet_gui_derive/" } thiserror = "2.0.11" +serde = { version = "1.0.104", features = ["derive"] } +serde_json = "1.0.48" +glam = { version = "0.23", optional = true } [[example]] name = "tests" diff --git a/murrelet_gui/out b/murrelet_gui/out index e69de29..a9bdc5a 100644 --- a/murrelet_gui/out +++ b/murrelet_gui/out @@ -0,0 +1,180 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +use std::collections::HashMap; +use murrelet_gui::{ + CanMakeGUI, MurreletEnumValGUI, MurreletGUI, MurreletGUISchema, ValueGUI, +}; +pub struct BasicTypes { + a_number: f32, + b_number: usize, + c_number: u64, + d_number: i32, + bool: bool, + something: Vec, + s: String, + #[murrelet_gui(reference = "test")] + referenced_string: String, +} +impl murrelet_gui::CanMakeGUI for BasicTypes { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + let mut v = ::alloc::vec::Vec::new(); + v.push(("a_number".to_owned(), f32::make_gui())); + v.push(("b_number".to_owned(), usize::make_gui())); + v.push(("c_number".to_owned(), u64::make_gui())); + v.push(("d_number".to_owned(), i32::make_gui())); + v.push(("bool".to_owned(), bool::make_gui())); + v.push(("something".to_owned(), Vec::::make_gui())); + v.push(("s".to_owned(), String::make_gui())); + v.push(( + "referenced_string".to_owned(), + murrelet_gui::MurreletGUISchema::Val( + murrelet_gui::ValueGUI::Name("test".to_owned(), false), + ), + )); + murrelet_gui::MurreletGUISchema::Struct("BasicTypes".to_owned(), v) + } +} +fn custom_func() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Num) +} +pub struct OverridesAndRecursive { + a_number: f32, + something: Vec, + #[murrelet_gui(func = "custom_func")] + label: String, + #[murrelet_gui(kind = "skip")] + b: HashMap, +} +impl murrelet_gui::CanMakeGUI for OverridesAndRecursive { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + let mut v = ::alloc::vec::Vec::new(); + v.push(("a_number".to_owned(), f32::make_gui())); + v.push(("something".to_owned(), Vec::::make_gui())); + v.push(("label".to_owned(), custom_func())); + v.push(("b".to_owned(), murrelet_gui::MurreletGUISchema::Skip)); + murrelet_gui::MurreletGUISchema::Struct("OverridesAndRecursive".to_owned(), v) + } +} +enum EnumTest { + A, + B(OverridesAndRecursive), +} +impl murrelet_gui::CanMakeGUI for EnumTest { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::Enum( + "EnumTest".to_owned(), + <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([ + murrelet_gui::MurreletEnumValGUI::Unit("A".to_owned()), + (murrelet_gui::MurreletEnumValGUI::Unnamed( + "B".to_string(), + OverridesAndRecursive::make_gui(), + )), + ]), + ), + ) + } +} +struct SimpleNewtype(f32); +impl murrelet_gui::CanMakeGUI for SimpleNewtype { + fn make_gui() -> murrelet_gui::MurreletGUISchema { + murrelet_gui::MurreletGUISchema::new_type( + "SimpleNewtype".to_owned(), + f32::make_gui(), + ) + } +} +fn main() { + let test_val = BasicTypes::make_gui(); + let basic_types_schema = MurreletGUISchema::Struct( + <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([ + ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("bool".to_owned(), MurreletGUISchema::Val(ValueGUI::Bool)), + ( + "something".to_owned(), + MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), + ), + ("s".to_owned(), MurreletGUISchema::Skip), + ( + "referenced_string".to_owned(), + MurreletGUISchema::Val(ValueGUI::Name("test".to_owned())), + ), + ]), + ), + ); + match (&test_val, &basic_types_schema) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::None, + ); + } + } + }; + let test_val = OverridesAndRecursive::make_gui(); + let overrides_and_recursive_schema = MurreletGUISchema::Struct( + <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([ + ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("something".to_owned(), MurreletGUISchema::list(basic_types_schema)), + ("label".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("b".to_owned(), MurreletGUISchema::Skip), + ]), + ), + ); + match (&test_val, &overrides_and_recursive_schema) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::None, + ); + } + } + }; + let test_val = EnumTest::make_gui(); + match ( + &test_val, + &MurreletGUISchema::Enum( + <[_]>::into_vec( + #[rustc_box] + ::alloc::boxed::Box::new([ + (MurreletEnumValGUI::Unit("A".to_owned())), + (MurreletEnumValGUI::Unnamed( + "B".to_owned(), + overrides_and_recursive_schema, + )), + ]), + ), + ), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::None, + ); + } + } + }; +} diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs index 108e758..397a189 100644 --- a/murrelet_gui/src/lib.rs +++ b/murrelet_gui/src/lib.rs @@ -1,4 +1,5 @@ pub use murrelet_gui_derive::MurreletGUI; +use serde::Serialize; // #[derive(Debug, Error)] // pub enum MurreletGUIErr { @@ -8,35 +9,38 @@ pub use murrelet_gui_derive::MurreletGUI; // pub type MurreletGUIResult = Result; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub enum ValueGUI { - Bool, // should be a ControlBool - Num, // should be a ControlF32 - Name(String), // clue for the front-end to sink the strings iwth the same name - Color, // expected to give h s v a - Defs, // make a ctx node - Vec2, - Vec3, + Bool, // should be a ControlBool + Num, // should be a ControlF32 + Name(String, bool), // clue for the front-end to sync the strings, bool is if it's def + Color, // expected to give h s v a + Defs, // make a ctx node + Vec2, // arbitrary vec2, also see Coords + Vec3, // arbitrary vec3 + Style, // murrelet style + Angle, // angle pi + Coords, // global coords, so the user can click things } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub enum MurreletEnumValGUI { Unnamed(String, MurreletGUISchema), Unit(String), } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub enum MurreletGUISchema { - Val(ValueGUI), - NewType(Box), - Struct(Vec<(String, MurreletGUISchema)>), // field val + NewType(String, Box), + Struct(String, Vec<(String, MurreletGUISchema)>), // field val + Enum(String, Vec), // type, val List(Box), - Enum(Vec), // type, val + Val(ValueGUI), Skip, } impl MurreletGUISchema { - pub fn new_type(m: MurreletGUISchema) -> Self { - Self::NewType(Box::new(m)) + pub fn new_type(name: String, m: MurreletGUISchema) -> Self { + Self::NewType(name, Box::new(m)) } pub fn list(m: MurreletGUISchema) -> Self { @@ -44,7 +48,7 @@ impl MurreletGUISchema { } pub fn as_enum(&self) -> Option<&Vec> { - if let Self::Enum(v) = self { + if let Self::Enum(_, v) = self { Some(v) } else { None @@ -52,12 +56,19 @@ impl MurreletGUISchema { } pub fn as_new_type(&self) -> Option<&Box> { - if let Self::NewType(v) = self { + if let Self::NewType(_, v) = self { Some(v) } else { None } } + + pub fn unwrap_to_struct_fields(self) -> Vec<(String, MurreletGUISchema)> { + match self { + MurreletGUISchema::Struct(_, items) => items, + _ => unreachable!("tried to flatten a struct that wasn't a struct"), + } + } } // this should be on the Control version @@ -100,3 +111,17 @@ impl CanMakeGUI for bool { MurreletGUISchema::Val(ValueGUI::Bool) } } + +#[cfg(feature = "glam")] +impl CanMakeGUI for glam::Vec2 { + fn make_gui() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Vec2) + } +} + +#[cfg(feature = "glam")] +impl CanMakeGUI for glam::Vec3 { + fn make_gui() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Vec3) + } +} diff --git a/murrelet_gui_derive/src/derive_gui.rs b/murrelet_gui_derive/src/derive_gui.rs index e5655b2..00f1b35 100644 --- a/murrelet_gui_derive/src/derive_gui.rs +++ b/murrelet_gui_derive/src/derive_gui.rs @@ -14,6 +14,7 @@ impl GenFinal for FieldTokensGUI { variants: Vec, ) -> TokenStream2 { let name = idents.name; + let name_str = name.to_string(); let for_make_gui = variants.iter().map(|x| x.for_make_gui.clone()); // let for_gui_to_livecode = variants.iter().map(|x| x.for_gui_to_livecode.clone()); @@ -21,7 +22,7 @@ impl GenFinal for FieldTokensGUI { quote! { impl murrelet_gui::CanMakeGUI for #name { fn make_gui() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::new_type(#(#for_make_gui,)*) + murrelet_gui::MurreletGUISchema::new_type(#name_str.to_owned(), #(#for_make_gui,)*) } // fn gui_to_livecode(&self, gui_val: murrelet_gui::MurreletGUISchema) -> murrelet_gui::MurreletGUISchemaResult { @@ -38,6 +39,7 @@ impl GenFinal for FieldTokensGUI { fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2 { let name = idents.name; + let name_str = name.to_string(); let for_make_gui = variants.iter().map(|x| x.for_make_gui.clone()); @@ -47,7 +49,11 @@ impl GenFinal for FieldTokensGUI { quote! { impl murrelet_gui::CanMakeGUI for #name { fn make_gui() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::Struct(vec![#(#for_make_gui,)*]) + + let mut v = vec![]; + #(#for_make_gui;)* + + murrelet_gui::MurreletGUISchema::Struct(#name_str.to_owned(), v) } // fn gui_to_livecode(&self, ux_val: murrelet_gui::MurreletGUISchema) -> murrelet_gui::MurreletGUISchemaResult { @@ -62,6 +68,7 @@ impl GenFinal for FieldTokensGUI { fn make_enum_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2 { let name = idents.name; + let name_str = name.to_string(); let for_make_gui = variants.iter().map(|x| x.for_make_gui.clone()); // let for_gui_to_livecode = variants.iter().map(|x| x.for_gui_to_livecode.clone()); @@ -69,7 +76,7 @@ impl GenFinal for FieldTokensGUI { quote! { impl murrelet_gui::CanMakeGUI for #name { fn make_gui() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::Enum(vec![#(#for_make_gui,)*]) + murrelet_gui::MurreletGUISchema::Enum(#name_str.to_owned(), vec![#(#for_make_gui,)*]) } // fn gui_to_livecode(&self, gui_val: murrelet_gui::MurreletGUISchema) -> murrelet_gui::MurreletGUISchemaResult { @@ -91,9 +98,7 @@ impl GenFinal for FieldTokensGUI { #method() }; - FieldTokensGUI { - for_make_gui, - } + FieldTokensGUI { for_make_gui } } fn from_newtype_struct(idents: StructIdents, _parent_ident: syn::Ident) -> FieldTokensGUI { @@ -155,7 +160,9 @@ impl GenFinal for FieldTokensGUI { .reference .expect("from name called without a reference!"); - let for_make_gui = quote! { (#field_name.to_owned(), murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Name(#name_reference.to_owned()))) }; + let is_main = idents.data.is_ref_def.unwrap_or(false); + + let for_make_gui = quote! { v.push((#field_name.to_owned(), murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Name(#name_reference.to_owned(), #is_main)))) }; // let for_assign_vars // let for_gui_to_livecode = quote! { murrelet_gui::ValueGUIResponse::Name(name) => name }; @@ -172,7 +179,7 @@ impl GenFinal for FieldTokensGUI { let field_name = idents.data.ident.unwrap().to_string(); let for_make_gui = - quote! { (#field_name.to_owned(), murrelet_gui::MurreletGUISchema::Skip) }; + quote! { v.push((#field_name.to_owned(), murrelet_gui::MurreletGUISchema::Skip)) }; // let for_gui_to_livecode = // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; @@ -189,7 +196,13 @@ impl GenFinal for FieldTokensGUI { // to call a static function, we need to let kind = convert_vec_type(&idents.data.ty); - let for_make_gui = quote! { (#field_name_str.to_owned(), #kind::make_gui()) }; + let is_flat = idents.data.flatten.unwrap_or(false); + + let for_make_gui = if is_flat { + quote! { v.extend(#kind::make_gui().unwrap_to_struct_fields().into_iter()) } + } else { + quote! { v.push((#field_name_str.to_owned(), #kind::make_gui())) } + }; FieldTokensGUI { for_make_gui } } @@ -200,11 +213,9 @@ impl GenFinal for FieldTokensGUI { let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); - let for_make_gui = quote! { (#field_name_str.to_owned(), #method()) }; + let for_make_gui = quote! { v.push((#field_name_str.to_owned(), #method())) }; - FieldTokensGUI { - for_make_gui, - } + FieldTokensGUI { for_make_gui } } } diff --git a/murrelet_gui_derive/src/parser.rs b/murrelet_gui_derive/src/parser.rs index d77607c..52f7e1a 100644 --- a/murrelet_gui_derive/src/parser.rs +++ b/murrelet_gui_derive/src/parser.rs @@ -1,8 +1,6 @@ use darling::{ast, FromDeriveInput, FromField, FromVariant}; use proc_macro2::TokenStream as TokenStream2; -use crate::FieldTokensGUI; - #[derive(Debug)] pub(crate) struct ParsedFieldIdent { pub(crate) name: syn::Ident, @@ -93,7 +91,7 @@ where .iter() .map(|variant| { let ident = EnumIdents { - enum_name: name.clone(), + // enum_name: name.clone(), data: variant.clone(), }; @@ -160,7 +158,9 @@ pub(crate) struct LivecodeFieldReceiver { pub(crate) ty: syn::Type, pub(crate) kind: Option, pub(crate) reference: Option, + pub(crate) is_ref_def: Option, pub(crate) func: Option, + pub(crate) flatten: Option, } impl LivecodeFieldReceiver { fn how_to_control_this(&self) -> HowToControlThis { @@ -200,7 +200,7 @@ impl LivecodeReceiver {} // represents an enum pub(crate) struct EnumIdents { - pub(crate) enum_name: syn::Ident, + // pub(crate) enum_name: syn::Ident, pub(crate) data: LivecodeVariantReceiver, } diff --git a/murrelet_livecode/Cargo.toml b/murrelet_livecode/Cargo.toml index dc87586..23b3bc7 100644 --- a/murrelet_livecode/Cargo.toml +++ b/murrelet_livecode/Cargo.toml @@ -12,7 +12,7 @@ schemars = ["dep:schemars"] [dependencies] murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/" } +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } lerpable = "0.0.2" @@ -26,5 +26,6 @@ noise = "0.9.0" evalexpr = { version = "11.1.0", features = ["serde_support"] } serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" +thiserror = "2.0.11" schemars = { version = "0.8.21", optional = true } diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 2442ec0..651ee62 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -112,6 +112,7 @@ impl LazyNodeF32Inner { self.n .eval_float_with_context(&ctx) .or_else(|_| self.n.eval_int_with_context(&ctx).map(|x| x as f64)) + .or_else(|_| self.n.eval_boolean_with_context(&ctx).map(|x| if x { 1.0 } else { -1.0 })) .map(|x| x as f32) .map_err(|err| LivecodeError::EvalExpr("error evaluating lazy".to_string(), err)) } diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index 406f254..868227a 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -467,15 +467,15 @@ impl ControlF32 { } ControlF32::Int(i) => Ok(*i as f32), ControlF32::Float(x) => Ok(*x), - ControlF32::Expr(e) => match e.eval_float_with_context(w.ctx()).map(|x| x as f32) { - Ok(r) => Ok(r), - Err(_) => { - let b = e - .eval_boolean_with_context(w.ctx()) - .map_err(|err| LivecodeError::EvalExpr("evalexpr err".to_string(), err)); - Ok(if b? { 1.0 } else { -1.0 }) - } - }, + ControlF32::Expr(e) => e + .eval_float_with_context(w.ctx()) + .map(|x| x as f32) + .or_else(|_| e.eval_int_with_context(w.ctx()).map(|b| b as f32)) + .or_else(|_| { + e.eval_boolean_with_context(w.ctx()) + .map(|b| if b { 1.0 } else { -1.0 }) + .map_err(|err| LivecodeError::EvalExpr("evalexpr err".to_string(), err)) + }), } } } @@ -523,15 +523,15 @@ impl ControlBool { ControlBool::Raw(b) => Ok(*b), ControlBool::Int(i) => Ok(*i > 0), ControlBool::Float(x) => Ok(*x > 0.0), - ControlBool::Expr(e) => match e.eval_boolean_with_context(w.ctx()) { - Ok(r) => Ok(r), - Err(_) => { - let b = e.eval_float_with_context(w.ctx()).map_err(|err| { - LivecodeError::EvalExpr("error evaluing bool".to_string(), err) - }); - b.map(|x| x > 0.0) - } - }, + + ControlBool::Expr(e) => e + .eval_boolean_with_context(w.ctx()) + .or_else(|_| e.eval_float_with_context(w.ctx()).map(|b| b > 0.0)) + .or_else(|_| { + e.eval_int_with_context(w.ctx()) + .map(|b| b > 0) + .map_err(|err| LivecodeError::EvalExpr("evalexpr err".to_string(), err)) + }), } } diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index e439046..bb21372 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -6,7 +6,7 @@ use lerpable::{step, Lerpable}; use murrelet_common::IdxInRange2d; use murrelet_gui::CanMakeGUI; use serde::{Deserialize, Deserializer}; -use serde_yaml::Location; +use thiserror::Error; use crate::{ expr::IntoExprWorldContext, @@ -15,40 +15,49 @@ use crate::{ unitcells::UnitCellExprWorldContext, }; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum LivecodeError { + #[error("{0}")] Raw(String), // my custom errors + #[error("{0}: {1}")] EvalExpr(String, EvalexprError), + #[error("{0}: {1}")] Io(String, std::io::Error), + #[error("nest get requested for odd thing...: {0}")] NestGetExtra(String), + #[error("nest get requested for odd thing...: {0}")] NestGetInvalid(String), - SerdeLoc(Location, String), + #[error("parse_error :: loc: {0}, err: {1}")] + SerdeLoc(String, String), + #[error("shader parse error: {0}")] WGPU(String), + #[error("parse: {0}")] + JsonParse(String), } impl LivecodeError {} -impl std::fmt::Display for LivecodeError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - LivecodeError::Raw(msg) => write!(f, "{}", msg), - LivecodeError::EvalExpr(msg, err) => write!(f, "{}: {}", msg, err), - LivecodeError::Io(msg, err) => write!(f, "{}: {}", msg, err), - LivecodeError::NestGetExtra(err) => { - write!(f, "nest get has unusable tokens...: {}", err) - } - LivecodeError::NestGetInvalid(err) => { - write!(f, "nest get requested for odd thing...: {}", err) - } - LivecodeError::SerdeLoc(location, err) => { - // if it's err, hrm, remove the controlvec ones - let loc = format!("{},{}", location.line(), location.column()); - write!(f, "parse_error :: loc: {}, err: {}", loc, err) - } - LivecodeError::WGPU(err) => write!(f, "shader parse error: {}", err), - } - } -} +// impl std::fmt::Display for LivecodeError { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// match self { +// LivecodeError::Raw(msg) => write!(f, "{}", msg), +// LivecodeError::EvalExpr(msg, err) => write!(f, "{}: {}", msg, err), +// LivecodeError::Io(msg, err) => write!(f, "{}: {}", msg, err), +// LivecodeError::NestGetExtra(err) => { +// write!(f, "nest get has unusable tokens...: {}", err) +// } +// LivecodeError::NestGetInvalid(err) => { +// write!(f, "nest get requested for odd thing...: {}", err) +// } +// LivecodeError::SerdeLoc(location, err) => { +// // if it's err, hrm, remove the controlvec ones +// let loc = format!("{},{}", location.line(), location.column()); +// write!(f, "parse_error :: loc: {},{}, err: {}", loc, err) +// } +// LivecodeError::WGPU(err) => write!(f, "shader parse error: {}", err), +// } +// } +// } -impl std::error::Error for LivecodeError {} +// impl std::error::Error for LivecodeError {} pub type LivecodeResult = Result; diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml index 2520bae..b8e3f1f 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml +++ b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml @@ -29,7 +29,7 @@ glam = "0.28.0" palette = "0.7.6" lerpable = "0.0.2" schemars = "0.8.21" -murrelet_gui = { version = "0.1.2", path = "../../murrelet_gui"} +murrelet_gui = { version = "0.1.2", path = "../../murrelet_gui", features = ["glam"] } [[example]] name = "tests" diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs index c262859..3c8eee5 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs @@ -23,7 +23,7 @@ use parser::{GenFinal, LivecodeReceiver}; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use syn::parse_macro_input; -use toplevel::{impl_all_the_traits, top_level_livecode}; +use toplevel::{impl_all_the_traits, top_level_livecode, top_level_livecode_json}; use quote::quote; @@ -111,6 +111,12 @@ pub fn murrelet_livecode_top_level_livecode(input: TokenStream) -> TokenStream { top_level_livecode(ast.ident).into() } +#[proc_macro_derive(TopLevelLiveCodeJson, attributes(livecode))] +pub fn murrelet_livecode_top_level_livecode_json(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as syn::DeriveInput); + top_level_livecode_json(ast.ident).into() +} + #[proc_macro_derive(LiveCoderTrait, attributes(livecode))] pub fn murrelet_livecode_livecoder_traits(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as syn::DeriveInput); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/toplevel.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/toplevel.rs index 0701e97..e4a9b4b 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/toplevel.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/toplevel.rs @@ -15,8 +15,43 @@ pub(crate) fn top_level_livecode(ident: syn::Ident) -> TokenStream2 { impl LiveCoderLoader for #control_ident { fn _app_config(&self) -> &murrelet_perform::perform::ControlAppConfig { &self.app } - fn parse(text: &str) -> Result { - serde_yaml::from_str(&text) + fn parse(text: &str) -> murrelet_livecode::types::LivecodeResult { + serde_yaml::from_str(&text).map_err(|err| { + let line = if let Some(location) = err.location() { + format!("{},{}", location.line(), location.column()) + } else { + "".to_owned() + }; + murrelet_livecode::types::LivecodeError::SerdeLoc(line, err.to_string()) + }) + } + } + + impl murrelet_perform::perform::ConfCommon for #conf_ident { + fn config_app_loc(&self) -> &murrelet_perform::perform::AppConfig { &self.app } + } + + impl murrelet_perform::perform::CommonTrait for #conf_ident {} + impl murrelet_perform::perform::CommonTrait for #control_ident {} + impl murrelet_perform::perform::LiveCodeCommon<#conf_ident> for #control_ident {} + } +} + +// ugly quick fix, just generate this code +pub(crate) fn top_level_livecode_json(ident: syn::Ident) -> TokenStream2 { + let conf_ident = ident.clone(); + let control_ident = update_to_control_ident(ident.clone()); + + quote! { + type LiveCode = LiveCoder<#conf_ident, #control_ident>; + + impl LiveCoderLoader for #control_ident { + fn _app_config(&self) -> &murrelet_perform::perform::ControlAppConfig { &self.app } + + fn parse(text: &str) -> murrelet_livecode::types::LivecodeResult { + serde_json::from_str(&text).map_err(|err| { + LivecodeError::JsonParse(err.to_string()) + }) } } diff --git a/murrelet_perform/Cargo.toml b/murrelet_perform/Cargo.toml index d309a5b..e35f538 100644 --- a/murrelet_perform/Cargo.toml +++ b/murrelet_perform/Cargo.toml @@ -42,7 +42,6 @@ palette = "0.7.6" anyhow = "1.0.86" schemars = { version = "0.8.21", optional = true } -# murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", optional = true } -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/" } +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } clap = { version = "4.5.23", features = ["derive"] } diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index fe1ee77..72524d0 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -71,4 +71,12 @@ impl BaseConfigArgs { height: self.resolution.height * self.texture_multiplier, } } + + pub(crate) fn config_path(&self) -> PathBuf { + self.config_path.clone() + } + + pub(crate) fn should_capture(&self) -> bool { + self.capture + } } diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index b713418..24dafb5 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -8,7 +8,7 @@ use murrelet_gui::MurreletGUI; use murrelet_livecode::lazy::ControlLazyNodeF32; use murrelet_livecode::state::{LivecodeTimingConfig, LivecodeWorldState}; use murrelet_livecode::types::{ - AdditionalContextNode, ControlVecElement, LivecodeError, LivecodeResult, + AdditionalContextNode, ControlVecElement, LivecodeResult, }; use std::collections::HashMap; use std::fs; @@ -41,7 +41,7 @@ pub trait ConfCommon: CommonTrait { #[derive(Clone, Debug)] pub struct SvgDrawConfig { size: f32, // todo, what's the difference between this and texture sizes? - pub resolution: TextureDimensions, + pub resolution: Option, capture_path: Option, // if it's missing, we don't save (e.g. web browser) frame: u64, target_size: f32, // in mm @@ -51,7 +51,7 @@ pub struct SvgDrawConfig { impl SvgDrawConfig { pub fn new( size: f32, - resolution: TextureDimensions, + resolution: Option, capture_path: Option, target_size: f32, frame: u64, @@ -470,7 +470,7 @@ impl AppConfig { // todo, this is all a little weird (svg save path), i should revisit it.. pub struct LilLiveConfig<'a> { - resolution: TextureDimensions, + resolution: Option, save_path: Option<&'a PathBuf>, run_id: u64, w: &'a LivecodeWorldState, @@ -547,13 +547,14 @@ where livecode_src: LivecodeSrc, load_funcs: &AssetLoaders, ) -> LivecodeResult> { - let controlconfig = ControlConfType::parse(&conf).map_err(|err| { - if let Some(error) = err.location() { - LivecodeError::SerdeLoc(error, err.to_string()) - } else { - LivecodeError::Raw(err.to_string()) - } - })?; + let controlconfig = ControlConfType::parse(&conf)?; + // .map_err(|err| { + // if let Some(error) = err.location() { + // LivecodeError::SerdeLoc(error, err.to_string()) + // } else { + // LivecodeError::Raw(err.to_string()) + // } + // })?; Self::new_full(controlconfig, None, livecode_src, load_funcs, None) } @@ -686,7 +687,7 @@ where run_id: self.run_id, w: self.world(), app_config: self.app_config(), - resolution: self.maybe_args.as_ref().unwrap().resolution, + resolution: self.maybe_args.as_ref().map(|x| x.resolution), }) } @@ -943,8 +944,8 @@ where self.world().time().seconds_between_render_times() } - pub fn args(&self) -> Option { - self.maybe_args.clone() + pub fn args(&self) -> BaseConfigArgs { + self.maybe_args.clone().unwrap() } pub fn sketch_args(&self) -> Vec { diff --git a/murrelet_perform/src/reload.rs b/murrelet_perform/src/reload.rs index 002970f..4d9cbf3 100644 --- a/murrelet_perform/src/reload.rs +++ b/murrelet_perform/src/reload.rs @@ -25,12 +25,12 @@ pub trait LiveCoderLoader: Sized { fn _app_config(&self) -> &ControlAppConfig; // usually just serde_yaml::from_str(&str) - fn parse(text: &str) -> Result; + fn parse(text: &str) -> LivecodeResult; fn fs_parse>( text: &str, includes_dir: P, - ) -> Result { + ) -> Result { let preprocessed = crate::load::preprocess_yaml(text, includes_dir); //serde_yaml::from_str(&stripped_json) Self::parse(&preprocessed) @@ -39,14 +39,14 @@ pub trait LiveCoderLoader: Sized { fn fs_parse_data, P2: AsRef>( filename: P, includes_dir: P2, - ) -> Result { + ) -> Result { let mut file = fs::File::open(filename).unwrap(); let mut data = String::new(); std::io::Read::read_to_string(&mut file, &mut data).unwrap(); Self::fs_parse(&data, includes_dir) } - fn _fs_load() -> Result { + fn _fs_load() -> Result { let args: Vec = env::args().collect(); Self::fs_parse_data(&args[1], &args[2]) } @@ -163,11 +163,12 @@ pub trait LiveCoderLoader: Sized { } Err(err) => { util.update_info_error(); - if let Some(error) = err.location() { - Err(LivecodeError::SerdeLoc(error, err.to_string())) - } else { - Err(LivecodeError::Raw(err.to_string())) - } + // if let Some(error) = err.location() { + // Err(LivecodeError::SerdeLoc(format!("{},{}", location.line(), location.column()), err.to_string())) + // } else { + // Err(LivecodeError::Raw(err.to_string())) + // } + Err(err) } } } else { diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index cedfac5..d338c78 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -113,6 +113,16 @@ impl ToStyledGroup for TransformedSvgShape { Some(g) } + SvgShape::Circle(s) => { + let mut circ = svg::node::element::Circle::new() + .set("cx", s.x) + .set("cy", s.y) + .set("r", s.r); + + circ = style.add_svg_attributes(circ); + g.append(circ); + Some(g) + } } } } @@ -396,9 +406,14 @@ impl SvgDocCreator { for p in patterns { defs.push(p.to_string()); } + + // TODO REMOVE THISS + defs.push(name.clone()); } + (doc, defs.into_iter().join("\n")) + } // this one's meant for svgs for pen plotters, so it drops fill styles From a41268d6949b03a4243c50d7b3ad65da37146185 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 1 Mar 2025 19:18:06 -0500 Subject: [PATCH 011/149] before neum --- murrelet_gen/Cargo.toml | 2 + murrelet_gen/examples/tests.rs | 41 +++- murrelet_gen/out | 14 +- murrelet_gen/src/lib.rs | 15 +- murrelet_gen_derive/src/derive_gen.rs | 315 ++++++++++++++++++++------ murrelet_gen_derive/src/parser.rs | 44 +++- murrelet_gui_derive/src/derive_gui.rs | 5 - 7 files changed, 339 insertions(+), 97 deletions(-) diff --git a/murrelet_gen/Cargo.toml b/murrelet_gen/Cargo.toml index b3602fe..f4a6107 100644 --- a/murrelet_gen/Cargo.toml +++ b/murrelet_gen/Cargo.toml @@ -13,6 +13,8 @@ thiserror = "2.0.11" serde = { version = "1.0.104", features = ["derive"] } serde_json = "1.0.48" rand = "0.8" +glam = "0.28.0" +murrelet_common = { path = "../murrelet_common/" } [[example]] diff --git a/murrelet_gen/examples/tests.rs b/murrelet_gen/examples/tests.rs index 199f8ad..5f880d0 100644 --- a/murrelet_gen/examples/tests.rs +++ b/murrelet_gen/examples/tests.rs @@ -1,15 +1,36 @@ +use glam::Vec2; +use murrelet_common::MurreletColor; use murrelet_gen::{CanSampleFromDist, MurreletGen}; #[derive(Debug, MurreletGen)] pub struct BasicTypes { - #[murrelet_gen(method_f32(uniform(start = -10.0, end = 1.0 )))] + #[murrelet_gen(method_f32(uniform(start = 0.0, end = 1.0)))] a_number: f32, - #[murrelet_gen(method_f32(uniform(start = -10.0, end = 1.0 )))] - b_number: usize, - // c_number: u64, - // d_number: i32, - // bool: bool, - // something: Vec, + #[murrelet_gen(method_f32(uniform(start = 0.0, end = 1.0)))] + another_number: f32, + #[murrelet_gen(method_f32(uniform(start = 1.0, end = 100.0)))] + another_number_wider_range: f32, + #[murrelet_gen(method_f32(uniform(start = -10.0, end = 0.0 )))] + a_neg_number: f32, + #[murrelet_gen(method_f32(uniform(start = 0.0, end = 30.0)))] + a_usize: usize, + #[murrelet_gen(method_f32(uniform(start = 0.0, end = 30.0)))] + c_number: u64, + #[murrelet_gen(method_f32(uniform(start = 0.0, end = 30.0)))] + d_number: i32, + #[murrelet_gen(method_bool(binomial(pct = 0.3)))] + bool: bool, + #[murrelet_gen(method_vec2(uniform_grid(x = 0.0, y = 0.0, width = 100.0, height = 100.0)))] + xy: Vec2, + #[murrelet_gen(method_vec2(circle(x = 0.0, y = 0.0, radius = 100.0)))] + other_xy: Vec2, + #[murrelet_gen(method_color(normal))] + normal_color: MurreletColor, // 3 rn + #[murrelet_gen(method_color(transparency))] + transparent_color: MurreletColor, // 4 rn + + #[murrelet_gen(method_vec(length(min = 1, max = 10)))] + something: Vec, // s: String, // #[murrelet_gui(reference = "test")] // referenced_string: String, @@ -50,6 +71,12 @@ fn main() { let test_val = BasicTypes::gen_from_seed(seed); + assert!(BasicTypes::rn_count() == 19); + + // there's a small chance they will be equal, but we know for this seed they aren't + assert!(test_val.a_number != test_val.another_number); + assert!(test_val.another_number_wider_range > 1.0); + // println!("test_val {:?}", test_val); panic!("{:?}", test_val) diff --git a/murrelet_gen/out b/murrelet_gen/out index d48fcf9..02f360d 100644 --- a/murrelet_gen/out +++ b/murrelet_gen/out @@ -5,7 +5,7 @@ use std::prelude::rust_2021::*; extern crate std; use murrelet_gen::{CanSampleFromDist, MurreletGen}; pub struct BasicTypes { - #[murrelet_gen(method_f32(uniform(start = -10.0, end = 1.0)))] + #[murrelet_gen(method_f32(uniform(start = 0.0, end = 1.0)))] a_number: f32, } #[automatically_derived] @@ -21,9 +21,17 @@ impl ::core::fmt::Debug for BasicTypes { } } impl murrelet_gen::CanSampleFromDist for BasicTypes { - fn sample_dist(rng: &mut R) -> Self { + fn rn_count() -> usize { + <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([1])).iter().sum() + } + fn sample_dist(rn: &[f32], start_idx: usize) -> Self { + let mut rn_start_idx = start_idx; Self { - a_number: rng.gen::() * (1.0 - -10.0) + -10.0, + a_number: { + let result = rn[rn_start_idx] * (1.0 - 0.0) + 0.0; + rn_start_idx += 1; + result + }, } } } diff --git a/murrelet_gen/src/lib.rs b/murrelet_gen/src/lib.rs index 2afb8e7..cb26af4 100644 --- a/murrelet_gen/src/lib.rs +++ b/murrelet_gen/src/lib.rs @@ -5,11 +5,22 @@ use rand::Rng; use rand::SeedableRng; pub trait CanSampleFromDist: Sized { - fn sample_dist(rng: &mut R) -> Self; + // returns the right number of rn needed to generate this. + fn rn_count() -> usize; + // given rn_count, it'll generate! + fn sample_dist(rn: &[f32], start_idx: usize) -> Self; + + // usually you'll call this one fn gen_from_seed(seed: u64) -> Self { let mut rng = StdRng::seed_from_u64(seed); - Self::sample_dist(&mut rng) + + let rns: Vec = (0..Self::rn_count()) + .into_iter() + .map(|_| rng.gen()) + .collect(); + + Self::sample_dist(&rns, 0) } } diff --git a/murrelet_gen_derive/src/derive_gen.rs b/murrelet_gen_derive/src/derive_gen.rs index b9caae2..c1b5d9d 100644 --- a/murrelet_gen_derive/src/derive_gen.rs +++ b/murrelet_gen_derive/src/derive_gen.rs @@ -4,6 +4,7 @@ use quote::quote; use crate::parser::*; pub(crate) struct FieldTokensGen { + pub(crate) for_rn_count: TokenStream2, pub(crate) for_make_gen: TokenStream2, } impl GenFinal for FieldTokensGen { @@ -14,11 +15,18 @@ impl GenFinal for FieldTokensGen { ) -> TokenStream2 { let name = idents.name; + let for_rn_count = variants.iter().map(|x| x.for_rn_count.clone()); let for_make_gen = variants.iter().map(|x| x.for_make_gen.clone()); quote! { impl murrelet_gen::CanSampleFromDist for #name { - fn sample_dist(rng: &mut R) -> Self { + fn rn_count() -> usize { + vec![ + #(#for_rn_count,)* + ].iter().sum() + } + + fn sample_dist(rn: &[f32], start_idx: usize) -> Self { Self(#(#for_make_gen,)*) } } @@ -28,11 +36,19 @@ impl GenFinal for FieldTokensGen { fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2 { let name = idents.name; + let for_rn_count = variants.iter().map(|x| x.for_rn_count.clone()); let for_make_gen = variants.iter().map(|x| x.for_make_gen.clone()); quote! { impl murrelet_gen::CanSampleFromDist for #name { - fn sample_dist(rng: &mut R) -> Self { + fn rn_count() -> usize { + vec![ + #(#for_rn_count,)* + ].iter().sum() + } + + fn sample_dist(rn: &[f32], start_idx: usize) -> Self { + let mut rn_start_idx = start_idx; Self { #(#for_make_gen,)* } @@ -47,120 +63,194 @@ impl GenFinal for FieldTokensGen { variant_receiver: &[LivecodeVariantReceiver], ) -> TokenStream2 { let name = idents.name; + let for_rn_count = variants.iter().map(|x| x.for_rn_count.clone()); + + // let mut cumulative_probabilities = vec![]; + + // let mut running_total = 0.0; + // for receiver in variant_receiver.iter() { + // let weight = receiver.weight; + // running_total += weight; + // cumulative_probabilities.push(running_total); + // } + + // // normalize + // for i in 0..cumulative_probabilities.len() { + // cumulative_probabilities[i] /= running_total; + // } + + // let mut q = vec![]; + // for (i, variant) in variants.iter().enumerate() { + // let create_variant = &variant.for_make_gen; + // let prob_upper_bound = cumulative_probabilities[i]; + // q.push(quote! { + // x if x < #prob_upper_bound => #create_variant + // }); + // } + + let mut weights = vec![]; + let mut comps = vec![]; + for (i, (variant, receiver)) in variants.iter().zip(variant_receiver.iter()).enumerate() { + let create_variant = &variant.for_make_gen; + let rn_gen = &variant.for_rn_count; - let mut cumulative_probabilities = vec![]; - - let mut running_total = 0.0; - for receiver in variant_receiver.iter() { let weight = receiver.weight; - running_total += weight; - cumulative_probabilities.push(running_total); - } - - // normalize - for i in 0..cumulative_probabilities.len() { - cumulative_probabilities[i] /= running_total; - } + weights.push(quote! {#weight * rn[rn_start_idx + #i]}); - let mut q = vec![]; - for (i, variant) in variants.iter().enumerate() { - let create_variant = &variant.for_make_gen; - let prob_upper_bound = cumulative_probabilities[i]; - q.push(quote! { - x if x < #prob_upper_bound => #create_variant + // hm, if this turns out slow, look into closures + comps.push(quote! { + (#rn_gen, #create_variant) }); } + // one hot encoding + let number_of_choices = variants.len(); + quote! { impl murrelet_gen::CanSampleFromDist for #name { - fn gen(rng: &mut R) -> Self { + // we add up each one individually, and then add one more for the type + fn rn_count() -> usize { + vec![ + #(#for_rn_count,)* + ].iter().sum() + #number_of_choices + } + + fn sample_dist(rn: &[f32], start_idx: usize) -> Self { + let mut rn_start_idx = start_idx; + + let weighted_rns = vec![#(#weights,)*]; + // first choose which enum - let enum_rn = rng.gen(); - match enum_rn { - #(#q,)* + if let Some((max_idx, max_val)) = weighted_rns.iter().enumerate().max_by(|a, b| a.1.partial_cmp(b.1).unwrap()) { + println!("Max value {} is at index {}", max_val, max_idx); + } else { + unimplemented!("hrmrm, empty enum?") + } + + rn_start_idx += #number_of_choices; + + for (i, (rn_offset, comp)) in vec![#(#comps,)*].into_iter().enumerate() { + if i == max_idx { + return comp + } else { + rn_start_idx += rn_offset; + } } + + // match enum_rn { + // #(#q,)* + // } } } } } - fn from_override_enum(func: &str) -> FieldTokensGen { + fn from_override_enum(func: &str, rn_count: usize) -> FieldTokensGen { let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); + let for_rn_count = quote! { #rn_count }; + let for_make_gen = quote! { #method() }; - FieldTokensGen { for_make_gen } + FieldTokensGen { + for_rn_count, + for_make_gen, + } } - fn from_newtype_struct(_idents: StructIdents, _parent_ident: syn::Ident) -> FieldTokensGen { + fn from_newtype_struct(idents: StructIdents, _parent_ident: syn::Ident) -> FieldTokensGen { + let ty = convert_vec_type(&idents.data.ty); + + let for_rn_count = quote! { #ty::rn_count() }; + let for_make_gen = quote! { self.0.sample_dist(rng) }; - FieldTokensGen { for_make_gen } + FieldTokensGen { + for_rn_count, + for_make_gen, + } } // e.g. TileAxisLocs::V(TileAxisVs) fn from_unnamed_enum(idents: EnumIdents) -> FieldTokensGen { let variant_ident = idents.data.ident; + let ty = convert_vec_type(&idents.data.fields.fields.first().unwrap().ty); let name = idents.enum_name; + let for_rn_count = quote! { #ty::rn_count() }; + let for_make_gen = quote! { quote! { #name::#variant_ident(s.sample_dist()) }; }; - FieldTokensGen { for_make_gen } + FieldTokensGen { + for_rn_count, + for_make_gen, + } } // e.g. TileAxis::Diag fn from_unit_enum(idents: EnumIdents) -> FieldTokensGen { let variant_ident = idents.data.ident; - let variant_ident_str = variant_ident.to_string(); + let name = idents.enum_name; - let for_make_gen = - quote! { murrelet_gui::MurreletEnumValGUI::Unit(#variant_ident_str.to_owned()) }; - // let for_gui_to_livecode = - // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; + // just the one-hot encoding + let for_rn_count = quote! { 0 }; + + let for_make_gen = quote! { #name::#variant_ident }; FieldTokensGen { + for_rn_count, for_make_gen, - // for_gui_to_livecode, - // for_assign_vars: quote!(), } } // skip fn from_noop_struct(idents: StructIdents) -> FieldTokensGen { let field_name = idents.data.ident.unwrap().to_string(); + let ty = idents.data.ty; - let for_make_gen = - quote! { v.push((#field_name.to_owned(), murrelet_gui::MurreletGUISchema::Skip)) }; - // let for_gui_to_livecode = - // quote! { murrelet_gui::Unit(#variant_ident_str) => #name::#variant_ident }; + let for_rn_count = quote! { 0 }; + let for_make_gen = quote! { #field_name: #ty::default() }; FieldTokensGen { + for_rn_count, for_make_gen, - // for_gui_to_livecode, } } // f32, Vec2, etc fn from_type_struct(idents: StructIdents, htctt: &HowToControlThisType) -> FieldTokensGen { let field_name = idents.data.ident.unwrap(); + let ty = idents.data.ty; - let for_make_gen = match htctt { + let (for_rn_count, for_make_gen) = match htctt { HowToControlThisType::Bool(rand_method_bool) => match rand_method_bool { RandMethodBool::Binomial { pct } => { - quote! { - #field_name: rng.gen() > #pct - } + let for_rn_count = quote! { 1 }; + let for_make_gen = quote! { { + let result = rn[rn_start_idx] > #pct; + rn_start_idx += #for_rn_count; + result + } }; + + (for_rn_count, for_make_gen) } }, HowToControlThisType::F32(rand_method_f32) => match rand_method_f32 { RandMethodF32::Uniform { start, end } => { - quote! { #field_name: rng.gen::() * (#end - #start) + #start } + let for_rn_count = quote! { 1 }; + let for_make_gen = quote! { { + let result = rn[rn_start_idx] * (#end - #start) + #start; + rn_start_idx += #for_rn_count; + result as #ty + } }; + + (for_rn_count, for_make_gen) } }, HowToControlThisType::Vec2(rand_method_vec2) => { @@ -171,56 +261,123 @@ impl GenFinal for FieldTokensGen { width, height, } => { - quote! { - let width = rng.gen() * #width; - let height = rng.gen() * #height; + let for_rn_count = quote! { 2 }; + let for_make_gen = quote! {{ + let width = rn[rn_start_idx] * #width; + let height = rn[rn_start_idx + 1] * #height; - #field_name: vec2(#x, #y) - 0.5 * vec2(#width, #height) + vec2(width, height) + rn_start_idx += #for_rn_count; - } + glam::vec2(#x, #y) - 0.5 * glam::vec2(#width, #height) + glam::vec2(width, height) + }}; + + (for_rn_count, for_make_gen) } RandMethodVec2::Circle { x, y, radius } => { - quote! {{ - let angle = rng.gen() * 2.0 * std::f32::consts::PI; - let dist = rng.gen(); // sqrt it to even out the sampling - #field_name: vec2(#x, #y) + vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() - }} + let for_rn_count = quote! { 2 }; + + let for_make_gen = quote! {{ + let angle = rn[rn_start_idx] * 2.0 * std::f32::consts::PI; + let dist = rn[rn_start_idx + 1]; // sqrt it to even out the sampling + rn_start_idx += #for_rn_count; + glam::vec2(#x, #y) + glam::vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() + }}; + + (for_rn_count, for_make_gen) } } } HowToControlThisType::Vec(rand_method_vec) => match rand_method_vec { RandMethodVec::Length { min, max } => { - let ty = idents.data.ty; let inside_type = nested_ident(&ty); - println!("INSIDE TYPE {:?}", inside_type); - let i = inside_type[2].clone(); + let i = inside_type[1].clone(); - quote! {{ - let how_many = rng.gen_range(#min..#max) as usize; + let for_rn_count = quote! { + #i::rn_count() * #max + 1 + }; + // // in this case, we _don't_ want one-hot, because it actually does make + // // sense to interpolate between say, 3 and 6. + // // i want to add extra indicators for everything between min and max + // // but i'm not sure how to do that! because i'm just generating, + // // not making the input data for something else... + // let for_make_gen = quote! {{ + // let how_many = rn[rn_start_idx] * (#max - #min) + #min as usize; - let mut v = vec![]; - for _ in 0..how_many { - v.push(#i::sample_dist(rng)) - } + // rn_start_idx += 1; + + // let mut v = vec![]; + // for _ in 0..how_many { + // v.push(#i::sample_dist(rn)) + // rn_start_idx += #i::rn_count(); + // } + + // v + // }}; + +// let for_rn_count = quote! { +// 1 +// }; - #field_name: v - }} + let for_make_gen = quote! { + vec![1.0] + }; + + + + (for_rn_count, for_make_gen) + } + }, + HowToControlThisType::Color(rand_method_color) => match rand_method_color { + RandMethodColor::Normal => { + let for_rn_count = quote! { 3 }; + + let for_make_gen = quote! {{ + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, 1.0) + }}; + + (for_rn_count, for_make_gen) + } + RandMethodColor::Transparency => { + let for_rn_count = quote! { 4 }; + + let for_make_gen = quote! {{ + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + let a = rn[rn_start_idx + 3]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, a) + }}; + + (for_rn_count, for_make_gen) } }, }; - FieldTokensGen { for_make_gen } + FieldTokensGen { + for_make_gen: quote! { #field_name: #for_make_gen }, + for_rn_count, + } } - fn from_override_struct(idents: StructIdents, func: &str) -> FieldTokensGen { + fn from_override_struct(idents: StructIdents, func: &str, rn_count: usize) -> FieldTokensGen { let field_name = idents.data.ident.unwrap(); + let for_rn_count = quote! { #rn_count }; + let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); let for_make_gen = quote! { #field_name: #method() }; - FieldTokensGen { for_make_gen } + FieldTokensGen { + for_rn_count, + for_make_gen, + } } } @@ -253,3 +410,21 @@ fn nested_ident(t: &syn::Type) -> Vec { recursive_ident_from_path(t, &mut acc); return acc; } + +// we need to use turbofish to call an associated function +fn convert_vec_type(ty: &syn::Type) -> TokenStream2 { + if let syn::Type::Path(type_path) = ty { + if let Some(last_segment) = type_path.path.segments.last() { + if last_segment.ident == "Vec" { + if let syn::PathArguments::AngleBracketed(angle_bracketed) = &last_segment.arguments + { + if let Some(inner_arg) = angle_bracketed.args.first() { + return quote! { Vec:: < #inner_arg > }; + } + } + } + } + } + + quote! { #ty } +} diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index 68c3a0f..234f55e 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -30,8 +30,8 @@ where ast::Data::Struct(_) => Self::make_struct(&ast_receiver), } } - fn from_override_struct(idents: StructIdents, func: &str) -> Self; - fn from_override_enum(func: &str) -> Self; + fn from_override_struct(idents: StructIdents, func: &str, rn_count: usize) -> Self; + fn from_override_enum(func: &str, rn_count: usize) -> Self; fn make_enum_final( idents: ParsedFieldIdent, @@ -58,7 +58,9 @@ where }; match field.how_to_control_this() { - HowToControlThis::Override(func) => Self::from_override_struct(idents, &func), + HowToControlThis::Override(func, count) => { + Self::from_override_struct(idents, &func, count) + } HowToControlThis::Normal => panic!("should have an annotation"), //., HowToControlThis::Type(how_to_control_this_type) => { Self::from_type_struct(idents, &how_to_control_this_type) @@ -130,8 +132,10 @@ where log::info!("-> from_noop_struct"); Self::from_noop_struct(idents) } - HowToControlThis::Override(func) => Self::from_override_enum(&func), HowToControlThis::Type(_) => panic!("hm, hsouldn't have a type here"), + HowToControlThis::Override(func, count) => { + Self::from_override_enum(&func, count) + } } }) .collect::>(); @@ -142,13 +146,19 @@ where } } +#[derive(Debug, FromMeta, Clone)] +pub struct OverrideFn { + func: String, + count: usize, +} + #[derive(Debug, FromField, Clone)] #[darling(attributes(murrelet_gen))] pub(crate) struct LivecodeFieldReceiver { pub(crate) ident: Option, pub(crate) ty: syn::Type, #[darling(default, rename = "override")] - pub(crate) override_fn: Option, + pub(crate) override_fn: Option, #[darling(default)] pub(crate) method_bool: Option, #[darling(default)] @@ -157,6 +167,8 @@ pub(crate) struct LivecodeFieldReceiver { pub(crate) method_vec2: Option, #[darling(default)] pub(crate) method_vec: Option, + #[darling(default)] + pub(crate) method_color: Option, } impl LivecodeFieldReceiver { fn how_to_control_this(&self) -> HowToControlThis { @@ -176,16 +188,19 @@ impl LivecodeFieldReceiver { if self.method_vec.is_some() { method_counts += 1 }; + if self.method_color.is_some() { + method_counts += 1 + }; // only one should be if method_counts > 1 { panic!("more than one method or override specified!"); } - if let Some(override_fn) = &self.override_fn { - match override_fn.as_str() { + if let Some(OverrideFn { func, count }) = &self.override_fn { + match func.as_str() { "default" => HowToControlThis::Default, - _ => HowToControlThis::Override(override_fn.clone()), + _ => HowToControlThis::Override(func.clone(), *count), } } else if let Some(r) = self.method_bool { HowToControlThis::Type(HowToControlThisType::Bool(r)) @@ -195,6 +210,8 @@ impl LivecodeFieldReceiver { HowToControlThis::Type(HowToControlThisType::Vec2(r.clone())) } else if let Some(r) = self.method_vec { HowToControlThis::Type(HowToControlThisType::Vec(r)) + } else if let Some(r) = self.method_color { + HowToControlThis::Type(HowToControlThisType::Color(r)) } else { HowToControlThis::Normal } @@ -230,7 +247,13 @@ pub enum RandMethodVec2 { #[derive(Debug, Copy, Clone, FromMeta)] pub enum RandMethodVec { - Length { min: usize, max: usize }, + Length { min: usize, max: usize, inside_fn: String }, +} + +#[derive(Debug, Copy, Clone, FromMeta)] +pub enum RandMethodColor { + Normal, + Transparency, } // for enums @@ -266,7 +289,7 @@ pub(crate) enum HowToControlThis { Normal, Type(HowToControlThisType), Default, // just do the default values - Override(String), + Override(String, usize), } #[derive(Clone, Debug)] @@ -275,4 +298,5 @@ pub(crate) enum HowToControlThisType { F32(RandMethodF32), Vec2(RandMethodVec2), Vec(RandMethodVec), + Color(RandMethodColor), } diff --git a/murrelet_gui_derive/src/derive_gui.rs b/murrelet_gui_derive/src/derive_gui.rs index 00f1b35..1158572 100644 --- a/murrelet_gui_derive/src/derive_gui.rs +++ b/murrelet_gui_derive/src/derive_gui.rs @@ -107,14 +107,9 @@ impl GenFinal for FieldTokensGUI { let for_make_gui = quote! { #ty::make_gui() }; - // let for_gui_to_livecode = quote! { - // self.0.gui_to_livecode(v) - // }; FieldTokensGUI { for_make_gui, - // for_gui_to_livecode, - // for_assign_vars: quote!(), } } From 1d35963b6a74382b2419b2b696b0abeb884d6ea8 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 1 Mar 2025 22:08:46 -0500 Subject: [PATCH 012/149] vec works! --- murrelet_gen/examples/tests.rs | 49 +++--- murrelet_gen/out | 44 ------ murrelet_gen_derive/src/derive_gen.rs | 186 ++++++++--------------- murrelet_gen_derive/src/parser.rs | 206 ++++++++++++++++---------- 4 files changed, 216 insertions(+), 269 deletions(-) delete mode 100644 murrelet_gen/out diff --git a/murrelet_gen/examples/tests.rs b/murrelet_gen/examples/tests.rs index 5f880d0..af496fc 100644 --- a/murrelet_gen/examples/tests.rs +++ b/murrelet_gen/examples/tests.rs @@ -4,32 +4,33 @@ use murrelet_gen::{CanSampleFromDist, MurreletGen}; #[derive(Debug, MurreletGen)] pub struct BasicTypes { - #[murrelet_gen(method_f32(uniform(start = 0.0, end = 1.0)))] + #[murrelet_gen(method(f32_uniform(start = 0.0, end = 1.0)))] a_number: f32, - #[murrelet_gen(method_f32(uniform(start = 0.0, end = 1.0)))] + #[murrelet_gen(method(f32_uniform(start = 0.0, end = 1.0)))] another_number: f32, - #[murrelet_gen(method_f32(uniform(start = 1.0, end = 100.0)))] + #[murrelet_gen(method(f32_uniform(start = 1.0, end = 100.0)))] another_number_wider_range: f32, - #[murrelet_gen(method_f32(uniform(start = -10.0, end = 0.0 )))] + #[murrelet_gen(method(f32_uniform(start = -10.0, end = 0.0 )))] a_neg_number: f32, - #[murrelet_gen(method_f32(uniform(start = 0.0, end = 30.0)))] + #[murrelet_gen(method(f32_uniform(start = 0.0, end = 30.0)))] a_usize: usize, - #[murrelet_gen(method_f32(uniform(start = 0.0, end = 30.0)))] + #[murrelet_gen(method(f32_uniform(start = 0.0, end = 30.0)))] c_number: u64, - #[murrelet_gen(method_f32(uniform(start = 0.0, end = 30.0)))] + #[murrelet_gen(method(f32_uniform(start = 0.0, end = 30.0)))] d_number: i32, - #[murrelet_gen(method_bool(binomial(pct = 0.3)))] + #[murrelet_gen(method(bool_binomial(pct = 0.3)))] bool: bool, - #[murrelet_gen(method_vec2(uniform_grid(x = 0.0, y = 0.0, width = 100.0, height = 100.0)))] + #[murrelet_gen(method(vec2_uniform_grid(x = 0.0, y = 0.0, width = 100.0, height = 100.0)))] xy: Vec2, - #[murrelet_gen(method_vec2(circle(x = 0.0, y = 0.0, radius = 100.0)))] + #[murrelet_gen(method(vec2_circle(x = 0.0, y = 0.0, radius = 100.0)))] other_xy: Vec2, - #[murrelet_gen(method_color(normal))] + #[murrelet_gen(method(color_normal))] normal_color: MurreletColor, // 3 rn - #[murrelet_gen(method_color(transparency))] + #[murrelet_gen(method(color_transparency))] transparent_color: MurreletColor, // 4 rn - #[murrelet_gen(method_vec(length(min = 1, max = 10)))] + #[murrelet_gen(method(vec_length(min = 4, max = 10)))] + #[murrelet_gen(method_inner(f32_uniform(start = 0.0, end = 1.0)))] something: Vec, // s: String, // #[murrelet_gui(reference = "test")] @@ -67,17 +68,25 @@ pub struct BasicTypes { // #[derive(Debug, Clone, MurreletUX)] fn main() { - let seed = 42; + for seed in 32..43 { + let test_val = BasicTypes::gen_from_seed(seed); - let test_val = BasicTypes::gen_from_seed(seed); + println!("BasicTypes::rn_count() {:?}", BasicTypes::rn_count()); - assert!(BasicTypes::rn_count() == 19); + assert!(BasicTypes::rn_count() == 30); - // there's a small chance they will be equal, but we know for this seed they aren't - assert!(test_val.a_number != test_val.another_number); - assert!(test_val.another_number_wider_range > 1.0); + // there's a small chance they will be equal, but we know for this seed they aren't + assert!(test_val.a_number != test_val.another_number); + assert!(test_val.another_number_wider_range > 1.0); + + assert!(test_val.something.len() > 3); + assert!(test_val.something.len() <= 10); + + // println!("test_val {:?}", test_val); + } + + let test_val = BasicTypes::gen_from_seed(42); - // println!("test_val {:?}", test_val); panic!("{:?}", test_val) // let basic_types_schema = MurreletGenSchema::Struct(vec![ diff --git a/murrelet_gen/out b/murrelet_gen/out deleted file mode 100644 index 02f360d..0000000 --- a/murrelet_gen/out +++ /dev/null @@ -1,44 +0,0 @@ -#![feature(prelude_import)] -#[prelude_import] -use std::prelude::rust_2021::*; -#[macro_use] -extern crate std; -use murrelet_gen::{CanSampleFromDist, MurreletGen}; -pub struct BasicTypes { - #[murrelet_gen(method_f32(uniform(start = 0.0, end = 1.0)))] - a_number: f32, -} -#[automatically_derived] -impl ::core::fmt::Debug for BasicTypes { - #[inline] - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field1_finish( - f, - "BasicTypes", - "a_number", - &&self.a_number, - ) - } -} -impl murrelet_gen::CanSampleFromDist for BasicTypes { - fn rn_count() -> usize { - <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([1])).iter().sum() - } - fn sample_dist(rn: &[f32], start_idx: usize) -> Self { - let mut rn_start_idx = start_idx; - Self { - a_number: { - let result = rn[rn_start_idx] * (1.0 - 0.0) + 0.0; - rn_start_idx += 1; - result - }, - } - } -} -fn main() { - let seed = 42; - let test_val = BasicTypes::gen_from_seed(seed); - { - ::core::panicking::panic_fmt(format_args!("{0:?}", test_val)); - } -} diff --git a/murrelet_gen_derive/src/derive_gen.rs b/murrelet_gen_derive/src/derive_gen.rs index c1b5d9d..780285c 100644 --- a/murrelet_gen_derive/src/derive_gen.rs +++ b/murrelet_gen_derive/src/derive_gen.rs @@ -224,144 +224,82 @@ impl GenFinal for FieldTokensGen { } // f32, Vec2, etc - fn from_type_struct(idents: StructIdents, htctt: &HowToControlThisType) -> FieldTokensGen { + fn from_type_struct(idents: StructIdents, method: &RandMethod) -> FieldTokensGen { let field_name = idents.data.ident.unwrap(); let ty = idents.data.ty; - let (for_rn_count, for_make_gen) = match htctt { - HowToControlThisType::Bool(rand_method_bool) => match rand_method_bool { - RandMethodBool::Binomial { pct } => { - let for_rn_count = quote! { 1 }; - let for_make_gen = quote! { { - let result = rn[rn_start_idx] > #pct; - rn_start_idx += #for_rn_count; - result - } }; - - (for_rn_count, for_make_gen) - } - }, - HowToControlThisType::F32(rand_method_f32) => match rand_method_f32 { - RandMethodF32::Uniform { start, end } => { - let for_rn_count = quote! { 1 }; - let for_make_gen = quote! { { - let result = rn[rn_start_idx] * (#end - #start) + #start; - rn_start_idx += #for_rn_count; - result as #ty - } }; - - (for_rn_count, for_make_gen) - } - }, - HowToControlThisType::Vec2(rand_method_vec2) => { - match rand_method_vec2 { - RandMethodVec2::UniformGrid { - x, - y, - width, - height, - } => { - let for_rn_count = quote! { 2 }; - let for_make_gen = quote! {{ - let width = rn[rn_start_idx] * #width; - let height = rn[rn_start_idx + 1] * #height; - - rn_start_idx += #for_rn_count; - - glam::vec2(#x, #y) - 0.5 * glam::vec2(#width, #height) + glam::vec2(width, height) - }}; - - (for_rn_count, for_make_gen) - } - RandMethodVec2::Circle { x, y, radius } => { - let for_rn_count = quote! { 2 }; + let (for_rn_count, for_make_gen) = method.to_methods(Some(ty)); - let for_make_gen = quote! {{ - let angle = rn[rn_start_idx] * 2.0 * std::f32::consts::PI; - let dist = rn[rn_start_idx + 1]; // sqrt it to even out the sampling - rn_start_idx += #for_rn_count; - glam::vec2(#x, #y) + glam::vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() - }}; + FieldTokensGen { + for_make_gen: quote! { #field_name: #for_make_gen }, + for_rn_count, + } + } - (for_rn_count, for_make_gen) - } - } - } - HowToControlThisType::Vec(rand_method_vec) => match rand_method_vec { - RandMethodVec::Length { min, max } => { - let inside_type = nested_ident(&ty); + fn from_type_recurse( + idents: StructIdents, + outer: &RandMethod, + inner: &Option, + ) -> Self { + let field_name = idents.data.ident.unwrap(); + let ty = idents.data.ty; - let i = inside_type[1].clone(); + let (for_rn_count, for_make_gen) = match outer { + RandMethod::VecLength { min, max } => { + let inside_type = nested_ident(&ty); - let for_rn_count = quote! { - #i::rn_count() * #max + 1 - }; - // // in this case, we _don't_ want one-hot, because it actually does make - // // sense to interpolate between say, 3 and 6. - // // i want to add extra indicators for everything between min and max - // // but i'm not sure how to do that! because i'm just generating, - // // not making the input data for something else... - // let for_make_gen = quote! {{ - // let how_many = rn[rn_start_idx] * (#max - #min) + #min as usize; - - // rn_start_idx += 1; - - // let mut v = vec![]; - // for _ in 0..how_many { - // v.push(#i::sample_dist(rn)) - // rn_start_idx += #i::rn_count(); - // } - - // v - // }}; - -// let for_rn_count = quote! { -// 1 -// }; - - let for_make_gen = quote! { - vec![1.0] + let i = inside_type[1].clone(); + + // (for_rn_count, for_make_gen) + let (for_rn_count_per_item, for_make_gen_per_item) = + if let Some(inner_method) = inner { + inner_method.to_methods(None) + } else { + ( + quote! { + #i::rn_count() + }, + quote! { + v.push(#i::sample_dist(rn)); + rn_start_idx += #i::rn_count(); + }, + ) }; + let for_rn_count = quote! { + #for_rn_count_per_item * #max + 1 + }; + + // in this case, we _don't_ want one-hot, because it actually does make + // sense to interpolate between say, 3 and 6. + // i want to add extra indicators for everything between min and max + // but i'm not sure how to do that! because i'm just generating, + // not making the input data for something else... + let for_make_gen = quote! {{ + let range = (#max - #min) as f32; + let how_many = (rn[rn_start_idx] * range) as usize + #min; + rn_start_idx += 1; + let mut v = vec![]; + for _ in 0..how_many { + v.push(#for_make_gen_per_item); + } + v + }}; + println!("done"); - (for_rn_count, for_make_gen) - } - }, - HowToControlThisType::Color(rand_method_color) => match rand_method_color { - RandMethodColor::Normal => { - let for_rn_count = quote! { 3 }; - - let for_make_gen = quote! {{ - let h = rn[rn_start_idx]; - let s = rn[rn_start_idx + 1]; - let v = rn[rn_start_idx + 2]; - rn_start_idx += #for_rn_count; - murrelet_common::MurreletColor::hsva(h, s, v, 1.0) - }}; - - (for_rn_count, for_make_gen) - } - RandMethodColor::Transparency => { - let for_rn_count = quote! { 4 }; - - let for_make_gen = quote! {{ - let h = rn[rn_start_idx]; - let s = rn[rn_start_idx + 1]; - let v = rn[rn_start_idx + 2]; - let a = rn[rn_start_idx + 3]; - rn_start_idx += #for_rn_count; - murrelet_common::MurreletColor::hsva(h, s, v, a) - }}; - - (for_rn_count, for_make_gen) - } - }, + // let for_make_gen = quote! { + // vec![1.0] + // }; + + (for_rn_count, for_make_gen) + } + _ => unreachable!("not expecting an inner without a recursive outer"), }; - FieldTokensGen { - for_make_gen: quote! { #field_name: #for_make_gen }, + Self { for_rn_count, + for_make_gen: quote! { #field_name: #for_make_gen }, } } diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index 234f55e..cf94857 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -1,5 +1,6 @@ use darling::{ast, FromDeriveInput, FromField, FromMeta, FromVariant}; use proc_macro2::TokenStream as TokenStream2; +use quote::quote; #[derive(Debug)] pub(crate) struct ParsedFieldIdent { @@ -15,9 +16,11 @@ where fn from_unnamed_enum(idents: EnumIdents) -> Self; fn from_unit_enum(idents: EnumIdents) -> Self; fn from_noop_struct(idents: StructIdents) -> Self; - fn from_type_struct( + fn from_type_struct(idents: StructIdents, how_to_control_this_type: &RandMethod) -> Self; + fn from_type_recurse( idents: StructIdents, - how_to_control_this_type: &HowToControlThisType, + how_to_control_outer_type: &RandMethod, + how_to_control_inner_type: &Option, ) -> Self; fn from_ast(ast_receiver: LivecodeReceiver) -> TokenStream2 { @@ -61,11 +64,14 @@ where HowToControlThis::Override(func, count) => { Self::from_override_struct(idents, &func, count) } - HowToControlThis::Normal => panic!("should have an annotation"), //., + HowToControlThis::Normal => panic!("should have an annotation"), HowToControlThis::Type(how_to_control_this_type) => { Self::from_type_struct(idents, &how_to_control_this_type) } HowToControlThis::Default => todo!(), + HowToControlThis::Recurse(outer, inner) => { + Self::from_type_recurse(idents, &outer, &inner) + } } }) .collect::>(); @@ -133,6 +139,7 @@ where Self::from_noop_struct(idents) } HowToControlThis::Type(_) => panic!("hm, hsouldn't have a type here"), + HowToControlThis::Recurse(_, _) => panic!("hm, hsouldn't have a type here"), HowToControlThis::Override(func, count) => { Self::from_override_enum(&func, count) } @@ -159,101 +166,146 @@ pub(crate) struct LivecodeFieldReceiver { pub(crate) ty: syn::Type, #[darling(default, rename = "override")] pub(crate) override_fn: Option, + pub(crate) method: RandMethod, #[darling(default)] - pub(crate) method_bool: Option, - #[darling(default)] - pub(crate) method_f32: Option, - #[darling(default)] - pub(crate) method_vec2: Option, - #[darling(default)] - pub(crate) method_vec: Option, - #[darling(default)] - pub(crate) method_color: Option, + pub(crate) method_inner: Option, } impl LivecodeFieldReceiver { fn how_to_control_this(&self) -> HowToControlThis { - let mut method_counts = 0; - if self.override_fn.is_some() { - method_counts += 1 - }; - if self.method_bool.is_some() { - method_counts += 1 - }; - if self.method_f32.is_some() { - method_counts += 1 - }; - if self.method_vec2.is_some() { - method_counts += 1 - }; - if self.method_vec.is_some() { - method_counts += 1 - }; - if self.method_color.is_some() { - method_counts += 1 - }; - - // only one should be - if method_counts > 1 { - panic!("more than one method or override specified!"); - } - if let Some(OverrideFn { func, count }) = &self.override_fn { match func.as_str() { "default" => HowToControlThis::Default, _ => HowToControlThis::Override(func.clone(), *count), } - } else if let Some(r) = self.method_bool { - HowToControlThis::Type(HowToControlThisType::Bool(r)) - } else if let Some(r) = &self.method_f32 { - HowToControlThis::Type(HowToControlThisType::F32(r.clone())) - } else if let Some(r) = &self.method_vec2 { - HowToControlThis::Type(HowToControlThisType::Vec2(r.clone())) - } else if let Some(r) = self.method_vec { - HowToControlThis::Type(HowToControlThisType::Vec(r)) - } else if let Some(r) = self.method_color { - HowToControlThis::Type(HowToControlThisType::Color(r)) + } else if let Some(r) = &self.method_inner { + HowToControlThis::Recurse(self.method.clone(), Some(r.clone())) + } else if matches!(self.method, RandMethod::VecLength { .. }) { + HowToControlThis::Recurse(self.method.clone(), None) } else { - HowToControlThis::Normal + HowToControlThis::Type(self.method.clone()) } } } -#[derive(Debug, Copy, Clone, FromMeta)] -pub enum RandMethodBool { - Binomial { +#[derive(Debug, Clone, FromMeta)] +pub enum RandMethod { + BoolBinomial { pct: f32, // true }, -} - -#[derive(Debug, Clone, FromMeta)] -pub enum RandMethodF32 { - Uniform { start: syn::Expr, end: syn::Expr }, -} - -#[derive(Debug, Clone, FromMeta)] -pub enum RandMethodVec2 { - UniformGrid { + F32Uniform { + start: syn::Expr, + end: syn::Expr, + }, + Vec2UniformGrid { x: syn::Expr, y: syn::Expr, width: f32, height: f32, }, - Circle { + Vec2Circle { x: syn::Expr, y: syn::Expr, radius: f32, }, + VecLength { + min: usize, + max: usize, + }, + ColorNormal, + ColorTransparency, } +impl RandMethod { + pub(crate) fn to_methods(&self, ty: Option) -> (TokenStream2, TokenStream2) { + let maybe_as = if let Some(typ) = ty { + quote! { as #typ } + } else { + quote! {} + }; -#[derive(Debug, Copy, Clone, FromMeta)] -pub enum RandMethodVec { - Length { min: usize, max: usize, inside_fn: String }, -} + match self { + RandMethod::BoolBinomial { pct } => { + let for_rn_count = quote! { 1 }; + let for_make_gen = quote! { { + let result = rn[rn_start_idx] > #pct; + rn_start_idx += #for_rn_count; + result + } }; -#[derive(Debug, Copy, Clone, FromMeta)] -pub enum RandMethodColor { - Normal, - Transparency, + (for_rn_count, for_make_gen) + } + + RandMethod::F32Uniform { start, end } => { + let for_rn_count = quote! { 1 }; + let for_make_gen = quote! { { + let result = rn[rn_start_idx] * (#end - #start) + #start; + rn_start_idx += #for_rn_count; + result #maybe_as + } }; + + (for_rn_count, for_make_gen) + } + RandMethod::Vec2UniformGrid { + x, + y, + width, + height, + } => { + let for_rn_count = quote! { 2 }; + let for_make_gen = quote! {{ + let width = rn[rn_start_idx] * #width; + let height = rn[rn_start_idx + 1] * #height; + + rn_start_idx += #for_rn_count; + + glam::vec2(#x, #y) - 0.5 * glam::vec2(#width, #height) + glam::vec2(width, height) + }}; + + (for_rn_count, for_make_gen) + } + RandMethod::Vec2Circle { x, y, radius } => { + let for_rn_count = quote! { 2 }; + + let for_make_gen = quote! {{ + let angle = rn[rn_start_idx] * 2.0 * std::f32::consts::PI; + let dist = rn[rn_start_idx + 1]; // sqrt it to even out the sampling + rn_start_idx += #for_rn_count; + glam::vec2(#x, #y) + glam::vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() + }}; + + (for_rn_count, for_make_gen) + } + RandMethod::ColorNormal => { + let for_rn_count = quote! { 3 }; + + let for_make_gen = quote! {{ + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, 1.0) + }}; + + (for_rn_count, for_make_gen) + } + RandMethod::ColorTransparency => { + let for_rn_count = quote! { 4 }; + + let for_make_gen = quote! {{ + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + let a = rn[rn_start_idx + 3]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, a) + }}; + + (for_rn_count, for_make_gen) + } + RandMethod::VecLength { .. } => { + unreachable!("hm, this should be in teh recurse func, are you missing an inner?") + } + } + } } // for enums @@ -287,16 +339,8 @@ pub struct StructIdents { #[derive(Clone, Debug)] pub(crate) enum HowToControlThis { Normal, - Type(HowToControlThisType), - Default, // just do the default values + Type(RandMethod), + Recurse(RandMethod, Option), // one level... defaults to calling its func + Default, // just do the default values Override(String, usize), } - -#[derive(Clone, Debug)] -pub(crate) enum HowToControlThisType { - Bool(RandMethodBool), - F32(RandMethodF32), - Vec2(RandMethodVec2), - Vec(RandMethodVec), - Color(RandMethodColor), -} From 3c3302ad274faecf46c777c30ead783b2c90ba3c Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 1 Mar 2025 23:35:29 -0500 Subject: [PATCH 013/149] i think it works! --- murrelet_gen/examples/tests.rs | 78 ++++++++++++++---------- murrelet_gen_derive/src/derive_gen.rs | 85 +++++++++------------------ murrelet_gen_derive/src/parser.rs | 54 ++++++++++++++--- 3 files changed, 121 insertions(+), 96 deletions(-) diff --git a/murrelet_gen/examples/tests.rs b/murrelet_gen/examples/tests.rs index af496fc..ef964d6 100644 --- a/murrelet_gen/examples/tests.rs +++ b/murrelet_gen/examples/tests.rs @@ -32,49 +32,59 @@ pub struct BasicTypes { #[murrelet_gen(method(vec_length(min = 4, max = 10)))] #[murrelet_gen(method_inner(f32_uniform(start = 0.0, end = 1.0)))] something: Vec, - // s: String, - // #[murrelet_gui(reference = "test")] - // referenced_string: String, + #[murrelet_gen(method(string_choice(choices(a = 1.0, b = 1.0, c = 1.0))))] + s: String, } // fn custom_func() -> MurreletGenSchema { // MurreletGenSchema::Val(ValueGUI::Num) // } -// #[derive(MurreletGen)] -// pub struct OverridesAndRecursive { -// a_number: f32, -// something: Vec, -// #[murrelet_gui(func = "custom_func")] -// label: String, -// #[murrelet_gui(kind = "skip")] -// b: HashMap, -// } +#[derive(Debug, MurreletGen)] +pub struct OverridesAndRecursive { + #[murrelet_gen(method(recurse))] + basic: BasicTypes, + #[murrelet_gen(method(vec_length(min = 1, max = 4)))] + #[murrelet_gen(method_inner(recurse))] + something: Vec, + // #[murrelet_gen(method(string_choice(choices(a = 1.0, b = 1.0, c = 1.0))))] + // label: String, + // b: HashMap, +} -// #[derive(MurreletGen)] -// enum EnumTest { -// A, -// B(OverridesAndRecursive), -// } +#[derive(Debug, MurreletGen)] +pub struct Tiny { + #[murrelet_gen(method(f32_uniform(start = 0.0, end = 1.0)))] + a_number: f32, + #[murrelet_gen(method(f32_uniform(start = 0.0, end = 1.0)))] + another_number: f32, +} + +#[derive(Debug, MurreletGen)] +enum EnumTest { + #[murrelet_gen(weight = 1.0)] + A, + #[murrelet_gen(weight = 1.0)] + B(#[murrelet_gen(method(recurse))] Tiny), +} // #[derive(MurreletGen)] // struct SimpleNewtype(f32); -// fn lerp_partial(&self, pct: T) -> Self { -// SimpleNewtype(pct.lerp_pct() as f32) -// } -// } +fn main() { + println!("BasicTypes::rn_count() {:?}", BasicTypes::rn_count()); + println!( + "OverridesAndRecursive::rn_count() {:?}", + OverridesAndRecursive::rn_count() + ); -// #[derive(Debug, Clone, MurreletUX)] + assert_eq!(BasicTypes::rn_count(), 33); + + assert_eq!(OverridesAndRecursive::rn_count(), 166); -fn main() { for seed in 32..43 { let test_val = BasicTypes::gen_from_seed(seed); - println!("BasicTypes::rn_count() {:?}", BasicTypes::rn_count()); - - assert!(BasicTypes::rn_count() == 30); - // there's a small chance they will be equal, but we know for this seed they aren't assert!(test_val.a_number != test_val.another_number); assert!(test_val.another_number_wider_range > 1.0); @@ -82,12 +92,20 @@ fn main() { assert!(test_val.something.len() > 3); assert!(test_val.something.len() <= 10); + let test_val2 = OverridesAndRecursive::gen_from_seed(seed); + assert!(test_val2.something.len() >= 1); + assert!(test_val2.something.len() < 4); + // println!("test_val {:?}", test_val); - } - let test_val = BasicTypes::gen_from_seed(42); + let test_val = BasicTypes::gen_from_seed(seed); + let test_val2 = OverridesAndRecursive::gen_from_seed(seed); + let test_val3 = EnumTest::gen_from_seed(seed); - panic!("{:?}", test_val) + println!("test_val {:?}", test_val); + // println!("test_val2 {:?}", test_val2); + // println!("test_val3 {:?}", test_val3); + } // let basic_types_schema = MurreletGenSchema::Struct(vec![ // ("a_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), diff --git a/murrelet_gen_derive/src/derive_gen.rs b/murrelet_gen_derive/src/derive_gen.rs index 780285c..72b7eee 100644 --- a/murrelet_gen_derive/src/derive_gen.rs +++ b/murrelet_gen_derive/src/derive_gen.rs @@ -21,9 +21,7 @@ impl GenFinal for FieldTokensGen { quote! { impl murrelet_gen::CanSampleFromDist for #name { fn rn_count() -> usize { - vec![ - #(#for_rn_count,)* - ].iter().sum() + #(#for_rn_count+)* } fn sample_dist(rn: &[f32], start_idx: usize) -> Self { @@ -89,17 +87,18 @@ impl GenFinal for FieldTokensGen { // } let mut weights = vec![]; - let mut comps = vec![]; - for (i, (variant, receiver)) in variants.iter().zip(variant_receiver.iter()).enumerate() { + + for (variant, receiver) in variants.iter().zip(variant_receiver.iter()) { let create_variant = &variant.for_make_gen; let rn_gen = &variant.for_rn_count; let weight = receiver.weight; - weights.push(quote! {#weight * rn[rn_start_idx + #i]}); - - // hm, if this turns out slow, look into closures - comps.push(quote! { - (#rn_gen, #create_variant) + // we need the closures so we offset it right... hrm, shadowign the variable, mgiht regret that + weights.push(quote! { + let weight = #weight * rn[rn_start_idx]; + rn_start_idx += 1; + weighted_rns.push((weight, #create_variant)); + rn_start_idx += #rn_gen; }); } @@ -112,34 +111,21 @@ impl GenFinal for FieldTokensGen { fn rn_count() -> usize { vec![ #(#for_rn_count,)* - ].iter().sum() + #number_of_choices + ].iter().sum::() + #number_of_choices } fn sample_dist(rn: &[f32], start_idx: usize) -> Self { let mut rn_start_idx = start_idx; - let weighted_rns = vec![#(#weights,)*]; + let mut weighted_rns: Vec<(f32, _)> = vec![]; + #(#weights;)* // first choose which enum - if let Some((max_idx, max_val)) = weighted_rns.iter().enumerate().max_by(|a, b| a.1.partial_cmp(b.1).unwrap()) { - println!("Max value {} is at index {}", max_val, max_idx); - } else { - unimplemented!("hrmrm, empty enum?") - } - - rn_start_idx += #number_of_choices; + let (_, comp) = weighted_rns.into_iter() + .max_by(|a, b| a.0.partial_cmp(&b.0).unwrap()) + .expect("no enum values?"); - for (i, (rn_offset, comp)) in vec![#(#comps,)*].into_iter().enumerate() { - if i == max_idx { - return comp - } else { - rn_start_idx += rn_offset; - } - } - - // match enum_rn { - // #(#q,)* - // } + comp } } } @@ -166,7 +152,7 @@ impl GenFinal for FieldTokensGen { let for_rn_count = quote! { #ty::rn_count() }; let for_make_gen = quote! { - self.0.sample_dist(rng) + self.0.sample_dist(rn, rn_start_idx) }; FieldTokensGen { @@ -183,8 +169,14 @@ impl GenFinal for FieldTokensGen { let for_rn_count = quote! { #ty::rn_count() }; + // hm, i'm not sure that the method in the enum is actually used let for_make_gen = quote! { - quote! { #name::#variant_ident(s.sample_dist()) }; + { + let result = #name::#variant_ident(#ty::sample_dist(rn, rn_start_idx)); + rn_start_idx += #for_rn_count; + result + + } }; FieldTokensGen { @@ -228,7 +220,7 @@ impl GenFinal for FieldTokensGen { let field_name = idents.data.ident.unwrap(); let ty = idents.data.ty; - let (for_rn_count, for_make_gen) = method.to_methods(Some(ty)); + let (for_rn_count, for_make_gen) = method.to_methods(ty, true); FieldTokensGen { for_make_gen: quote! { #field_name: #for_make_gen }, @@ -236,11 +228,7 @@ impl GenFinal for FieldTokensGen { } } - fn from_type_recurse( - idents: StructIdents, - outer: &RandMethod, - inner: &Option, - ) -> Self { + fn from_type_recurse(idents: StructIdents, outer: &RandMethod, inner: &RandMethod) -> Self { let field_name = idents.data.ident.unwrap(); let ty = idents.data.ty; @@ -249,22 +237,11 @@ impl GenFinal for FieldTokensGen { let inside_type = nested_ident(&ty); let i = inside_type[1].clone(); + let inside_type_val: syn::Type = syn::parse_quote! { #i }; // (for_rn_count, for_make_gen) let (for_rn_count_per_item, for_make_gen_per_item) = - if let Some(inner_method) = inner { - inner_method.to_methods(None) - } else { - ( - quote! { - #i::rn_count() - }, - quote! { - v.push(#i::sample_dist(rn)); - rn_start_idx += #i::rn_count(); - }, - ) - }; + inner.to_methods(inside_type_val, false); let for_rn_count = quote! { #for_rn_count_per_item * #max + 1 @@ -286,12 +263,6 @@ impl GenFinal for FieldTokensGen { v }}; - println!("done"); - - // let for_make_gen = quote! { - // vec![1.0] - // }; - (for_rn_count, for_make_gen) } _ => unreachable!("not expecting an inner without a recursive outer"), diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index cf94857..be76466 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use darling::{ast, FromDeriveInput, FromField, FromMeta, FromVariant}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; @@ -20,7 +22,7 @@ where fn from_type_recurse( idents: StructIdents, how_to_control_outer_type: &RandMethod, - how_to_control_inner_type: &Option, + how_to_control_inner_type: &RandMethod, ) -> Self; fn from_ast(ast_receiver: LivecodeReceiver) -> TokenStream2 { @@ -178,9 +180,10 @@ impl LivecodeFieldReceiver { _ => HowToControlThis::Override(func.clone(), *count), } } else if let Some(r) = &self.method_inner { - HowToControlThis::Recurse(self.method.clone(), Some(r.clone())) + HowToControlThis::Recurse(self.method.clone(), r.clone()) } else if matches!(self.method, RandMethod::VecLength { .. }) { - HowToControlThis::Recurse(self.method.clone(), None) + panic!("vec missing inner") + // HowToControlThis::Recurse(self.method.clone(), None) } else { HowToControlThis::Type(self.method.clone()) } @@ -189,6 +192,7 @@ impl LivecodeFieldReceiver { #[derive(Debug, Clone, FromMeta)] pub enum RandMethod { + Recurse, BoolBinomial { pct: f32, // true }, @@ -213,16 +217,29 @@ pub enum RandMethod { }, ColorNormal, ColorTransparency, + StringChoice { + choices: HashMap, + }, } impl RandMethod { - pub(crate) fn to_methods(&self, ty: Option) -> (TokenStream2, TokenStream2) { - let maybe_as = if let Some(typ) = ty { - quote! { as #typ } + pub(crate) fn to_methods(&self, ty: syn::Type, convert: bool) -> (TokenStream2, TokenStream2) { + let maybe_as = if convert { + quote! { as #ty } } else { quote! {} }; match self { + RandMethod::Recurse => { + let for_rn_count = quote! { #ty::rn_count() }; + let for_make_gen = quote! {{ + let r = #ty::sample_dist(rn, rn_start_idx); + rn_start_idx += #for_rn_count; + r + }}; + + (for_rn_count, for_make_gen) + } RandMethod::BoolBinomial { pct } => { let for_rn_count = quote! { 1 }; let for_make_gen = quote! { { @@ -233,7 +250,6 @@ impl RandMethod { (for_rn_count, for_make_gen) } - RandMethod::F32Uniform { start, end } => { let for_rn_count = quote! { 1 }; let for_make_gen = quote! { { @@ -304,6 +320,26 @@ impl RandMethod { RandMethod::VecLength { .. } => { unreachable!("hm, this should be in teh recurse func, are you missing an inner?") } + RandMethod::StringChoice { choices } => { + let one_hot = choices.len(); + let for_rn_count = quote! { #one_hot }; + + let weighted_rns = choices + .iter() + .enumerate() + .map(|(i, (key, weight))| { + quote! { (#key.clone(), #weight * rn[rn_start_idx + #i]) } + }) + .collect::>(); + + let for_make_gen = quote! { { + let result = vec![#(#weighted_rns,)*].into_iter().max_by(|a, b| a.1.partial_cmp(&b.1).unwrap()).expect("empty string choices??"); + rn_start_idx += #for_rn_count; + result.0.to_string() + } }; + + (for_rn_count, for_make_gen) + } } } } @@ -340,7 +376,7 @@ pub struct StructIdents { pub(crate) enum HowToControlThis { Normal, Type(RandMethod), - Recurse(RandMethod, Option), // one level... defaults to calling its func - Default, // just do the default values + Recurse(RandMethod, RandMethod), // one level... defaults to calling its func + Default, // just do the default values Override(String, usize), } From eec9393555c202802eb125527fa2e2b71fc8ef6a Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 2 Mar 2025 12:00:42 -0500 Subject: [PATCH 014/149] maybe fix bug in vec --- murrelet_draw/src/curve_drawer.rs | 12 ++-- murrelet_gen/examples/tests.rs | 80 +++++++-------------------- murrelet_gen_derive/src/derive_gen.rs | 23 +++++--- murrelet_gen_derive/src/parser.rs | 14 ++++- 4 files changed, 53 insertions(+), 76 deletions(-) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 396442c..3ff7896 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -75,14 +75,14 @@ impl CurveDrawer { Self::new(vec![], false) } - pub fn first_point(&self) -> Vec2 { - let first_command = self.segments().first().unwrap(); - first_command.first_point() + pub fn first_point(&self) -> Option { + let first_command = self.segments().first()?; + Some(first_command.first_point()) } - pub fn last_point(&self) -> Vec2 { - let last_command = self.segments().last().unwrap(); - last_command.last_point() + pub fn last_point(&self) -> Option { + let last_command = self.segments().last()?; + Some(last_command.last_point()) } } diff --git a/murrelet_gen/examples/tests.rs b/murrelet_gen/examples/tests.rs index ef964d6..f5c63e5 100644 --- a/murrelet_gen/examples/tests.rs +++ b/murrelet_gen/examples/tests.rs @@ -8,6 +8,8 @@ pub struct BasicTypes { a_number: f32, #[murrelet_gen(method(f32_uniform(start = 0.0, end = 1.0)))] another_number: f32, + #[murrelet_gen(method(f32_fixed(val = 0.23)))] + fixed_number: f32, #[murrelet_gen(method(f32_uniform(start = 1.0, end = 100.0)))] another_number_wider_range: f32, #[murrelet_gen(method(f32_uniform(start = -10.0, end = 0.0 )))] @@ -36,21 +38,6 @@ pub struct BasicTypes { s: String, } -// fn custom_func() -> MurreletGenSchema { -// MurreletGenSchema::Val(ValueGUI::Num) -// } - -#[derive(Debug, MurreletGen)] -pub struct OverridesAndRecursive { - #[murrelet_gen(method(recurse))] - basic: BasicTypes, - #[murrelet_gen(method(vec_length(min = 1, max = 4)))] - #[murrelet_gen(method_inner(recurse))] - something: Vec, - // #[murrelet_gen(method(string_choice(choices(a = 1.0, b = 1.0, c = 1.0))))] - // label: String, - // b: HashMap, -} #[derive(Debug, MurreletGen)] pub struct Tiny { @@ -60,16 +47,27 @@ pub struct Tiny { another_number: f32, } +#[derive(Debug, MurreletGen)] +pub struct OverridesAndRecursive { + #[murrelet_gen(method(recurse))] + basic: Tiny, + #[murrelet_gen(method(vec_length(min = 1, max = 4)))] + #[murrelet_gen(method_inner(recurse))] + something: Vec, +} + #[derive(Debug, MurreletGen)] enum EnumTest { #[murrelet_gen(weight = 1.0)] A, #[murrelet_gen(weight = 1.0)] B(#[murrelet_gen(method(recurse))] Tiny), + #[murrelet_gen(weight = 1.0)] + C(#[murrelet_gen(method(recurse))] Tiny), } // #[derive(MurreletGen)] -// struct SimpleNewtype(f32); +// struct SimpleNewtype(#[murrelet_gen(method(f32_uniform(start = 0.0, end = 1.0)))] f32); fn main() { println!("BasicTypes::rn_count() {:?}", BasicTypes::rn_count()); @@ -80,7 +78,11 @@ fn main() { assert_eq!(BasicTypes::rn_count(), 33); - assert_eq!(OverridesAndRecursive::rn_count(), 166); + + println!("OverridesAndRecursive::gen_from_seed(42) {:?}", OverridesAndRecursive::gen_from_seed(42)); + + assert_eq!(OverridesAndRecursive::rn_count(), 11); + assert_eq!(EnumTest::rn_count(), 7); for seed in 32..43 { let test_val = BasicTypes::gen_from_seed(seed); @@ -88,13 +90,14 @@ fn main() { // there's a small chance they will be equal, but we know for this seed they aren't assert!(test_val.a_number != test_val.another_number); assert!(test_val.another_number_wider_range > 1.0); + assert_eq!(test_val.fixed_number, 0.23); assert!(test_val.something.len() > 3); assert!(test_val.something.len() <= 10); let test_val2 = OverridesAndRecursive::gen_from_seed(seed); assert!(test_val2.something.len() >= 1); - assert!(test_val2.something.len() < 4); + assert!(test_val2.something.len() <= 4); // println!("test_val {:?}", test_val); @@ -107,45 +110,4 @@ fn main() { // println!("test_val3 {:?}", test_val3); } - // let basic_types_schema = MurreletGenSchema::Struct(vec![ - // ("a_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), - // ("b_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), - // ("c_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), - // ("d_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), - // ("bool".to_owned(), MurreletGenSchema::Val(ValueGUI::Bool)), - // ( - // "something".to_owned(), - // MurreletGenSchema::list(MurreletGenSchema::Val(ValueGUI::Num)), - // ), - // ("s".to_owned(), MurreletGenSchema::Skip), - // ( - // "referenced_string".to_owned(), - // MurreletGenSchema::Val(ValueGUI::Name("test".to_owned())), - // ), - // ]); - - // assert_eq!(test_val, basic_types_schema); - - // let test_val = OverridesAndRecursive::make_gui(); - - // let overrides_and_recursive_schema = MurreletGenSchema::Struct(vec![ - // ("a_number".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), - // ( - // "something".to_owned(), - // MurreletGenSchema::list(basic_types_schema), - // ), - // ("label".to_owned(), MurreletGenSchema::Val(ValueGUI::Num)), // make sure it calls the override - // ("b".to_owned(), MurreletGenSchema::Skip), - // ]); - // assert_eq!(test_val, overrides_and_recursive_schema); - - // let test_val = EnumTest::make_gui(); - - // assert_eq!( - // test_val, - // MurreletGenSchema::Enum(vec![ - // (MurreletEnumValGUI::Unit("A".to_owned())), - // (MurreletEnumValGUI::Unnamed("B".to_owned(), overrides_and_recursive_schema)), - // ]) - // ); } diff --git a/murrelet_gen_derive/src/derive_gen.rs b/murrelet_gen_derive/src/derive_gen.rs index 72b7eee..5afc8b1 100644 --- a/murrelet_gen_derive/src/derive_gen.rs +++ b/murrelet_gen_derive/src/derive_gen.rs @@ -47,6 +47,7 @@ impl GenFinal for FieldTokensGen { fn sample_dist(rn: &[f32], start_idx: usize) -> Self { let mut rn_start_idx = start_idx; + Self { #(#for_make_gen,)* } @@ -90,19 +91,18 @@ impl GenFinal for FieldTokensGen { for (variant, receiver) in variants.iter().zip(variant_receiver.iter()) { let create_variant = &variant.for_make_gen; - let rn_gen = &variant.for_rn_count; let weight = receiver.weight; - // we need the closures so we offset it right... hrm, shadowign the variable, mgiht regret that + // hrm, might want to use closures if it's expensive + // also the create_variant will modify rn_start_idx for us. weights.push(quote! { let weight = #weight * rn[rn_start_idx]; rn_start_idx += 1; weighted_rns.push((weight, #create_variant)); - rn_start_idx += #rn_gen; }); } - // one hot encoding + // one hot encoding, i might be off-by-one here for how many vars.. let number_of_choices = variants.len(); quote! { @@ -243,7 +243,7 @@ impl GenFinal for FieldTokensGen { let (for_rn_count_per_item, for_make_gen_per_item) = inner.to_methods(inside_type_val, false); - let for_rn_count = quote! { + let for_rn_count: TokenStream2 = quote! { #for_rn_count_per_item * #max + 1 }; @@ -253,12 +253,19 @@ impl GenFinal for FieldTokensGen { // but i'm not sure how to do that! because i'm just generating, // not making the input data for something else... let for_make_gen = quote! {{ - let range = (#max - #min) as f32; + let range = (#max - #min + 1) as f32; let how_many = (rn[rn_start_idx] * range) as usize + #min; rn_start_idx += 1; let mut v = vec![]; - for _ in 0..how_many { - v.push(#for_make_gen_per_item); + // need to go through the fill list so we increment + // through the rns right + for i in 0..#max { + if i < how_many { + v.push(#for_make_gen_per_item); + } else { + // just run it + #for_make_gen_per_item; + } } v }}; diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index be76466..766e807 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -140,10 +140,14 @@ where log::info!("-> from_noop_struct"); Self::from_noop_struct(idents) } - HowToControlThis::Type(_) => panic!("hm, hsouldn't have a type here"), - HowToControlThis::Recurse(_, _) => panic!("hm, hsouldn't have a type here"), + HowToControlThis::Type(how_to_control_this_type) => { + Self::from_type_struct(idents, &how_to_control_this_type) + } + HowToControlThis::Recurse(outer, inner) => { + Self::from_type_recurse(idents, &outer, &inner) + } HowToControlThis::Override(func, count) => { - Self::from_override_enum(&func, count) + Self::from_override_struct(idents, &func, count) } } }) @@ -200,6 +204,9 @@ pub enum RandMethod { start: syn::Expr, end: syn::Expr, }, + F32Fixed { + val: syn::Expr, + }, Vec2UniformGrid { x: syn::Expr, y: syn::Expr, @@ -340,6 +347,7 @@ impl RandMethod { (for_rn_count, for_make_gen) } + RandMethod::F32Fixed { val } => (quote! { 0 }, quote! { #val }), } } } From 48aac05e27cf0a84060d4479d1fc93e46e7b0d01 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 7 Mar 2025 15:23:49 -0500 Subject: [PATCH 015/149] wip --- murrelet_common/src/assets.rs | 5 ---- murrelet_common/src/color.rs | 14 +++++----- murrelet_common/src/lib.rs | 2 +- murrelet_draw/src/compass.rs | 29 ++++++++++---------- murrelet_draw/src/lib.rs | 2 +- murrelet_draw/src/livecodetypes.rs | 1 - murrelet_draw/src/style.rs | 6 ++++- murrelet_draw/src/svg.rs | 13 +++++++++ murrelet_gen/examples/tests.rs | 8 +++--- murrelet_gen_derive/src/lib.rs | 2 +- murrelet_gen_derive/src/parser.rs | 38 ++++++++++++++++++++++++++- murrelet_gpu/src/device_state.rs | 3 --- murrelet_gpu/src/gpu_macros.rs | 4 --- murrelet_gui_derive/src/derive_gui.rs | 4 +-- murrelet_gui_derive/src/lib.rs | 2 +- murrelet_livecode/src/lazy.rs | 6 ++++- murrelet_perform/src/perform.rs | 14 +++++----- murrelet_svg/src/svg.rs | 2 -- 18 files changed, 95 insertions(+), 60 deletions(-) diff --git a/murrelet_common/src/assets.rs b/murrelet_common/src/assets.rs index 4e7c227..962ab4e 100644 --- a/murrelet_common/src/assets.rs +++ b/murrelet_common/src/assets.rs @@ -50,8 +50,6 @@ impl VectorAsset { } } - - #[derive(Debug, Clone)] pub struct VectorLayersAssetLookup { filename_to_polyline_layers: HashMap, @@ -79,7 +77,6 @@ impl VectorLayersAssetLookup { } } - pub trait IsColorType {} #[derive(Debug, Clone, Copy)] @@ -89,7 +86,6 @@ impl IsColorType for BlackWhite {} // struct RGBAu8([u8; 4]),; // struct RGBAf32([f32; 4]); - #[derive(Debug, Clone)] pub enum RasterAsset { RasterBW(Vec>), @@ -107,7 +103,6 @@ impl RasterAssetLookup { } } - #[derive(Debug, Clone)] pub struct Assets { diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index c5c547a..ca2c3d4 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -1,4 +1,7 @@ -use std::{fmt, ops::{Add, Mul}}; +use std::{ + fmt, + ops::{Add, Mul}, +}; use lerpable::{IsLerpingMethod, Lerpable}; use murrelet_gui::CanMakeGUI; @@ -199,11 +202,6 @@ impl Mul for MurreletColor { fn mul(self, scalar: f32) -> Self::Output { let [h, s, v, a] = self.into_hsva_components(); - MurreletColor::hsva( - h * scalar, - s * scalar, - v * scalar, - a * scalar, - ) + MurreletColor::hsva(h * scalar, s * scalar, v * scalar, a * scalar) } -} \ No newline at end of file +} diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 0c6d73f..86e8423 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -862,4 +862,4 @@ pub fn make_gui_vec2_coords() -> murrelet_gui::MurreletGUISchema { pub fn make_gui_vec3() -> murrelet_gui::MurreletGUISchema { murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Vec3) -} \ No newline at end of file +} diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index ac695c4..c0b8ebe 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -35,10 +35,10 @@ fn empty_string() -> String { pub struct CompassDir { angle_pi: LivecodeAnglePi, #[livecode(serde_default = "false")] - #[murrelet_gui(kind="skip")] + #[murrelet_gui(kind = "skip")] is_absolute: bool, #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] - #[murrelet_gui(kind="skip")] + #[murrelet_gui(kind = "skip")] label: String, } @@ -57,10 +57,10 @@ pub struct CompassArc { radius: f32, arc_length: LivecodeAnglePi, #[livecode(serde_default = "false")] - #[murrelet_gui(kind="skip")] + #[murrelet_gui(kind = "skip")] is_absolute: bool, #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] - #[murrelet_gui(kind="skip")] + #[murrelet_gui(kind = "skip")] label: String, } @@ -72,7 +72,7 @@ pub struct CompassArc { pub struct CompassLine { length: f32, // how far should we head in the current direction #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] - #[murrelet_gui(kind="skip")] + #[murrelet_gui(kind = "skip")] label: String, } @@ -181,16 +181,15 @@ impl InteractiveCompassBuilder { } CompassAction::Arc(x) => { vec![self.add_arc(x)] - } - // CompassAction::Repeat(x) => { - // let mut n = Vec::new(); - // for _ in 0..x.times { - // for w in &x.what { - // n.extend(self.new_segments(w)) - // } - // } - // n - // } + } // CompassAction::Repeat(x) => { + // let mut n = Vec::new(); + // for _ in 0..x.times { + // for w in &x.what { + // n.extend(self.new_segments(w)) + // } + // } + // n + // } } } diff --git a/murrelet_draw/src/lib.rs b/murrelet_draw/src/lib.rs index 6efc860..420ae05 100644 --- a/murrelet_draw/src/lib.rs +++ b/murrelet_draw/src/lib.rs @@ -5,5 +5,5 @@ pub mod livecodetypes; pub mod newtypes; pub mod sequencers; pub mod style; -pub mod transform2d; pub mod svg; +pub mod transform2d; diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs index 05fe8ee..003d43c 100644 --- a/murrelet_draw/src/livecodetypes.rs +++ b/murrelet_draw/src/livecodetypes.rs @@ -36,7 +36,6 @@ pub mod anglepi { } } - impl CanMakeGUI for LivecodeAnglePi { fn make_gui() -> murrelet_gui::MurreletGUISchema { murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Angle) diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index b77b680..8a28323 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -37,7 +37,11 @@ pub struct MurreletStyleFilled { } impl MurreletStyleFilled { pub fn new(color: MurreletColor, stroke_weight: f32, stroke_color: MurreletColor) -> Self { - Self { color, stroke_weight, stroke_color } + Self { + color, + stroke_weight, + stroke_color, + } } fn to_style(&self) -> MurreletStyle { diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index 1d6218b..e7a4041 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -19,6 +19,19 @@ pub struct SvgRect { pub height: f32, } +impl SvgRect { + pub fn new_centered(width: f32, height: f32) -> Self { + Self { + x: 0.0, + y: 0.0, + rx: 0.0, + ry: 0.0, + width, + height, + } + } +} + #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] pub struct SvgCircle { #[livecode(serde_default = "0")] diff --git a/murrelet_gen/examples/tests.rs b/murrelet_gen/examples/tests.rs index f5c63e5..16a94be 100644 --- a/murrelet_gen/examples/tests.rs +++ b/murrelet_gen/examples/tests.rs @@ -38,7 +38,6 @@ pub struct BasicTypes { s: String, } - #[derive(Debug, MurreletGen)] pub struct Tiny { #[murrelet_gen(method(f32_uniform(start = 0.0, end = 1.0)))] @@ -78,8 +77,10 @@ fn main() { assert_eq!(BasicTypes::rn_count(), 33); - - println!("OverridesAndRecursive::gen_from_seed(42) {:?}", OverridesAndRecursive::gen_from_seed(42)); + println!( + "OverridesAndRecursive::gen_from_seed(42) {:?}", + OverridesAndRecursive::gen_from_seed(42) + ); assert_eq!(OverridesAndRecursive::rn_count(), 11); assert_eq!(EnumTest::rn_count(), 7); @@ -109,5 +110,4 @@ fn main() { // println!("test_val2 {:?}", test_val2); // println!("test_val3 {:?}", test_val3); } - } diff --git a/murrelet_gen_derive/src/lib.rs b/murrelet_gen_derive/src/lib.rs index 64cc315..7be123e 100644 --- a/murrelet_gen_derive/src/lib.rs +++ b/murrelet_gen_derive/src/lib.rs @@ -5,8 +5,8 @@ use derive_gen::FieldTokensGen; use parser::{GenFinal, LivecodeReceiver}; use proc_macro::TokenStream; -mod parser; mod derive_gen; +mod parser; #[proc_macro_derive(MurreletGen, attributes(murrelet_gen))] pub fn murrelet_livecode_derive_murrelet_gen(input: TokenStream) -> TokenStream { diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index 766e807..ffe73ce 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -204,6 +204,14 @@ pub enum RandMethod { start: syn::Expr, end: syn::Expr, }, + F32UniformPosNeg { + start: f32, + end: f32, + }, + F32Normal { + mu: syn::Expr, + sigma: syn::Expr, + }, F32Fixed { val: syn::Expr, }, @@ -267,6 +275,35 @@ impl RandMethod { (for_rn_count, for_make_gen) } + RandMethod::F32UniformPosNeg { start, end } => { + let for_rn_count = quote! { 2 }; + let for_make_gen = quote! { { + let sgn = if(rn[rn_start_idx] > 0.5) { 1.0 } else { -1.0 }; + let result = rn[rn_start_idx + 1] * (#end - #start) + #start; + rn_start_idx += #for_rn_count; + (sgn * result) #maybe_as + } }; + + (for_rn_count, for_make_gen) + } + RandMethod::F32Fixed { val } => (quote! { 0 }, quote! { #val #maybe_as }), + // box muller copy-pasta + RandMethod::F32Normal { mu, sigma } => { + let for_rn_count = quote! { 2 }; + let for_make_gen = quote! { { + // avoid nans, make sure u1 is positive and non-zero + let u1 = rn[rn_start_idx].clamp(std::f32::MIN_POSITIVE, 1.0); + let u2 = rn[rn_start_idx + 1].clamp(0.0, 1.0); + rn_start_idx += 2; + + let r = (-2.0 * u1.ln()).sqrt(); + let theta = 2.0 * std::f32::consts::PI * u2; + + #mu + #sigma * r * theta.cos() #maybe_as + } }; + (for_rn_count, for_make_gen) + } + RandMethod::Vec2UniformGrid { x, y, @@ -347,7 +384,6 @@ impl RandMethod { (for_rn_count, for_make_gen) } - RandMethod::F32Fixed { val } => (quote! { 0 }, quote! { #val }), } } } diff --git a/murrelet_gpu/src/device_state.rs b/murrelet_gpu/src/device_state.rs index c1fa48a..600b812 100644 --- a/murrelet_gpu/src/device_state.rs +++ b/murrelet_gpu/src/device_state.rs @@ -106,13 +106,11 @@ impl OwnedDeviceState { // borrowing from bevy pub fn align_byte_size(value: u32) -> u32 { - if value % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT != 0 { value + (wgpu::COPY_BYTES_PER_ROW_ALIGNMENT - (value % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT)) } else { value } - } pub fn check_img_size(path: &PathBuf) -> Result<(Vec, u32, u32), Box> { @@ -161,7 +159,6 @@ fn write_png_to_texture( padded_img[start..end].copy_from_slice(data); } - let mut hist = HashMap::new(); for value in &padded_img { *hist.entry(value).or_insert(0) += 1; diff --git a/murrelet_gpu/src/gpu_macros.rs b/murrelet_gpu/src/gpu_macros.rs index a0372d1..72d3fa2 100644 --- a/murrelet_gpu/src/gpu_macros.rs +++ b/murrelet_gpu/src/gpu_macros.rs @@ -1144,7 +1144,3 @@ impl ImageTexture { .update_uniforms(c, [offset.x, offset.y, size.x, size.y]); } } - - - - diff --git a/murrelet_gui_derive/src/derive_gui.rs b/murrelet_gui_derive/src/derive_gui.rs index 1158572..c42cc10 100644 --- a/murrelet_gui_derive/src/derive_gui.rs +++ b/murrelet_gui_derive/src/derive_gui.rs @@ -108,9 +108,7 @@ impl GenFinal for FieldTokensGUI { #ty::make_gui() }; - FieldTokensGUI { - for_make_gui, - } + FieldTokensGUI { for_make_gui } } // e.g. TileAxisLocs::V(TileAxisVs) diff --git a/murrelet_gui_derive/src/lib.rs b/murrelet_gui_derive/src/lib.rs index 4d64f30..37b4a86 100644 --- a/murrelet_gui_derive/src/lib.rs +++ b/murrelet_gui_derive/src/lib.rs @@ -5,8 +5,8 @@ use derive_gui::FieldTokensGUI; use parser::{GenFinal, LivecodeReceiver}; use proc_macro::TokenStream; -mod parser; mod derive_gui; +mod parser; #[proc_macro_derive(MurreletGUI, attributes(murrelet_gui))] pub fn murrelet_livecode_derive_murrelet_gui(input: TokenStream) -> TokenStream { diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 651ee62..2aa9037 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -112,7 +112,11 @@ impl LazyNodeF32Inner { self.n .eval_float_with_context(&ctx) .or_else(|_| self.n.eval_int_with_context(&ctx).map(|x| x as f64)) - .or_else(|_| self.n.eval_boolean_with_context(&ctx).map(|x| if x { 1.0 } else { -1.0 })) + .or_else(|_| { + self.n + .eval_boolean_with_context(&ctx) + .map(|x| if x { 1.0 } else { -1.0 }) + }) .map(|x| x as f32) .map_err(|err| LivecodeError::EvalExpr("error evaluating lazy".to_string(), err)) } diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 24dafb5..afe0642 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -7,9 +7,7 @@ use murrelet_common::{MurreletColor, TransformVec2}; use murrelet_gui::MurreletGUI; use murrelet_livecode::lazy::ControlLazyNodeF32; use murrelet_livecode::state::{LivecodeTimingConfig, LivecodeWorldState}; -use murrelet_livecode::types::{ - AdditionalContextNode, ControlVecElement, LivecodeResult, -}; +use murrelet_livecode::types::{AdditionalContextNode, ControlVecElement, LivecodeResult}; use std::collections::HashMap; use std::fs; @@ -549,11 +547,11 @@ where ) -> LivecodeResult> { let controlconfig = ControlConfType::parse(&conf)?; // .map_err(|err| { - // if let Some(error) = err.location() { - // LivecodeError::SerdeLoc(error, err.to_string()) - // } else { - // LivecodeError::Raw(err.to_string()) - // } + // if let Some(error) = err.location() { + // LivecodeError::SerdeLoc(error, err.to_string()) + // } else { + // LivecodeError::Raw(err.to_string()) + // } // })?; Self::new_full(controlconfig, None, livecode_src, load_funcs, None) } diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index d338c78..731f54c 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -411,9 +411,7 @@ impl SvgDocCreator { defs.push(name.clone()); } - (doc, defs.into_iter().join("\n")) - } // this one's meant for svgs for pen plotters, so it drops fill styles From 9aca5119e2031f2e16b939d88fbdab3127cc8d5a Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 10 Mar 2025 19:57:43 -0400 Subject: [PATCH 016/149] wip --- murrelet_common/src/color.rs | 101 +++++++++++++++++------------ murrelet_common/src/geometry.rs | 8 +-- murrelet_draw/src/curve_drawer.rs | 6 ++ murrelet_draw/src/draw.rs | 18 +++++ murrelet_draw/src/livecodetypes.rs | 11 ++++ murrelet_gen_derive/src/parser.rs | 12 +++- 6 files changed, 107 insertions(+), 49 deletions(-) diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index ca2c3d4..14d9e46 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -5,14 +5,12 @@ use std::{ use lerpable::{IsLerpingMethod, Lerpable}; use murrelet_gui::CanMakeGUI; -use palette::{ - rgb::Rgb, FromColor, Hsva, IntoColor, LinSrgb, LinSrgba, RgbHue, Srgb, Srgba, WithAlpha, -}; +use palette::{rgb::Rgb, FromColor, Hsva, IntoColor, LinSrgb, LinSrgba, Srgb, Srgba, WithAlpha}; // hrm, color is confusing, so make a newtype around LinSrgba for all our color stuff // i need to double check i'm handling linear/not rgb right #[derive(Copy, Clone, Default)] -pub struct MurreletColor(LinSrgba); +pub struct MurreletColor([f32; 4]); // hsva impl fmt::Debug for MurreletColor { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -22,30 +20,29 @@ impl fmt::Debug for MurreletColor { } impl MurreletColor { - pub fn from_palette_linsrgba(c: LinSrgba) -> Self { - Self(c) + // 0 to 1 + pub fn alpha(&self) -> f32 { + let [_, _, _, a] = self.into_hsva_components(); + a } - pub fn black() -> Self { - Self::hsva(0.0, 0.0, 0.0, 1.0) + pub fn from_palette_linsrgba(c: LinSrgba) -> Self { + let srgba: Srgba = Srgba::from_linear(c.into_format::()); + Self::from_hsva(Hsva::from_color(srgba)) } - pub fn white() -> Self { - Self::rgba(1.0, 1.0, 1.0, 1.0) - } + pub fn from_hsva(c: Hsva) -> Self { + // let c = Srgba::from_color(c).into_color(); - pub fn transparent() -> Self { - Self::hsva(0.0, 0.0, 0.0, 0.0) - } - - // shorthand for a really bright color - pub fn hue(h: f32) -> MurreletColor { - Self::hsva(h, 1.0, 1.0, 1.0) + let (h, s, v, a) = c.into_components(); + Self([h.into_degrees() / 360.0, s, v, a]) } pub fn hsva(h: f32, s: f32, v: f32, a: f32) -> MurreletColor { - let c = Hsva::new(RgbHue::from_degrees(h * 360.0), s, v, a); - Self::from_hsva(c) + // let c = Hsva::new(RgbHue::from_degrees(h * 360.0), s, v, a); + // Self::from_hsva(c) + + Self([h, s, v, a]) } pub fn srgb(r: f32, g: f32, b: f32) -> Self { @@ -68,7 +65,7 @@ impl MurreletColor { let b = b as f32 / 255.0; let c = LinSrgba::from_components((r, g, b, 1.0)); - MurreletColor(c) + MurreletColor::from_palette_linsrgba(c) } pub fn rgb_u8_tuple(rgb: (u8, u8, u8)) -> Self { @@ -80,51 +77,69 @@ impl MurreletColor { pub fn from_srgb(c: Srgb) -> Self { let c = LinSrgb::from_color(c).with_alpha(1.0); - MurreletColor(c) - } - - pub fn from_srgb_u8(c: Srgb) -> Self { - Self::rgb_u8(c.red, c.green, c.blue) + MurreletColor::from_palette_linsrgba(c) } pub fn from_srgba(c: Srgba) -> Self { let c = LinSrgba::from_color(c); - MurreletColor(c) + MurreletColor::from_palette_linsrgba(c) } pub fn from_rgb_u8(c: Rgb) -> Self { Self::rgb_u8(c.red, c.green, c.blue) } - pub fn from_hsva(c: Hsva) -> Self { - let c = Srgba::from_color(c).into_color(); - MurreletColor(c) + pub fn from_srgb_u8(c: Srgb) -> Self { + Self::rgb_u8(c.red, c.green, c.blue) } // getting info out of it pub fn into_hsva_components(&self) -> [f32; 4] { - let srgba: Srgba = self.0.into_format().into_color(); - let hsva: Hsva = Hsva::from_color(srgba); - [ - hsva.hue.into_raw_degrees() / 360.0, - hsva.saturation, - hsva.value, - self.0.alpha, - ] + // let srgba: Srgba = self.0.into_format().into_color(); + // println!("sRGBA before HSV conversion: {:?}", srgba); + + // let hsva: Hsva = Hsva::from_color(srgba); + self.0 + } + + pub fn to_srgba(&self) -> Srgba { + let [h, s, v, a] = self.into_hsva_components(); + let hsva: Hsva = Hsva::from_components((h * 360.0, s, v, a)); + Srgba::from_color(hsva) } pub fn into_rgba_components(&self) -> [f32; 4] { - let srgb: Srgba = self.0.into_format().into_color(); - srgb.into_components().into() + self.to_srgba().into_components().into() } pub fn to_linsrgba(&self) -> LinSrgba { - self.0 + self.to_srgba().into_color() } pub fn with_alpha(&self, alpha: f32) -> MurreletColor { - Self(self.0.with_alpha(alpha)) + let [h, s, v, _a] = self.into_hsva_components(); + Self([h, s, v, alpha]) + } +} + +// getters and such that use the functions already +impl MurreletColor { + pub fn black() -> Self { + Self::hsva(0.0, 0.0, 0.0, 1.0) + } + + pub fn white() -> Self { + Self::rgba(1.0, 1.0, 1.0, 1.0) + } + + pub fn transparent() -> Self { + Self::hsva(0.0, 0.0, 0.0, 0.0) + } + + // shorthand for a really bright color + pub fn hue(h: f32) -> MurreletColor { + Self::hsva(h, 1.0, 1.0, 1.0) } pub fn to_svg_rgb(&self) -> String { @@ -157,7 +172,7 @@ impl MurreletIntoLinSrgba for Rgb { impl MurreletIntoLinSrgba for LinSrgba { fn into_murrelet_color(&self) -> MurreletColor { - MurreletColor(*self) + MurreletColor::from_palette_linsrgba(*self) } } diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index f03bd6f..7e1ea39 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -169,7 +169,7 @@ impl Angle { Angle(self.angle() * s) } - pub fn hyp_given_opp(&self, opp: Length) -> Length { + pub fn hyp_given_opp(&self, opp: L) -> Length { Length(opp.len() / self.angle().sin()) } @@ -452,7 +452,7 @@ impl CornerAngleToAngle { } // dist is how far away from the current point. left is positive (inside of angle) (i think) - pub fn corner_at_point(&self, dist: Length) -> Vec2 { + pub fn corner_at_point(&self, dist: L) -> Vec2 { // mid-way between the two angles, and then go perpindicular at some point let p = if dist.len() < 0.0 { @@ -544,11 +544,11 @@ pub struct LineFromVecAndLen { length: Length, } impl LineFromVecAndLen { - pub fn new(start: Vec2, angle: Angle, length: Length) -> Self { + pub fn new(start: Vec2, angle: Angle, length: L) -> Self { Self { start, angle, - length, + length: length.to_length(), } } diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 3ff7896..d707305 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -229,6 +229,12 @@ impl CurveArc { self.start_angle().perp_to_right() } } + + pub fn set_radius(&self, radius: f32) -> CurveArc { + let mut m = self.clone(); + m.radius = radius; + m + } } #[derive(Debug, Clone, Livecode, Lerpable)] diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index 6d68ed7..53bc3f9 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -129,6 +129,17 @@ impl MurreletStyle { } } + pub fn new_outline_color(color: MurreletColor, stroke_weight: f32) -> MurreletStyle { + MurreletStyle { + closed: true, + filled: false, + color: MurreletColorStyle::Color(color), + stroke_weight, + stroke_color: MurreletColorStyle::Color(color), + ..Default::default() + } + } + pub fn new_outline() -> MurreletStyle { MurreletStyle { closed: true, @@ -188,6 +199,13 @@ impl MurreletStyle { } } + pub fn with_fill(&self) -> MurreletStyle { + MurreletStyle { + filled: true, + ..*self + } + } + pub fn with_no_fill(&self) -> MurreletStyle { MurreletStyle { filled: false, diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs index 003d43c..24a9b8f 100644 --- a/murrelet_draw/src/livecodetypes.rs +++ b/murrelet_draw/src/livecodetypes.rs @@ -1,6 +1,8 @@ // place to put newtypes for livecode pub mod anglepi { + use std::ops::Sub; + use lerpable::Lerpable; use murrelet_common::{Angle, AnglePi, IsAngle}; use murrelet_gui::CanMakeGUI; @@ -41,4 +43,13 @@ pub mod anglepi { murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Angle) } } + + impl Sub for LivecodeAnglePi { + type Output = Self; + + fn sub(self, other: Self) -> Self::Output { + let new_angle = self.0 - other.0; + LivecodeAnglePi(new_angle) + } + } } diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index ffe73ce..775a8cc 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -196,6 +196,7 @@ impl LivecodeFieldReceiver { #[derive(Debug, Clone, FromMeta)] pub enum RandMethod { + Default, Recurse, BoolBinomial { pct: f32, // true @@ -287,7 +288,6 @@ impl RandMethod { (for_rn_count, for_make_gen) } RandMethod::F32Fixed { val } => (quote! { 0 }, quote! { #val #maybe_as }), - // box muller copy-pasta RandMethod::F32Normal { mu, sigma } => { let for_rn_count = quote! { 2 }; let for_make_gen = quote! { { @@ -303,7 +303,6 @@ impl RandMethod { } }; (for_rn_count, for_make_gen) } - RandMethod::Vec2UniformGrid { x, y, @@ -382,6 +381,15 @@ impl RandMethod { result.0.to_string() } }; + (for_rn_count, for_make_gen) + } + RandMethod::Default => { + let for_rn_count = quote! { 0 }; + + let for_make_gen = quote! { { + Default::default() + } }; + (for_rn_count, for_make_gen) } } From 41fa9ec07b4ad03f208e1e55efe72493ec6bd1f4 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 11 Mar 2025 18:17:20 -0400 Subject: [PATCH 017/149] wip --- murrelet_draw/src/draw.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index 53bc3f9..8e6aed5 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -78,6 +78,10 @@ impl MurreletColorStyle { MurreletColorStyle::SvgFill(c) => MurreletColorStyle::SvgFill(c.with_alpha(alpha)), } } + + fn hue(hue: f32) -> MurreletColorStyle { + MurreletColorStyle::Color(MurreletColor::hsva(hue, 1.0, 1.0, 1.0)) + } } pub enum MurreletDrawPlan { @@ -170,6 +174,22 @@ impl MurreletStyle { } } + pub fn white_font() -> Self { + MurreletStyle { + color: MurreletColorStyle::white(), + stroke_weight: 24.0, + ..Default::default() + } + } + + pub fn red_font() -> Self { + MurreletStyle { + color: MurreletColorStyle::hue(0.0), + stroke_weight: 24.0, + ..Default::default() + } + } + pub fn with_color(&self, c: MurreletColor) -> MurreletStyle { MurreletStyle { color: MurreletColorStyle::color(c), From 207a810d6ab61bc74ab8599aea84eaf8695da2f7 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 21 Mar 2025 22:38:07 -0400 Subject: [PATCH 018/149] add rn_names, hopefully more correct than not --- murrelet_common/src/geometry.rs | 24 +++ murrelet_common/src/idx.rs | 5 + murrelet_common/src/transform.rs | 4 + murrelet_draw/src/style.rs | 2 +- murrelet_gen/examples/tests.rs | 30 ++- murrelet_gen/src/lib.rs | 56 +---- murrelet_gen_derive/src/derive_gen.rs | 149 +++++++++----- murrelet_gen_derive/src/gen_methods.rs | 234 +++++++++++++++++++++ murrelet_gen_derive/src/lib.rs | 3 + murrelet_gen_derive/src/parser.rs | 270 ++++--------------------- murrelet_src_osc/src/osc.rs | 20 +- murrelet_svg/src/svg.rs | 6 +- 12 files changed, 461 insertions(+), 342 deletions(-) create mode 100644 murrelet_gen_derive/src/gen_methods.rs diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 7e1ea39..d768c70 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -487,6 +487,30 @@ impl PrevCurrNextVec2 { let next_to_curr = self.next - self.curr; Angle::new(curr_to_prev.angle_to(next_to_curr)) } + + pub fn tri_contains(&self, other_v: &Vec2) -> bool { + // https://nils-olovsson.se/articles/ear_clipping_triangulation/ + let v0 = self.next - self.prev; + let v1 = self.curr - self.prev; + let v2 = *other_v - self.prev; + + let dot00 = v0.dot(v0); + let dot01 = v0.dot(v1); + let dot02 = v0.dot(v2); + let dot11 = v1.dot(v1); + let dot12 = v1.dot(v2); + + let denom = dot00 * dot11 - dot01 * dot01; + if denom.abs() < 1e-10 { + return true; + } + + let inv_denom = 1.0 / denom; + let u = (dot11 * dot02 - dot01 * dot12) * inv_denom; + let v = (dot00 * dot12 - dot01 * dot02) * inv_denom; + + (u >= 0.0) && (v >= 0.0) && (u + v < 1.0) + } } #[derive(Copy, Clone, Debug)] diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index fbaacee..bdb8dba 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -158,6 +158,11 @@ impl IdxInRange2d { IdxInRange2d { i, j } } + pub fn new_from_single_idx(i: IdxInRange) -> IdxInRange2d { + let j = IdxInRange::new(0, 1); + IdxInRange2d { i, j } + } + pub fn pct(&self) -> Vec2 { vec2(self.i.pct(), self.j.pct()) } diff --git a/murrelet_common/src/transform.rs b/murrelet_common/src/transform.rs index ca1e98f..92a7142 100644 --- a/murrelet_common/src/transform.rs +++ b/murrelet_common/src/transform.rs @@ -168,6 +168,10 @@ impl SimpleTransform2d { Self(v) } + pub fn noop() -> Self { + Self(vec![]) + } + pub fn steps(&self) -> &Vec { &self.0 } diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 8a28323..d5b0a56 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -504,7 +504,7 @@ impl MurreletPath { pub fn transform_with(&self, t: &T) -> Self { match self { MurreletPath::Polyline(x) => MurreletPath::Polyline(x.transform_with(t)), - MurreletPath::Curve(_) => todo!(), + MurreletPath::Curve(mc) => todo!(), MurreletPath::Svg(_) => todo!(), // i'm not sure how i want to handle this yet } } diff --git a/murrelet_gen/examples/tests.rs b/murrelet_gen/examples/tests.rs index 16a94be..089c646 100644 --- a/murrelet_gen/examples/tests.rs +++ b/murrelet_gen/examples/tests.rs @@ -2,6 +2,7 @@ use glam::Vec2; use murrelet_common::MurreletColor; use murrelet_gen::{CanSampleFromDist, MurreletGen}; +// #[derive(Debug, MurreletGen)] #[derive(Debug, MurreletGen)] pub struct BasicTypes { #[murrelet_gen(method(f32_uniform(start = 0.0, end = 1.0)))] @@ -20,6 +21,10 @@ pub struct BasicTypes { c_number: u64, #[murrelet_gen(method(f32_uniform(start = 0.0, end = 30.0)))] d_number: i32, + #[murrelet_gen(method(f32_uniform_pos_neg(start = 10.0, end = 30.0)))] + uniform_pos_neg: f32, + #[murrelet_gen(method(f32_normal(mu = 10.0, sigma = 30.0)))] + normal: f32, #[murrelet_gen(method(bool_binomial(pct = 0.3)))] bool: bool, #[murrelet_gen(method(vec2_uniform_grid(x = 0.0, y = 0.0, width = 100.0, height = 100.0)))] @@ -70,12 +75,31 @@ enum EnumTest { fn main() { println!("BasicTypes::rn_count() {:?}", BasicTypes::rn_count()); + + println!("BasicTypes::rn_names() {:?}", BasicTypes::rn_names()); + println!( - "OverridesAndRecursive::rn_count() {:?}", - OverridesAndRecursive::rn_count() + "OverridesAndRecursive::rn_names() {:?}", + OverridesAndRecursive::rn_names() ); - assert_eq!(BasicTypes::rn_count(), 33); + println!("EnumTest::rn_names() {:?}", EnumTest::rn_names()); + + assert_eq!(BasicTypes::rn_count(), BasicTypes::rn_names().len()); + assert_eq!(Tiny::rn_count(), Tiny::rn_names().len()); + assert_eq!( + OverridesAndRecursive::rn_count(), + OverridesAndRecursive::rn_names().len() + ); + + assert_eq!(EnumTest::rn_count(), EnumTest::rn_names().len()); + + // println!( + // "OverridesAndRecursive::rn_count() {:?}", + // OverridesAndRecursive::rn_count() + // ); + + assert_eq!(BasicTypes::rn_count(), 37); println!( "OverridesAndRecursive::gen_from_seed(42) {:?}", diff --git a/murrelet_gen/src/lib.rs b/murrelet_gen/src/lib.rs index cb26af4..042ea90 100644 --- a/murrelet_gen/src/lib.rs +++ b/murrelet_gen/src/lib.rs @@ -7,8 +7,9 @@ use rand::SeedableRng; pub trait CanSampleFromDist: Sized { // returns the right number of rn needed to generate this. fn rn_count() -> usize; + fn rn_names() -> Vec; - // given rn_count, it'll generate! + // given rn of length ^, it'll generate! fn sample_dist(rn: &[f32], start_idx: usize) -> Self; // usually you'll call this one @@ -24,50 +25,9 @@ pub trait CanSampleFromDist: Sized { } } -// macro_rules! impl_can_make_gui_for_num { -// ($ty:ty) => { -// impl CanGenerate for $ty { -// fn gen(&self, seed: u64) -> Self; -// } -// }; -// } - -// impl_can_make_gui_for_num!(f32); -// impl_can_make_gui_for_num!(f64); -// impl_can_make_gui_for_num!(u32); -// impl_can_make_gui_for_num!(u64); -// impl_can_make_gui_for_num!(i32); -// impl_can_make_gui_for_num!(i64); -// impl_can_make_gui_for_num!(usize); - -// impl CanMakeGUI for Vec { -// fn make_gui() -> MurreletGUISchema { -// MurreletGUISchema::List(Box::new(T::make_gui())) -// } -// } - -// impl CanMakeGUI for String { -// fn make_gui() -> MurreletGUISchema { -// MurreletGUISchema::Skip -// } -// } - -// impl CanMakeGUI for bool { -// fn make_gui() -> MurreletGUISchema { -// MurreletGUISchema::Val(ValueGUI::Bool) -// } -// } - -// #[cfg(feature = "glam")] -// impl CanMakeGUI for glam::Vec2 { -// fn make_gui() -> MurreletGUISchema { -// MurreletGUISchema::Val(ValueGUI::Vec2) -// } -// } - -// #[cfg(feature = "glam")] -// impl CanMakeGUI for glam::Vec3 { -// fn make_gui() -> MurreletGUISchema { -// MurreletGUISchema::Val(ValueGUI::Vec3) -// } -// } +pub fn prefix_field_names(prefix: String, names: Vec) -> Vec { + names + .into_iter() + .map(|s| format!("{}.{}", prefix, s)) + .collect() +} diff --git a/murrelet_gen_derive/src/derive_gen.rs b/murrelet_gen_derive/src/derive_gen.rs index 5afc8b1..a2b09b0 100644 --- a/murrelet_gen_derive/src/derive_gen.rs +++ b/murrelet_gen_derive/src/derive_gen.rs @@ -1,10 +1,20 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use crate::parser::*; +use crate::{gen_methods::GenMethod, parser::*}; + +fn str_to_tokens(s: &str) -> TokenStream2 { + let lit = syn::LitStr::new(s, proc_macro2::Span::call_site()); + quote! { (#lit).to_string() } +} + +fn strs_to_tokens(s: Vec) -> Vec { + s.iter().map(|x| str_to_tokens(x)).collect() +} pub(crate) struct FieldTokensGen { pub(crate) for_rn_count: TokenStream2, + pub(crate) for_rn_names: TokenStream2, pub(crate) for_make_gen: TokenStream2, } impl GenFinal for FieldTokensGen { @@ -16,6 +26,7 @@ impl GenFinal for FieldTokensGen { let name = idents.name; let for_rn_count = variants.iter().map(|x| x.for_rn_count.clone()); + let for_rn_names = variants.iter().map(|x| x.for_rn_names.clone()); let for_make_gen = variants.iter().map(|x| x.for_make_gen.clone()); quote! { @@ -24,6 +35,10 @@ impl GenFinal for FieldTokensGen { #(#for_rn_count+)* } + fn rn_names() -> Vec { + #(#for_rn_names+)* + } + fn sample_dist(rn: &[f32], start_idx: usize) -> Self { Self(#(#for_make_gen,)*) } @@ -35,6 +50,7 @@ impl GenFinal for FieldTokensGen { let name = idents.name; let for_rn_count = variants.iter().map(|x| x.for_rn_count.clone()); + let for_rn_names = variants.iter().map(|x| x.for_rn_names.clone()); let for_make_gen = variants.iter().map(|x| x.for_make_gen.clone()); quote! { @@ -45,6 +61,12 @@ impl GenFinal for FieldTokensGen { ].iter().sum() } + fn rn_names() -> Vec { + vec![ + #(#for_rn_names,)* + ].concat() + } + fn sample_dist(rn: &[f32], start_idx: usize) -> Self { let mut rn_start_idx = start_idx; @@ -63,34 +85,30 @@ impl GenFinal for FieldTokensGen { ) -> TokenStream2 { let name = idents.name; let for_rn_count = variants.iter().map(|x| x.for_rn_count.clone()); - - // let mut cumulative_probabilities = vec![]; - - // let mut running_total = 0.0; - // for receiver in variant_receiver.iter() { - // let weight = receiver.weight; - // running_total += weight; - // cumulative_probabilities.push(running_total); - // } - - // // normalize - // for i in 0..cumulative_probabilities.len() { - // cumulative_probabilities[i] /= running_total; - // } - - // let mut q = vec![]; - // for (i, variant) in variants.iter().enumerate() { - // let create_variant = &variant.for_make_gen; - // let prob_upper_bound = cumulative_probabilities[i]; - // q.push(quote! { - // x if x < #prob_upper_bound => #create_variant - // }); - // } + let for_rn_names_all = variants.iter().map(|x| x.for_rn_names.clone()); let mut weights = vec![]; + let mut for_rn_names: Vec = vec![]; - for (variant, receiver) in variants.iter().zip(variant_receiver.iter()) { + for ((variant, receiver), names) in variants + .iter() + .zip(variant_receiver.iter()) + .zip(for_rn_names_all) + { let create_variant = &variant.for_make_gen; + let receiver_name = receiver.ident.to_string(); + + for_rn_names.push(quote! { + murrelet_gen::prefix_field_names(#receiver_name.to_string(), vec![vec!["[weight]".to_string()], #names].concat()) + }); + + // for_rn_names.extend( + // variant + // .for_rn_names + // .clone() + // .into_iter() + // .map(|x| quote!( murrelet_gen::prefix_field_names(#x, #receiver_name))), + // ); let weight = receiver.weight; // hrm, might want to use closures if it's expensive @@ -114,6 +132,12 @@ impl GenFinal for FieldTokensGen { ].iter().sum::() + #number_of_choices } + fn rn_names() -> Vec { + vec![ + #(#for_rn_names,)* + ].concat() + } + fn sample_dist(rn: &[f32], start_idx: usize) -> Self { let mut rn_start_idx = start_idx; @@ -131,32 +155,20 @@ impl GenFinal for FieldTokensGen { } } - fn from_override_enum(func: &str, rn_count: usize) -> FieldTokensGen { - let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); - - let for_rn_count = quote! { #rn_count }; - - let for_make_gen = quote! { - #method() - }; - - FieldTokensGen { - for_rn_count, - for_make_gen, - } - } - fn from_newtype_struct(idents: StructIdents, _parent_ident: syn::Ident) -> FieldTokensGen { let ty = convert_vec_type(&idents.data.ty); let for_rn_count = quote! { #ty::rn_count() }; + let for_rn_names = quote! { #ty::rn_names() }; + let for_make_gen = quote! { self.0.sample_dist(rn, rn_start_idx) }; FieldTokensGen { for_rn_count, + for_rn_names, for_make_gen, } } @@ -169,6 +181,8 @@ impl GenFinal for FieldTokensGen { let for_rn_count = quote! { #ty::rn_count() }; + let for_rn_names = quote! { #ty::rn_names() }; + // hm, i'm not sure that the method in the enum is actually used let for_make_gen = quote! { { @@ -181,6 +195,7 @@ impl GenFinal for FieldTokensGen { FieldTokensGen { for_rn_count, + for_rn_names, for_make_gen, } } @@ -193,10 +208,13 @@ impl GenFinal for FieldTokensGen { // just the one-hot encoding let for_rn_count = quote! { 0 }; + let for_rn_names = quote! { vec![] }; + let for_make_gen = quote! { #name::#variant_ident }; FieldTokensGen { for_rn_count, + for_rn_names, for_make_gen, } } @@ -207,46 +225,67 @@ impl GenFinal for FieldTokensGen { let ty = idents.data.ty; let for_rn_count = quote! { 0 }; + let for_rn_names = quote! { vec![] }; + let for_make_gen = quote! { #field_name: #ty::default() }; FieldTokensGen { for_rn_count, for_make_gen, + for_rn_names, } } // f32, Vec2, etc - fn from_type_struct(idents: StructIdents, method: &RandMethod) -> FieldTokensGen { + fn from_type_struct(idents: StructIdents, method: &GenMethod) -> FieldTokensGen { let field_name = idents.data.ident.unwrap(); + let field_name_str = field_name.to_string(); let ty = idents.data.ty; - let (for_rn_count, for_make_gen) = method.to_methods(ty, true); + let (for_rn_count, for_rn_names, for_make_gen) = method.to_methods(ty, true); FieldTokensGen { for_make_gen: quote! { #field_name: #for_make_gen }, + for_rn_names: quote! { murrelet_gen::prefix_field_names(#field_name_str.to_string(), #for_rn_names)}, for_rn_count, } } - fn from_type_recurse(idents: StructIdents, outer: &RandMethod, inner: &RandMethod) -> Self { + fn from_type_recurse(idents: StructIdents, outer: &GenMethod, inner: &GenMethod) -> Self { let field_name = idents.data.ident.unwrap(); let ty = idents.data.ty; - let (for_rn_count, for_make_gen) = match outer { - RandMethod::VecLength { min, max } => { + let (for_rn_count, for_rn_names, for_make_gen) = match outer { + GenMethod::VecLength { min, max } => { let inside_type = nested_ident(&ty); let i = inside_type[1].clone(); let inside_type_val: syn::Type = syn::parse_quote! { #i }; - // (for_rn_count, for_make_gen) - let (for_rn_count_per_item, for_make_gen_per_item) = + let (for_rn_count_per_item, for_rn_names_per_item, for_make_gen_per_item) = inner.to_methods(inside_type_val, false); let for_rn_count: TokenStream2 = quote! { #for_rn_count_per_item * #max + 1 }; + let for_rn_names_all = (0..*max).into_iter().map(|x| { + let i_name = x.to_string(); + quote! { murrelet_gen::prefix_field_names(#i_name.to_string(), #for_rn_names_per_item) } + }); + + let field_name_str = field_name.to_string(); + + let for_rn_names = quote! { + murrelet_gen::prefix_field_names( + #field_name_str.to_string(), + vec![ + vec!["[len]".to_string()], + #(#for_rn_names_all,)* + ].concat() + ) + }; + // in this case, we _don't_ want one-hot, because it actually does make // sense to interpolate between say, 3 and 6. // i want to add extra indicators for everything between min and max @@ -270,28 +309,40 @@ impl GenFinal for FieldTokensGen { v }}; - (for_rn_count, for_make_gen) + (for_rn_count, for_rn_names, for_make_gen) } _ => unreachable!("not expecting an inner without a recursive outer"), }; Self { for_rn_count, + for_rn_names, for_make_gen: quote! { #field_name: #for_make_gen }, } } - fn from_override_struct(idents: StructIdents, func: &str, rn_count: usize) -> FieldTokensGen { + fn from_override_struct( + idents: StructIdents, + func: &str, + rn_names: Vec, + rn_count: usize, + ) -> FieldTokensGen { let field_name = idents.data.ident.unwrap(); let for_rn_count = quote! { #rn_count }; + let strs = strs_to_tokens(rn_names); + let for_rn_names = quote! { + #(#strs,)* + }; + let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); let for_make_gen = quote! { #field_name: #method() }; FieldTokensGen { for_rn_count, + for_rn_names, for_make_gen, } } diff --git a/murrelet_gen_derive/src/gen_methods.rs b/murrelet_gen_derive/src/gen_methods.rs new file mode 100644 index 0000000..5ad782c --- /dev/null +++ b/murrelet_gen_derive/src/gen_methods.rs @@ -0,0 +1,234 @@ +use darling::FromMeta; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use std::collections::HashMap; + +#[derive(Debug, Clone, FromMeta)] +pub enum GenMethod { + Default, + Recurse, + BoolBinomial { + pct: f32, // pct that is true + }, + F32Uniform { + start: syn::Expr, + end: syn::Expr, + }, + F32UniformPosNeg { + // includes between start and end as negative and positive + start: f32, + end: f32, + }, + F32Normal { + mu: syn::Expr, + sigma: syn::Expr, + }, + F32Fixed { + val: syn::Expr, + }, + Vec2UniformGrid { + // chooses random points + x: syn::Expr, + y: syn::Expr, + width: f32, + height: f32, + }, + Vec2Circle { + // selects random points within a circle + x: syn::Expr, + y: syn::Expr, + radius: f32, + }, + VecLength { + // determines how long the vector will be + min: usize, + max: usize, + }, + ColorNormal, // samples h, s, and v values + ColorTransparency, // same as ColorNormal, plus alpha + StringChoice { + choices: HashMap, // string mapped to its weight + }, +} +impl GenMethod { + pub(crate) fn to_methods( + &self, + ty: syn::Type, + convert: bool, + ) -> (TokenStream2, TokenStream2, TokenStream2) { + let maybe_as = if convert { + quote! { as #ty } + } else { + quote! {} + }; + + match self { + GenMethod::Recurse => { + let for_rn_count = quote! { #ty::rn_count() }; + let for_rn_names = quote! { #ty::rn_names() }; + let for_make_gen = quote! {{ + let r = #ty::sample_dist(rn, rn_start_idx); + rn_start_idx += #for_rn_count; + r + }}; + + (for_rn_count, for_rn_names, for_make_gen) + } + GenMethod::BoolBinomial { pct } => { + let for_rn_count = quote! { 1 }; + let for_rn_names = quote! { vec!["pct".to_string()] }; + let for_make_gen = quote! { { + let result = rn[rn_start_idx] > #pct; + rn_start_idx += #for_rn_count; + result + } }; + + (for_rn_count, for_rn_names, for_make_gen) + } + GenMethod::F32Uniform { start, end } => { + let for_rn_count = quote! { 1 }; + let for_rn_names = quote! { vec!["uniform".to_string()] }; + let for_make_gen = quote! { { + let result = rn[rn_start_idx] * (#end - #start) + #start; + rn_start_idx += #for_rn_count; + result #maybe_as + } }; + + (for_rn_count, for_rn_names, for_make_gen) + } + GenMethod::F32UniformPosNeg { start, end } => { + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec!["uniform".to_string(), "sign".to_string()] }; + let for_make_gen = quote! { { + let sgn = if(rn[rn_start_idx] > 0.5) { 1.0 } else { -1.0 }; + let result = rn[rn_start_idx + 1] * (#end - #start) + #start; + rn_start_idx += #for_rn_count; + (sgn * result) #maybe_as + } }; + + (for_rn_count, for_rn_names, for_make_gen) + } + GenMethod::F32Fixed { val } => { + (quote! { 0 }, quote! {vec![]}, quote! { #val #maybe_as }) + } + GenMethod::F32Normal { mu, sigma } => { + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec![ "BoxMuller1".to_string(), "BoxMuller2".to_string()] }; + let for_make_gen = quote! { { + // avoid nans, make sure u1 is positive and non-zero + let u1 = rn[rn_start_idx].clamp(std::f32::MIN_POSITIVE, 1.0); + let u2 = rn[rn_start_idx + 1].clamp(0.0, 1.0); + rn_start_idx += 2; + + let r = (-2.0 * u1.ln()).sqrt(); + let theta = 2.0 * std::f32::consts::PI * u2; + + #mu + #sigma * r * theta.cos() #maybe_as + } }; + (for_rn_count, for_rn_names, for_make_gen) + } + GenMethod::Vec2UniformGrid { + x, + y, + width, + height, + } => { + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec![ "x".to_string(), "y".to_string()] }; + let for_make_gen = quote! {{ + let width = rn[rn_start_idx] * #width; + let height = rn[rn_start_idx + 1] * #height; + + rn_start_idx += #for_rn_count; + + glam::vec2(#x, #y) - 0.5 * glam::vec2(#width, #height) + glam::vec2(width, height) + }}; + + (for_rn_count, for_rn_names, for_make_gen) + } + GenMethod::Vec2Circle { x, y, radius } => { + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec![ "theta".to_string(), "rad".to_string()] }; + + let for_make_gen = quote! {{ + let angle = rn[rn_start_idx] * 2.0 * std::f32::consts::PI; + let dist = rn[rn_start_idx + 1]; // sqrt it to even out the sampling + rn_start_idx += #for_rn_count; + glam::vec2(#x, #y) + glam::vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() + }}; + + (for_rn_count, for_rn_names, for_make_gen) + } + GenMethod::ColorNormal => { + let for_rn_count = quote! { 3 }; + + let for_rn_names = quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string()] }; + + let for_make_gen = quote! {{ + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, 1.0) + }}; + + (for_rn_count, for_rn_names, for_make_gen) + } + GenMethod::ColorTransparency => { + let for_rn_count = quote! { 4 }; + + let for_rn_names = quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string(), "alpha".to_string()] }; + + let for_make_gen = quote! {{ + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + let a = rn[rn_start_idx + 3]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, a) + }}; + + (for_rn_count, for_rn_names, for_make_gen) + } + GenMethod::VecLength { .. } => { + // this is handled in the vec parser + unreachable!("this location of veclength isn't supported yet!") + } + GenMethod::StringChoice { choices } => { + let one_hot = choices.len(); + let for_rn_count = quote! { #one_hot }; + + // let for_rn_names = quote! { vec![ "hue", "sat", "val", "alpha"] }; + let rn_names = choices.iter().map(|(key, _)| quote! { #key.to_string() }); + let for_rn_names = quote! { vec![#(#rn_names,)*] }; + + let weighted_rns = choices + .iter() + .enumerate() + .map(|(i, (key, weight))| { + quote! { (#key.clone(), #weight * rn[rn_start_idx + #i]) } + }) + .collect::>(); + + let for_make_gen = quote! { { + let result = vec![#(#weighted_rns,)*].into_iter().max_by(|a, b| a.1.partial_cmp(&b.1).unwrap()).expect("empty string choices??"); + rn_start_idx += #for_rn_count; + result.0.to_string() + } }; + + (for_rn_count, for_rn_names, for_make_gen) + } + GenMethod::Default => { + let for_rn_count = quote! { 0 }; + + let for_rn_names = quote! { vec![] }; + + let for_make_gen = quote! { { + Default::default() + } }; + + (for_rn_count, for_rn_names, for_make_gen) + } + } + } +} diff --git a/murrelet_gen_derive/src/lib.rs b/murrelet_gen_derive/src/lib.rs index 7be123e..15b4d48 100644 --- a/murrelet_gen_derive/src/lib.rs +++ b/murrelet_gen_derive/src/lib.rs @@ -6,8 +6,11 @@ use parser::{GenFinal, LivecodeReceiver}; use proc_macro::TokenStream; mod derive_gen; +mod gen_methods; mod parser; +use gen_methods::GenMethod; + #[proc_macro_derive(MurreletGen, attributes(murrelet_gen))] pub fn murrelet_livecode_derive_murrelet_gen(input: TokenStream) -> TokenStream { let ast = syn::parse_macro_input!(input as syn::DeriveInput); diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index 775a8cc..dc9132c 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -1,8 +1,8 @@ -use std::collections::HashMap; - use darling::{ast, FromDeriveInput, FromField, FromMeta, FromVariant}; use proc_macro2::TokenStream as TokenStream2; -use quote::quote; +use syn::LitStr; + +use crate::GenMethod; #[derive(Debug)] pub(crate) struct ParsedFieldIdent { @@ -18,11 +18,11 @@ where fn from_unnamed_enum(idents: EnumIdents) -> Self; fn from_unit_enum(idents: EnumIdents) -> Self; fn from_noop_struct(idents: StructIdents) -> Self; - fn from_type_struct(idents: StructIdents, how_to_control_this_type: &RandMethod) -> Self; + fn from_type_struct(idents: StructIdents, how_to_control_this_type: &GenMethod) -> Self; fn from_type_recurse( idents: StructIdents, - how_to_control_outer_type: &RandMethod, - how_to_control_inner_type: &RandMethod, + how_to_control_outer_type: &GenMethod, + how_to_control_inner_type: &GenMethod, ) -> Self; fn from_ast(ast_receiver: LivecodeReceiver) -> TokenStream2 { @@ -35,8 +35,12 @@ where ast::Data::Struct(_) => Self::make_struct(&ast_receiver), } } - fn from_override_struct(idents: StructIdents, func: &str, rn_count: usize) -> Self; - fn from_override_enum(func: &str, rn_count: usize) -> Self; + fn from_override_struct( + idents: StructIdents, + func: &str, + rn_names: Vec, + rn_count: usize, + ) -> Self; fn make_enum_final( idents: ParsedFieldIdent, @@ -63,10 +67,9 @@ where }; match field.how_to_control_this() { - HowToControlThis::Override(func, count) => { - Self::from_override_struct(idents, &func, count) + HowToControlThis::Override(func, names, count) => { + Self::from_override_struct(idents, &func, names, count) } - HowToControlThis::Normal => panic!("should have an annotation"), HowToControlThis::Type(how_to_control_this_type) => { Self::from_type_struct(idents, &how_to_control_this_type) } @@ -130,24 +133,21 @@ where }; match field.how_to_control_this() { - HowToControlThis::Normal => { - #[cfg(feature = "debug_logging")] - log::info!("-> from_newtype_struct"); - Self::from_newtype_struct(idents, name.clone()) - } HowToControlThis::Default => { #[cfg(feature = "debug_logging")] log::info!("-> from_noop_struct"); Self::from_noop_struct(idents) } - HowToControlThis::Type(how_to_control_this_type) => { - Self::from_type_struct(idents, &how_to_control_this_type) + HowToControlThis::Type(_how_to_control_this_type) => { + // Self::from_type_struct(idents, &how_to_control_this_type) + Self::from_newtype_struct(idents, name.clone()) } - HowToControlThis::Recurse(outer, inner) => { - Self::from_type_recurse(idents, &outer, &inner) + HowToControlThis::Recurse(_outer, _inner) => { + // Self::from_type_recurse(idents, &outer, &inner) + Self::from_newtype_struct(idents, name.clone()) } - HowToControlThis::Override(func, count) => { - Self::from_override_struct(idents, &func, count) + HowToControlThis::Override(func, labels, count) => { + Self::from_override_struct(idents, &func, labels, count) } } }) @@ -162,7 +162,7 @@ where #[derive(Debug, FromMeta, Clone)] pub struct OverrideFn { func: String, - count: usize, + labels: Vec, // this must be the same length as the usages in the func } #[derive(Debug, FromField, Clone)] @@ -172,20 +172,23 @@ pub(crate) struct LivecodeFieldReceiver { pub(crate) ty: syn::Type, #[darling(default, rename = "override")] pub(crate) override_fn: Option, - pub(crate) method: RandMethod, + pub(crate) method: GenMethod, #[darling(default)] - pub(crate) method_inner: Option, + pub(crate) method_inner: Option, } impl LivecodeFieldReceiver { fn how_to_control_this(&self) -> HowToControlThis { - if let Some(OverrideFn { func, count }) = &self.override_fn { + if let Some(OverrideFn { func, labels }) = &self.override_fn { match func.as_str() { "default" => HowToControlThis::Default, - _ => HowToControlThis::Override(func.clone(), *count), + _ => { + let label_str: Vec = labels.iter().map(|lit| lit.value()).collect(); + HowToControlThis::Override(func.clone(), label_str, labels.len()) + } } } else if let Some(r) = &self.method_inner { HowToControlThis::Recurse(self.method.clone(), r.clone()) - } else if matches!(self.method, RandMethod::VecLength { .. }) { + } else if matches!(self.method, GenMethod::VecLength { .. }) { panic!("vec missing inner") // HowToControlThis::Recurse(self.method.clone(), None) } else { @@ -194,208 +197,6 @@ impl LivecodeFieldReceiver { } } -#[derive(Debug, Clone, FromMeta)] -pub enum RandMethod { - Default, - Recurse, - BoolBinomial { - pct: f32, // true - }, - F32Uniform { - start: syn::Expr, - end: syn::Expr, - }, - F32UniformPosNeg { - start: f32, - end: f32, - }, - F32Normal { - mu: syn::Expr, - sigma: syn::Expr, - }, - F32Fixed { - val: syn::Expr, - }, - Vec2UniformGrid { - x: syn::Expr, - y: syn::Expr, - width: f32, - height: f32, - }, - Vec2Circle { - x: syn::Expr, - y: syn::Expr, - radius: f32, - }, - VecLength { - min: usize, - max: usize, - }, - ColorNormal, - ColorTransparency, - StringChoice { - choices: HashMap, - }, -} -impl RandMethod { - pub(crate) fn to_methods(&self, ty: syn::Type, convert: bool) -> (TokenStream2, TokenStream2) { - let maybe_as = if convert { - quote! { as #ty } - } else { - quote! {} - }; - - match self { - RandMethod::Recurse => { - let for_rn_count = quote! { #ty::rn_count() }; - let for_make_gen = quote! {{ - let r = #ty::sample_dist(rn, rn_start_idx); - rn_start_idx += #for_rn_count; - r - }}; - - (for_rn_count, for_make_gen) - } - RandMethod::BoolBinomial { pct } => { - let for_rn_count = quote! { 1 }; - let for_make_gen = quote! { { - let result = rn[rn_start_idx] > #pct; - rn_start_idx += #for_rn_count; - result - } }; - - (for_rn_count, for_make_gen) - } - RandMethod::F32Uniform { start, end } => { - let for_rn_count = quote! { 1 }; - let for_make_gen = quote! { { - let result = rn[rn_start_idx] * (#end - #start) + #start; - rn_start_idx += #for_rn_count; - result #maybe_as - } }; - - (for_rn_count, for_make_gen) - } - RandMethod::F32UniformPosNeg { start, end } => { - let for_rn_count = quote! { 2 }; - let for_make_gen = quote! { { - let sgn = if(rn[rn_start_idx] > 0.5) { 1.0 } else { -1.0 }; - let result = rn[rn_start_idx + 1] * (#end - #start) + #start; - rn_start_idx += #for_rn_count; - (sgn * result) #maybe_as - } }; - - (for_rn_count, for_make_gen) - } - RandMethod::F32Fixed { val } => (quote! { 0 }, quote! { #val #maybe_as }), - RandMethod::F32Normal { mu, sigma } => { - let for_rn_count = quote! { 2 }; - let for_make_gen = quote! { { - // avoid nans, make sure u1 is positive and non-zero - let u1 = rn[rn_start_idx].clamp(std::f32::MIN_POSITIVE, 1.0); - let u2 = rn[rn_start_idx + 1].clamp(0.0, 1.0); - rn_start_idx += 2; - - let r = (-2.0 * u1.ln()).sqrt(); - let theta = 2.0 * std::f32::consts::PI * u2; - - #mu + #sigma * r * theta.cos() #maybe_as - } }; - (for_rn_count, for_make_gen) - } - RandMethod::Vec2UniformGrid { - x, - y, - width, - height, - } => { - let for_rn_count = quote! { 2 }; - let for_make_gen = quote! {{ - let width = rn[rn_start_idx] * #width; - let height = rn[rn_start_idx + 1] * #height; - - rn_start_idx += #for_rn_count; - - glam::vec2(#x, #y) - 0.5 * glam::vec2(#width, #height) + glam::vec2(width, height) - }}; - - (for_rn_count, for_make_gen) - } - RandMethod::Vec2Circle { x, y, radius } => { - let for_rn_count = quote! { 2 }; - - let for_make_gen = quote! {{ - let angle = rn[rn_start_idx] * 2.0 * std::f32::consts::PI; - let dist = rn[rn_start_idx + 1]; // sqrt it to even out the sampling - rn_start_idx += #for_rn_count; - glam::vec2(#x, #y) + glam::vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() - }}; - - (for_rn_count, for_make_gen) - } - RandMethod::ColorNormal => { - let for_rn_count = quote! { 3 }; - - let for_make_gen = quote! {{ - let h = rn[rn_start_idx]; - let s = rn[rn_start_idx + 1]; - let v = rn[rn_start_idx + 2]; - rn_start_idx += #for_rn_count; - murrelet_common::MurreletColor::hsva(h, s, v, 1.0) - }}; - - (for_rn_count, for_make_gen) - } - RandMethod::ColorTransparency => { - let for_rn_count = quote! { 4 }; - - let for_make_gen = quote! {{ - let h = rn[rn_start_idx]; - let s = rn[rn_start_idx + 1]; - let v = rn[rn_start_idx + 2]; - let a = rn[rn_start_idx + 3]; - rn_start_idx += #for_rn_count; - murrelet_common::MurreletColor::hsva(h, s, v, a) - }}; - - (for_rn_count, for_make_gen) - } - RandMethod::VecLength { .. } => { - unreachable!("hm, this should be in teh recurse func, are you missing an inner?") - } - RandMethod::StringChoice { choices } => { - let one_hot = choices.len(); - let for_rn_count = quote! { #one_hot }; - - let weighted_rns = choices - .iter() - .enumerate() - .map(|(i, (key, weight))| { - quote! { (#key.clone(), #weight * rn[rn_start_idx + #i]) } - }) - .collect::>(); - - let for_make_gen = quote! { { - let result = vec![#(#weighted_rns,)*].into_iter().max_by(|a, b| a.1.partial_cmp(&b.1).unwrap()).expect("empty string choices??"); - rn_start_idx += #for_rn_count; - result.0.to_string() - } }; - - (for_rn_count, for_make_gen) - } - RandMethod::Default => { - let for_rn_count = quote! { 0 }; - - let for_make_gen = quote! { { - Default::default() - } }; - - (for_rn_count, for_make_gen) - } - } - } -} - // for enums #[derive(Debug, FromVariant, Clone)] #[darling(attributes(murrelet_gen))] @@ -426,9 +227,8 @@ pub struct StructIdents { #[derive(Clone, Debug)] pub(crate) enum HowToControlThis { - Normal, - Type(RandMethod), - Recurse(RandMethod, RandMethod), // one level... defaults to calling its func - Default, // just do the default values - Override(String, usize), + Type(GenMethod), + Recurse(GenMethod, GenMethod), // one level... defaults to calling its func + Default, // just do the default values + Override(String, Vec, usize), } diff --git a/murrelet_src_osc/src/osc.rs b/murrelet_src_osc/src/osc.rs index 3e72161..b888fcd 100644 --- a/murrelet_src_osc/src/osc.rs +++ b/murrelet_src_osc/src/osc.rs @@ -56,7 +56,7 @@ impl OscValues { } impl OscMng { - pub fn new_from_str(ip_address: &str) -> Self { + pub fn new_from_str(ip_address: &str, smoothed: bool) -> Self { let addr = match SocketAddrV4::from_str(ip_address) { Ok(addr) => addr, Err(_) => panic!( @@ -65,7 +65,7 @@ impl OscMng { ), }; - let cxn = OscCxn::new(&addr); + let cxn = OscCxn::new(&addr, smoothed); Self { cxn, values: OscValues { @@ -77,6 +77,7 @@ impl OscMng { } pub struct OscCxn { + smoothed: bool, _osc_cxn: JoinHandle<()>, // keep it alive! pub osc_rx: Receiver, } @@ -86,11 +87,17 @@ impl OscCxn { self.osc_rx.try_recv().map(|x| { // r.msg = Some(x); + // println!("osc message {:?}", x); + for (name, new_val) in x.to_livecode_vals().into_iter() { if let Some(old_val) = r.smooth_values.get(&name) { let actual_new_val = match (old_val, new_val) { (LivecodeValue::Float(old), LivecodeValue::Float(new)) => { - LivecodeValue::Float(*old * 0.9 + new * 0.1) + if self.smoothed { + LivecodeValue::Float(*old * 0.9 + new * 0.1) + } else { + LivecodeValue::Float(new) + } } _ => new_val, }; @@ -106,11 +113,14 @@ impl OscCxn { }) } - pub fn new(addr: &A) -> Self { + pub fn new(addr: &A, smoothed: bool) -> Self { let (event_tx, event_rx) = mpsc::channel::(); let sock = UdpSocket::bind(addr).unwrap(); + println!("setting up osc"); + println!("sock {:?}", sock); + let handle = std::thread::spawn(move || { let mut buf = [0u8; rosc::decoder::MTU]; @@ -132,6 +142,7 @@ impl OscCxn { }); OscCxn { + smoothed, _osc_cxn: handle, osc_rx: event_rx, } @@ -172,6 +183,7 @@ impl LivecodeOSC { } fn handle_packet(packet: &OscPacket) -> Option { + // println!("packet {:?}", packet); match packet { OscPacket::Message(msg) => { if let Some(osc_name) = msg.addr.as_str().strip_prefix(OSC_PREFIX) { diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 731f54c..bf4dd23 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -756,12 +756,14 @@ impl ToSvgData for Vec { return None; } + // whee, flip y's so we stop drawing everything upside down + let mut curr_item: Vec2 = *self.first().unwrap(); - let mut data = Data::new().move_to((curr_item.x, curr_item.y)); + let mut data = Data::new().move_to((curr_item.x, -curr_item.y)); for loc in self[1..].iter() { - data = data.line_by((loc.x - curr_item.x, loc.y - curr_item.y)); + data = data.line_by((loc.x - curr_item.x, -(loc.y - curr_item.y))); curr_item = *loc; } Some(data) From 0f622f11f497402c1a3fcbf4f7b8570ec99e70ae Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 22 Mar 2025 13:30:42 -0400 Subject: [PATCH 019/149] add oo_ helper --- murrelet_gen_derive/src/gen_methods.rs | 6 ++-- murrelet_livecode/src/livecode.rs | 3 +- murrelet_livecode/src/state.rs | 8 ++++- murrelet_livecode/src/types.rs | 11 +++++- murrelet_perform/src/perform.rs | 47 +++++++++++++++++++++++--- 5 files changed, 66 insertions(+), 9 deletions(-) diff --git a/murrelet_gen_derive/src/gen_methods.rs b/murrelet_gen_derive/src/gen_methods.rs index 5ad782c..af22a7d 100644 --- a/murrelet_gen_derive/src/gen_methods.rs +++ b/murrelet_gen_derive/src/gen_methods.rs @@ -113,7 +113,8 @@ impl GenMethod { } GenMethod::F32Normal { mu, sigma } => { let for_rn_count = quote! { 2 }; - let for_rn_names = quote! { vec![ "BoxMuller1".to_string(), "BoxMuller2".to_string()] }; + let for_rn_names = + quote! { vec![ "BoxMuller1".to_string(), "BoxMuller2".to_string()] }; let for_make_gen = quote! { { // avoid nans, make sure u1 is positive and non-zero let u1 = rn[rn_start_idx].clamp(std::f32::MIN_POSITIVE, 1.0); @@ -162,7 +163,8 @@ impl GenMethod { GenMethod::ColorNormal => { let for_rn_count = quote! { 3 }; - let for_rn_names = quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string()] }; + let for_rn_names = + quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string()] }; let for_make_gen = quote! {{ let h = rn[rn_start_idx]; diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index 868227a..629fa2a 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -294,9 +294,10 @@ impl GetLivecodeIdentifiers for String { } } +// impl GetLivecodeIdentifiers for AdditionalContextNode { fn variable_identifiers(&self) -> Vec { - vec![] + self.vars() } fn function_identifiers(&self) -> Vec { diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index 824343f..7f53f86 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -1,4 +1,6 @@ -use evalexpr::HashMapContext; +use std::collections::HashSet; + +use evalexpr::{HashMapContext, IterateVariablesContext}; use murrelet_common::*; use crate::{ @@ -28,6 +30,10 @@ pub struct LivecodeWorldState { assets: AssetsRef, } impl LivecodeWorldState { + pub fn vars(&self) -> HashSet { + self.context.iter_variable_names().collect() + } + fn clone_ctx_and_add_world( evalexpr_func_ctx: &HashMapContext, livecode_src: &LivecodeSrc, diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index bb21372..127b7d3 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -10,7 +10,7 @@ use thiserror::Error; use crate::{ expr::IntoExprWorldContext, - livecode::{GetLivecodeIdentifiers, LivecodeFromWorld}, + livecode::{GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeVariable}, state::LivecodeWorldState, unitcells::UnitCellExprWorldContext, }; @@ -73,6 +73,15 @@ impl Default for AdditionalContextNode { } impl AdditionalContextNode { + pub fn vars(&self) -> Vec { + self.0 + .iter_variable_identifiers() + .sorted() + .dedup() + .map(LivecodeVariable::from_str) + .collect_vec() + } + pub fn eval_raw(&self, ctx: &mut HashMapContext) -> LivecodeResult<()> { self.0 .eval_empty_with_context_mut(ctx) diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index afe0642..55dce31 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -5,10 +5,11 @@ use murrelet_common::{Assets, AssetsRef, LivecodeUsage}; use murrelet_common::{LivecodeSrc, LivecodeSrcUpdateInput, MurreletAppInput}; use murrelet_common::{MurreletColor, TransformVec2}; use murrelet_gui::MurreletGUI; +use murrelet_livecode::expr::MixedEvalDefs; use murrelet_livecode::lazy::ControlLazyNodeF32; use murrelet_livecode::state::{LivecodeTimingConfig, LivecodeWorldState}; use murrelet_livecode::types::{AdditionalContextNode, ControlVecElement, LivecodeResult}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fs; use murrelet_common::run_id; @@ -534,6 +535,7 @@ where assets: AssetsRef, maybe_args: Option, // should redesign this... lerp_pct: f32, // moving between things + used_variable_names: HashSet, } impl LiveCoder where @@ -587,6 +589,12 @@ where let util = LiveCodeUtil::new()?; + let used_variable_names = controlconfig + .variable_identifiers() + .into_iter() + .map(|x| x.name) + .collect(); + let mut s = LiveCoder { run_id, controlconfig: controlconfig.clone(), @@ -601,6 +609,7 @@ where assets: Assets::empty_ref(), maybe_args, lerp_pct: 1.0, // start in the done state! + used_variable_names, }; // hrm, before doing most things, load the assets (but we'll do this line again...) @@ -708,6 +717,22 @@ where self.controlconfig = d; self.lerp_pct = 0.0; // reloaded, so time to reload it! } + + // set the current vars + let variables_iter = self + .controlconfig + .variable_identifiers() + .into_iter() + .chain(self.prev_controlconfig.variable_identifiers().into_iter()); + let variables = if let Some(queued) = &self.queued_configcontrol { + variables_iter + .chain(queued.variable_identifiers().into_iter()) + .map(|x| x.name) + .collect::>() + } else { + variables_iter.map(|x| x.name).collect::>() + }; + self.used_variable_names = variables; } else if let Err(e) = result { eprintln!("Error {}", e); } @@ -803,9 +828,23 @@ where let ctx = &self.controlconfig._app_config().ctx; - let world = self - .util - .world(&self.livecode_src, &timing_conf, ctx, self.assets.clone())?; + let mut world = + self.util + .world(&self.livecode_src, &timing_conf, ctx, self.assets.clone())?; + + let mut md = MixedEvalDefs::new(); + + for x in self.used_variable_names.difference(&world.vars()) { + // argh, so used_variable_names includes non-global things, but right now i'm global + // so just do the stuff I care about right now, osc things... + if x.starts_with("oo_") { + if world.actual_frame_u64() % 1000 == 0 { + println!("adding default value for {:?}", x); + } + md.set_val(x, murrelet_common::LivecodeValue::Float(0.0)); + } + } + world.update_with_defs(&md).unwrap(); // i'm setting this so it should be okay.. self.cached_world = Some(world); Ok(()) From 0bea14b2bf60b0ac764c3ce99d8a4890415eab7d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 23 Mar 2025 11:07:57 -0400 Subject: [PATCH 020/149] osc --- murrelet_src_osc/src/osc.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/murrelet_src_osc/src/osc.rs b/murrelet_src_osc/src/osc.rs index b888fcd..716152c 100644 --- a/murrelet_src_osc/src/osc.rs +++ b/murrelet_src_osc/src/osc.rs @@ -15,11 +15,17 @@ const OSC_PREFIX: &str = "/livecode/"; impl IsLivecodeSrc for OscMng { fn update(&mut self, _: &LivecodeSrcUpdateInput) { // drain all the messages available - for _ in 0..10 { + let mut i = 0; + let count = 300; + for _ in 0..count { let r = self.cxn.check_and_maybe_update(&mut self.values); if r.is_err() { break; } // leave early + i += 1 + } + if i >= count - 1 { + println!("that's a lot of osc messages to go through!"); } } @@ -108,6 +114,7 @@ impl OscCxn { r.smooth_values.insert(name.clone(), new_val); } + // println!("{:?} {:?}", name, new_val); r.last_values.insert(name.clone(), new_val); // todo, probably good to get timestamp } }) From 0c4e421269dce782e3fcda22980d28037bff9c52 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 25 Mar 2025 00:08:15 -0400 Subject: [PATCH 021/149] add derive_gen inverse --- murrelet_draw/src/draw.rs | 11 ++ murrelet_gen/examples/tests.rs | 110 ++++++++++++-------- murrelet_gen/src/lib.rs | 3 + murrelet_gen_derive/src/derive_gen.rs | 133 ++++++++++++++++++++----- murrelet_gen_derive/src/gen_methods.rs | 132 +++++++++++++++++++----- murrelet_gen_derive/src/parser.rs | 71 +++++++++---- 6 files changed, 346 insertions(+), 114 deletions(-) diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index 8e6aed5..db1c4ac 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -219,6 +219,13 @@ impl MurreletStyle { } } + pub fn with_stroke_color(&self, c: MurreletColor) -> MurreletStyle { + MurreletStyle { + stroke_color: MurreletColorStyle::Color(c), + ..*self + } + } + pub fn with_fill(&self) -> MurreletStyle { MurreletStyle { filled: true, @@ -271,6 +278,10 @@ pub trait Sdraw: Sized { self.with_svg_style(self.svg_style().with_stroke(w)) } + fn with_stroke_color(&self, line_color: MurreletColor) -> Self { + self.with_svg_style(self.svg_style().with_stroke_color(line_color)) + } + fn with_close(&self, closed: bool) -> Self { let svg_style = MurreletStyle { closed, diff --git a/murrelet_gen/examples/tests.rs b/murrelet_gen/examples/tests.rs index 089c646..6de6d40 100644 --- a/murrelet_gen/examples/tests.rs +++ b/murrelet_gen/examples/tests.rs @@ -23,8 +23,8 @@ pub struct BasicTypes { d_number: i32, #[murrelet_gen(method(f32_uniform_pos_neg(start = 10.0, end = 30.0)))] uniform_pos_neg: f32, - #[murrelet_gen(method(f32_normal(mu = 10.0, sigma = 30.0)))] - normal: f32, + // #[murrelet_gen(method(f32_normal(mu = 10.0, sigma = 30.0)))] + // normal: f32, #[murrelet_gen(method(bool_binomial(pct = 0.3)))] bool: bool, #[murrelet_gen(method(vec2_uniform_grid(x = 0.0, y = 0.0, width = 100.0, height = 100.0)))] @@ -35,7 +35,6 @@ pub struct BasicTypes { normal_color: MurreletColor, // 3 rn #[murrelet_gen(method(color_transparency))] transparent_color: MurreletColor, // 4 rn - #[murrelet_gen(method(vec_length(min = 4, max = 10)))] #[murrelet_gen(method_inner(f32_uniform(start = 0.0, end = 1.0)))] something: Vec, @@ -78,60 +77,85 @@ fn main() { println!("BasicTypes::rn_names() {:?}", BasicTypes::rn_names()); - println!( - "OverridesAndRecursive::rn_names() {:?}", - OverridesAndRecursive::rn_names() - ); + let b = BasicTypes::gen_from_seed(1); + + println!("b {:?}", b); + println!("BasicTypes::to_dist() {:?}", b.to_dist()); + + // let c = Vec2::ONE; + + assert_eq!(BasicTypes::rn_count(), b.to_dist().len()); - println!("EnumTest::rn_names() {:?}", EnumTest::rn_names()); + println!("round trip {:?}", BasicTypes::sample_dist(&b.to_dist(), 0)); - assert_eq!(BasicTypes::rn_count(), BasicTypes::rn_names().len()); - assert_eq!(Tiny::rn_count(), Tiny::rn_names().len()); - assert_eq!( - OverridesAndRecursive::rn_count(), - OverridesAndRecursive::rn_names().len() - ); + for i in b.to_dist() { + assert!(i >= 0.0); + assert!(i <= 1.0); + } - assert_eq!(EnumTest::rn_count(), EnumTest::rn_names().len()); + // todo, maybe see if partial eq works? + // assert_eq!(, b) // println!( - // "OverridesAndRecursive::rn_count() {:?}", - // OverridesAndRecursive::rn_count() + // "OverridesAndRecursive::rn_names() {:?}", + // OverridesAndRecursive::rn_names() // ); - assert_eq!(BasicTypes::rn_count(), 37); + // println!("EnumTest::rn_names() {:?}", EnumTest::rn_names()); - println!( - "OverridesAndRecursive::gen_from_seed(42) {:?}", - OverridesAndRecursive::gen_from_seed(42) - ); + // assert_eq!(BasicTypes::rn_count(), BasicTypes::rn_names().len()); + // assert_eq!(Tiny::rn_count(), Tiny::rn_names().len()); + // assert_eq!( + // OverridesAndRecursive::rn_count(), + // OverridesAndRecursive::rn_names().len() + // ); - assert_eq!(OverridesAndRecursive::rn_count(), 11); - assert_eq!(EnumTest::rn_count(), 7); + // assert_eq!(EnumTest::rn_count(), EnumTest::rn_names().len()); - for seed in 32..43 { - let test_val = BasicTypes::gen_from_seed(seed); + // // println!( + // // "OverridesAndRecursive::rn_count() {:?}", + // // OverridesAndRecursive::rn_count() + // // ); - // there's a small chance they will be equal, but we know for this seed they aren't - assert!(test_val.a_number != test_val.another_number); - assert!(test_val.another_number_wider_range > 1.0); - assert_eq!(test_val.fixed_number, 0.23); + // assert_eq!(BasicTypes::rn_count(), 37); - assert!(test_val.something.len() > 3); - assert!(test_val.something.len() <= 10); + // println!( + // "OverridesAndRecursive::gen_from_seed(42) {:?}", + // OverridesAndRecursive::gen_from_seed(42) + // ); - let test_val2 = OverridesAndRecursive::gen_from_seed(seed); - assert!(test_val2.something.len() >= 1); - assert!(test_val2.something.len() <= 4); + // assert_eq!(OverridesAndRecursive::rn_count(), 11); + // assert_eq!(EnumTest::rn_count(), 7); - // println!("test_val {:?}", test_val); + // for seed in 32..43 { + // let test_val = BasicTypes::gen_from_seed(seed); - let test_val = BasicTypes::gen_from_seed(seed); - let test_val2 = OverridesAndRecursive::gen_from_seed(seed); - let test_val3 = EnumTest::gen_from_seed(seed); + // // there's a small chance they will be equal, but we know for this seed they aren't + // assert!(test_val.a_number != test_val.another_number); + // assert!(test_val.another_number_wider_range > 1.0); + // assert_eq!(test_val.fixed_number, 0.23); - println!("test_val {:?}", test_val); - // println!("test_val2 {:?}", test_val2); - // println!("test_val3 {:?}", test_val3); - } + // assert!(test_val.something.len() > 3); + // assert!(test_val.something.len() <= 10); + + // let test_val2 = OverridesAndRecursive::gen_from_seed(seed); + // assert!(test_val2.something.len() >= 1); + // assert!(test_val2.something.len() <= 4); + + // // println!("test_val {:?}", test_val); + + // let test_val = BasicTypes::gen_from_seed(seed); + // let test_val2 = OverridesAndRecursive::gen_from_seed(seed); + // let test_val3 = EnumTest::gen_from_seed(seed); + + // println!("test_val {:?}", test_val); + // // println!("test_val2 {:?}", test_val2); + // // println!("test_val3 {:?}", test_val3); + + // for i in b.to_dist() { + // assert!(i >= 0.0); + // assert!(i < 1.0); + // } + + // } } diff --git a/murrelet_gen/src/lib.rs b/murrelet_gen/src/lib.rs index 042ea90..37076d5 100644 --- a/murrelet_gen/src/lib.rs +++ b/murrelet_gen/src/lib.rs @@ -23,6 +23,9 @@ pub trait CanSampleFromDist: Sized { Self::sample_dist(&rns, 0) } + + // creates an arbitrary floats that should turn back into the same values + fn to_dist(&self) -> Vec; } pub fn prefix_field_names(prefix: String, names: Vec) -> Vec { diff --git a/murrelet_gen_derive/src/derive_gen.rs b/murrelet_gen_derive/src/derive_gen.rs index a2b09b0..15a82cc 100644 --- a/murrelet_gen_derive/src/derive_gen.rs +++ b/murrelet_gen_derive/src/derive_gen.rs @@ -1,5 +1,6 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; +use syn::Ident; use crate::{gen_methods::GenMethod, parser::*}; @@ -16,6 +17,7 @@ pub(crate) struct FieldTokensGen { pub(crate) for_rn_count: TokenStream2, pub(crate) for_rn_names: TokenStream2, pub(crate) for_make_gen: TokenStream2, + pub(crate) for_to_dist: TokenStream2, } impl GenFinal for FieldTokensGen { // Something(f32) @@ -28,6 +30,7 @@ impl GenFinal for FieldTokensGen { let for_rn_count = variants.iter().map(|x| x.for_rn_count.clone()); let for_rn_names = variants.iter().map(|x| x.for_rn_names.clone()); let for_make_gen = variants.iter().map(|x| x.for_make_gen.clone()); + let for_to_dist = variants.iter().map(|x| x.for_to_dist.clone()); quote! { impl murrelet_gen::CanSampleFromDist for #name { @@ -42,6 +45,11 @@ impl GenFinal for FieldTokensGen { fn sample_dist(rn: &[f32], start_idx: usize) -> Self { Self(#(#for_make_gen,)*) } + + fn to_dist(&self) -> Vec { + let val = self; + vec![#(#for_to_dist,)*].concat() + } } } } @@ -52,6 +60,7 @@ impl GenFinal for FieldTokensGen { let for_rn_count = variants.iter().map(|x| x.for_rn_count.clone()); let for_rn_names = variants.iter().map(|x| x.for_rn_names.clone()); let for_make_gen = variants.iter().map(|x| x.for_make_gen.clone()); + let for_to_dist = variants.iter().map(|x| x.for_to_dist.clone()); quote! { impl murrelet_gen::CanSampleFromDist for #name { @@ -74,6 +83,11 @@ impl GenFinal for FieldTokensGen { #(#for_make_gen,)* } } + + fn to_dist(&self) -> Vec { + let val = self; + vec![#(#for_to_dist,)*].concat() + } } } } @@ -86,6 +100,7 @@ impl GenFinal for FieldTokensGen { let name = idents.name; let for_rn_count = variants.iter().map(|x| x.for_rn_count.clone()); let for_rn_names_all = variants.iter().map(|x| x.for_rn_names.clone()); + let for_to_dist = variants.iter().map(|x| x.for_to_dist.clone()); let mut weights = vec![]; let mut for_rn_names: Vec = vec![]; @@ -151,6 +166,14 @@ impl GenFinal for FieldTokensGen { comp } + + fn to_dist(&self) -> Vec { + let val = self; + let mut result: Vec = vec![]; + #(#for_to_dist;)* + result + } + } } } @@ -166,10 +189,15 @@ impl GenFinal for FieldTokensGen { self.0.sample_dist(rn, rn_start_idx) }; + let for_to_dist = quote! { + self.0.to_dist() + }; + FieldTokensGen { for_rn_count, for_rn_names, for_make_gen, + for_to_dist, } } @@ -183,6 +211,16 @@ impl GenFinal for FieldTokensGen { let for_rn_names = quote! { #ty::rn_names() }; + let for_to_dist = quote! { + if let #name::#variant_ident(x) = &self { + result.push(1.0); + result.extend(x.to_dist().into_iter()) + } else { + result.push(0.0); + result.extend((0..#ty::rn_count()).map(|x| 0.5)); + } + }; + // hm, i'm not sure that the method in the enum is actually used let for_make_gen = quote! { { @@ -197,6 +235,7 @@ impl GenFinal for FieldTokensGen { for_rn_count, for_rn_names, for_make_gen, + for_to_dist, } } @@ -212,10 +251,19 @@ impl GenFinal for FieldTokensGen { let for_make_gen = quote! { #name::#variant_ident }; + let for_to_dist = quote! { + if let #name::#variant_ident = &self { + result.push(1.0); + } else { + result.push(0.0); + } + }; + FieldTokensGen { for_rn_count, for_rn_names, for_make_gen, + for_to_dist, } } @@ -226,13 +274,14 @@ impl GenFinal for FieldTokensGen { let for_rn_count = quote! { 0 }; let for_rn_names = quote! { vec![] }; - let for_make_gen = quote! { #field_name: #ty::default() }; + let for_to_dist = quote! { vec![] }; FieldTokensGen { for_rn_count, for_make_gen, for_rn_names, + for_to_dist, } } @@ -242,12 +291,14 @@ impl GenFinal for FieldTokensGen { let field_name_str = field_name.to_string(); let ty = idents.data.ty; - let (for_rn_count, for_rn_names, for_make_gen) = method.to_methods(ty, true); + let (for_rn_count, for_rn_names, for_make_gen, for_to_dist) = + method.to_methods(ty, quote! {self.#field_name}, true); FieldTokensGen { for_make_gen: quote! { #field_name: #for_make_gen }, for_rn_names: quote! { murrelet_gen::prefix_field_names(#field_name_str.to_string(), #for_rn_names)}, for_rn_count, + for_to_dist, } } @@ -255,15 +306,23 @@ impl GenFinal for FieldTokensGen { let field_name = idents.data.ident.unwrap(); let ty = idents.data.ty; - let (for_rn_count, for_rn_names, for_make_gen) = match outer { + let (for_rn_count, for_rn_names, for_make_gen, for_to_dist) = match outer { GenMethod::VecLength { min, max } => { let inside_type = nested_ident(&ty); let i = inside_type[1].clone(); let inside_type_val: syn::Type = syn::parse_quote! { #i }; - let (for_rn_count_per_item, for_rn_names_per_item, for_make_gen_per_item) = - inner.to_methods(inside_type_val, false); + let mut idents_for_vec = vec![]; + recursive_ident_from_path(&ty, &mut idents_for_vec); + let internal_type = idents_for_vec[1].clone(); + + let ( + for_rn_count_per_item, + for_rn_names_per_item, + for_make_gen_per_item, + for_make_to_dist_per_item, + ) = inner.to_methods(inside_type_val, quote! {val.clone()}, false); let for_rn_count: TokenStream2 = quote! { #for_rn_count_per_item * #max + 1 @@ -309,7 +368,25 @@ impl GenFinal for FieldTokensGen { v }}; - (for_rn_count, for_rn_names, for_make_gen) + let for_to_dist = quote! {{ + let mut result = vec![]; + let x = self.#field_name.len() as f32; + let v = (x - #min as f32) / ((#max - #min) as f32); + result.push(v); + for val in self.#field_name.iter() { + // always extend it + let vv = #for_make_to_dist_per_item; + result.extend(vv.into_iter()); + } + + for _ in self.#field_name.len()..#max { + let vv: Vec = (0..#for_rn_count_per_item).into_iter().map(|_| 0.5f32).collect(); + result.extend(vv.into_iter()); + } + result + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } _ => unreachable!("not expecting an inner without a recursive outer"), }; @@ -318,34 +395,38 @@ impl GenFinal for FieldTokensGen { for_rn_count, for_rn_names, for_make_gen: quote! { #field_name: #for_make_gen }, + for_to_dist, } } - fn from_override_struct( - idents: StructIdents, - func: &str, - rn_names: Vec, - rn_count: usize, - ) -> FieldTokensGen { - let field_name = idents.data.ident.unwrap(); + // fn from_override_struct( + // idents: StructIdents, + // func: &str, + // rn_names: Vec, + // rn_count: usize, + // ) -> FieldTokensGen { + // let field_name = idents.data.ident.unwrap(); - let for_rn_count = quote! { #rn_count }; + // let for_rn_count = quote! { #rn_count }; - let strs = strs_to_tokens(rn_names); - let for_rn_names = quote! { - #(#strs,)* - }; + // let strs = strs_to_tokens(rn_names); + // let for_rn_names = quote! { + // #(#strs,)* + // }; - let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); + // let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); - let for_make_gen = quote! { #field_name: #method() }; + // let for_make_gen = quote! { #field_name: #method() }; - FieldTokensGen { - for_rn_count, - for_rn_names, - for_make_gen, - } - } + // let for_to_dist = quote! { #method_inv() } + + // FieldTokensGen { + // for_rn_count, + // for_rn_names, + // for_make_gen, + // for_to_dist + // } + // } } fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { diff --git a/murrelet_gen_derive/src/gen_methods.rs b/murrelet_gen_derive/src/gen_methods.rs index af22a7d..ea2347d 100644 --- a/murrelet_gen_derive/src/gen_methods.rs +++ b/murrelet_gen_derive/src/gen_methods.rs @@ -54,8 +54,9 @@ impl GenMethod { pub(crate) fn to_methods( &self, ty: syn::Type, + name: TokenStream2, convert: bool, - ) -> (TokenStream2, TokenStream2, TokenStream2) { + ) -> (TokenStream2, TokenStream2, TokenStream2, TokenStream2) { let maybe_as = if convert { quote! { as #ty } } else { @@ -71,8 +72,9 @@ impl GenMethod { rn_start_idx += #for_rn_count; r }}; + let for_to_dist = quote! { #name.to_dist() }; - (for_rn_count, for_rn_names, for_make_gen) + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } GenMethod::BoolBinomial { pct } => { let for_rn_count = quote! { 1 }; @@ -82,8 +84,15 @@ impl GenMethod { rn_start_idx += #for_rn_count; result } }; + let for_to_dist = quote! { + if #name { + vec![1.0] + } else { + vec![0.0] + } + }; - (for_rn_count, for_rn_names, for_make_gen) + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } GenMethod::F32Uniform { start, end } => { let for_rn_count = quote! { 1 }; @@ -93,8 +102,9 @@ impl GenMethod { rn_start_idx += #for_rn_count; result #maybe_as } }; + let for_to_dist = quote! { vec![(#name as f32 - #start) / (#end - #start)] }; - (for_rn_count, for_rn_names, for_make_gen) + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } GenMethod::F32UniformPosNeg { start, end } => { let for_rn_count = quote! { 2 }; @@ -106,11 +116,19 @@ impl GenMethod { (sgn * result) #maybe_as } }; - (for_rn_count, for_rn_names, for_make_gen) - } - GenMethod::F32Fixed { val } => { - (quote! { 0 }, quote! {vec![]}, quote! { #val #maybe_as }) + let for_to_dist = quote! { vec![ + if #name > 0.0 { 1.0 } else { 0.0 }, + (#name.abs() - #start) / (#end - #start) + ] }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } + GenMethod::F32Fixed { val } => ( + quote! { 0 }, + quote! {vec![]}, + quote! { #val #maybe_as }, + quote! {vec![]}, + ), GenMethod::F32Normal { mu, sigma } => { let for_rn_count = quote! { 2 }; let for_rn_names = @@ -126,7 +144,8 @@ impl GenMethod { #mu + #sigma * r * theta.cos() #maybe_as } }; - (for_rn_count, for_rn_names, for_make_gen) + let for_to_dist = quote! { todo!() }; + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } GenMethod::Vec2UniformGrid { x, @@ -145,7 +164,12 @@ impl GenMethod { glam::vec2(#x, #y) - 0.5 * glam::vec2(#width, #height) + glam::vec2(width, height) }}; - (for_rn_count, for_rn_names, for_make_gen) + let for_to_dist = quote! { { + let c = (#name + 0.5 * glam::vec2(#width, #height)); + vec![c.x / #width, c.y / #height] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } GenMethod::Vec2Circle { x, y, radius } => { let for_rn_count = quote! { 2 }; @@ -158,7 +182,18 @@ impl GenMethod { glam::vec2(#x, #y) + glam::vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() }}; - (for_rn_count, for_rn_names, for_make_gen) + let for_to_dist = quote! { { + let c = #name - glam::vec2(#x, #y); + let dist = (c.length() / #radius).powi(2); + let mut angle = c.to_angle(); + if angle <= 0.0 { + angle += 2.0 * std::f32::consts::PI + } + angle /= (2.0 * std::f32::consts::PI); + vec![angle, dist] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } GenMethod::ColorNormal => { let for_rn_count = quote! { 3 }; @@ -174,23 +209,44 @@ impl GenMethod { murrelet_common::MurreletColor::hsva(h, s, v, 1.0) }}; - (for_rn_count, for_rn_names, for_make_gen) + let for_to_dist = quote! {{ + let [h, s, v, _] = #name.into_hsva_components(); + vec![ + h % 1.0, + s % 1.0, + v % 1.0, + ] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } GenMethod::ColorTransparency => { let for_rn_count = quote! { 4 }; let for_rn_names = quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string(), "alpha".to_string()] }; - let for_make_gen = quote! {{ - let h = rn[rn_start_idx]; - let s = rn[rn_start_idx + 1]; - let v = rn[rn_start_idx + 2]; - let a = rn[rn_start_idx + 3]; - rn_start_idx += #for_rn_count; - murrelet_common::MurreletColor::hsva(h, s, v, a) - }}; + let for_make_gen = quote! { + { + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + let a = rn[rn_start_idx + 3]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, a) + } + }; + + let for_to_dist = quote! { { + let [h, s, v, a] = #name.into_hsva_components(); + vec![ + h % 1.0, + s % 1.0, + v % 1.0, + a % 1.0, + ] + } }; - (for_rn_count, for_rn_names, for_make_gen) + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } GenMethod::VecLength { .. } => { // this is handled in the vec parser @@ -218,7 +274,20 @@ impl GenMethod { result.0.to_string() } }; - (for_rn_count, for_rn_names, for_make_gen) + let for_to_dist_choices = choices + .iter() + .map(|(key, _)| { + quote! { if #key.clone() == #name { result.push(1.0) } else { result.push(0.0) } } + }) + .collect::>(); + + let for_to_dist = quote! { { + let mut result = vec![]; + #(#for_to_dist_choices;)* + result + } }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } GenMethod::Default => { let for_rn_count = quote! { 0 }; @@ -229,8 +298,25 @@ impl GenMethod { Default::default() } }; - (for_rn_count, for_rn_names, for_make_gen) + let for_to_dist = quote! { vec![] }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } } } + + // pub(crate) fn from_methods(&self) -> TokenStream2 { + // match self { + // GenMethod::Default => todo!(), + // GenMethod::Recurse => todo!(), + // GenMethod::BoolBinomial { pct } => { + // // put at extremes + + // }, + // GenMethod::F32Uniform { start, end } => { + + // }, + + // } + // } } diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index dc9132c..6f9ba0c 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -35,12 +35,12 @@ where ast::Data::Struct(_) => Self::make_struct(&ast_receiver), } } - fn from_override_struct( - idents: StructIdents, - func: &str, - rn_names: Vec, - rn_count: usize, - ) -> Self; + // fn from_override_struct( + // idents: StructIdents, + // func: &str, + // rn_names: Vec, + // rn_count: usize, + // ) -> Self; fn make_enum_final( idents: ParsedFieldIdent, @@ -67,9 +67,9 @@ where }; match field.how_to_control_this() { - HowToControlThis::Override(func, names, count) => { - Self::from_override_struct(idents, &func, names, count) - } + // HowToControlThis::Override(func, names, count) => { + // Self::from_override_struct(idents, &func, names, count) + // } HowToControlThis::Type(how_to_control_this_type) => { Self::from_type_struct(idents, &how_to_control_this_type) } @@ -146,9 +146,9 @@ where // Self::from_type_recurse(idents, &outer, &inner) Self::from_newtype_struct(idents, name.clone()) } - HowToControlThis::Override(func, labels, count) => { - Self::from_override_struct(idents, &func, labels, count) - } + // HowToControlThis::Override(func, labels, count) => { + // Self::from_override_struct(idents, &func, labels, count) + // } } }) .collect::>(); @@ -178,15 +178,16 @@ pub(crate) struct LivecodeFieldReceiver { } impl LivecodeFieldReceiver { fn how_to_control_this(&self) -> HowToControlThis { - if let Some(OverrideFn { func, labels }) = &self.override_fn { - match func.as_str() { - "default" => HowToControlThis::Default, - _ => { - let label_str: Vec = labels.iter().map(|lit| lit.value()).collect(); - HowToControlThis::Override(func.clone(), label_str, labels.len()) - } - } - } else if let Some(r) = &self.method_inner { + // if let Some(OverrideFn { func, labels }) = &self.override_fn { + // match func.as_str() { + // "default" => HowToControlThis::Default, + // _ => { + // let label_str: Vec = labels.iter().map(|lit| lit.value()).collect(); + // HowToControlThis::Override(func.clone(), label_str, labels.len()) + // } + // } + // } else + if let Some(r) = &self.method_inner { HowToControlThis::Recurse(self.method.clone(), r.clone()) } else if matches!(self.method, GenMethod::VecLength { .. }) { panic!("vec missing inner") @@ -230,5 +231,31 @@ pub(crate) enum HowToControlThis { Type(GenMethod), Recurse(GenMethod, GenMethod), // one level... defaults to calling its func Default, // just do the default values - Override(String, Vec, usize), + // Override(String, Vec, usize), } + + + +pub fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { + match t { + syn::Type::Path(syn::TypePath { path, .. }) => { + let s = path.segments.last().unwrap(); + let main_type = s.ident.clone(); + + acc.push(main_type); + + if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, + .. + }) = s.arguments.clone() + { + if let syn::GenericArgument::Type(other_ty) = args.first().unwrap() { + recursive_ident_from_path(other_ty, acc); + } else { + panic!("recursive ident not implemented yet {:?}", args); + } + } + } + x => panic!("no name for type {:?}", x), + } +} \ No newline at end of file From 7f7590d8ff8292922555d48d5d505388f4c8f8dc Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Mar 2025 20:02:32 -0700 Subject: [PATCH 022/149] wip --- murrelet_gen/examples/tests.rs | 47 +++++++++---- murrelet_gen/src/lib.rs | 1 + murrelet_gen_derive/src/derive_gen.rs | 96 ++++++++++++++++++++++----- murrelet_gui/src/lib.rs | 4 +- murrelet_gui_derive/src/derive_gui.rs | 4 +- murrelet_gui_derive/src/parser.rs | 24 ++++++- murrelet_svg/src/svg.rs | 6 ++ 7 files changed, 147 insertions(+), 35 deletions(-) diff --git a/murrelet_gen/examples/tests.rs b/murrelet_gen/examples/tests.rs index 6de6d40..8c3f51a 100644 --- a/murrelet_gen/examples/tests.rs +++ b/murrelet_gen/examples/tests.rs @@ -82,9 +82,12 @@ fn main() { println!("b {:?}", b); println!("BasicTypes::to_dist() {:?}", b.to_dist()); - // let c = Vec2::ONE; + println!("BasicTypes::to_dist_mask() {:?}", b.to_dist_mask()); + + // // let c = Vec2::ONE; assert_eq!(BasicTypes::rn_count(), b.to_dist().len()); + assert_eq!(BasicTypes::rn_count(), b.to_dist_mask().len()); println!("round trip {:?}", BasicTypes::sample_dist(&b.to_dist(), 0)); @@ -93,24 +96,40 @@ fn main() { assert!(i <= 1.0); } - // todo, maybe see if partial eq works? - // assert_eq!(, b) + let c = OverridesAndRecursive::gen_from_seed(13); - // println!( - // "OverridesAndRecursive::rn_names() {:?}", - // OverridesAndRecursive::rn_names() - // ); + println!("c {:?}", c); + + println!( + "OverridesAndRecursive::rn_names() {:?}", + OverridesAndRecursive::rn_names() + ); + + println!("OverridesAndRecursive::to_dist() {:?}", c.to_dist()); + println!( + "OverridesAndRecursive::to_dist_mask() {:?}", + c.to_dist_mask() + ); // println!("EnumTest::rn_names() {:?}", EnumTest::rn_names()); - // assert_eq!(BasicTypes::rn_count(), BasicTypes::rn_names().len()); - // assert_eq!(Tiny::rn_count(), Tiny::rn_names().len()); - // assert_eq!( - // OverridesAndRecursive::rn_count(), - // OverridesAndRecursive::rn_names().len() - // ); + assert_eq!(BasicTypes::rn_count(), BasicTypes::rn_names().len()); + assert_eq!(Tiny::rn_count(), Tiny::rn_names().len()); + assert_eq!( + OverridesAndRecursive::rn_count(), + OverridesAndRecursive::rn_names().len() + ); + + let d = EnumTest::gen_from_seed(13); + + assert_eq!(EnumTest::rn_count(), EnumTest::rn_names().len()); + + println!("d {:?}", d); + + println!("EnumTest::rn_names() {:?}", EnumTest::rn_names()); - // assert_eq!(EnumTest::rn_count(), EnumTest::rn_names().len()); + println!("EnumTest::to_dist() {:?}", d.to_dist()); + println!("EnumTest::to_dist_mask() {:?}", d.to_dist_mask()); // // println!( // // "OverridesAndRecursive::rn_count() {:?}", diff --git a/murrelet_gen/src/lib.rs b/murrelet_gen/src/lib.rs index 37076d5..aa3cf86 100644 --- a/murrelet_gen/src/lib.rs +++ b/murrelet_gen/src/lib.rs @@ -26,6 +26,7 @@ pub trait CanSampleFromDist: Sized { // creates an arbitrary floats that should turn back into the same values fn to_dist(&self) -> Vec; + fn to_dist_mask(&self) -> Vec; } pub fn prefix_field_names(prefix: String, names: Vec) -> Vec { diff --git a/murrelet_gen_derive/src/derive_gen.rs b/murrelet_gen_derive/src/derive_gen.rs index 15a82cc..74d5701 100644 --- a/murrelet_gen_derive/src/derive_gen.rs +++ b/murrelet_gen_derive/src/derive_gen.rs @@ -1,23 +1,14 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::Ident; use crate::{gen_methods::GenMethod, parser::*}; -fn str_to_tokens(s: &str) -> TokenStream2 { - let lit = syn::LitStr::new(s, proc_macro2::Span::call_site()); - quote! { (#lit).to_string() } -} - -fn strs_to_tokens(s: Vec) -> Vec { - s.iter().map(|x| str_to_tokens(x)).collect() -} - pub(crate) struct FieldTokensGen { pub(crate) for_rn_count: TokenStream2, pub(crate) for_rn_names: TokenStream2, pub(crate) for_make_gen: TokenStream2, pub(crate) for_to_dist: TokenStream2, + pub(crate) for_to_dist_mask: TokenStream2, } impl GenFinal for FieldTokensGen { // Something(f32) @@ -31,6 +22,7 @@ impl GenFinal for FieldTokensGen { let for_rn_names = variants.iter().map(|x| x.for_rn_names.clone()); let for_make_gen = variants.iter().map(|x| x.for_make_gen.clone()); let for_to_dist = variants.iter().map(|x| x.for_to_dist.clone()); + let for_to_dist_mask = variants.iter().map(|x| x.for_to_dist_mask.clone()); quote! { impl murrelet_gen::CanSampleFromDist for #name { @@ -50,6 +42,11 @@ impl GenFinal for FieldTokensGen { let val = self; vec![#(#for_to_dist,)*].concat() } + + fn to_dist_mask(&self) -> Vec { + let val = self; + vec![#(#for_to_dist_mask,)*].concat() + } } } } @@ -61,6 +58,7 @@ impl GenFinal for FieldTokensGen { let for_rn_names = variants.iter().map(|x| x.for_rn_names.clone()); let for_make_gen = variants.iter().map(|x| x.for_make_gen.clone()); let for_to_dist = variants.iter().map(|x| x.for_to_dist.clone()); + let for_to_dist_mask = variants.iter().map(|x| x.for_to_dist_mask.clone()); quote! { impl murrelet_gen::CanSampleFromDist for #name { @@ -88,6 +86,11 @@ impl GenFinal for FieldTokensGen { let val = self; vec![#(#for_to_dist,)*].concat() } + + fn to_dist_mask(&self) -> Vec { + let val = self; + vec![#(#for_to_dist_mask,)*].concat() + } } } } @@ -101,6 +104,7 @@ impl GenFinal for FieldTokensGen { let for_rn_count = variants.iter().map(|x| x.for_rn_count.clone()); let for_rn_names_all = variants.iter().map(|x| x.for_rn_names.clone()); let for_to_dist = variants.iter().map(|x| x.for_to_dist.clone()); + let for_to_dist_mask = variants.iter().map(|x| x.for_to_dist_mask.clone()); let mut weights = vec![]; let mut for_rn_names: Vec = vec![]; @@ -174,6 +178,12 @@ impl GenFinal for FieldTokensGen { result } + fn to_dist_mask(&self) -> Vec { + let val = self; + let mut result: Vec = vec![]; + #(#for_to_dist_mask;)* + result + } } } } @@ -193,11 +203,16 @@ impl GenFinal for FieldTokensGen { self.0.to_dist() }; + let for_to_dist_mask = quote! { + self.0.to_dist_mask() + }; + FieldTokensGen { for_rn_count, for_rn_names, for_make_gen, for_to_dist, + for_to_dist_mask, } } @@ -221,6 +236,16 @@ impl GenFinal for FieldTokensGen { } }; + let for_to_dist_mask = quote! { + if let #name::#variant_ident(x) = &self { + result.push(true); + result.extend((0..#ty::rn_count()).map(|x| true)); + } else { + result.push(true); + result.extend((0..#ty::rn_count()).map(|x| false)); + } + }; + // hm, i'm not sure that the method in the enum is actually used let for_make_gen = quote! { { @@ -236,6 +261,7 @@ impl GenFinal for FieldTokensGen { for_rn_names, for_make_gen, for_to_dist, + for_to_dist_mask, } } @@ -259,11 +285,20 @@ impl GenFinal for FieldTokensGen { } }; + let for_to_dist_mask = quote! { + if let #name::#variant_ident = &self { + result.push(true); + } else { + result.push(true); + } + }; + FieldTokensGen { for_rn_count, for_rn_names, for_make_gen, for_to_dist, + for_to_dist_mask, } } @@ -276,12 +311,14 @@ impl GenFinal for FieldTokensGen { let for_rn_names = quote! { vec![] }; let for_make_gen = quote! { #field_name: #ty::default() }; let for_to_dist = quote! { vec![] }; + let for_to_dist_mask = quote! { vec![] }; FieldTokensGen { for_rn_count, for_make_gen, for_rn_names, for_to_dist, + for_to_dist_mask, } } @@ -294,11 +331,15 @@ impl GenFinal for FieldTokensGen { let (for_rn_count, for_rn_names, for_make_gen, for_to_dist) = method.to_methods(ty, quote! {self.#field_name}, true); + // for the mask. if we're here, we probably want to count it! + let rn_count = for_rn_count.clone(); + FieldTokensGen { for_make_gen: quote! { #field_name: #for_make_gen }, for_rn_names: quote! { murrelet_gen::prefix_field_names(#field_name_str.to_string(), #for_rn_names)}, for_rn_count, for_to_dist, + for_to_dist_mask: quote! { (0..{#rn_count}).into_iter().map(|x| true).collect::>() }, } } @@ -306,16 +347,17 @@ impl GenFinal for FieldTokensGen { let field_name = idents.data.ident.unwrap(); let ty = idents.data.ty; - let (for_rn_count, for_rn_names, for_make_gen, for_to_dist) = match outer { + let (for_rn_count, for_rn_names, for_make_gen, for_to_dist, for_to_dist_mask) = match outer + { GenMethod::VecLength { min, max } => { let inside_type = nested_ident(&ty); let i = inside_type[1].clone(); let inside_type_val: syn::Type = syn::parse_quote! { #i }; - let mut idents_for_vec = vec![]; - recursive_ident_from_path(&ty, &mut idents_for_vec); - let internal_type = idents_for_vec[1].clone(); + // let mut idents_for_vec = vec![]; + // recursive_ident_from_path(&ty, &mut idents_for_vec); + // let internal_type = idents_for_vec[1].clone(); let ( for_rn_count_per_item, @@ -386,7 +428,30 @@ impl GenFinal for FieldTokensGen { result }}; - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + let for_to_dist_mask = quote! {{ + let mut result: Vec = vec![]; + result.push(true); + for val in self.#field_name.iter() { + // always extend it + let vv: Vec = (0..#for_rn_count_per_item).into_iter().map(|_| true).collect(); + result.extend(vv.into_iter()); + } + + for _ in self.#field_name.len()..#max { + // these ones are all false! + let vv: Vec = (0..#for_rn_count_per_item).into_iter().map(|_| false).collect(); + result.extend(vv.into_iter()); + } + result + }}; + + ( + for_rn_count, + for_rn_names, + for_make_gen, + for_to_dist, + for_to_dist_mask, + ) } _ => unreachable!("not expecting an inner without a recursive outer"), }; @@ -396,6 +461,7 @@ impl GenFinal for FieldTokensGen { for_rn_names, for_make_gen: quote! { #field_name: #for_make_gen }, for_to_dist, + for_to_dist_mask, } } diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs index 397a189..9238744 100644 --- a/murrelet_gui/src/lib.rs +++ b/murrelet_gui/src/lib.rs @@ -33,7 +33,7 @@ pub enum MurreletEnumValGUI { pub enum MurreletGUISchema { NewType(String, Box), Struct(String, Vec<(String, MurreletGUISchema)>), // field val - Enum(String, Vec), // type, val + Enum(String, Vec, bool), // type, val, is untagged List(Box), Val(ValueGUI), Skip, @@ -48,7 +48,7 @@ impl MurreletGUISchema { } pub fn as_enum(&self) -> Option<&Vec> { - if let Self::Enum(_, v) = self { + if let Self::Enum(_, v, _) = self { Some(v) } else { None diff --git a/murrelet_gui_derive/src/derive_gui.rs b/murrelet_gui_derive/src/derive_gui.rs index c42cc10..f3e8056 100644 --- a/murrelet_gui_derive/src/derive_gui.rs +++ b/murrelet_gui_derive/src/derive_gui.rs @@ -66,7 +66,7 @@ impl GenFinal for FieldTokensGUI { } } - fn make_enum_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2 { + fn make_enum_final(idents: ParsedFieldIdent, variants: Vec, is_untagged: bool) -> TokenStream2 { let name = idents.name; let name_str = name.to_string(); @@ -76,7 +76,7 @@ impl GenFinal for FieldTokensGUI { quote! { impl murrelet_gui::CanMakeGUI for #name { fn make_gui() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::Enum(#name_str.to_owned(), vec![#(#for_make_gui,)*]) + murrelet_gui::MurreletGUISchema::Enum(#name_str.to_owned(), vec![#(#for_make_gui,)*], #is_untagged) } // fn gui_to_livecode(&self, gui_val: murrelet_gui::MurreletGUISchema) -> murrelet_gui::MurreletGUISchemaResult { diff --git a/murrelet_gui_derive/src/parser.rs b/murrelet_gui_derive/src/parser.rs index 52f7e1a..fc13306 100644 --- a/murrelet_gui_derive/src/parser.rs +++ b/murrelet_gui_derive/src/parser.rs @@ -32,7 +32,7 @@ where fn from_override_struct(idents: StructIdents, func: &str) -> Self; fn from_override_enum(func: &str) -> Self; - fn make_enum_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; + fn make_enum_final(idents: ParsedFieldIdent, variants: Vec, is_untagged: bool) -> TokenStream2; fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; fn make_newtype_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; @@ -105,7 +105,26 @@ where let idents = ParsedFieldIdent { name: name.clone() }; - Self::make_enum_final(idents, variants) + // "external" => quote! {}, + // "internal" => default, + // "untagged" => quote! {#[serde(untagged)]}, + // _ => default, + // let is_external = match &e.enum_tag.map(|x| x.as_str()) { + // Some("external") => true, + // None => false, + // _ => unimplemented!("enum type not implemented") + // }; + let is_untagged = if let Some(enum_tag) = &e.enum_tag { + if enum_tag.as_str() == "external" { + true + } else { + false + } + } else { + false + }; + + Self::make_enum_final(idents, variants, is_untagged) } fn make_newtype(s: &LivecodeReceiver) -> TokenStream2 { @@ -195,6 +214,7 @@ pub(crate) struct LivecodeVariantReceiver { pub(crate) struct LivecodeReceiver { ident: syn::Ident, data: ast::Data, + enum_tag: Option, } impl LivecodeReceiver {} diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index bf4dd23..1d4df58 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -242,6 +242,8 @@ impl AddSvgAttributes for MurreletStyle { if self.stroke_weight > 0.0 { p.assign("stroke-width", self.stroke_weight); + p.assign("stroke-linejoin", "round"); + p.assign("stroke-linecap", "round"); p.assign("stroke", self.stroke_color.get_svg_attributes()); } } @@ -250,6 +252,8 @@ impl AddSvgAttributes for MurreletStyle { if self.stroke_weight > 0.0 { p.assign("stroke-width", self.stroke_weight); + p.assign("stroke-linejoin", "round"); + p.assign("stroke-linecap", "round"); p.assign("stroke", self.color.get_svg_attributes()); } } @@ -258,6 +262,8 @@ impl AddSvgAttributes for MurreletStyle { if self.stroke_weight > 0.0 { p.assign("stroke-width", self.stroke_weight); + p.assign("stroke-linejoin", "round"); + p.assign("stroke-linecap", "round"); p.assign("stroke", self.color.get_svg_attributes()); } } From fd4a2f04a721bfe88251f214f93ecaffd6bf191b Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 30 Mar 2025 18:45:30 -0400 Subject: [PATCH 023/149] wip --- murrelet_draw/src/livecodetypes.rs | 4 ++++ murrelet_gen/examples/tests.rs | 7 +++++++ murrelet_gen_derive/src/gen_methods.rs | 7 ++++++- murrelet_gen_derive/src/parser.rs | 13 +++++-------- murrelet_gui_derive/src/derive_gui.rs | 6 +++++- murrelet_gui_derive/src/parser.rs | 6 +++++- 6 files changed, 32 insertions(+), 11 deletions(-) diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs index 24a9b8f..4db2435 100644 --- a/murrelet_draw/src/livecodetypes.rs +++ b/murrelet_draw/src/livecodetypes.rs @@ -24,6 +24,10 @@ pub mod anglepi { pub fn from_angle_pi(angle_pi: f32) -> Self { Self(angle_pi) } + + pub fn scale(&self, scale: f32) -> Self { + Self(self.0 * scale) + } } impl From for Angle { diff --git a/murrelet_gen/examples/tests.rs b/murrelet_gen/examples/tests.rs index 8c3f51a..f6b43aa 100644 --- a/murrelet_gen/examples/tests.rs +++ b/murrelet_gen/examples/tests.rs @@ -31,6 +31,13 @@ pub struct BasicTypes { xy: Vec2, #[murrelet_gen(method(vec2_circle(x = 0.0, y = 0.0, radius = 100.0)))] other_xy: Vec2, + #[murrelet_gen(method(vec2_uniform_grid( + x = 500.0, + y = 500.0, + width = 200.0, + height = 200.0 + )))] + another_xy: Vec2, #[murrelet_gen(method(color_normal))] normal_color: MurreletColor, // 3 rn #[murrelet_gen(method(color_transparency))] diff --git a/murrelet_gen_derive/src/gen_methods.rs b/murrelet_gen_derive/src/gen_methods.rs index ea2347d..b2555a8 100644 --- a/murrelet_gen_derive/src/gen_methods.rs +++ b/murrelet_gen_derive/src/gen_methods.rs @@ -165,7 +165,12 @@ impl GenMethod { }}; let for_to_dist = quote! { { - let c = (#name + 0.5 * glam::vec2(#width, #height)); + // let c = (#name + 0.5 * glam::vec2(#width, #height)); + // vec![c.x / #width, c.y / #height] + + let c = #name + - glam::vec2(#x, #y) + + 0.5 * glam::vec2(#width, #height); vec![c.x / #width, c.y / #height] }}; diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index 6f9ba0c..c64376b 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -145,10 +145,9 @@ where HowToControlThis::Recurse(_outer, _inner) => { // Self::from_type_recurse(idents, &outer, &inner) Self::from_newtype_struct(idents, name.clone()) - } - // HowToControlThis::Override(func, labels, count) => { - // Self::from_override_struct(idents, &func, labels, count) - // } + } // HowToControlThis::Override(func, labels, count) => { + // Self::from_override_struct(idents, &func, labels, count) + // } } }) .collect::>(); @@ -231,11 +230,9 @@ pub(crate) enum HowToControlThis { Type(GenMethod), Recurse(GenMethod, GenMethod), // one level... defaults to calling its func Default, // just do the default values - // Override(String, Vec, usize), + // Override(String, Vec, usize), } - - pub fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { match t { syn::Type::Path(syn::TypePath { path, .. }) => { @@ -258,4 +255,4 @@ pub fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { } x => panic!("no name for type {:?}", x), } -} \ No newline at end of file +} diff --git a/murrelet_gui_derive/src/derive_gui.rs b/murrelet_gui_derive/src/derive_gui.rs index f3e8056..491ffe2 100644 --- a/murrelet_gui_derive/src/derive_gui.rs +++ b/murrelet_gui_derive/src/derive_gui.rs @@ -66,7 +66,11 @@ impl GenFinal for FieldTokensGUI { } } - fn make_enum_final(idents: ParsedFieldIdent, variants: Vec, is_untagged: bool) -> TokenStream2 { + fn make_enum_final( + idents: ParsedFieldIdent, + variants: Vec, + is_untagged: bool, + ) -> TokenStream2 { let name = idents.name; let name_str = name.to_string(); diff --git a/murrelet_gui_derive/src/parser.rs b/murrelet_gui_derive/src/parser.rs index fc13306..23c2511 100644 --- a/murrelet_gui_derive/src/parser.rs +++ b/murrelet_gui_derive/src/parser.rs @@ -32,7 +32,11 @@ where fn from_override_struct(idents: StructIdents, func: &str) -> Self; fn from_override_enum(func: &str) -> Self; - fn make_enum_final(idents: ParsedFieldIdent, variants: Vec, is_untagged: bool) -> TokenStream2; + fn make_enum_final( + idents: ParsedFieldIdent, + variants: Vec, + is_untagged: bool, + ) -> TokenStream2; fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; fn make_newtype_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; From df0d166c5c568d3b1c3c71c4ff5c5c73382e4c3d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 5 Apr 2025 23:17:05 -0400 Subject: [PATCH 024/149] wip --- murrelet_common/src/transform.rs | 6 +++++ murrelet_draw/src/compass.rs | 26 +++++++++++++------- murrelet_draw/src/draw.rs | 9 +++++++ murrelet_draw/src/style.rs | 41 ++++++++++++++++++++++++++++++-- murrelet_draw/src/svg.rs | 13 +++++++++- murrelet_svg/src/svg.rs | 32 +++++++++++++++++++++++-- 6 files changed, 113 insertions(+), 14 deletions(-) diff --git a/murrelet_common/src/transform.rs b/murrelet_common/src/transform.rs index 92a7142..cdebda7 100644 --- a/murrelet_common/src/transform.rs +++ b/murrelet_common/src/transform.rs @@ -168,6 +168,12 @@ impl SimpleTransform2d { Self(v) } + pub fn rotate_pi(angle_pi: f32) -> Self { + Self(vec![SimpleTransform2dStep::rotate_pi(AnglePi::new( + angle_pi, + ))]) + } + pub fn noop() -> Self { Self(vec![]) } diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index c0b8ebe..fe72876 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -181,18 +181,22 @@ impl InteractiveCompassBuilder { } CompassAction::Arc(x) => { vec![self.add_arc(x)] - } // CompassAction::Repeat(x) => { - // let mut n = Vec::new(); - // for _ in 0..x.times { - // for w in &x.what { - // n.extend(self.new_segments(w)) - // } - // } - // n - // } + } } } + pub fn add_qline(&mut self, length: f32) { + self.add_segment(&CompassAction::qline(length)); + } + + pub fn add_qangle(&mut self, angle: A) { + self.add_segment(&CompassAction::qangle(angle)); + } + + pub fn add_qarc(&mut self, rad: f32, arc: A) { + self.add_segment(&CompassAction::qarc(rad, arc)); + } + pub fn add_segment(&mut self, dir: &CompassAction) { let r = { self.new_segments(dir) }; self.so_far.extend(r); @@ -295,6 +299,10 @@ impl InteractiveCompassBuilder { pub fn set_curr_angle(&mut self, curr_angle: A) { self.curr_angle = curr_angle.as_angle_pi(); } + + pub fn add_absolute_point(&mut self, loc: Vec2) { + self.so_far.push(CurveSegment::Points(CurvePoints { points: vec![loc] })) + } } #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index db1c4ac..b346fe3 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -322,6 +322,15 @@ pub trait Sdraw: Sized { self.with_svg_style(svg_style) } + fn as_line(&self) -> Self { + let svg_style = MurreletStyle { + filled: false, + closed: false, + ..self.svg_style() + }; + self.with_svg_style(svg_style) + } + fn transform(&self) -> Mat4; fn set_transform(&self, m: Mat4) -> Self; diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index d5b0a56..bae84f7 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -1,4 +1,6 @@ #![allow(dead_code)] +use std::collections::HashMap; + use crate::{curve_drawer::CurveDrawer, draw::*, svg::TransformedSvgShape, transform2d::*}; use glam::*; use lerpable::Lerpable; @@ -417,7 +419,7 @@ pub mod styleconf { Self::Fill(MurreletStyleFilled { color, stroke_weight: 0.0, - stroke_color: MurreletColor::black(), + stroke_color: MurreletColor::transparent(), }) } } @@ -534,20 +536,54 @@ impl MurreletPath { } } +#[derive(Debug, Clone)] +pub struct MurreletPathAnnotation(Vec<(String, String)>); +impl MurreletPathAnnotation { + pub fn noop() -> MurreletPathAnnotation { + Self(vec![]) + } + + pub fn new(annotation: (String, String)) -> Self { + Self(vec![annotation]) + } + + pub fn vals(&self) -> &Vec<(String, String)> { + &self.0 + } +} + #[derive(Debug, Clone)] pub struct StyledPath { pub path: MurreletPath, pub style: MurreletStyle, + pub annotations: MurreletPathAnnotation, // can be useful to attach information to a particular path, for interactions } impl StyledPath { pub fn new_from_path(path: MurreletPath, style: MurreletStyle) -> Self { - Self { path, style } + Self { + path, + style, + annotations: MurreletPathAnnotation::noop(), + } + } + + pub fn new_from_path_with_annotation( + path: MurreletPath, + style: MurreletStyle, + annotation: (String, String), + ) -> Self { + Self { + path, + style, + annotations: MurreletPathAnnotation::new(annotation), + } } pub fn new(path: F, style: MurreletStyle) -> Self { Self { path: MurreletPath::Polyline(path.as_polyline()), style, + annotations: MurreletPathAnnotation::noop(), } } @@ -555,6 +591,7 @@ impl StyledPath { StyledPath { path: MurreletPath::Polyline(path.as_polyline()), style: MurreletStyle::default(), + annotations: MurreletPathAnnotation::noop(), } } diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index e7a4041..5f2a119 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -1,6 +1,6 @@ // defines the SVG basic shapes -use glam::Mat4; +use glam::{Mat4, Vec2}; use lerpable::Lerpable; use murrelet_gui::MurreletGUI; use murrelet_livecode_derive::Livecode; @@ -30,6 +30,17 @@ impl SvgRect { height, } } + + pub fn new_at_loc(loc: Vec2, w_h: Vec2) -> Self { + Self { + x: loc.x, + y: loc.y, + rx: 0.0, + ry: 0.0, + width: w_h.x, + height: w_h.y, + } + } } #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 1d4df58..1a9f103 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -13,7 +13,7 @@ use murrelet_draw::{ curve_drawer::CurveDrawer, draw::{MurreletColorStyle, MurreletStyle}, newtypes::RGBandANewtype, - style::{MurreletCurve, MurreletPath, StyledPath, StyledPathSvgFill}, + style::{MurreletCurve, MurreletPath, MurreletPathAnnotation, StyledPath, StyledPathSvgFill}, svg::{SvgShape, TransformedSvgShape}, }; use murrelet_perform::perform::SvgDrawConfig; @@ -59,6 +59,10 @@ pub trait ToStyledGroup { pub trait ToSvgData { fn to_svg_data(&self) -> Option; + fn transform(&self) -> Option { + None + } + fn make_path(&self, style: &MurreletStyle) -> Option { if let Some(d) = self.to_svg_data() { let mut d = d; @@ -79,6 +83,9 @@ impl ToStyledGroup for T { fn to_group(&self, style: &MurreletStyle) -> Option { if let Some(p) = self.make_path(style) { let mut g = Group::new(); + if let Some(t) = self.transform() { + g = g.set("transform", t.to_svg_matrix()); + } g.append(p); Some(g) } else { @@ -91,6 +98,10 @@ impl ToSvgData for MurreletCurve { fn to_svg_data(&self) -> Option { self.curve().to_svg_data() } + + fn transform(&self) -> Option { + Some(self.mat4()) + } } impl ToStyledGroup for TransformedSvgShape { @@ -189,6 +200,7 @@ impl GetSvgAttributes for RGBandANewtype { impl AddSvgAttributes for MurreletColorStyle { fn add_svg_attributes(&self, p: T) -> T { let mut p = p; + p.assign("fill-rule", "evenodd"); match self { MurreletColorStyle::Color(c) => p.assign("fill", c.get_svg_attributes()), MurreletColorStyle::RgbaFill(c) => p.assign("fill", c.get_svg_attributes()), @@ -232,6 +244,8 @@ impl AddSvgAttributes for MurreletStyle { fn add_svg_attributes(&self, p: T) -> T { let mut p = p; + p.assign("fill-rule", "evenodd"); + match self.drawing_plan() { murrelet_draw::draw::MurreletDrawPlan::Shader(fill) => { p.assign("fill", fill.get_svg_attributes()) @@ -276,12 +290,23 @@ impl AddSvgAttributes for MurreletStyle { impl AddSvgAttributes for StyledPath { fn add_svg_attributes(&self, p: T) -> T { let mut p = p; + p = self.annotations.add_svg_attributes(p); p = self.path.add_svg_attributes(p); p = self.style.add_svg_attributes(p); p } } +impl AddSvgAttributes for MurreletPathAnnotation { + fn add_svg_attributes(&self, p: T) -> T { + let mut p = p; + for (k, v) in self.vals() { + p.assign(k.to_string(), v.to_string()); + } + p + } +} + pub trait ToSvgPath { fn make_group(&self) -> Option; fn make_pattern(&self) -> Option<(String, svg::node::element::Pattern)>; @@ -314,7 +339,9 @@ impl ToSvgPath for StyledPath { } fn make_group(&self) -> Option { - self.path.to_group(&self.style) + self.path + .to_group(&self.style) + .map(|x| self.annotations.add_svg_attributes(x)) } } @@ -661,6 +688,7 @@ impl SvgPathCache { StyledPath { path: MurreletPath::Polyline(path.as_polyline()), style: MurreletStyle::new_outline(), + annotations: MurreletPathAnnotation::noop(), }, ) } From 7dee3e94b03f0b3a712496e95c7994a404b7dc54 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 12 Apr 2025 18:14:14 -0400 Subject: [PATCH 025/149] wip --- murrelet_common/src/geometry.rs | 55 ++++++++++++++++++++++++++++++ murrelet_common/src/lib.rs | 1 + murrelet_draw/src/compass.rs | 3 +- murrelet_draw/src/livecodetypes.rs | 2 +- murrelet_perform/src/cli.rs | 2 ++ 5 files changed, 61 insertions(+), 2 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index d768c70..9d153bc 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -580,3 +580,58 @@ impl LineFromVecAndLen { self.start + self.length.len() * self.angle.to_norm_dir() } } + +// more curve things + +//https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Tangents_between_two_circles + +pub enum TangentBetweenCirclesKind { + RightRight, + LeftLeft, + RightLeft, + LeftRight, +} + +pub fn tangents_between_two_circles( + kind: TangentBetweenCirclesKind, + v1: Vec2, + r1: f32, + v2: Vec2, + r2: f32, +) -> Option<(Vec2, Vec2)> { + let d_sq = v1.distance_squared(v2); + + // these are too close together + if d_sq <= (r1 - r2).powi(2) { + return None; + } + + let d = d_sq.sqrt(); + + let v = (v2 - v1) / d; + + let (sign1, sign2) = match kind { + TangentBetweenCirclesKind::RightRight => (1.0, 1.0), + TangentBetweenCirclesKind::LeftLeft => (1.0, -1.0), + TangentBetweenCirclesKind::RightLeft => (-1.0, 1.0), + TangentBetweenCirclesKind::LeftRight => (-1.0, -1.0), + }; + + let c = (r1 - sign1 * r2) / d; + + // Now we're just intersecting a line with a circle: v*n=c, n*n=1 + + if c.powi(2) > 1.0 { + return None; + } + let h = (1.0 - c.powi(2)).max(0.0).sqrt(); + + let nx = v.x * c - sign2 * h * v.y; + let ny = v.y * c + sign2 * h * v.x; + + let start = vec2(v1.x + r1 * nx, v1.y + r1 * ny); + + let end = vec2(v2.x + sign1 * r2 * nx, v2.y + sign1 * r2 * ny); + + Some((start, end)) +} diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 86e8423..a429b67 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -10,6 +10,7 @@ use std::hash::{Hash, Hasher}; pub mod intersection; +pub mod arcs; mod assets; mod color; mod geometry; diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index fe72876..a3e3bf4 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -301,7 +301,8 @@ impl InteractiveCompassBuilder { } pub fn add_absolute_point(&mut self, loc: Vec2) { - self.so_far.push(CurveSegment::Points(CurvePoints { points: vec![loc] })) + self.so_far + .push(CurveSegment::Points(CurvePoints { points: vec![loc] })) } } diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs index 4db2435..9259058 100644 --- a/murrelet_draw/src/livecodetypes.rs +++ b/murrelet_draw/src/livecodetypes.rs @@ -8,7 +8,7 @@ pub mod anglepi { use murrelet_gui::CanMakeGUI; use murrelet_livecode_derive::Livecode; - #[derive(Clone, Copy, Debug, Livecode, Lerpable, Default)] + #[derive(Clone, Copy, Debug, Livecode, Lerpable, Default, PartialEq)] pub struct LivecodeAnglePi(f32); impl LivecodeAnglePi { pub const ZERO: Self = LivecodeAnglePi(0.0); diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index 72524d0..667325c 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -39,6 +39,8 @@ impl ToString for TextureDimensions { impl Default for TextureDimensions { fn default() -> Self { Self { + // width: 3000, + // height: 2000, width: 1080, height: 1080, } From 741dd39e879c67c9e8f7e2ad5c76992f131ef4d5 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 14 Apr 2025 16:19:30 -0400 Subject: [PATCH 026/149] wip --- murrelet_draw/src/style.rs | 8 +++++++ murrelet_svg/src/svg.rs | 48 +++++++++++++++++++++++++------------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index bae84f7..afdd298 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -408,6 +408,14 @@ pub mod styleconf { }) } + pub fn font(color: MurreletColor, font_size: f32) -> Self { + Self::Fill(MurreletStyleFilled { + color, + stroke_weight: font_size, + stroke_color: MurreletColor::transparent(), + }) + } + pub fn line(color: MurreletColor, stroke_weight: f32) -> Self { Self::Line(MurreletStyleLined { color, diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 1a9f103..310487f 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -458,21 +458,33 @@ impl SvgDocCreator { .set( "viewBox", ( - target_size / 2.0, - target_size / 2.0, - target_size, - target_size, + 0, 0, 200, + 200, // target_size / 2.0, + // target_size / 2.0, + // target_size, + // target_size, ), ) .set("width", format!("{:?}mm", target_size)) .set("height", format!("{:?}mm", target_size)); - for (name, layer) in paths.layers.iter() { - let (g, _) = self.make_layer(name, layer, true); - doc.append(g); - } + // todo, maybe figure out defs? + let (group, _) = self.make_html(paths); + + let mut centering_group = svg::node::element::Group::new(); + centering_group = centering_group.set("transform", "translate(400px, 400px)"); + centering_group = centering_group.add(group); + + doc = doc.add(centering_group); doc + + // for (name, layer) in paths.layers.iter() { + // let (g, _) = self.make_layer(name, layer, true); + // doc.append(g); + // } + + // doc } pub fn create_guides(&self) -> Vec> { @@ -790,16 +802,20 @@ impl ToSvgData for Vec { return None; } - // whee, flip y's so we stop drawing everything upside down + // todo, hmmm, see if we can consolidate this. + let cd = CurveDrawer::new_simple_points(self.clone(), false); + cd.to_svg_data() - let mut curr_item: Vec2 = *self.first().unwrap(); + // // whee, flip y's so we stop drawing everything upside down - let mut data = Data::new().move_to((curr_item.x, -curr_item.y)); + // let mut curr_item: Vec2 = *self.first().unwrap(); - for loc in self[1..].iter() { - data = data.line_by((loc.x - curr_item.x, -(loc.y - curr_item.y))); - curr_item = *loc; - } - Some(data) + // let mut data = Data::new().move_to((curr_item.x, -curr_item.y)); + + // for loc in self[1..].iter() { + // data = data.line_by((loc.x - curr_item.x, -(loc.y - curr_item.y))); + // curr_item = *loc; + // } + // Some(data) } } From b9aee5d7685997ee0a92c1420ea82c0d91cdd95a Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 17 Apr 2025 16:09:29 -0400 Subject: [PATCH 027/149] wip --- murrelet_draw/src/transform2d.rs | 4 ++-- murrelet_perform/src/cli.rs | 4 ++-- murrelet_perform/src/perform.rs | 12 ++++++++++++ murrelet_svg/src/svg.rs | 33 +++++++++++++++++++++----------- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index 15979e3..a087404 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -106,10 +106,10 @@ impl Transform2d { mat4_from_mat3_transform(self.to_mat3()) } - pub fn rotate_around(angle_pi: f32, v: Vec2) -> Transform2d { + pub fn rotate_around(angle_pi: A, v: Vec2) -> Transform2d { Transform2d::new(vec![Transform2dStep::Rotate(Rotate2::new( v, - a_pi(angle_pi), + angle_pi, ))]) } diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index 667325c..f55d781 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -41,8 +41,8 @@ impl Default for TextureDimensions { Self { // width: 3000, // height: 2000, - width: 1080, - height: 1080, + width: 1540, + height: 1540, } } } diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 55dce31..1ddc69a 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -46,6 +46,7 @@ pub struct SvgDrawConfig { target_size: f32, // in mm margin_size: f32, should_resize: bool, // sorry, something to force it not to resize my shapes on the web! + bg_color: Option, } impl SvgDrawConfig { pub fn new( @@ -63,9 +64,16 @@ impl SvgDrawConfig { frame, should_resize: true, resolution, + bg_color: None } } + pub fn with_bg_color(&self, bg_color: MurreletColor) -> Self { + let mut c = self.clone(); + c.bg_color = Some(bg_color); + c + } + pub fn with_no_resize(&self) -> Self { let mut c = self.clone(); c.should_resize = false; @@ -108,6 +116,10 @@ impl SvgDrawConfig { pub fn frame(&self) -> u64 { self.frame } + + pub fn bg_color(&self) -> Option { + self.bg_color + } } impl TransformVec2 for SvgDrawConfig { diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 310487f..d96ae84 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -450,29 +450,40 @@ impl SvgDocCreator { // this one's meant for svgs for pen plotters, so it drops fill styles fn make_doc(&self, paths: &SvgPathCache) -> Document { let target_size = self.svg_draw_config.full_target_width(); // guides are at 10x 10x, gives 1cm margin + + let (view_box_x, view_box_y) = if let Some(r) = self.svg_draw_config.resolution { + let [width, height] = r.as_dims(); + (width * 2, height * 2) + } else { + (800, 800) + }; let mut doc = Document::new() .set( "xmlns:inkscape", "http://www.inkscape.org/namespaces/inkscape", ) - .set( - "viewBox", - ( - 0, 0, 200, - 200, // target_size / 2.0, - // target_size / 2.0, - // target_size, - // target_size, - ), - ) + .set("viewBox", (0, 0, view_box_x, view_box_y)) .set("width", format!("{:?}mm", target_size)) .set("height", format!("{:?}mm", target_size)); + if let Some(bg_color) = self.svg_draw_config.bg_color() { + let bg_rect = svg::node::element::Rectangle::new() + .set("x", 0) + .set("y", 0) + .set("width", view_box_x) + .set("height", view_box_y) + .set("fill", bg_color.to_svg_rgb()); + doc = doc.add(bg_rect); + } + // todo, maybe figure out defs? let (group, _) = self.make_html(paths); let mut centering_group = svg::node::element::Group::new(); - centering_group = centering_group.set("transform", "translate(400px, 400px)"); + centering_group = centering_group.set( + "transform", + format!("translate({}px, {}px)", view_box_x / 2, view_box_y / 2), + ); centering_group = centering_group.add(group); doc = doc.add(centering_group); From dfcb1d37a1267689948ca653fa8f9bbd1115d5cd Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 29 Apr 2025 22:03:53 -0400 Subject: [PATCH 028/149] wip --- murrelet_common/src/geometry.rs | 6 +- murrelet_common/src/idx.rs | 2 +- murrelet_common/src/lib.rs | 72 +++++++++++++++++ murrelet_common/src/transform.rs | 6 +- murrelet_draw/src/curve_drawer.rs | 20 +++++ murrelet_draw/src/style.rs | 28 ++++++- murrelet_draw/src/svg.rs | 120 ++++++++++++++++++++++++++++- murrelet_draw/src/transform2d.rs | 5 ++ murrelet_livecode/src/unitcells.rs | 13 ++++ murrelet_perform/src/cli.rs | 4 +- murrelet_svg/src/svg.rs | 33 +++++++- 11 files changed, 296 insertions(+), 13 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 9d153bc..e2cece3 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -6,7 +6,7 @@ use glam::{vec2, Mat3, Mat4, Vec2}; use crate::{ intersection::{find_intersection_inf, within_segment}, - transform::TransformVec2, + transform::TransformVec2, SimpleTransform2d, }; pub fn a_pi(a: f32) -> AnglePi { @@ -22,6 +22,10 @@ impl AnglePi { AnglePi(v) } + pub fn to_transform(&self) -> SimpleTransform2d { + SimpleTransform2d::rotate_pi(self.angle_pi()) + } + pub fn _angle(&self) -> f32 { let a: Angle = (*self).into(); a.angle() diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index bdb8dba..52daf59 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -4,7 +4,7 @@ use glam::{vec2, Vec2}; use itertools::Itertools; use rand::{rngs::StdRng, Rng, SeedableRng}; -use crate::lerp; +use crate::{lerp, Dim2d}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct IdxInRange { diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index a429b67..c6ed4b6 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -864,3 +864,75 @@ pub fn make_gui_vec2_coords() -> murrelet_gui::MurreletGUISchema { pub fn make_gui_vec3() -> murrelet_gui::MurreletGUISchema { murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Vec3) } + +#[derive(Clone, Copy, Debug)] +pub struct Dim2d { + x: usize, // e.g. rows + y: usize, // e.g. cols +} + +impl Dim2d { + pub fn new(x: usize, y: usize) -> Self { + Self { x, y } + } + + pub fn x(&self) -> usize { + self.x + } + + pub fn y(&self) -> usize { + self.y + } + + pub fn row(&self) -> usize { + self.y + } + + pub fn col(&self) -> usize { + self.x + } + + pub fn i(&self) -> usize { + self.y + } + + pub fn j(&self) -> usize { + self.x + } + + pub fn i_plus_1(&self) -> Self { + let i = self.i(); + let j = self.j(); + Self::from_i_j(i + 1, j) + } + + pub fn j_plus_1(&self) -> Self { + let i = self.i(); + let j = self.j(); + Self::from_i_j(i, j + 1) + } + + pub fn from_x_y>(x: T, y: T) -> Self + where + T::Error: std::fmt::Debug, + { + Self { + x: x.try_into().expect("Conversion failed") as usize, + y: y.try_into().expect("Conversion failed") as usize, + } + } + + pub fn from_row_col>(row: T, col: T) -> Self + where + T::Error: std::fmt::Debug, + { + Self::from_x_y(col, row) + } + + pub fn from_i_j>(i: T, j: T) -> Self + where + T::Error: std::fmt::Debug, + { + Self::from_x_y(j, i) + } +} diff --git a/murrelet_common/src/transform.rs b/murrelet_common/src/transform.rs index cdebda7..4ce3f33 100644 --- a/murrelet_common/src/transform.rs +++ b/murrelet_common/src/transform.rs @@ -58,9 +58,9 @@ pub fn mat4_from_mat3_transform(m: Mat3) -> Mat4 { // i don't know about this.. // println!("_z_z {:?}", _z_z); if z_z != 1.0 { - println!("trying to turn a mat3 to mat4 with invalid values"); - println!("z_z {:?}", z_z); - println!("m {:?}", m); + // println!("trying to turn a mat3 to mat4 with invalid values"); + // println!("z_z {:?}", z_z); + // println!("m {:?}", m); } let z_axis = vec3(z_x, z_y, 0.0); diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index d707305..fe7d8f1 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -1,4 +1,5 @@ use glam::*; +use itertools::Itertools; use lerpable::Lerpable; use murrelet_common::*; use murrelet_livecode_derive::*; @@ -107,6 +108,13 @@ impl CurveSegment { } } + pub fn reverse(&self) -> Self { + match self { + CurveSegment::Arc(curve_arc) => CurveSegment::Arc(curve_arc.reverse()), + CurveSegment::Points(curve_points) => CurveSegment::Points(curve_points.reverse()), + } + } + pub fn new_simple_arc( loc: Vec2, radius: Rad, @@ -186,6 +194,14 @@ impl CurveArc { self.end_pi.angle_pi() > self.start_pi.angle_pi() } + pub fn reverse(&self) -> Self { + Self { + start_pi: self.end_pi, + end_pi: self.start_pi, + ..*self + } + } + // useful for svg pub fn is_large_arc(&self) -> bool { (self.end_pi.angle_pi() - self.start_pi.angle_pi()).abs() > 1.0 @@ -259,4 +275,8 @@ impl CurvePoints { pub fn points(&self) -> &Vec { &self.points } + + pub fn reverse(&self) -> Self { + CurvePoints::new(self.points.iter().cloned().rev().collect_vec()) + } } diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index afdd298..43ef2f9 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -430,6 +430,18 @@ pub mod styleconf { stroke_color: MurreletColor::transparent(), }) } + + pub fn outlined_fill( + color: MurreletColor, + stroke_weight: f32, + stroke_color: MurreletColor, + ) -> Self { + Self::Fill(MurreletStyleFilled { + color, + stroke_weight, + stroke_color, + }) + } } impl Default for StyleConf { @@ -483,6 +495,8 @@ impl MurreletCurve { pub fn curve(&self) -> &CurveDrawer { &self.cd } + + } #[derive(Debug, Clone)] @@ -490,6 +504,7 @@ pub enum MurreletPath { Polyline(Polyline), Curve(MurreletCurve), Svg(TransformedSvgShape), + MaskedCurve(MurreletCurve, MurreletCurve), // first is mask } impl MurreletPath { pub fn polyline(path: F) -> Self { @@ -508,6 +523,7 @@ impl MurreletPath { }, MurreletPath::Curve(c) => c.clone(), MurreletPath::Svg(_) => todo!(), + MurreletPath::MaskedCurve(_mask, c) => c.clone(), } } @@ -515,7 +531,8 @@ impl MurreletPath { match self { MurreletPath::Polyline(x) => MurreletPath::Polyline(x.transform_with(t)), MurreletPath::Curve(mc) => todo!(), - MurreletPath::Svg(_) => todo!(), // i'm not sure how i want to handle this yet + MurreletPath::Svg(_) => todo!(), + MurreletPath::MaskedCurve(murrelet_curve, murrelet_curve1) => todo!(), } } @@ -524,6 +541,10 @@ impl MurreletPath { MurreletPath::Polyline(_) => self.transform_with(&t), MurreletPath::Curve(c) => MurreletPath::Curve(c.transform_with_mat4_after(t)), MurreletPath::Svg(c) => MurreletPath::Svg(c.transform_with_mat4_after(t)), + MurreletPath::MaskedCurve(mask, curve) => MurreletPath::MaskedCurve( + mask.transform_with_mat4_after(t), + curve.transform_with_mat4_after(t), + ), } } @@ -532,6 +553,10 @@ impl MurreletPath { MurreletPath::Polyline(_) => self.transform_with(&t), MurreletPath::Curve(c) => MurreletPath::Curve(c.transform_with_mat4_before(t)), MurreletPath::Svg(c) => MurreletPath::Svg(c.transform_with_mat4_before(t)), + MurreletPath::MaskedCurve(mask, curve) => MurreletPath::MaskedCurve( + mask.transform_with_mat4_before(t), + curve.transform_with_mat4_before(t), + ), } } @@ -540,6 +565,7 @@ impl MurreletPath { MurreletPath::Polyline(_) => None, MurreletPath::Curve(c) => Some(c.t), MurreletPath::Svg(c) => Some(c.t), + MurreletPath::MaskedCurve(_mask, c) => Some(c.t), } } } diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index 5f2a119..4a3c3d1 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -22,8 +22,8 @@ pub struct SvgRect { impl SvgRect { pub fn new_centered(width: f32, height: f32) -> Self { Self { - x: 0.0, - y: 0.0, + x: -width / 2.0, + y: -height / 2.0, rx: 0.0, ry: 0.0, width, @@ -33,8 +33,8 @@ impl SvgRect { pub fn new_at_loc(loc: Vec2, w_h: Vec2) -> Self { Self { - x: loc.x, - y: loc.y, + x: loc.x - w_h.x / 2.0, + y: loc.y - w_h.y / 2.0, rx: 0.0, ry: 0.0, width: w_h.x, @@ -53,10 +53,122 @@ pub struct SvgCircle { pub r: f32, } +#[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] +pub struct SvgTo { + x: f32, + y: f32, +} +impl SvgTo { + pub fn params(&self) -> (f32, f32) { + (self.x, self.y) + } +} + +#[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] +pub struct SvgCubicBezier { + ctrl1_x: f32, + ctrl1_y: f32, + ctrl2_x: f32, + ctrl2_y: f32, + to_x: f32, + to_y: f32, +} +impl SvgCubicBezier { + pub fn params(&self) -> (f32, f32, f32, f32, f32, f32) { + ( + self.ctrl1_x, + self.ctrl1_y, + self.ctrl2_x, + self.ctrl2_y, + self.to_x, + self.to_y, + ) + } +} + +#[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] +pub enum SvgCmd { + Line(SvgTo), + CubicBezier(SvgCubicBezier), +} + +#[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] +pub struct SvgPathDef { + start: SvgTo, + v: Vec, +} + +impl SvgPathDef { + pub fn new(start: Vec2) -> Self { + Self { + start: SvgTo { + x: start.x, + y: start.y, + }, + v: Vec::new(), + } + } + + pub fn add_line(&mut self, to: Vec2) { + self.v.push(SvgCmd::Line(SvgTo { x: to.x, y: to.y })); + } + + pub fn add_cubic_bezier(&mut self, ctrl1: Vec2, ctrl2: Vec2, to: Vec2) { + self.v.push(SvgCmd::CubicBezier(SvgCubicBezier { + ctrl1_x: ctrl1.x, + ctrl1_y: ctrl1.y, + ctrl2_x: ctrl2.x, + ctrl2_y: ctrl2.y, + to_x: to.x, + to_y: to.y, + })) + } + + pub fn svg_move_to(&self) -> (f32, f32) { + (self.start.x, self.start.y) + } + + pub fn cmds(&self) -> &[SvgCmd] { + &self.v + } +} + #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] pub enum SvgShape { Rect(SvgRect), Circle(SvgCircle), + Path(SvgPathDef), +} + +impl SvgShape { + pub fn new_rect_at_loc(loc: Vec2, w_h: Vec2) -> Self { + SvgShape::Rect(SvgRect::new_at_loc(loc, w_h)) + } + + pub fn new_centered_rect(width: f32, height: f32) -> Self { + SvgShape::Rect(SvgRect::new_centered(width, height)) + } + pub fn new_centered_circle(radius: f32) -> Self { + SvgShape::Circle(SvgCircle { + x: 0.0, + y: 0.0, + r: radius, + }) + } + + pub fn transform(&self, t: Mat4) -> TransformedSvgShape { + TransformedSvgShape { + shape: self.clone(), + t, + } + } + + pub fn as_transform(&self) -> TransformedSvgShape { + TransformedSvgShape { + shape: self.clone(), + t: Mat4::IDENTITY, + } + } } #[derive(Clone, Debug)] diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index a087404..c7e9587 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -22,6 +22,11 @@ impl Transform2d { self.0 = vec![actions.to_vec(), self.0.clone()].concat(); } + pub fn prepend_one_action(&mut self, action: Transform2dStep) { + self.0 = vec![vec![action], self.0.clone()].concat(); + + } + pub fn append_one_action(&mut self, action: Transform2dStep) { self.0.push(action) } diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index a73c324..76b449e 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -290,6 +290,15 @@ impl UnitCellLookup { Self { data, maxes } } + pub fn x_y_z_max(&self) -> (u64, u64, u64) { + self.maxes + } + + pub fn dims(&self) -> Dim2d { + let (x, y, _) = self.maxes; + Dim2d::from_x_y(x, y) + } + pub fn to_vec2d(&self) -> Vec>>> { let mut vs = vec![]; @@ -370,6 +379,10 @@ impl UnitCellLookup { .get(&(i as u64, j as u64, 0)) .map(|x| *(x.node).clone()) } + + pub fn force_get_dim(&self, dim: &Dim2d) -> &UnitCell { + self.force_get_ij(dim.i(), dim.j()) + } } #[derive(PartialEq, Eq, Copy, Clone)] diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index f55d781..667325c 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -41,8 +41,8 @@ impl Default for TextureDimensions { Self { // width: 3000, // height: 2000, - width: 1540, - height: 1540, + width: 1080, + height: 1080, } } } diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index d96ae84..662155d 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -14,7 +14,7 @@ use murrelet_draw::{ draw::{MurreletColorStyle, MurreletStyle}, newtypes::RGBandANewtype, style::{MurreletCurve, MurreletPath, MurreletPathAnnotation, StyledPath, StyledPathSvgFill}, - svg::{SvgShape, TransformedSvgShape}, + svg::{SvgPathDef, SvgShape, TransformedSvgShape}, }; use murrelet_perform::perform::SvgDrawConfig; use svg::{ @@ -134,6 +134,14 @@ impl ToStyledGroup for TransformedSvgShape { g.append(circ); Some(g) } + SvgShape::Path(svg_path) => { + if let Some(data) = svg_path.to_svg_data() { + let mut path = svg::node::element::Path::new().set("d", data); + path = style.add_svg_attributes(path); + g.append(path); + } + Some(g) + } } } } @@ -144,6 +152,7 @@ impl ToStyledGroup for MurreletPath { MurreletPath::Polyline(path) => path.into_iter_vec2().collect_vec().to_group(style), MurreletPath::Curve(c) => c.to_group(style), MurreletPath::Svg(c) => c.to_group(style), + MurreletPath::MaskedCurve(_, _) => todo!(), //curve.to_group_with_mask(style, mask), } } } @@ -380,7 +389,9 @@ impl SvgDocCreator { .set("x", text.loc.x) .set("y", text.loc.y) .set("text-anchor", "middle") + .set("font-family", "monospace".to_string()) .set("font-size", format!("{}px", text_size)) + .set("fill", text.style.color.get_svg_attributes()) .add(svg::node::Text::new(text.text.clone())); text @@ -738,6 +749,26 @@ impl SvgPathCache { // } +// why am i doing this again, it is a good question +impl ToSvgData for SvgPathDef { + fn to_svg_data(&self) -> Option { + let mut path = Data::new(); + + path = path.move_to(self.svg_move_to()); + + for v in self.cmds() { + match v { + murrelet_draw::svg::SvgCmd::Line(svg_to) => path = path.line_to(svg_to.params()), + murrelet_draw::svg::SvgCmd::CubicBezier(svg_cubic_bezier) => { + path = path.cubic_curve_to(svg_cubic_bezier.params()) + } + } + } + + Some(path) + } +} + impl ToSvgData for CurveDrawer { fn to_svg_data(&self) -> Option { let segments = self.segments(); From c6832fb28d3bca4259fb66a618098d7c84833af3 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 29 Apr 2025 23:25:33 -0400 Subject: [PATCH 029/149] wip --- murrelet_livecode/src/expr.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index 8fcf4a0..4c9fc4e 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -241,6 +241,10 @@ impl ExprWorldContextValues { Self(v) } + pub fn to_mixed_defs(&self) -> MixedEvalDefs { + MixedEvalDefs::new_from_expr(self.clone()) + } + pub fn update_ctx(&self, ctx: &mut HashMapContext) -> LivecodeResult<()> { for (identifier, value) in &self.0 { // todo, maybe handle the result here to help dev From 9de0378c3623c77cf497984feef2effa7228a226 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 30 Apr 2025 15:27:22 -0400 Subject: [PATCH 030/149] wip --- murrelet_gpu/src/graphics_ref.rs | 85 ++++++++++++++++++++++++++++++-- murrelet_gpu/src/shader_str.rs | 4 +- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index 89d0f5a..b846116 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -178,6 +178,10 @@ impl Triangulate { } } + pub fn set_order(&mut self, u: Vec) { + self.order = u; + } + fn order(&self) -> &[u16] { &self.order } @@ -185,6 +189,10 @@ impl Triangulate { pub fn indices(&self) -> &[u16] { &self.order } + + pub fn add_order(&mut self, collect: &[u16]) { + self.order.extend_from_slice(collect); + } } // this is the conf that you'll interface with @@ -203,6 +211,13 @@ impl InputVertexConf { self.order.as_slice() } + pub fn from_triangulate_2d(t: &Triangulate) -> Self { + let mut c = Self::default(); + c.vertices = t.vertices.clone(); + c.order = t.order.clone(); + c + } + pub fn from_triangulate(t: &Triangulate) -> Self { let mut c = Self::default(); c.is_3d = true; @@ -378,8 +393,12 @@ impl GraphicsCreator { self } - pub fn with_custom_triangle(mut self, t: &Triangulate) -> Self { - self.input_vertex = InputVertexConf::from_triangulate(t); + pub fn with_custom_triangle(mut self, t: &Triangulate, is_3d: bool) -> Self { + if is_3d { + self.input_vertex = InputVertexConf::from_triangulate(t); + } else { + self.input_vertex = InputVertexConf::from_triangulate_2d(t); + } self } @@ -671,6 +690,62 @@ impl GraphicsRef { let col = self.graphics.borrow().conf.input_vertex.view.view_proj; Mat4::from_cols_array_2d(&col) } + + pub fn update_tri(&mut self, c: &GraphicsWindowConf, tri: Triangulate) { + // capture previous buffer sizes + let (old_vert_bytes_len, old_index_bytes_len) = { + let g = self.graphics.borrow(); + ( + bytemuck::cast_slice::(&g.conf.input_vertex.vertices).len(), + bytemuck::cast_slice::(&g.conf.input_vertex.order).len(), + ) + }; + + { + let mut g = self.graphics.borrow_mut(); + g.conf.input_vertex.vertices = tri.vertices.clone(); + g.conf.input_vertex.order = tri.order.clone(); + let queue = c.device.queue(); + + // vertex buffer: either recreate or overwrite + let new_vert_bytes = bytemuck::cast_slice::(&g.conf.input_vertex.vertices); + if new_vert_bytes.len() > old_vert_bytes_len { + // recreate vertex buffer with new size + let vb = c.device.device().create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("vertex buffer"), + contents: new_vert_bytes, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + }); + g.vertex_buffers.vertex = vb; + } else { + queue.write_buffer(&g.vertex_buffers.vertex, 0, new_vert_bytes); + } + + // index buffer with 4-byte alignment: recreate if growing + const ALIGN: usize = 4; + let raw_index = bytemuck::cast_slice::(&g.conf.input_vertex.order); + let (index_bytes, needs_recreate) = if raw_index.len() % ALIGN != 0 { + // pad to alignment + let pad = ALIGN - (raw_index.len() % ALIGN); + let mut data = Vec::with_capacity(raw_index.len() + pad); + data.extend_from_slice(raw_index); + data.extend(std::iter::repeat(0).take(pad)); + (data.into_boxed_slice(), raw_index.len() + pad > old_index_bytes_len) + } else { + (raw_index.into(), raw_index.len() > old_index_bytes_len) + }; + if needs_recreate { + let ib = c.device.device().create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("index buffer"), + contents: &index_bytes, + usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, + }); + g.vertex_buffers.index = ib; + } else { + queue.write_buffer(&g.vertex_buffers.index, 0, &index_bytes); + } + } + } } #[derive(Clone)] @@ -904,7 +979,7 @@ impl Graphics { fn _sampler(device: &wgpu::Device, details: ShaderOptions) -> wgpu::Sampler { let sampler_desc = details.as_sampler_desc(); let sampler = device.create_sampler(&sampler_desc); - println!("sampler: {:?}, {:?}", sampler, sampler_desc); + // println!("sampler: {:?}, {:?}", sampler, sampler_desc); sampler } @@ -1451,12 +1526,12 @@ impl VertexBuffers { let vertex = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(&conf.vertices[..]), - usage: wgpu::BufferUsages::VERTEX, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, }); let order = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(&conf.order[..]), - usage: wgpu::BufferUsages::INDEX, + usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, }); let uniform = conf.view.to_buffer(device); diff --git a/murrelet_gpu/src/shader_str.rs b/murrelet_gpu/src/shader_str.rs index 509cdd1..4bc95b6 100644 --- a/murrelet_gpu/src/shader_str.rs +++ b/murrelet_gpu/src/shader_str.rs @@ -297,8 +297,8 @@ fn main(@location(0) pos: vec3, @location(1) normal: vec3, @location(2 return VertexOutput( tex_coords, vec4(0.0), // shad_info - vec3(0.0), //normal - vec4(0.0), //light space pos + normal, + vec4(face_loc, 0.0, 0.0), //face loc vec3(0.0), //world_pos, out_pos); }"; From 1ca55934097c44dae8b2b95058a8de62191a1ff7 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 27 May 2025 01:12:54 +0200 Subject: [PATCH 031/149] add sending osc --- murrelet_common/src/geometry.rs | 13 ++++++++- murrelet_common/src/lib.rs | 6 ++-- murrelet_draw/src/curve_drawer.rs | 2 ++ murrelet_draw/src/draw.rs | 1 + murrelet_draw/src/livecodetypes.rs | 9 ++++++ murrelet_draw/src/style.rs | 8 +++++ murrelet_draw/src/svg.rs | 1 + murrelet_gpu/src/graphics_ref.rs | 35 ++++++++++++---------- murrelet_livecode/src/types.rs | 22 +++++++++----- murrelet_perform/src/perform.rs | 12 ++++++-- murrelet_src_midi/src/midi.rs | 6 +++- murrelet_src_osc/src/osc.rs | 47 +++++++++++++++++++++++++++--- 12 files changed, 129 insertions(+), 33 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index e2cece3..28f7abf 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -6,7 +6,8 @@ use glam::{vec2, Mat3, Mat4, Vec2}; use crate::{ intersection::{find_intersection_inf, within_segment}, - transform::TransformVec2, SimpleTransform2d, + transform::TransformVec2, + SimpleTransform2d, }; pub fn a_pi(a: f32) -> AnglePi { @@ -563,6 +564,16 @@ impl PointToPoint { pub fn end(&self) -> Vec2 { self.end } + + pub fn closest_pt_to_pt(&self, intersection: Vec2) -> PointToPoint { + let closest_point = + self.start + (intersection - self.start).dot(self.to_norm_dir()) * self.to_norm_dir(); + + PointToPoint { + start: intersection, + end: closest_point, + } + } } #[derive(Copy, Clone, Debug)] diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index c6ed4b6..74b602b 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -469,7 +469,7 @@ pub trait IsLivecodeSrc { fn to_exec_funcs(&self) -> Vec<(String, LivecodeValue)>; // this is a way to give usage feedback to the livecode src, e.g. tell a MIDI controller // we're using a parameter, or what value to set indicator lights to. - fn feedback(&mut self, _variables: &HashMap) { + fn feedback(&mut self, _variables: &HashMap, _outgoing_msgs: &[(String, String, LivecodeValue)]) { // default don't do anything } } @@ -579,9 +579,9 @@ impl LivecodeSrc { self.vs.iter().flat_map(|v| v.to_exec_funcs()).collect_vec() } - pub fn feedback(&mut self, variables: &HashMap) { + pub fn feedback(&mut self, variables: &HashMap, outgoing_msgs: &[(String, String, LivecodeValue)]) { for v in self.vs.iter_mut() { - v.feedback(variables); + v.feedback(variables, outgoing_msgs); } } } diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index fe7d8f1..eac0664 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -85,6 +85,8 @@ impl CurveDrawer { let last_command = self.segments().last()?; Some(last_command.last_point()) } + + } #[derive(Debug, Clone, Livecode, Lerpable)] diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index b346fe3..835634f 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -84,6 +84,7 @@ impl MurreletColorStyle { } } +#[derive(Debug, Clone)] pub enum MurreletDrawPlan { Shader(StyledPathSvgFill), DebugPoints(PixelShape), diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs index 9259058..e68cd0d 100644 --- a/murrelet_draw/src/livecodetypes.rs +++ b/murrelet_draw/src/livecodetypes.rs @@ -3,11 +3,14 @@ pub mod anglepi { use std::ops::Sub; + use glam::Vec2; use lerpable::Lerpable; use murrelet_common::{Angle, AnglePi, IsAngle}; use murrelet_gui::CanMakeGUI; use murrelet_livecode_derive::Livecode; + use crate::transform2d::Transform2d; + #[derive(Clone, Copy, Debug, Livecode, Lerpable, Default, PartialEq)] pub struct LivecodeAnglePi(f32); impl LivecodeAnglePi { @@ -28,6 +31,12 @@ pub mod anglepi { pub fn scale(&self, scale: f32) -> Self { Self(self.0 * scale) } + + pub fn transform_vec2(&self, v: glam::Vec2) -> Vec2 { + Transform2d::rotate(self.angle_pi()) + .to_mat3() + .transform_vector2(v) + } } impl From for Angle { diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 43ef2f9..6303960 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -401,6 +401,10 @@ pub mod styleconf { self.to_style().color.as_color() } + pub fn stroke_weight(&self) -> f32 { + self.to_style().stroke_weight + } + pub fn outline(color: MurreletColor, stroke_weight: f32) -> Self { Self::Outline(MurreletStyleOutlined { color, @@ -442,6 +446,10 @@ pub mod styleconf { stroke_color, }) } + + pub fn fill_color(&self) -> MurreletColor { + self.color() + } } impl Default for StyleConf { diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index 4a3c3d1..ae3ef26 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -74,6 +74,7 @@ pub struct SvgCubicBezier { to_y: f32, } impl SvgCubicBezier { + pub fn params(&self) -> (f32, f32, f32, f32, f32, f32) { ( self.ctrl1_x, diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index b846116..f6810a7 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -139,7 +139,7 @@ pub struct Scene { #[derive(Debug, Clone)] pub struct Triangulate { vertices: Vec, - order: Vec, + order: Vec, } impl Triangulate { @@ -154,10 +154,10 @@ impl Triangulate { &self.vertices } - pub fn add_vertex(&mut self, v: [f32; 3], n: [f32; 3], face_pos: [f32; 2]) -> u16 { + pub fn add_vertex(&mut self, v: [f32; 3], n: [f32; 3], face_pos: [f32; 2]) -> u32 { let vv = Vertex::new(v, n, face_pos); self.vertices.push(vv); - (self.vertices.len() - 1) as u16 + (self.vertices.len() - 1) as u32 } // alternatively can add vertices and then add teh vec @@ -178,19 +178,19 @@ impl Triangulate { } } - pub fn set_order(&mut self, u: Vec) { + pub fn set_order(&mut self, u: Vec) { self.order = u; } - fn order(&self) -> &[u16] { + fn order(&self) -> &[u32] { &self.order } - pub fn indices(&self) -> &[u16] { + pub fn indices(&self) -> &[u32] { &self.order } - pub fn add_order(&mut self, collect: &[u16]) { + pub fn add_order(&mut self, collect: &[u32]) { self.order.extend_from_slice(collect); } } @@ -203,11 +203,11 @@ pub struct InputVertexConf { view: VertexUniforms, topology: wgpu::PrimitiveTopology, vertices: Vec, - order: Vec, + order: Vec, } impl InputVertexConf { - pub fn buffer_slice(&self) -> &[u16] { + pub fn buffer_slice(&self) -> &[u32] { self.order.as_slice() } @@ -381,6 +381,8 @@ impl Default for GraphicsCreator { } } impl GraphicsCreator { + + pub fn with_first_texture_format(mut self, format: wgpu::TextureFormat) -> Self { self.first_texture = TextureCreator { format }; self @@ -697,7 +699,7 @@ impl GraphicsRef { let g = self.graphics.borrow(); ( bytemuck::cast_slice::(&g.conf.input_vertex.vertices).len(), - bytemuck::cast_slice::(&g.conf.input_vertex.order).len(), + bytemuck::cast_slice::(&g.conf.input_vertex.order).len(), ) }; @@ -723,7 +725,7 @@ impl GraphicsRef { // index buffer with 4-byte alignment: recreate if growing const ALIGN: usize = 4; - let raw_index = bytemuck::cast_slice::(&g.conf.input_vertex.order); + let raw_index = bytemuck::cast_slice::(&g.conf.input_vertex.order); let (index_bytes, needs_recreate) = if raw_index.len() % ALIGN != 0 { // pad to alignment let pad = ALIGN - (raw_index.len() % ALIGN); @@ -1052,12 +1054,13 @@ impl Graphics { } fn _render_pipeline( - vertex_conf: &InputVertexConf, + conf: &GraphicsCreator, device: &wgpu::Device, bind_group_layout: &wgpu::BindGroupLayout, fs_mod: &wgpu::ShaderModule, dst_format: wgpu::TextureFormat, ) -> wgpu::RenderPipeline { + let vertex_conf = &conf.input_vertex; let pipeline_layout = Graphics::_pipeline_layout(device, bind_group_layout); let vertex_buffer_layouts = wgpu::VertexBufferLayout { @@ -1075,7 +1078,9 @@ impl Graphics { let color_state = vec![Some(wgpu::ColorTargetState { format: dst_format, + // blend: Some(wgpu::BlendState::ALPHA_BLENDING), blend: Some(wgpu::BlendState::REPLACE), //None, + //Some(conf.blend_state()), // todo get this to work! write_mask: wgpu::ColorWrites::ALL, })]; @@ -1164,7 +1169,7 @@ impl Graphics { ) -> Self { let conf_c = conf.clone(); let has_second_texture = conf.second_texture.is_some(); - let details = conf.details; + let details = conf.clone().details; let first_format = conf.first_texture.format; let second_format = conf.second_texture.map(|x| x.format); let dst_format = conf.dst_texture.format; @@ -1207,7 +1212,7 @@ impl Graphics { let initial_uniform_buffer = initial_uniform.to_buffer(device); let render_pipeline = Graphics::_render_pipeline( - &conf.input_vertex, + &conf, device, &bind_group_layout, &fs_mod, @@ -1492,7 +1497,7 @@ impl Graphics { rpass.set_vertex_buffer(0, self.vertex_buffers.vertex.slice(..)); rpass.set_index_buffer( self.vertex_buffers.index.slice(..), - wgpu::IndexFormat::Uint16, + wgpu::IndexFormat::Uint32, ); rpass.draw_indexed(0..self.conf.input_vertex.indices(), 0, 0..1); drop(rpass); diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 127b7d3..3fb2152 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -139,15 +139,15 @@ impl ControlVecElementRepeatMethod { #[derive(Debug, Clone, Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct ControlVecElementRepeat { +pub struct ControlVecElementRepeat { repeat: ControlVecElementRepeatMethod, // #[serde(default)] prefix: String, - what: Vec, + what: Vec>, } // impl GetLivecodeIdentifiers for ControlVecElement -impl GetLivecodeIdentifiers for ControlVecElement +impl GetLivecodeIdentifiers for ControlVecElement where Source: Clone + Debug + GetLivecodeIdentifiers, // Sequencer: UnitCellCreator + GetLivecodeIdentifiers, @@ -197,7 +197,7 @@ where } } -impl ControlVecElementRepeat { +impl ControlVecElementRepeat { pub fn eval_and_expand_vec(&self, w: &LivecodeWorldState) -> LivecodeResult> where Source: LivecodeFromWorld, @@ -216,9 +216,17 @@ impl ControlVecElementRepeat { let new_w = w.clone_with_vals(expr, &prefix)?; for src in &self.what { - let o = src.o(&new_w)?; - result.push(o); - } + match src { + ControlVecElement::Single(c) => { + let o = c.o(&new_w)?; + result.push(o); + }, + ControlVecElement::Repeat(c) => { + let o = c.eval_and_expand_vec(&new_w)?; + result.extend(o.into_iter()); + } + } + } } Ok(result) } diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 1ddc69a..ddbdda7 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use glam::{vec3, Mat4, Vec2}; use lerpable::Lerpable; -use murrelet_common::{Assets, AssetsRef, LivecodeUsage}; +use murrelet_common::{Assets, AssetsRef, LivecodeUsage, LivecodeValue}; use murrelet_common::{LivecodeSrc, LivecodeSrcUpdateInput, MurreletAppInput}; use murrelet_common::{MurreletColor, TransformVec2}; use murrelet_gui::MurreletGUI; @@ -548,6 +548,7 @@ where maybe_args: Option, // should redesign this... lerp_pct: f32, // moving between things used_variable_names: HashSet, + outgoing_msgs: Vec<(String, String, LivecodeValue)>, // addr, name, value } impl LiveCoder where @@ -622,6 +623,7 @@ where maybe_args, lerp_pct: 1.0, // start in the done state! used_variable_names, + outgoing_msgs: vec![] }; // hrm, before doing most things, load the assets (but we'll do this line again...) @@ -773,6 +775,10 @@ where self.app_config().bg_alpha() } + pub fn add_outgoing_msg(&mut self, addr: String, name: String, value: LivecodeValue) { + self.outgoing_msgs.push((addr, name, value)); + } + // called every frame pub fn update(&mut self, app: &MurreletAppInput, reload: bool) -> LivecodeResult<()> { // use the previous frame's world for this @@ -801,7 +807,9 @@ where }) .collect::>(); - self.livecode_src.feedback(&variables); + let outgoing_msgs = std::mem::take(&mut self.outgoing_msgs); + + self.livecode_src.feedback(&variables, &outgoing_msgs); } // needs to happen before checking is on bar diff --git a/murrelet_src_midi/src/midi.rs b/murrelet_src_midi/src/midi.rs index ebe6236..8752b35 100644 --- a/murrelet_src_midi/src/midi.rs +++ b/murrelet_src_midi/src/midi.rs @@ -49,7 +49,11 @@ impl IsLivecodeSrc for MidiMng { vals } - fn feedback(&mut self, variables: &HashMap) { + fn feedback( + &mut self, + variables: &HashMap, + _outgoing_msgs: &[(String, String, LivecodeValue)], + ) { if let Some(out) = self.out.get_mut(&MidiDevice::MidiTwister) { let mut twister = TwisterController { out }; diff --git a/murrelet_src_osc/src/osc.rs b/murrelet_src_osc/src/osc.rs index 716152c..350b136 100644 --- a/murrelet_src_osc/src/osc.rs +++ b/murrelet_src_osc/src/osc.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] -use murrelet_common::{print_expect, IsLivecodeSrc, LivecodeSrcUpdateInput, LivecodeValue}; +use murrelet_common::{ + print_expect, IsLivecodeSrc, LivecodeSrcUpdateInput, LivecodeUsage, LivecodeValue, +}; use rosc::{OscPacket, OscType}; use std::collections::HashMap; use std::net::UdpSocket; @@ -32,6 +34,36 @@ impl IsLivecodeSrc for OscMng { fn to_exec_funcs(&self) -> Vec<(String, murrelet_common::LivecodeValue)> { self.values.to_livecode_vals() } + + fn feedback( + &mut self, + _variables: &HashMap, + outgoing_msgs: &[(String, String, LivecodeValue)], + ) { + for (_addr, name, vals) in outgoing_msgs.iter() { + match vals { + LivecodeValue::Float(v) => { + let args: Vec = vec![OscType::Float(*v as f32)]; + let osc_path = format!("{}{}", OSC_PREFIX, name); + + let msg = rosc::OscMessage { + addr: osc_path.to_string(), + args, + }; + let packet_data = rosc::encoder::encode(&OscPacket::Message(msg)); + if let Ok(pd) = packet_data { + if let Some(a) = &self.cxn.target_addr { + self.cxn.send_socket.send_to(&pd, a); + } + } + } + LivecodeValue::Bool(_) => {} + LivecodeValue::Int(_) => {} + } + } + + // Ok(()) + } } pub struct OscMng { @@ -62,7 +94,7 @@ impl OscValues { } impl OscMng { - pub fn new_from_str(ip_address: &str, smoothed: bool) -> Self { + pub fn new_from_str(ip_address: &str, smoothed: bool, target_addr: Option) -> Self { let addr = match SocketAddrV4::from_str(ip_address) { Ok(addr) => addr, Err(_) => panic!( @@ -71,7 +103,7 @@ impl OscMng { ), }; - let cxn = OscCxn::new(&addr, smoothed); + let cxn = OscCxn::new(&addr, smoothed, target_addr); Self { cxn, values: OscValues { @@ -86,6 +118,8 @@ pub struct OscCxn { smoothed: bool, _osc_cxn: JoinHandle<()>, // keep it alive! pub osc_rx: Receiver, + send_socket: UdpSocket, + target_addr: Option, } impl OscCxn { @@ -120,10 +154,13 @@ impl OscCxn { }) } - pub fn new(addr: &A, smoothed: bool) -> Self { + pub fn new(addr: &A, smoothed: bool, target_addr: Option) -> Self { let (event_tx, event_rx) = mpsc::channel::(); let sock = UdpSocket::bind(addr).unwrap(); + let send_socket = sock + .try_clone() + .expect("Failed to clone socket for sending"); // Clone the socket println!("setting up osc"); println!("sock {:?}", sock); @@ -152,6 +189,8 @@ impl OscCxn { smoothed, _osc_cxn: handle, osc_rx: event_rx, + send_socket, + target_addr, } } } From 4dc449878f3d5828b852950fe8324d85036a45a6 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 4 Jun 2025 21:49:03 +0200 Subject: [PATCH 032/149] wip --- murrelet_common/src/lib.rs | 28 +++++++++++++++++---- murrelet_draw/src/curve_drawer.rs | 2 -- murrelet_draw/src/style.rs | 4 --- murrelet_draw/src/svg.rs | 1 - murrelet_draw/src/transform2d.rs | 6 +---- murrelet_gpu/src/graphics_ref.rs | 42 ++++++++++++++++--------------- murrelet_livecode/src/types.rs | 4 +-- murrelet_perform/src/perform.rs | 4 +-- 8 files changed, 50 insertions(+), 41 deletions(-) diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 74b602b..0724902 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -469,7 +469,11 @@ pub trait IsLivecodeSrc { fn to_exec_funcs(&self) -> Vec<(String, LivecodeValue)>; // this is a way to give usage feedback to the livecode src, e.g. tell a MIDI controller // we're using a parameter, or what value to set indicator lights to. - fn feedback(&mut self, _variables: &HashMap, _outgoing_msgs: &[(String, String, LivecodeValue)]) { + fn feedback( + &mut self, + _variables: &HashMap, + _outgoing_msgs: &[(String, String, LivecodeValue)], + ) { // default don't do anything } } @@ -579,7 +583,11 @@ impl LivecodeSrc { self.vs.iter().flat_map(|v| v.to_exec_funcs()).collect_vec() } - pub fn feedback(&mut self, variables: &HashMap, outgoing_msgs: &[(String, String, LivecodeValue)]) { + pub fn feedback( + &mut self, + variables: &HashMap, + outgoing_msgs: &[(String, String, LivecodeValue)], + ) { for v in self.vs.iter_mut() { v.feedback(variables, outgoing_msgs); } @@ -642,12 +650,14 @@ impl FixedPointF32 { pub const MAX: Self = FixedPointF32 { x: i64::MAX }; pub const MIN: Self = FixedPointF32 { x: i64::MIN }; + pub const GRANULARITY: f32 = 1e2f32; + fn f32_to_i64(f: f32) -> i64 { - (f * 1e4f32) as i64 + (f * Self::GRANULARITY) as i64 } fn i64_to_f32(f: i64) -> f32 { - f as f32 / 1e4f32 + f as f32 / Self::GRANULARITY } pub fn to_i64(&self) -> i64 { @@ -682,11 +692,19 @@ impl FixedPointF32 { } } -#[derive(Debug, Copy, Clone, Ord, Eq, PartialEq, PartialOrd, Hash)] +#[derive(Copy, Clone, Ord, Eq, PartialEq, PartialOrd, Hash)] pub struct FixedPointVec2 { pub x: FixedPointF32, pub y: FixedPointF32, } + +impl std::fmt::Debug for FixedPointVec2 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let vec2_representation = self.to_vec2(); + write!(f, "FixedPointVec2({:?})", vec2_representation) + } +} + impl FixedPointVec2 { pub fn round(&self, n: i64) -> FixedPointVec2 { FixedPointVec2::new_from_fixed_point(self.x.round(n), self.y.round(n)) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index eac0664..fe7d8f1 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -85,8 +85,6 @@ impl CurveDrawer { let last_command = self.segments().last()?; Some(last_command.last_point()) } - - } #[derive(Debug, Clone, Livecode, Lerpable)] diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 6303960..3539f0a 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -1,6 +1,4 @@ #![allow(dead_code)] -use std::collections::HashMap; - use crate::{curve_drawer::CurveDrawer, draw::*, svg::TransformedSvgShape, transform2d::*}; use glam::*; use lerpable::Lerpable; @@ -503,8 +501,6 @@ impl MurreletCurve { pub fn curve(&self) -> &CurveDrawer { &self.cd } - - } #[derive(Debug, Clone)] diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index ae3ef26..4a3c3d1 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -74,7 +74,6 @@ pub struct SvgCubicBezier { to_y: f32, } impl SvgCubicBezier { - pub fn params(&self) -> (f32, f32, f32, f32, f32, f32) { ( self.ctrl1_x, diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index c7e9587..e14a9ce 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -24,7 +24,6 @@ impl Transform2d { pub fn prepend_one_action(&mut self, action: Transform2dStep) { self.0 = vec![vec![action], self.0.clone()].concat(); - } pub fn append_one_action(&mut self, action: Transform2dStep) { @@ -112,10 +111,7 @@ impl Transform2d { } pub fn rotate_around(angle_pi: A, v: Vec2) -> Transform2d { - Transform2d::new(vec![Transform2dStep::Rotate(Rotate2::new( - v, - angle_pi, - ))]) + Transform2d::new(vec![Transform2dStep::Rotate(Rotate2::new(v, angle_pi))]) } pub fn new_from_scale_rotate(s: f32, angle_pi: A) -> Transform2d { diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index f6810a7..e5984a0 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -381,8 +381,6 @@ impl Default for GraphicsCreator { } } impl GraphicsCreator { - - pub fn with_first_texture_format(mut self, format: wgpu::TextureFormat) -> Self { self.first_texture = TextureCreator { format }; self @@ -713,11 +711,14 @@ impl GraphicsRef { let new_vert_bytes = bytemuck::cast_slice::(&g.conf.input_vertex.vertices); if new_vert_bytes.len() > old_vert_bytes_len { // recreate vertex buffer with new size - let vb = c.device.device().create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("vertex buffer"), - contents: new_vert_bytes, - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - }); + let vb = c + .device + .device() + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("vertex buffer"), + contents: new_vert_bytes, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + }); g.vertex_buffers.vertex = vb; } else { queue.write_buffer(&g.vertex_buffers.vertex, 0, new_vert_bytes); @@ -732,16 +733,22 @@ impl GraphicsRef { let mut data = Vec::with_capacity(raw_index.len() + pad); data.extend_from_slice(raw_index); data.extend(std::iter::repeat(0).take(pad)); - (data.into_boxed_slice(), raw_index.len() + pad > old_index_bytes_len) + ( + data.into_boxed_slice(), + raw_index.len() + pad > old_index_bytes_len, + ) } else { (raw_index.into(), raw_index.len() > old_index_bytes_len) }; if needs_recreate { - let ib = c.device.device().create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("index buffer"), - contents: &index_bytes, - usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, - }); + let ib = c + .device + .device() + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("index buffer"), + contents: &index_bytes, + usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, + }); g.vertex_buffers.index = ib; } else { queue.write_buffer(&g.vertex_buffers.index, 0, &index_bytes); @@ -1211,13 +1218,8 @@ impl Graphics { let initial_uniform_buffer = initial_uniform.to_buffer(device); - let render_pipeline = Graphics::_render_pipeline( - &conf, - device, - &bind_group_layout, - &fs_mod, - dst_format, - ); + let render_pipeline = + Graphics::_render_pipeline(&conf, device, &bind_group_layout, &fs_mod, dst_format); let vertex_buffers = VertexBuffers::from_conf(device, &conf.input_vertex); diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 3fb2152..9fbd531 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -220,13 +220,13 @@ impl ControlVecElementRepeat { ControlVecElement::Single(c) => { let o = c.o(&new_w)?; result.push(o); - }, + } ControlVecElement::Repeat(c) => { let o = c.eval_and_expand_vec(&new_w)?; result.extend(o.into_iter()); } } - } + } } Ok(result) } diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index ddbdda7..82fc98c 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -64,7 +64,7 @@ impl SvgDrawConfig { frame, should_resize: true, resolution, - bg_color: None + bg_color: None, } } @@ -623,7 +623,7 @@ where maybe_args, lerp_pct: 1.0, // start in the done state! used_variable_names, - outgoing_msgs: vec![] + outgoing_msgs: vec![], }; // hrm, before doing most things, load the assets (but we'll do this line again...) From de8b9b977b1293a70bfa6a54d2753c077b2bc806 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 11 Jun 2025 18:35:29 -0400 Subject: [PATCH 033/149] wip --- murrelet_common/src/geometry.rs | 2 +- murrelet_common/src/lib.rs | 28 +++++++++++++++++++++- murrelet_draw/Cargo.toml | 1 + murrelet_draw/src/curve_drawer.rs | 2 +- murrelet_draw/src/draw.rs | 11 +++++++++ murrelet_draw/src/style.rs | 39 ++++++++++++++++++++++++++----- murrelet_draw/src/svg.rs | 18 +++++++++++++- murrelet_livecode/src/lazy.rs | 4 ++++ murrelet_livecode/src/state.rs | 25 +++++++++++++++++++- murrelet_livecode/src/types.rs | 4 ++++ murrelet_svg/src/svg.rs | 3 ++- 11 files changed, 125 insertions(+), 12 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 28f7abf..c0ff1fd 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -649,4 +649,4 @@ pub fn tangents_between_two_circles( let end = vec2(v2.x + sign1 * r2 * nx, v2.y + sign1 * r2 * ny); Some((start, end)) -} +} \ No newline at end of file diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 0724902..c7df107 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -646,11 +646,37 @@ pub fn fixed_pt_f32_to_str(x: f32) -> String { pub struct FixedPointF32 { pub x: i64, } + +impl FixedPointF32 { + pub fn abs(&self) -> Self { + Self { x: self.x.abs() } + } +} + +impl std::ops::Sub for FixedPointF32 { + type Output = Self; + + fn sub(self, other: Self) -> Self { + Self { + x: self.x - other.x, + } + } +} + +impl std::ops::Add for FixedPointF32 { + type Output = Self; + + fn add(self, other: Self) -> Self { + Self { + x: self.x + other.x, + } + } +} impl FixedPointF32 { pub const MAX: Self = FixedPointF32 { x: i64::MAX }; pub const MIN: Self = FixedPointF32 { x: i64::MIN }; - pub const GRANULARITY: f32 = 1e2f32; + pub const GRANULARITY: f32 = 1e4f32; fn f32_to_i64(f: f32) -> i64 { (f * Self::GRANULARITY) as i64 diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index 13b8865..b2916e1 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -31,6 +31,7 @@ palette = "0.7.6" anyhow = "1.0.86" md-5 = "0.10.6" hex = "0.4.3" +lyon = "1.0.1" schemars = { version = "0.8.21", optional = true } murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index fe7d8f1..629054b 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -4,7 +4,7 @@ use lerpable::Lerpable; use murrelet_common::*; use murrelet_livecode_derive::*; -use crate::livecodetypes::anglepi::*; +use crate::{livecodetypes::anglepi::*, svg::SvgPathDef}; #[derive(Debug, Clone, Livecode, Lerpable)] pub struct CurveDrawer { diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index 835634f..9ae09ee 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -134,6 +134,17 @@ impl MurreletStyle { } } + pub fn new_line_color(color: MurreletColor, stroke_weight: f32) -> MurreletStyle { + MurreletStyle { + closed: false, + filled: false, + color: MurreletColorStyle::Color(color), + stroke_weight, + stroke_color: MurreletColorStyle::Color(color), + ..Default::default() + } + } + pub fn new_outline_color(color: MurreletColor, stroke_weight: f32) -> MurreletStyle { MurreletStyle { closed: true, diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 3539f0a..b74fe84 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -1,5 +1,10 @@ #![allow(dead_code)] -use crate::{curve_drawer::CurveDrawer, draw::*, svg::TransformedSvgShape, transform2d::*}; +use crate::{ + curve_drawer::CurveDrawer, + draw::*, + svg::{SvgPathDef, SvgShape, TransformedSvgShape}, + transform2d::*, +}; use glam::*; use lerpable::Lerpable; use md5::{Digest, Md5}; @@ -463,19 +468,25 @@ impl CanMakeGUI for StyleConf { } } +#[derive(Debug, Clone)] +pub enum MurreletCurveKinds { + CD(CurveDrawer), + Svg(SvgPathDef), +} + // this one attaches a transform to the curve. // you can _try_ to apply it using to_curve_maker, but this // will act funny for non-affine #[derive(Debug, Clone)] pub struct MurreletCurve { - cd: CurveDrawer, + cd: MurreletCurveKinds, t: Mat4, } impl MurreletCurve { pub fn new(cd: CurveDrawer) -> Self { Self { - cd, + cd: MurreletCurveKinds::CD(cd), t: Mat4::IDENTITY, } } @@ -498,7 +509,23 @@ impl MurreletCurve { self.t } - pub fn curve(&self) -> &CurveDrawer { + // pub fn curve(&self) -> &CurveDrawer { + // match &self.cd { + // MurreletCurveKinds::CD(cd) => cd, + // MurreletCurveKinds::Svg(pts) => { + // &CurveDrawer::new_simple_points(parse_svg_path_as_vec2(pts), false) + // }, + // } + // } + + fn new_transformed_svg(svg: &TransformedSvgShape) -> MurreletCurve { + Self { + cd: MurreletCurveKinds::Svg(svg.shape.as_path()), + t: svg.t, + } + } + + pub fn cd(&self) -> &MurreletCurveKinds { &self.cd } } @@ -522,11 +549,11 @@ impl MurreletPath { pub fn as_curve(&self) -> MurreletCurve { match self { MurreletPath::Polyline(c) => MurreletCurve { - cd: CurveDrawer::new_simple_points(c.clone_to_vec(), false), + cd: MurreletCurveKinds::CD(CurveDrawer::new_simple_points(c.clone_to_vec(), false)), t: Mat4::IDENTITY, }, MurreletPath::Curve(c) => c.clone(), - MurreletPath::Svg(_) => todo!(), + MurreletPath::Svg(svg) => MurreletCurve::new_transformed_svg(svg), MurreletPath::MaskedCurve(_mask, c) => c.clone(), } } diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index 4a3c3d1..6ce485a 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -1,10 +1,13 @@ // defines the SVG basic shapes -use glam::{Mat4, Vec2}; +use glam::{vec2, Mat4, Vec2}; use lerpable::Lerpable; +use lyon::geom::{euclid::Point2D, Point}; use murrelet_gui::MurreletGUI; use murrelet_livecode_derive::Livecode; +use crate::style::MurreletCurve; + #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] pub struct SvgRect { #[livecode(serde_default = "0")] @@ -131,6 +134,11 @@ impl SvgPathDef { pub fn cmds(&self) -> &[SvgCmd] { &self.v } + +} + +fn glam_to_lyon(vec: Vec2) -> Point2D { + Point::new(vec.x, vec.y) } #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] @@ -169,6 +177,14 @@ impl SvgShape { t: Mat4::IDENTITY, } } + + pub(crate) fn as_path(&self) -> SvgPathDef { + match self { + SvgShape::Rect(_) => todo!(), + SvgShape::Circle(_) => todo!(), + SvgShape::Path(svg_path_def) => svg_path_def.clone(), + } + } } #[derive(Clone, Debug)] diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 2aa9037..46a2805 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -141,6 +141,10 @@ impl LazyNodeF32 { } } + pub fn simple_number(val: f32) -> Self { + Self::new(ControlLazyNodeF32::Float(val), &LivecodeWorldState::new_dummy()) + } + pub fn n(&self) -> Option<&Node> { match self { LazyNodeF32::Uninitialized => None, diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index 7f53f86..a2af9c9 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{collections::HashSet, sync::Arc}; use evalexpr::{HashMapContext, IterateVariablesContext}; use murrelet_common::*; @@ -180,6 +180,17 @@ impl LivecodeWorldState { pub fn asset_layers_in_key(&self, key: &str) -> &[String] { self.assets.layer_for_key(key) } + + pub fn new_dummy() -> LivecodeWorldState { + let empty_ctx = HashMapContext::new(); + Self::new( + &empty_ctx, + &LivecodeSrc::new(vec![]), + LiveCodeTimeInstantInfo::new_dummy(), // time + AdditionalContextNode::new_dummy(), // node + Arc::new(Assets::empty()), // assets + ).unwrap() + } } #[derive(Copy, Clone, Debug)] @@ -319,6 +330,18 @@ impl LiveCodeTimeInstantInfo { self.seconds_since_updated_frame() } } + + fn new_dummy() -> LiveCodeTimeInstantInfo { + LiveCodeTimeInstantInfo { + timing_config: LivecodeTimingConfig { + bpm: 120.0, + fps: 60.0, + realtime: false, + beats_per_bar: 4.0, + }, + system_timing: LiveCodeTiming::default(), + } + } } impl IsLivecodeSrc for LiveCodeTimeInstantInfo { diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 9fbd531..74c2128 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -87,6 +87,10 @@ impl AdditionalContextNode { .eval_empty_with_context_mut(ctx) .map_err(|err| LivecodeError::EvalExpr("error evaluating ctx".to_owned(), err)) } + + pub fn new_dummy() -> AdditionalContextNode { + AdditionalContextNode(build_operator_tree("").unwrap()) + } } impl CanMakeGUI for AdditionalContextNode { diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 662155d..265b8ef 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -96,7 +96,8 @@ impl ToStyledGroup for T { impl ToSvgData for MurreletCurve { fn to_svg_data(&self) -> Option { - self.curve().to_svg_data() + // self.curve().to_svg_data() + todo!() } fn transform(&self) -> Option { From eb14c68263d40bbed99f99c3c1228b438bbc5f94 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 14 Jun 2025 19:43:26 -0400 Subject: [PATCH 034/149] wip --- murrelet_gpu/src/graphics_ref.rs | 19 +++++++++++-------- murrelet_livecode/src/state.rs | 9 +++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index e5984a0..a3aef56 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -532,14 +532,15 @@ impl BasicUniform { }) } - fn copy_to_buffer( - &self, - dest: &wgpu::Buffer, - device: &wgpu::Device, - encoder: &mut wgpu::CommandEncoder, - ) { - encoder.copy_buffer_to_buffer(&self.to_buffer(device), 0, dest, 0, self.uniforms_size()); - } + // fn copy_to_buffer( + // &self, + // dest: &wgpu::Buffer, + // device: &wgpu::Device, + // encoder: &mut wgpu::CommandEncoder, + // ) { + // println!("copy to buffer"); + // encoder.copy_buffer_to_buffer(&self.to_buffer(device), 0, dest, 0, self.uniforms_size()); + // } } pub struct UniformsPair { @@ -579,6 +580,7 @@ impl GraphicsRef { conf: &GraphicsCreator, assets: GraphicsAssets, ) -> Self { + println!("name {:?}", name); let graphics = Graphics::new_mut( name.to_string(), c, @@ -587,6 +589,7 @@ impl GraphicsRef { assets, conf.clone(), ); + println!("done name {:?}", name); Self { graphics } } diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index a2af9c9..ec5d19a 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -186,10 +186,11 @@ impl LivecodeWorldState { Self::new( &empty_ctx, &LivecodeSrc::new(vec![]), - LiveCodeTimeInstantInfo::new_dummy(), // time - AdditionalContextNode::new_dummy(), // node - Arc::new(Assets::empty()), // assets - ).unwrap() + LiveCodeTimeInstantInfo::new_dummy(), // time + AdditionalContextNode::new_dummy(), // node + Arc::new(Assets::empty()), // assets + ) + .unwrap() } } From af7b1288d49ed46e5e01db79c3f0943e22307173 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 20 Jun 2025 15:49:38 -0400 Subject: [PATCH 035/149] wip --- murrelet_common/Cargo.toml | 1 + murrelet_common/src/lib.rs | 24 +++++++++++++++++++++--- murrelet_draw/src/curve_drawer.rs | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/murrelet_common/Cargo.toml b/murrelet_common/Cargo.toml index 1e19f81..7e860b7 100644 --- a/murrelet_common/Cargo.toml +++ b/murrelet_common/Cargo.toml @@ -15,6 +15,7 @@ rand = "0.8" num-traits = "0.2.19" lerpable = "0.0.2" murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } +serde = { version = "1.0.104", features = ["derive"] } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index c7df107..2cee9a7 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -4,6 +4,7 @@ use glam::{vec3, Vec3}; use itertools::Itertools; use lerpable::{IsLerpingMethod, Lerpable}; use num_traits::NumCast; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::hash::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -598,7 +599,24 @@ const MAX_STRID_LEN: usize = 16; #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct StrId([u8; MAX_STRID_LEN]); +impl Serialize for StrId { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(self.as_str()) + } +} +impl<'de> Deserialize<'de> for StrId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Ok(StrId::new(&s)) + } +} // from chatgpt impl StrId { pub fn new(s: &str) -> Self { @@ -642,7 +660,7 @@ pub fn fixed_pt_f32_to_str(x: f32) -> String { FixedPointF32::new(x).to_str() } -#[derive(Debug, Copy, Clone, Ord, Eq, PartialEq, PartialOrd, Hash)] +#[derive(Debug, Copy, Clone, Ord, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)] pub struct FixedPointF32 { pub x: i64, } @@ -679,7 +697,7 @@ impl FixedPointF32 { pub const GRANULARITY: f32 = 1e4f32; fn f32_to_i64(f: f32) -> i64 { - (f * Self::GRANULARITY) as i64 + (f * Self::GRANULARITY).round() as i64 } fn i64_to_f32(f: i64) -> f32 { @@ -718,7 +736,7 @@ impl FixedPointF32 { } } -#[derive(Copy, Clone, Ord, Eq, PartialEq, PartialOrd, Hash)] +#[derive(Copy, Clone, Ord, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)] pub struct FixedPointVec2 { pub x: FixedPointF32, pub y: FixedPointF32, diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 629054b..fe7d8f1 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -4,7 +4,7 @@ use lerpable::Lerpable; use murrelet_common::*; use murrelet_livecode_derive::*; -use crate::{livecodetypes::anglepi::*, svg::SvgPathDef}; +use crate::livecodetypes::anglepi::*; #[derive(Debug, Clone, Livecode, Lerpable)] pub struct CurveDrawer { From 4842132c56f01495f9958b49c002360e9a7a003b Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 22 Jun 2025 13:15:33 -0400 Subject: [PATCH 036/149] wip --- murrelet_common/src/geometry.rs | 19 +++++ murrelet_common/src/lib.rs | 1 + murrelet_draw/Cargo.toml | 6 +- murrelet_draw/src/lib.rs | 2 + murrelet_gpu/Cargo.toml | 3 +- murrelet_gpu/src/graphics_ref.rs | 138 +++++++++++++++++-------------- murrelet_livecode/src/lazy.rs | 5 +- 7 files changed, 107 insertions(+), 67 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index c0ff1fd..df46b93 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -518,6 +518,7 @@ impl PrevCurrNextVec2 { } } + #[derive(Copy, Clone, Debug)] pub struct PointToPoint { start: Vec2, @@ -649,4 +650,22 @@ pub fn tangents_between_two_circles( let end = vec2(v2.x + sign1 * r2 * nx, v2.y + sign1 * r2 * ny); Some((start, end)) +} + + +#[derive(Copy, Clone, Debug)] +pub struct Tangent { + pub loc: Vec2, + pub dir: AnglePi, + // strength: f32, +} + +impl Tangent { + pub fn dir(&self) -> AnglePi { + self.dir + } + + pub fn loc(&self) -> Vec2 { + self.loc + } } \ No newline at end of file diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 2cee9a7..34a8be6 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -20,6 +20,7 @@ mod iter; mod metric; mod polyline; mod transform; +pub mod triangulate; pub use assets::*; pub use color::*; diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index b2916e1..f5067f2 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -31,7 +31,11 @@ palette = "0.7.6" anyhow = "1.0.86" md-5 = "0.10.6" hex = "0.4.3" -lyon = "1.0.1" +# lyon = "1.0.1" schemars = { version = "0.8.21", optional = true } murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } + +kurbo = "0.11" +svg = "0.10.0" +lyon = "0.17" \ No newline at end of file diff --git a/murrelet_draw/src/lib.rs b/murrelet_draw/src/lib.rs index 420ae05..62a1d7d 100644 --- a/murrelet_draw/src/lib.rs +++ b/murrelet_draw/src/lib.rs @@ -7,3 +7,5 @@ pub mod sequencers; pub mod style; pub mod svg; pub mod transform2d; +pub mod cubic; +pub mod tesselate; diff --git a/murrelet_gpu/Cargo.toml b/murrelet_gpu/Cargo.toml index 42eb1e9..fd8c3ea 100644 --- a/murrelet_gpu/Cargo.toml +++ b/murrelet_gpu/Cargo.toml @@ -54,4 +54,5 @@ image = "0.25.2" bytemuck = { version = "1.16.1", features = ["derive"] } schemars = { version = "0.8.21", optional = true } -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } \ No newline at end of file +murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } + diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index a3aef56..b31d3c7 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -3,6 +3,8 @@ use std::{cell::RefCell, sync::Arc}; use bytemuck::{Pod, Zeroable}; use glam::{Mat4, Vec3}; +use itertools::Itertools; +use murrelet_common::triangulate::{Triangulate, VertexSimple}; use std::rc::Rc; #[cfg(feature = "nannou")] @@ -61,6 +63,14 @@ impl Vertex { pub fn pos_vec3(&self) -> Vec3 { glam::vec3(self.position[0], self.position[1], self.position[2]) } + + pub fn from_simple(vs: &VertexSimple) -> Self { + Self { + position: vs.position, + normal: vs.normal, + face_pos: vs.face_pos, + } + } } unsafe impl Zeroable for Vertex {} @@ -136,64 +146,64 @@ pub struct Scene { } // this is the conf that you'll interface with -#[derive(Debug, Clone)] -pub struct Triangulate { - vertices: Vec, - order: Vec, -} - -impl Triangulate { - pub fn new() -> Self { - Triangulate { - vertices: vec![], - order: vec![], - } - } - - pub fn vertices(&self) -> &[Vertex] { - &self.vertices - } - - pub fn add_vertex(&mut self, v: [f32; 3], n: [f32; 3], face_pos: [f32; 2]) -> u32 { - let vv = Vertex::new(v, n, face_pos); - self.vertices.push(vv); - (self.vertices.len() - 1) as u32 - } - - // alternatively can add vertices and then add teh vec - pub fn add_rect(&mut self, v: &[Vec3; 4], flip: bool) { - let edge1 = v[0] - v[1]; - let edge2 = v[3] - v[1]; - let normal = edge1.cross(edge2).normalize().to_array(); - - let v0 = self.add_vertex(v[0].to_array(), normal, [1.0, 0.0]); - let v1 = self.add_vertex(v[1].to_array(), normal, [0.0, 0.0]); - let v2 = self.add_vertex(v[2].to_array(), normal, [1.0, 1.0]); - let v3 = self.add_vertex(v[3].to_array(), normal, [0.0, 1.0]); - - if !flip { - self.order.extend([v0, v2, v1, v1, v2, v3]) - } else { - self.order.extend([v0, v1, v2, v1, v3, v2]) - } - } - - pub fn set_order(&mut self, u: Vec) { - self.order = u; - } - - fn order(&self) -> &[u32] { - &self.order - } - - pub fn indices(&self) -> &[u32] { - &self.order - } - - pub fn add_order(&mut self, collect: &[u32]) { - self.order.extend_from_slice(collect); - } -} +// #[derive(Debug, Clone)] +// pub struct Triangulate { +// vertices: Vec, +// order: Vec, +// } + +// impl Triangulate { +// pub fn new() -> Self { +// Triangulate { +// vertices: vec![], +// order: vec![], +// } +// } + +// pub fn vertices(&self) -> &[Vertex] { +// &self.vertices +// } + +// pub fn add_vertex(&mut self, v: [f32; 3], n: [f32; 3], face_pos: [f32; 2]) -> u32 { +// let vv = Vertex::new(v, n, face_pos); +// self.vertices.push(vv); +// (self.vertices.len() - 1) as u32 +// } + +// // alternatively can add vertices and then add teh vec +// pub fn add_rect(&mut self, v: &[Vec3; 4], flip: bool) { +// let edge1 = v[0] - v[1]; +// let edge2 = v[3] - v[1]; +// let normal = edge1.cross(edge2).normalize().to_array(); + +// let v0 = self.add_vertex(v[0].to_array(), normal, [1.0, 0.0]); +// let v1 = self.add_vertex(v[1].to_array(), normal, [0.0, 0.0]); +// let v2 = self.add_vertex(v[2].to_array(), normal, [1.0, 1.0]); +// let v3 = self.add_vertex(v[3].to_array(), normal, [0.0, 1.0]); + +// if !flip { +// self.order.extend([v0, v2, v1, v1, v2, v3]) +// } else { +// self.order.extend([v0, v1, v2, v1, v3, v2]) +// } +// } + +// pub fn set_order(&mut self, u: Vec) { +// self.order = u; +// } + +// fn order(&self) -> &[u32] { +// &self.order +// } + +// pub fn indices(&self) -> &[u32] { +// &self.order +// } + +// pub fn add_order(&mut self, collect: &[u32]) { +// self.order.extend_from_slice(collect); +// } +// } // this is the conf that you'll interface with #[derive(Debug, Clone)] @@ -213,7 +223,7 @@ impl InputVertexConf { pub fn from_triangulate_2d(t: &Triangulate) -> Self { let mut c = Self::default(); - c.vertices = t.vertices.clone(); + c.vertices = t.vertices.iter().map(|x| Vertex::from_simple(x)).collect_vec(); c.order = t.order.clone(); c } @@ -222,7 +232,7 @@ impl InputVertexConf { let mut c = Self::default(); c.is_3d = true; c.vs_mod = VERTEX_SHADER_3D; - c.vertices = t.vertices.clone(); + c.vertices = t.vertices.iter().map(|x| Vertex::from_simple(x)).collect_vec(); c.order = t.order.clone(); c } @@ -250,7 +260,7 @@ fn main(@location(0) position: vec3) -> @builtin(position) vec4 { } pub fn with_custom_vertices(mut self, tri: &Triangulate) -> Self { - self.vertices = tri.vertices.clone(); + self.vertices = tri.vertices.iter().map(|x| Vertex::from_simple(x)).collect_vec(); self.topology = wgpu::PrimitiveTopology::TriangleList; self.order = tri.order.clone(); self @@ -706,7 +716,7 @@ impl GraphicsRef { { let mut g = self.graphics.borrow_mut(); - g.conf.input_vertex.vertices = tri.vertices.clone(); + g.conf.input_vertex.vertices = tri.vertices.iter().map(|x| Vertex::from_simple(x)).collect_vec(); g.conf.input_vertex.order = tri.order.clone(); let queue = c.device.queue(); @@ -1088,8 +1098,8 @@ impl Graphics { let color_state = vec![Some(wgpu::ColorTargetState { format: dst_format, - // blend: Some(wgpu::BlendState::ALPHA_BLENDING), - blend: Some(wgpu::BlendState::REPLACE), //None, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + // blend: Some(wgpu::BlendState::REPLACE), //None, //Some(conf.blend_state()), // todo get this to work! write_mask: wgpu::ColorWrites::ALL, })]; diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 46a2805..b43729c 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -142,7 +142,10 @@ impl LazyNodeF32 { } pub fn simple_number(val: f32) -> Self { - Self::new(ControlLazyNodeF32::Float(val), &LivecodeWorldState::new_dummy()) + Self::new( + ControlLazyNodeF32::Float(val), + &LivecodeWorldState::new_dummy(), + ) } pub fn n(&self) -> Option<&Node> { From 098c4a6b716f3450eaaeb183624cacedaecfe560 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 24 Jun 2025 15:33:11 -0400 Subject: [PATCH 037/149] wip --- murrelet_common/Cargo.toml | 2 +- murrelet_draw/Cargo.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/murrelet_common/Cargo.toml b/murrelet_common/Cargo.toml index 7e860b7..37b54de 100644 --- a/murrelet_common/Cargo.toml +++ b/murrelet_common/Cargo.toml @@ -16,7 +16,7 @@ num-traits = "0.2.19" lerpable = "0.0.2" murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } serde = { version = "1.0.104", features = ["derive"] } - +bytemuck = { version = "1.15", features = ["derive"] } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = "0.2" diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index f5067f2..b3c1c47 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -31,7 +31,6 @@ palette = "0.7.6" anyhow = "1.0.86" md-5 = "0.10.6" hex = "0.4.3" -# lyon = "1.0.1" schemars = { version = "0.8.21", optional = true } murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } From c525c68da6c3071e922ba26775e3843bcda3c18d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 24 Jun 2025 16:42:44 -0400 Subject: [PATCH 038/149] wip --- examples/foolish_guillemot/src/lib.rs | 1 + murrelet_common/src/color.rs | 23 +- murrelet_common/src/lib.rs | 16 +- murrelet_common/src/triangulate.rs | 103 ++++ murrelet_draw/src/cubic.rs | 81 +++ murrelet_draw/src/draw.rs | 1 + murrelet_draw/src/newtypes.rs | 4 + murrelet_draw/src/style.rs | 4 + murrelet_draw/src/svg.rs | 5 +- murrelet_draw/src/tesselate.rs | 732 ++++++++++++++++++++++++++ murrelet_svg/src/svg.rs | 275 +++++++--- 11 files changed, 1142 insertions(+), 103 deletions(-) create mode 100644 murrelet_common/src/triangulate.rs create mode 100644 murrelet_draw/src/cubic.rs create mode 100644 murrelet_draw/src/tesselate.rs diff --git a/examples/foolish_guillemot/src/lib.rs b/examples/foolish_guillemot/src/lib.rs index 488d4b0..5a5d2d0 100644 --- a/examples/foolish_guillemot/src/lib.rs +++ b/examples/foolish_guillemot/src/lib.rs @@ -166,6 +166,7 @@ impl MurreletModel { #[wasm_bindgen] pub fn bg_color(&self) -> String { + // it's fine that this one drops transparency self.livecode.app_config().bg_color.to_svg_rgb() } } diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index 14d9e46..db67ccc 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -7,6 +7,8 @@ use lerpable::{IsLerpingMethod, Lerpable}; use murrelet_gui::CanMakeGUI; use palette::{rgb::Rgb, FromColor, Hsva, IntoColor, LinSrgb, LinSrgba, Srgb, Srgba, WithAlpha}; +use crate::rgb_to_hex; + // hrm, color is confusing, so make a newtype around LinSrgba for all our color stuff // i need to double check i'm handling linear/not rgb right #[derive(Copy, Clone, Default)] @@ -121,10 +123,7 @@ impl MurreletColor { let [h, s, v, _a] = self.into_hsva_components(); Self([h, s, v, alpha]) } -} -// getters and such that use the functions already -impl MurreletColor { pub fn black() -> Self { Self::hsva(0.0, 0.0, 0.0, 1.0) } @@ -143,14 +142,16 @@ impl MurreletColor { } pub fn to_svg_rgb(&self) -> String { - let [r, g, b, a] = self.into_rgba_components(); - format!( - "rgba({} {} {} / {})", - (r * 255.0) as i32, - (g * 255.0) as i32, - (b * 255.0) as i32, - a - ) + self.hex() + } + + pub fn to_fill_opacity(&self) -> String { + format!("{}", self.alpha()) + } + + pub fn hex(&self) -> String { + let [r, g, b, _a] = self.into_rgba_components(); + rgb_to_hex(r, g, b) } } diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 34a8be6..a106cdc 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -661,7 +661,7 @@ pub fn fixed_pt_f32_to_str(x: f32) -> String { FixedPointF32::new(x).to_str() } -#[derive(Debug, Copy, Clone, Ord, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Ord, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)] pub struct FixedPointF32 { pub x: i64, } @@ -737,7 +737,7 @@ impl FixedPointF32 { } } -#[derive(Copy, Clone, Ord, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Ord, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)] pub struct FixedPointVec2 { pub x: FixedPointF32, pub y: FixedPointF32, @@ -999,3 +999,15 @@ impl Dim2d { Self::from_x_y(j, i) } } + +pub fn rgb_to_hex(r: f32, g: f32, b: f32) -> String { + let r = clamp(r, 0.0, 1.0); + let g = clamp(g, 0.0, 1.0); + let b = clamp(b, 0.0, 1.0); + + let r = (r * 255.0) as u8; + let g = (g * 255.0) as u8; + let b = (b * 255.0) as u8; + + format!("#{:02X}{:02X}{:02X}", r, g, b) +} diff --git a/murrelet_common/src/triangulate.rs b/murrelet_common/src/triangulate.rs new file mode 100644 index 0000000..6ed9e42 --- /dev/null +++ b/murrelet_common/src/triangulate.rs @@ -0,0 +1,103 @@ +use glam::{vec2, Vec2, Vec3}; + +#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct VertexSimple { + pub position: [f32; 3], + pub normal: [f32; 3], + pub face_pos: [f32; 2], +} + +impl VertexSimple { + pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { + Self { + position, + normal, + face_pos, + } + } + pub fn pos(&self) -> [f32; 3] { + self.position + } + + pub fn pos_vec3(&self) -> Vec3 { + glam::vec3(self.position[0], self.position[1], self.position[2]) + } + + pub fn pos2d(&self) -> Vec2 { + vec2(self.position[0], self.position[1]) + } + + pub fn attrs(&self) -> Vec { + vec![ + self.normal[0], + self.normal[1], + self.normal[2], + self.face_pos[0], + self.face_pos[1], + ] + } +} + +#[derive(Debug, Clone)] +pub struct Triangulate { + pub vertices: Vec, + pub order: Vec, +} + +impl Triangulate { + pub fn new() -> Self { + Triangulate { + vertices: vec![], + order: vec![], + } + } + + pub fn vertices(&self) -> &[VertexSimple] { + &self.vertices + } + + pub fn add_vertex(&mut self, v: [f32; 3], n: [f32; 3], face_pos: [f32; 2]) -> u32 { + let vv = VertexSimple::new(v, n, face_pos); + self.add_vertex_simple(vv) + } + + pub fn add_vertex_simple(&mut self, vv: VertexSimple) -> u32 { + self.vertices.push(vv); + (self.vertices.len() - 1) as u32 + } + + // alternatively can add vertices and then add teh vec + pub fn add_rect(&mut self, v: &[Vec3; 4], flip: bool) { + let edge1 = v[0] - v[1]; + let edge2 = v[3] - v[1]; + let normal = edge1.cross(edge2).normalize().to_array(); + + let v0 = self.add_vertex(v[0].to_array(), normal, [1.0, 0.0]); + let v1 = self.add_vertex(v[1].to_array(), normal, [0.0, 0.0]); + let v2 = self.add_vertex(v[2].to_array(), normal, [1.0, 1.0]); + let v3 = self.add_vertex(v[3].to_array(), normal, [0.0, 1.0]); + + if !flip { + self.order.extend([v0, v2, v1, v1, v2, v3]) + } else { + self.order.extend([v0, v1, v2, v1, v3, v2]) + } + } + + pub fn set_order(&mut self, u: Vec) { + self.order = u; + } + + fn order(&self) -> &[u32] { + &self.order + } + + pub fn indices(&self) -> &[u32] { + &self.order + } + + pub fn add_order(&mut self, collect: &[u32]) { + self.order.extend_from_slice(collect); + } +} diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs new file mode 100644 index 0000000..6e3d88d --- /dev/null +++ b/murrelet_draw/src/cubic.rs @@ -0,0 +1,81 @@ +use glam::Vec2; +use murrelet_common::{Angle, IsAngle, Tangent}; + +#[derive(Debug, Clone)] +pub struct CubicBezier { + pub from: Vec2, + pub ctrl1: Vec2, + pub ctrl2: Vec2, + pub to: Vec2, +} +impl CubicBezier { + pub fn new(from: Vec2, ctrl1: Vec2, ctrl2: Vec2, to: Vec2) -> Self { + Self { from, ctrl1, ctrl2, to } + } + + pub fn split(&self, t: f32) -> (CubicBezier, CubicBezier) { + let mid1 = self.from.lerp(self.ctrl1, t); + let mid2 = self.ctrl1.lerp(self.ctrl2, t); + let mid3 = self.ctrl2.lerp(self.to, t); + + let mid12 = mid1.lerp(mid2, t); + let mid23 = mid2.lerp(mid3, t); + + let mid123 = mid12.lerp(mid23, t); + + ( + CubicBezier { + from: self.from, + ctrl1: mid1, + ctrl2: mid12, + to: mid123, + }, + CubicBezier { + from: mid123, + ctrl1: mid23, + ctrl2: mid3, + to: self.to, + }, + ) + } + + pub fn start_to_tangent(&self) -> (Tangent, f32) { + // let side_len = self.from.distance(self.to); + + let ctrl_line = self.from - self.ctrl1; + let dir = Angle::new(ctrl_line.to_angle()) + .as_angle_pi() + .normalize_angle(); + + // let strength = ctrl_line.length() / side_len; + + ( + Tangent { + loc: self.from, + dir: dir.into(), + // strength, + }, + ctrl_line.length(), + ) + } + + pub fn end_to_tangent(&self) -> (Tangent, f32) { + // let side_len = self.from.distance(self.to); + + let ctrl_line = self.ctrl2 - self.to; + let dir = Angle::new(ctrl_line.to_angle()) + .as_angle_pi() + .normalize_angle(); + + // let strength = ctrl_line.length() / side_len; + + ( + Tangent { + loc: self.to, + dir: dir.into(), + // strength, + }, + ctrl_line.length(), + ) + } +} diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index 9ae09ee..b9b6f5a 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -82,6 +82,7 @@ impl MurreletColorStyle { fn hue(hue: f32) -> MurreletColorStyle { MurreletColorStyle::Color(MurreletColor::hsva(hue, 1.0, 1.0, 1.0)) } + } #[derive(Debug, Clone)] diff --git a/murrelet_draw/src/newtypes.rs b/murrelet_draw/src/newtypes.rs index b0ab1bd..7411b08 100644 --- a/murrelet_draw/src/newtypes.rs +++ b/murrelet_draw/src/newtypes.rs @@ -75,4 +75,8 @@ impl RGBandANewtype { c.a = alpha; c } + + pub fn alpha(&self) -> f32 { + self.a + } } diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index b74fe84..d508e82 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -145,6 +145,10 @@ impl StyledPathSvgFill { p.alpha = alpha; p } + + pub fn alpha(&self) -> f32 { + self.alpha + } } #[derive(Clone, Debug, Livecode, Lerpable)] diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index 6ce485a..a272158 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -1,13 +1,11 @@ // defines the SVG basic shapes -use glam::{vec2, Mat4, Vec2}; +use glam::{Mat4, Vec2}; use lerpable::Lerpable; use lyon::geom::{euclid::Point2D, Point}; use murrelet_gui::MurreletGUI; use murrelet_livecode_derive::Livecode; -use crate::style::MurreletCurve; - #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] pub struct SvgRect { #[livecode(serde_default = "0")] @@ -134,7 +132,6 @@ impl SvgPathDef { pub fn cmds(&self) -> &[SvgCmd] { &self.v } - } fn glam_to_lyon(vec: Vec2) -> Point2D { diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs new file mode 100644 index 0000000..b8ffc69 --- /dev/null +++ b/murrelet_draw/src/tesselate.rs @@ -0,0 +1,732 @@ +use std::{collections::HashMap, ops}; + +use crate::{ + cubic::CubicBezier, + // curve_drawer::{CurveDrawer, CurveSegment}, + svg::SvgPathDef, +}; +use glam::{vec2, Vec2}; +use itertools::Itertools; +use kurbo::BezPath; +use lyon::{ + geom::{ + euclid::{Point2D, UnknownUnit}, + point, + }, + path::{FillRule, Path}, + tessellation::{BuffersBuilder, FillOptions, FillTessellator, FillVertex}, +}; +use murrelet_common::{triangulate::VertexSimple, Polyline}; + +pub trait ToVecVec2 { + fn to_vec2(&self) -> Vec; +} + +impl ToVecVec2 for CubicBezier { + fn to_vec2(&self) -> Vec { + let mut svg = svg::node::element::path::Data::new(); + + let x = self.from.x; + let y = self.from.y; + let start: svg::node::element::path::Parameters = vec![x, y].into(); + svg = svg.move_to(start); + + let cubic: svg::node::element::path::Parameters = vec![ + self.ctrl1.x, + self.ctrl1.y, + self.ctrl2.x, + self.ctrl2.y, + self.to.x, + self.to.y, + ] + .into(); + svg = svg.cubic_curve_to(cubic); + + let path = parse_svg_data_as_vec2(&svg, 1.0); + + path.into_iter().map(|x| vec2(x.y, x.x)).collect_vec() + } +} + +fn vec2_to_kurbo(v: Vec2) -> kurbo::Point { + kurbo::Point::new(v.x as f64, v.y as f64) +} + +fn vec2_to_pt(x: Vec2) -> lyon::geom::euclid::Point2D { + point(x.x, x.y) +} + +fn _point_from_params(params: &Vec<&f32>, idx: usize) -> Pt { + Pt::new(*params[idx * 2], *params[idx * 2 + 1]) +} + +fn point_from_param1(params: &Vec<&f32>) -> Pt { + _point_from_params(params, 0) +} + +fn point_from_param2(params: &Vec<&f32>) -> (Pt, Pt) { + (_point_from_params(params, 0), _point_from_params(params, 1)) +} + +fn point_from_param3(params: &Vec<&f32>) -> (Pt, Pt, Pt) { + ( + _point_from_params(params, 0), + _point_from_params(params, 1), + _point_from_params(params, 2), + ) +} + +// fn point_from_param4(params: &Vec<&f32>) -> (Pt, Pt, Pt, Pt) { +// ( +// _point_from_params(params, 0), +// _point_from_params(params, 1), +// _point_from_params(params, 2), +// _point_from_params(params, 3), +// ) +// } + +pub fn many_pt2_to_vec2(ps: &Vec) -> Vec { + ps.iter().map(|p| p.as_vec2()).collect_vec() +} + +pub fn cubic_bezier_path_to_lyon(path: &[CubicBezier], closed: bool) -> Option { + // let mut builder = Path::builder(); + + if path.is_empty() { + return None; + } + let mut kurbo_path = BezPath::new(); + + kurbo_path.move_to(vec2_to_kurbo(path[0].from)); + for c in path { + kurbo_path.curve_to( + vec2_to_kurbo(c.ctrl1), + vec2_to_kurbo(c.ctrl2), + vec2_to_kurbo(c.to), + ) + } + + if closed { + kurbo_path.close_path(); + } + + let tolerance = 0.01; + + let mut lyon_builder = lyon::path::Path::builder(); + kurbo::flatten(kurbo_path, tolerance, |el| { + match el { + kurbo::PathEl::MoveTo(p) => { + lyon_builder.begin(point(p.x as f32, p.y as f32)); + } + kurbo::PathEl::LineTo(p) => { + lyon_builder.line_to(point(p.x as f32, p.y as f32)); + } + kurbo::PathEl::ClosePath => lyon_builder.close(), + // The flatten iterator produces only MoveTo, LineTo, and ClosePath. + _ => {} + } + }); + let path = lyon_builder.build(); + Some(path) +} + +pub fn tesselate_lyon_vertex_simple(outline: &[VertexSimple]) -> (Vec, Vec) { + let mut path_builder = Path::builder_with_attributes(5); + + // convert path to lyon + if let Some(first_vertex) = outline.first() { + path_builder.begin(vec2_to_pt(first_vertex.pos2d()), &first_vertex.attrs()); + for vertex in outline.iter().skip(1) { + path_builder.line_to(vec2_to_pt(vertex.pos2d()), &vertex.attrs()); + } + path_builder.close(); + } else { + return (Vec::new(), Vec::new()); + } + + let path = path_builder.build(); + + let opts = FillOptions::default() + .with_tolerance(10.1) + .with_fill_rule(FillRule::EvenOdd) + .with_intersections(true); + + let mut geometry: lyon::lyon_tessellation::VertexBuffers = + lyon::lyon_tessellation::VertexBuffers::new(); + let mut tess = FillTessellator::new(); + tess.tessellate_path( + path.as_slice(), + &opts, + &mut BuffersBuilder::new(&mut geometry, |mut v: FillVertex| { + let pos = v.position(); + let attrs = v.interpolated_attributes(); + + VertexSimple { + position: [pos.x, pos.y, 0.0], + normal: [attrs[0], attrs[1], attrs[2]], + face_pos: [attrs[3], attrs[4]], + } + }), + ) + .expect("tessellation failed"); + + (geometry.indices, geometry.vertices) +} + +pub fn tesselate_lyon(path: &Path) -> (Vec, Vec<[f32; 3]>) { + let opts = FillOptions::default() + .with_tolerance(0.1) + .with_fill_rule(FillRule::EvenOdd) + .with_intersections(true); + + let mut geometry: lyon::lyon_tessellation::VertexBuffers<[f32; 3], u32> = + lyon::lyon_tessellation::VertexBuffers::new(); + let mut tess = FillTessellator::new(); + tess.tessellate_path( + path.as_slice(), + &opts, + &mut BuffersBuilder::new(&mut geometry, |v: FillVertex| { + let p = v.position(); + [p.x, p.y, 0.0] + }), + ) + .expect("tessellation failed"); + + (geometry.indices, geometry.vertices) +} + +pub fn parse_svg_data_as_vec2(data: &svg::node::element::path::Data, tolerance: f32) -> Vec { + parse_data(data, tolerance) +} + +// svg loader +fn parse_data(data: &svg::node::element::path::Data, tolerance: f32) -> Vec { + let mut segment_state = SegmentState::new_with_line_space(tolerance); + + let mut from = Pt::new(0.0, 0.0); + + // this is needed for smooth cubic blah + // let mut prev_ctrl = None; + + // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths + for command in data.iter() { + // println!("{:?}", command); + + let _ly = match command { + svg::node::element::path::Command::Move(_pos, params) => { + let curve: Vec<&f32> = params.iter().collect(); + from = point_from_param1(&curve); + } + svg::node::element::path::Command::Line(pos, params) => { + for raw_curve in ¶ms.iter().chunks(2) { + let curve: Vec<&f32> = raw_curve.collect(); + + let to = point_for_position(pos, Pt::new(*curve[0], *curve[1]), from); + + let line = lyon::geom::LineSegment { + from: from.into(), + to: to.into(), + }; + + let length = line.length(); + + segment_state.add_segment(line, length); + + from = to; + } + } + svg::node::element::path::Command::HorizontalLine(pos, params) => { + for next_point in params.iter() { + let to = match pos { + svg::node::element::path::Position::Absolute => { + Pt::new(*next_point, from.y()) + } + svg::node::element::path::Position::Relative => { + Pt::new(next_point + from.x(), from.y()) + } + }; + + let line = lyon::geom::LineSegment { + from: from.into(), + to: to.into(), + }; + + let length = line.length(); + + segment_state.add_segment(line, length); + + from = to; + } + } + svg::node::element::path::Command::VerticalLine(pos, params) => { + for next_point in params.iter() { + let to = match pos { + svg::node::element::path::Position::Absolute => { + Pt::new(from.x(), *next_point) + } + svg::node::element::path::Position::Relative => { + Pt::new(from.x(), next_point + from.y()) + } + }; + + let line = lyon::geom::LineSegment { + from: from.into(), + to: to.into(), + }; + + let length = line.length(); + + segment_state.add_segment(line, length); + + from = to; + } + } + svg::node::element::path::Command::CubicCurve(pos, params) => { + for raw_curve in ¶ms.iter().chunks(6) { + let curve: Vec<&f32> = raw_curve.collect(); + let (raw_ctrl1, raw_ctrl2, raw_to) = point_from_param3(&curve); + + let ctrl1 = point_for_position(pos, raw_ctrl1, from); + let ctrl2 = point_for_position(pos, raw_ctrl2, from); + let to = point_for_position(pos, raw_to, from); + + let line = lyon::geom::CubicBezierSegment { + from: from.into(), + ctrl1: ctrl1.into(), + ctrl2: ctrl2.into(), + to: to.into(), + }; + + let length = line.approximate_length(0.1); + + segment_state.add_segment(line, length); + + // prev_ctrl = Some(raw_ctrl2); + + from = to; + } + } + svg::node::element::path::Command::SmoothCubicCurve( + svg::node::element::path::Position::Relative, + params, + ) => { + for raw_curve in ¶ms.iter().chunks(4) { + let curve: Vec<&f32> = raw_curve.collect(); + let (raw_ctrl2, raw_to) = point_from_param2(&curve); + + let ctrl2 = raw_ctrl2 + from; + let to = raw_to + from; + + // let ctrl1_raw = prev_ctrl.unwrap(); // better exist + // let ctrl1 = point(from.x - ctrl1_raw.y, from.y - ctrl1_raw.x); + let ctrl1 = Pt::new(from.x(), from.y()); // i'm.. surprised this works + + let line = lyon::geom::CubicBezierSegment { + from: from.into(), + ctrl1: ctrl1.into(), + ctrl2: ctrl2.into(), + to: to.into(), + }; + + let length = line.approximate_length(0.1); + + segment_state.add_segment(line, length); + + from = to; + } + } + svg::node::element::path::Command::Close => {} + svg::node::element::path::Command::QuadraticCurve(pos, params) => { + for raw_curve in ¶ms.iter().chunks(4) { + let curve: Vec<&f32> = raw_curve.collect(); + let (raw_ctrl, raw_to) = point_from_param2(&curve); + + let to = point_for_position(pos, raw_to, from); + let ctrl = point_for_position(pos, raw_ctrl, from); + + let line = lyon::geom::QuadraticBezierSegment { + from: from.into(), + ctrl: ctrl.into(), + to: to.into(), + }; + + let length = line.approximate_length(0.1); + + segment_state.add_segment(line, length); + + from = to; + } + } + svg::node::element::path::Command::SmoothQuadraticCurve(_, _) => todo!(), + svg::node::element::path::Command::EllipticalArc(_, _) => todo!(), + _ => todo!(), + }; + } + + // println!("processed {:?} pts", segment_state.vertices.len()); + + segment_state + .vertices + .into_iter() + .map(|x| x.as_vec2()) + .collect_vec() +} + +#[derive(Debug, Copy, Clone)] +pub struct Pt { + pt: Point2D, +} +impl Pt { + pub fn new(x: f32, y: f32) -> Pt { + Pt { + pt: Point2D::::new(x, y), + } + } + + fn x(&self) -> f32 { + self.pt.x + } + + fn y(&self) -> f32 { + self.pt.y + } + + pub fn as_vec2(&self) -> Vec2 { + Vec2::new(self.y(), self.x()) + } +} + +impl ops::Add for Pt { + type Output = Pt; + + fn add(self, rhs: Pt) -> Pt { + Pt::new(self.x() + rhs.x(), self.y() + rhs.y()) + } +} + +impl Into> for Pt { + fn into(self) -> Point2D { + self.pt + } +} + +fn point_for_position(pos: &svg::node::element::path::Position, pt: Pt, from: Pt) -> Pt { + match pos { + svg::node::element::path::Position::Absolute => pt.into(), + svg::node::element::path::Position::Relative => (pt + from).into(), + } +} + +pub struct SegmentState { + vertices: Vec, + line_space: f32, + dist_towards_next: f32, +} +impl SegmentState { + pub fn new() -> SegmentState { + SegmentState { + vertices: Vec::::new(), + line_space: 5.0, + dist_towards_next: 0.0, + } + } + + pub fn new_with_line_space(line_space: f32) -> SegmentState { + SegmentState { + vertices: Vec::::new(), + line_space, + dist_towards_next: 0.0, + } + } + + fn update(&mut self, length: f32, new_vertices: Vec) { + self.dist_towards_next = (length + self.dist_towards_next) % self.line_space; + self.vertices.extend(new_vertices); + } + pub fn vertices(&self) -> Vec { + self.vertices.iter().map(|x| vec2(x.x(), x.y())).collect() + } + + pub fn add_segment(&mut self, segment: impl lyon::geom::Segment, length: f32) { + let mut vertices: Vec = Vec::::new(); + + let pt_count = ((length) / self.line_space) as u32; + + // println!("pt count {:?}", pt_count); + // println!("{:?}", self.dist_towards_next); + + // if it's an even number, we'll need one more. just include it, then + // trim it when t turns out > 1 + for pt_i in 0..=pt_count { + let t_n = (self.line_space * pt_i as f32) + self.dist_towards_next; + let t = t_n / length; + // println!("{:?} {:?}", t_n, t); + + if t <= 1.0 { + let x = segment.x(t); + let y = segment.y(t); + // println!("({:?}, {:?})", x, y); + vertices.push(Pt::new(x, y)); + } + } + + self.update(length, vertices) + } +} + +// fn parse_data_as_curve(data: &svg::node::element::path::Data, _tolerance: f32) -> CurveDrawer { +// let mut curve_segments: Vec = vec![]; + +// let mut from = Pt::new(0.0, 0.0); +// let mut close = false; + +// // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths +// for command in data.iter() { +// // println!("{:?}", command); + +// let _ly = match command { +// svg::node::element::path::Command::Move(_pos, params) => { +// let curve: Vec<&f32> = params.iter().collect(); +// from = point_from_param1(&curve); + +// curve_segments.push(CurveSegment::new_simple_point(from.as_vec2())); +// } +// svg::node::element::path::Command::Line(pos, params) => { +// for raw_curve in ¶ms.iter().chunks(2) { +// let curve: Vec<&f32> = raw_curve.collect(); + +// let to = point_for_position(pos, Pt::new(*curve[0], *curve[1]), from); + +// // let line = lyon::geom::LineSegment { +// // from: from.into(), +// // to: to.into(), +// // }; + +// // let length = line.length(); + +// // segment_state.add_segment(line, length); +// curve_segments.push(CurveSegment::new_simple_point(to.as_vec2())); + +// from = to; +// } +// } +// svg::node::element::path::Command::HorizontalLine(_, _) => todo!(), +// svg::node::element::path::Command::VerticalLine( +// svg::node::element::path::Position::Relative, +// params, +// ) => { +// for next_point in params.iter() { +// let to = Pt::new(from.x(), next_point + from.y()); + +// // let line = lyon::geom::LineSegment { +// // from: from.into(), +// // to: to.into(), +// // }; + +// // let length = line.length(); + +// // segment_state.add_segment(line, length); + +// curve_segments.push(CurveSegment::new_simple_point(to.as_vec2())); + +// from = to; +// } +// } +// svg::node::element::path::Command::CubicCurve(pos, params) => { +// for raw_curve in ¶ms.iter().chunks(6) { +// let curve: Vec<&f32> = raw_curve.collect(); +// let (raw_ctrl1, raw_ctrl2, raw_to) = point_from_param3(&curve); + +// let _ctrl1 = point_for_position(pos, raw_ctrl1, from); +// let _ctrl2 = point_for_position(pos, raw_ctrl2, from); +// let _to = point_for_position(pos, raw_to, from); + +// // let line = lyon::geom::CubicBezierSegment { +// // from: from.into(), +// // ctrl1: ctrl1.into(), +// // ctrl2: ctrl2.into(), +// // to: to.into(), +// // }; + +// // let length = line.approximate_length(0.1); + +// // segment_state.add_segment(line, length); + +// todo!("cubic!"); +// // curve_segments.push(CurveSegment::new_simple_point(to.as_vec2())); + +// // prev_ctrl = Some(raw_ctrl2); + +// //from = to; +// } +// } +// svg::node::element::path::Command::SmoothCubicCurve( +// svg::node::element::path::Position::Relative, +// params, +// ) => { +// for raw_curve in ¶ms.iter().chunks(4) { +// let curve: Vec<&f32> = raw_curve.collect(); +// let (raw_ctrl2, raw_to) = point_from_param2(&curve); + +// let ctrl2 = raw_ctrl2 + from; +// let to = raw_to + from; + +// // let ctrl1_raw = prev_ctrl.unwrap(); // better exist +// // let ctrl1 = point(from.x - ctrl1_raw.y, from.y - ctrl1_raw.x); +// let ctrl1 = Pt::new(from.x(), from.y()); // i'm.. surprised this works + +// let _line = lyon::geom::CubicBezierSegment { +// from: from.into(), +// ctrl1: ctrl1.into(), +// ctrl2: ctrl2.into(), +// to: to.into(), +// }; + +// // let length = line.approximate_length(0.1); +// // segment_state.add_segment(line, length); +// todo!("cubic!"); + +// //from = to; +// } +// } +// svg::node::element::path::Command::Close => { +// close = true; +// } +// svg::node::element::path::Command::QuadraticCurve(pos, params) => { +// for raw_curve in ¶ms.iter().chunks(4) { +// let curve: Vec<&f32> = raw_curve.collect(); +// let (raw_ctrl, raw_to) = point_from_param2(&curve); + +// let to = point_for_position(pos, raw_to, from); +// let ctrl = point_for_position(pos, raw_ctrl, from); + +// let _line = lyon::geom::QuadraticBezierSegment { +// from: from.into(), +// ctrl: ctrl.into(), +// to: to.into(), +// }; + +// todo!("quad!"); + +// // let length = line.approximate_length(0.1); + +// // segment_state.add_segment(line, length); + +// //from = to; +// } +// } +// svg::node::element::path::Command::SmoothQuadraticCurve(_, _) => todo!(), +// svg::node::element::path::Command::EllipticalArc(_, _) => todo!(), +// _ => todo!(), +// }; +// } + +// // println!("processed {:?} pts", segment_state.vertices.len()); + +// // segment_state.vertices + +// // todo, figure out closed + +// CurveDrawer::new(curve_segments, close) +// } + +pub fn load_all_data(path: T, tolerance: f32) -> HashMap>> +where + T: AsRef, +{ + let map = load_all_data_into_map(path); + + // println!("loaded into map"); + + let r: HashMap>> = map + .iter() + .map(|(k, v)| { + println!("processing {:?}", k); + ( + k.to_string(), + v.iter().map(|vv| parse_data(vv, tolerance)).collect_vec(), + ) + }) + .collect(); + r +} + +pub fn load_all_data_into_map(path: T) -> HashMap> +where + T: AsRef, +{ + let mut content = String::new(); + + let mut maps: HashMap> = HashMap::new(); + + let mut recent_id: String = "".to_string(); // i hate this + + for event in svg::open(path, &mut content).unwrap() { + if let svg::parser::Event::Tag(_, _, attributes) = event { + if let Some(id) = attributes.get("id") { + println!("loading {:?}", id); + recent_id = id.to_string(); + } + + if let Some(path_data) = attributes.get("d") { + // println!("path_data {:?}", path_data); + let data = svg::node::element::path::Data::parse(path_data).unwrap(); + maps.entry(recent_id.to_owned()).or_default().push(data); + } + }; + } + + maps +} + +// SvgPathDef is a simplified svg thingy.. this just converts back to the full +// svg and then parses like usual +pub fn parse_svg_path_as_vec2(data: &SvgPathDef, tolerance: f32) -> Vec { + let mut cmds = svg::node::element::path::Data::new(); + let (start_x, start_y) = data.svg_move_to(); + cmds = cmds.move_to(vec![start_x, start_y]); + // (Command::Move(Position::Absolute, ); + + for cmd in data.cmds() { + match cmd { + crate::svg::SvgCmd::Line(svg_to) => { + let (x, y) = svg_to.params(); + cmds = cmds.line_to(vec![x, y]); + } + crate::svg::SvgCmd::CubicBezier(svg_cubic_bezier) => { + let (a, b, c, d, e, f) = svg_cubic_bezier.params(); + cmds = cmds.cubic_curve_to(vec![a, b, c, d, e, f]); + } + } + } + + parse_svg_data_as_vec2(&cmds, tolerance) +} + +// fn point_to_param(pt: &Point2D) -> Vec { +// vec![pt.x, pt.y] +// } + +// fn points_to_param(pts: Vec<&Point2D>) -> Vec { +// pts.iter().map(|pt| point_to_param(*pt)).flatten().collect() +// } +// todo, can i combine this with the output? +pub struct LayersFromSvg { + pub layers: HashMap>, +} +impl LayersFromSvg { + pub fn load(path: T) -> LayersFromSvg + where + T: AsRef, + { + let vecs = load_all_data(path, 5.0); + + let mut layers = HashMap::new(); + for (layer_name, vec) in &vecs { + let polylines = vec.iter().map(|x| Polyline::new(x.clone())).collect(); + layers.insert(layer_name.clone(), polylines); + } + + LayersFromSvg { layers } + } +} diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 265b8ef..8bda410 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -22,6 +22,43 @@ use svg::{ Document, Node, }; +pub struct MurreletSvgAttributes(Vec<(String, String)>); +impl MurreletSvgAttributes { + pub fn add(&mut self, key: &str, value: &str) { + self.0.push((key.to_string(), value.to_string())) + } + + fn new(v: Vec<(String, String)>) -> Self { + Self(v) + } + + fn empty() -> Self { + Self(vec![]) + } + + fn new_single(key: &str, value: &str) -> Self { + let mut v = Self::empty(); + v.add(key, value); + v + } + + fn iter(&self) -> std::slice::Iter<'_, (String, String)> { + self.0.iter() + } + + fn add_other(&mut self, other: &Self) { + self.0.extend(other.0.clone()); + } + + fn add_float(&mut self, key: &str, stroke_weight: f32) { + self.add(key, &format!("{}", stroke_weight)) + } + + fn add_px(&mut self, key: &str, val: f32) { + self.add(key, &format!("{}px", val)) + } +} + // this area actually follows the spec more closely #[derive(Debug, Clone)] @@ -171,57 +208,59 @@ impl ToSvgMatrix for Mat4 { } } -// for a type that means something, e.g. "style fro a shape" -trait AddSvgAttributes { - fn add_svg_attributes(&self, p: T) -> T; -} +// // for a type that means something, e.g. "style fro a shape" +// trait AddSvgAttributes { +// fn add_svg_attributes(&self, p: T) -> T; +// } // for component types, e.g. "color" trait GetSvgAttributes { - fn get_svg_attributes(&self) -> String; + fn get_svg_attributes(&self) -> MurreletSvgAttributes; + + fn add_svg_attributes(&self, p: T) -> T { + let mut p = p; + let updates = self.get_svg_attributes(); + for (key, value) in &updates.0 { + p.assign(key, value.clone()); + } + p + } } impl GetSvgAttributes for StyledPathSvgFill { - fn get_svg_attributes(&self) -> String { - format!("url(#P{})", self.hash()) + fn get_svg_attributes(&self) -> MurreletSvgAttributes { + MurreletSvgAttributes::new_single("fill", &format!("url(#P{})", self.hash())) } } impl GetSvgAttributes for MurreletColor { - fn get_svg_attributes(&self) -> String { - let [r, g, b, a] = self.into_rgba_components(); - let fill = format!( - "rgba({}, {}, {}, {})", - (r * 255.0) as i32, - (g * 255.0) as i32, - (b * 255.0) as i32, - a - ); - fill + fn get_svg_attributes(&self) -> MurreletSvgAttributes { + let mut v = MurreletSvgAttributes::new_single("fill", &self.to_svg_rgb()); + v.add("fill-rule", "evenodd"); + if self.alpha() < 1.0 { + v.add("fill-opacity", &format!("{}", self.alpha())); + } + v } } impl GetSvgAttributes for RGBandANewtype { - fn get_svg_attributes(&self) -> String { + fn get_svg_attributes(&self) -> MurreletSvgAttributes { self.color().get_svg_attributes() } } -impl AddSvgAttributes for MurreletColorStyle { - fn add_svg_attributes(&self, p: T) -> T { - let mut p = p; - p.assign("fill-rule", "evenodd"); - match self { - MurreletColorStyle::Color(c) => p.assign("fill", c.get_svg_attributes()), - MurreletColorStyle::RgbaFill(c) => p.assign("fill", c.get_svg_attributes()), - MurreletColorStyle::SvgFill(c) => p.assign("fill", c.get_svg_attributes()), - } - p - } -} +// impl AddSvgAttributes for MurreletColorStyle { +// // fn add_svg_attributes(&self, p: T) -> T { +// // let mut p = p; +// // p.assign("fill-rule", "evenodd"); +// // update_node(&mut p, &self.get_svg_attributes()); +// // p +// // } +// } impl GetSvgAttributes for MurreletColorStyle { - fn get_svg_attributes(&self) -> String { + fn get_svg_attributes(&self) -> MurreletSvgAttributes { match self { MurreletColorStyle::Color(c) => c.get_svg_attributes(), MurreletColorStyle::RgbaFill(c) => c.get_svg_attributes(), @@ -230,93 +269,152 @@ impl GetSvgAttributes for MurreletColorStyle { } } -impl AddSvgAttributes for MurreletPath { - fn add_svg_attributes(&self, p: T) -> T { - let mut p = p; - let svg_str = self.get_svg_attributes(); - if !svg_str.is_empty() { - p.assign("transform", svg_str); - } - p - } -} +// impl AddSvgAttributes for MurreletPath { +// fn add_svg_attributes(&self, p: T) -> T { +// let mut p = p; +// update_node(&mut p, &self.get_svg_attributes()); +// p +// } +// } impl GetSvgAttributes for MurreletPath { - fn get_svg_attributes(&self) -> String { + fn get_svg_attributes(&self) -> MurreletSvgAttributes { if let Some(t) = self.transform() { - t.to_svg_matrix() + MurreletSvgAttributes::new_single("transform", &t.to_svg_matrix()) } else { - "".to_string() + MurreletSvgAttributes::empty() } } } -impl AddSvgAttributes for MurreletStyle { - fn add_svg_attributes(&self, p: T) -> T { - let mut p = p; - - p.assign("fill-rule", "evenodd"); +impl GetSvgAttributes for MurreletStyle { + fn get_svg_attributes(&self) -> MurreletSvgAttributes { + let mut v = MurreletSvgAttributes::new_single("fill-rule", "evenodd"); match self.drawing_plan() { murrelet_draw::draw::MurreletDrawPlan::Shader(fill) => { - p.assign("fill", fill.get_svg_attributes()) + v.add_other(&fill.get_svg_attributes()); } murrelet_draw::draw::MurreletDrawPlan::DebugPoints(_) => unimplemented!(), murrelet_draw::draw::MurreletDrawPlan::FilledClosed => { - p.assign("fill", self.color.get_svg_attributes()); + // v.add("fill", self.color.get_svg_attributes()); + + v.add_other(&self.color.get_svg_attributes()); if self.stroke_weight > 0.0 { - p.assign("stroke-width", self.stroke_weight); - p.assign("stroke-linejoin", "round"); - p.assign("stroke-linecap", "round"); - p.assign("stroke", self.stroke_color.get_svg_attributes()); + v.add_float("stroke-width", self.stroke_weight); + v.add("stroke-linejoin", "round"); + v.add("stroke-linecap", "round"); + v.add("stroke", &self.stroke_color.as_color().hex()); } } murrelet_draw::draw::MurreletDrawPlan::Outline => { - p.assign("fill", "none"); + v.add("fill", "none"); if self.stroke_weight > 0.0 { - p.assign("stroke-width", self.stroke_weight); - p.assign("stroke-linejoin", "round"); - p.assign("stroke-linecap", "round"); - p.assign("stroke", self.color.get_svg_attributes()); + v.add_float("stroke-width", self.stroke_weight); + v.add("stroke-linejoin", "round"); + v.add("stroke-linecap", "round"); + v.add("stroke", &self.color.as_color().hex()); } } murrelet_draw::draw::MurreletDrawPlan::Line => { - p.assign("fill", "none"); + v.add("fill", "none"); if self.stroke_weight > 0.0 { - p.assign("stroke-width", self.stroke_weight); - p.assign("stroke-linejoin", "round"); - p.assign("stroke-linecap", "round"); - p.assign("stroke", self.color.get_svg_attributes()); + v.add_float("stroke-width", self.stroke_weight); + v.add("stroke-linejoin", "round"); + v.add("stroke-linecap", "round"); + v.add("stroke", &self.color.as_color().hex()); } } } + v + } +} + +// impl AddSvgAttributes for MurreletStyle { +// fn add_svg_attributes(&self, p: T) -> T { +// let mut p = p; + +// p.assign("fill-rule", "evenodd"); + +// match self.drawing_plan() { +// murrelet_draw::draw::MurreletDrawPlan::Shader(fill) => { +// p.assign("fill", fill.get_svg_attributes()) +// } +// murrelet_draw::draw::MurreletDrawPlan::DebugPoints(_) => unimplemented!(), +// murrelet_draw::draw::MurreletDrawPlan::FilledClosed => { +// p.assign("fill", self.color.get_svg_attributes()); + +// if self.stroke_weight > 0.0 { +// p.assign("stroke-width", self.stroke_weight); +// p.assign("stroke-linejoin", "round"); +// p.assign("stroke-linecap", "round"); +// p.assign("stroke", self.stroke_color.get_svg_attributes()); +// } +// } +// murrelet_draw::draw::MurreletDrawPlan::Outline => { +// p.assign("fill", "none"); + +// if self.stroke_weight > 0.0 { +// p.assign("stroke-width", self.stroke_weight); +// p.assign("stroke-linejoin", "round"); +// p.assign("stroke-linecap", "round"); +// p.assign("stroke", self.color.get_svg_attributes()); +// } +// } +// murrelet_draw::draw::MurreletDrawPlan::Line => { +// p.assign("fill", "none"); + +// if self.stroke_weight > 0.0 { +// p.assign("stroke-width", self.stroke_weight); +// p.assign("stroke-linejoin", "round"); +// p.assign("stroke-linecap", "round"); +// p.assign("stroke", self.color.get_svg_attributes()); +// } +// } +// } - p - } -} - -impl AddSvgAttributes for StyledPath { - fn add_svg_attributes(&self, p: T) -> T { - let mut p = p; - p = self.annotations.add_svg_attributes(p); - p = self.path.add_svg_attributes(p); - p = self.style.add_svg_attributes(p); - p - } -} +// p +// } +// } -impl AddSvgAttributes for MurreletPathAnnotation { - fn add_svg_attributes(&self, p: T) -> T { - let mut p = p; +impl GetSvgAttributes for StyledPath { + fn get_svg_attributes(&self) -> MurreletSvgAttributes { + let mut c = self.annotations.get_svg_attributes(); + c.add_other(&self.path.get_svg_attributes()); + c.add_other(&self.style.get_svg_attributes()); + c + } + // fn add_svg_attributes(&self, p: T) -> T { + // let mut p = p; + // p = self.annotations.add_svg_attributes(p); + // p = self.path.add_svg_attributes(p); + // p = self.style.add_svg_attributes(p); + // p + // } +} + +impl GetSvgAttributes for MurreletPathAnnotation { + fn get_svg_attributes(&self) -> MurreletSvgAttributes { + let mut a = MurreletSvgAttributes::empty(); for (k, v) in self.vals() { - p.assign(k.to_string(), v.to_string()); + a.add(k, v); } - p + a } } +// impl AddSvgAttributes for MurreletPathAnnotation { +// fn add_svg_attributes(&self, p: T) -> T { +// let mut p = p; +// for (k, v) in self.vals() { +// p.assign(k.to_string(), v.to_string()); +// } +// p +// } +// } + pub trait ToSvgPath { fn make_group(&self) -> Option; fn make_pattern(&self) -> Option<(String, svg::node::element::Pattern)>; @@ -392,7 +490,7 @@ impl SvgDocCreator { .set("text-anchor", "middle") .set("font-family", "monospace".to_string()) .set("font-size", format!("{}px", text_size)) - .set("fill", text.style.color.get_svg_attributes()) + .set("fill", text.style.color.as_color().hex()) .add(svg::node::Text::new(text.text.clone())); text @@ -465,7 +563,8 @@ impl SvgDocCreator { let (view_box_x, view_box_y) = if let Some(r) = self.svg_draw_config.resolution { let [width, height] = r.as_dims(); - (width * 2, height * 2) + // (width * 2, height * 2) // i'm not sure why i had this? + (width, height) } else { (800, 800) }; @@ -479,12 +578,16 @@ impl SvgDocCreator { .set("height", format!("{:?}mm", target_size)); if let Some(bg_color) = self.svg_draw_config.bg_color() { - let bg_rect = svg::node::element::Rectangle::new() + let mut bg_rect = svg::node::element::Rectangle::new() .set("x", 0) .set("y", 0) .set("width", view_box_x) .set("height", view_box_y) .set("fill", bg_color.to_svg_rgb()); + + if bg_color.alpha() < 1.0 { + bg_rect = bg_rect.set("fill-opacity", bg_color.to_svg_rgb()); + } doc = doc.add(bg_rect); } From c2b420d39e5b26819fa8e7ac791150c73006d82e Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 25 Jun 2025 14:00:13 -0400 Subject: [PATCH 039/149] wip --- murrelet_common/src/geometry.rs | 20 ++++++++++++++++++-- murrelet_common/src/lib.rs | 1 - murrelet_draw/src/cubic.rs | 22 +++++++++++++++++++++- murrelet_draw/src/transform2d.rs | 10 ++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index df46b93..c2b510c 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -7,7 +7,7 @@ use glam::{vec2, Mat3, Mat4, Vec2}; use crate::{ intersection::{find_intersection_inf, within_segment}, transform::TransformVec2, - SimpleTransform2d, + SimpleTransform2d, SimpleTransform2dStep, }; pub fn a_pi(a: f32) -> AnglePi { @@ -360,6 +360,9 @@ impl IsLength for PointToPoint { // Special types + +// should combine this with Tangent... + #[derive(Debug, Copy, Clone)] pub struct SpotOnCurve { loc: Vec2, @@ -438,6 +441,13 @@ impl SpotOnCurve { angle: self.angle + rotate, } } + + pub fn to_transform(&self) -> SimpleTransform2d { + SimpleTransform2d::new(vec![ + SimpleTransform2dStep::rotate_pi(self.angle()), + SimpleTransform2dStep::translate(self.loc()), + ]) + } } #[derive(Clone, Copy, Debug)] @@ -539,7 +549,8 @@ impl PointToPoint { } pub fn midpoint(&self) -> Vec2 { - 0.5 * (self.start + self.end) + // 0.5 * (self.start + self.end) + self.pct(0.5) } pub fn to_vec(&self) -> Vec { @@ -575,6 +586,10 @@ impl PointToPoint { end: closest_point, } } + + pub fn pct(&self, loc: f32) -> Vec2 { + self.start + loc * (self.end - self.start) + } } #[derive(Copy, Clone, Debug)] @@ -668,4 +683,5 @@ impl Tangent { pub fn loc(&self) -> Vec2 { self.loc } + } \ No newline at end of file diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index a106cdc..d3176da 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -11,7 +11,6 @@ use std::hash::{Hash, Hasher}; pub mod intersection; -pub mod arcs; mod assets; mod color; mod geometry; diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index 6e3d88d..4193c3c 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -10,7 +10,21 @@ pub struct CubicBezier { } impl CubicBezier { pub fn new(from: Vec2, ctrl1: Vec2, ctrl2: Vec2, to: Vec2) -> Self { - Self { from, ctrl1, ctrl2, to } + Self { + from, + ctrl1, + ctrl2, + to, + } + } + + pub fn line(from: Vec2, to: Vec2) -> Self { + Self { + from, + ctrl1: from, + ctrl2: to, + to, + } } pub fn split(&self, t: f32) -> (CubicBezier, CubicBezier) { @@ -78,4 +92,10 @@ impl CubicBezier { ctrl_line.length(), ) } + + pub fn tangent_at_pct(&self, pct: f32) -> Tangent { + let (start, _) = self.split(pct); + let (t, _a) = start.end_to_tangent(); + t + } } diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index e14a9ce..1c77f6c 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -213,6 +213,16 @@ impl Transform2d { pub fn to_simple(&self) -> SimpleTransform2d { SimpleTransform2d::new(self.0.iter().map(|t| t.to_simple()).collect_vec()) } + + pub fn from_simple(simple: &SimpleTransform2d) -> Self { + Self::new( + simple + .steps() + .iter() + .map(|x| Transform2dStep::from_simple(x.clone())) + .collect_vec(), + ) + } } impl Default for ControlTransform2d { From 5e05faa1760882ed945fb01044b68ddba37b19cf Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 25 Jun 2025 16:57:28 -0400 Subject: [PATCH 040/149] wip --- murrelet_common/src/geometry.rs | 8 ++++---- murrelet_common/src/idx.rs | 5 +++++ murrelet_livecode/src/expr.rs | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index c2b510c..43c67be 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -427,12 +427,12 @@ impl SpotOnCurve { } } - pub fn move_left_perp_dist(&self, length: Length) -> Vec2 { - self.turn_left_perp().to_line(length).to_last_point() + pub fn move_left_perp_dist(&self, length: L) -> Vec2 { + self.turn_left_perp().to_line(length.to_length()).to_last_point() } - pub fn move_right_perp_dist(&self, length: Length) -> Vec2 { - self.turn_right_perp().to_line(length).to_last_point() + pub fn move_right_perp_dist(&self, length: L) -> Vec2 { + self.turn_right_perp().to_line(length.to_length()).to_last_point() } pub fn rotate(&self, rotate: AnglePi) -> Self { diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 52daf59..c6c5ca7 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -111,6 +111,11 @@ impl IdxInRange { pub fn amount_from_end(&self) -> u64 { self.total - self.i - 1 } + + // scale + pub fn s(&self, range: Vec2) -> f32 { + lerp(range.x, range.y, self.pct()) + } } #[derive(Debug, Clone, Copy)] diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index 4c9fc4e..f360e1d 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -196,6 +196,22 @@ pub fn init_evalexpr_func_ctx() -> LivecodeResult { let f = (PI * 2.0 * (w * t + phase)).sin(); Ok(Value::Float(f)) }), + "sinpos" => Function::new(move |argument| { + let (t, w, phase) = match argument.as_fixed_len_tuple(3) { + Ok(tuple) => (tuple[0].as_number()?, tuple[1].as_number()?, tuple[2].as_number()?), + Err(_) => { + match argument.as_fixed_len_tuple(2) { + Ok(tuple) => (tuple[0].as_number()?, tuple[1].as_number()?, 0.0), + Err(_) => { + (argument.as_float()?, 1.0, 0.0) + }, + } + } + }; + let f = 0.5 + 0.5 * (PI * 2.0 * (w * t + phase)).sin(); + Ok(Value::Float(f)) + }), + "cos" => Function::new(move |argument| { let (t, w, phase) = match argument.as_fixed_len_tuple(3) { Ok(tuple) => (tuple[0].as_number()?, tuple[1].as_number()?, tuple[2].as_number()?), From d22f40ec7df9cd93a8432137b788fbd259e8af45 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 27 Jun 2025 23:08:23 -0400 Subject: [PATCH 041/149] wip --- murrelet_common/src/lib.rs | 7 +++++++ murrelet_livecode/src/expr.rs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index d3176da..9507c21 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -810,6 +810,13 @@ impl FixedPointVec2 { pub fn nudge(&self, x: i64, y: i64) -> Self { Self::new_from_fixed_point(self.x.nudge(x), self.y.nudge(y)) } + + // manhattan + pub fn dist_man(&self, other: FixedPointVec2) -> i64 { + let dx = (self.x - other.x).abs(); + let dy = (self.y - other.y).abs(); + (dx + dy).to_i64() + } } pub fn approx_eq_eps(x: f32, y: f32, eps: f32) -> bool { diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index f360e1d..c277974 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -211,6 +211,13 @@ pub fn init_evalexpr_func_ctx() -> LivecodeResult { let f = 0.5 + 0.5 * (PI * 2.0 * (w * t + phase)).sin(); Ok(Value::Float(f)) }), + "quantize" => Function::new(move |argument| { + let tuple = argument.as_fixed_len_tuple(2)?; + let (x, y) = (tuple[0].as_number()?, tuple[1].as_number()?); + + let p = (x * y).floor() / y; + Ok(Value::Float(p)) + }), "cos" => Function::new(move |argument| { let (t, w, phase) = match argument.as_fixed_len_tuple(3) { From 489fbe07b678c427952df7accf55d9860463ef8e Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 28 Jun 2025 16:28:35 -0400 Subject: [PATCH 042/149] wip --- murrelet_draw/src/tesselate.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index b8ffc69..4d4e21a 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -5,7 +5,7 @@ use crate::{ // curve_drawer::{CurveDrawer, CurveSegment}, svg::SvgPathDef, }; -use glam::{vec2, Vec2}; +use glam::{vec2, Vec2, Vec2Swizzles}; use itertools::Itertools; use kurbo::BezPath; use lyon::{ @@ -42,7 +42,13 @@ impl ToVecVec2 for CubicBezier { .into(); svg = svg.cubic_curve_to(cubic); - let path = parse_svg_data_as_vec2(&svg, 1.0); + let mut path = parse_svg_data_as_vec2(&svg, 1.0); + + if let Some(a) = path.last() { + if a.distance(self.to.yx()) > 1.0e-3 { + path.push(self.to.yx()) + } + } path.into_iter().map(|x| vec2(x.y, x.x)).collect_vec() } From 7ead208349dc87f315d098257504bbecc8e0c606 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 29 Jun 2025 14:14:03 -0400 Subject: [PATCH 043/149] wip --- murrelet_common/src/geometry.rs | 2 ++ murrelet_draw/src/curve_drawer.rs | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 43c67be..4ec9cfb 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -59,6 +59,8 @@ impl AnglePi { pub fn is_neg(&self) -> bool { self.0 < 0.0 } + + } impl std::ops::Neg for AnglePi { diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index fe7d8f1..e6c401c 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -124,6 +124,29 @@ impl CurveSegment { CurveSegment::Arc(CurveArc::new(loc, radius.len(), start, end)) } + // pub fn new_simple_arc_from( + // start: Vec2, + // radius: Rad, + // in_angle: A1, + // angle_length: A2, + // ccw: bool, + // ) -> Self { + // // calculate the center + // let (loc, end_pi) = if ccw { + // ( + // start + in_angle.to_norm_dir() * radius.len(), + // in_angle.as_angle() - angle_length.as_angle(), + // ) + // } else { + // ( + // start - in_angle.to_norm_dir() * radius.len(), + // in_angle.as_angle() + angle_length.as_angle(), + // ) + // }; + + // CurveSegment::Arc(CurveArc::new(loc, radius.len(), in_angle, end_pi)) + // } + pub fn new_simple_circle(loc: Vec2, radius: f32) -> Self { CurveSegment::Arc(CurveArc::new( loc, From 96a04a078b8442c3b0feca0971a7cb6f0d9e17a8 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 2 Jul 2025 23:26:51 -0400 Subject: [PATCH 044/149] bbllah --- murrelet_livecode/src/expr.rs | 29 ++++++++++++++++++++++++++++ murrelet_perform/src/perform.rs | 34 ++++++++++++++++++++++++++++++++- murrelet_svg/src/svg.rs | 2 +- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index c277974..42ee963 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -92,6 +92,17 @@ pub fn init_evalexpr_func_ctx() -> LivecodeResult { let f = 1.0 - (src * 2.0 - 1.0).abs(); Ok(Value::Float(f)) }), + // l2 version of this, use power instead! + "tri2" => Function::new(|argument| { + let src = argument.as_number()?; + let f = 1.0 - (src * 2.0 - 1.0).powi(2); + Ok(Value::Float(f)) + }), + "smooth" => Function::new(|argument| { + let t = argument.as_number()?; + let f = smoothstep(t, 0.0, 1.0); + Ok(Value::Float(f)) + }), // bounce(t, 0.25) "bounce" => Function::new(|argument| { let (src, mult, offset) = match argument.as_fixed_len_tuple(3) { @@ -128,6 +139,7 @@ pub fn init_evalexpr_func_ctx() -> LivecodeResult { let f = smoothstep(t, edge0, edge1); Ok(Value::Float(f)) }), + "step" => Function::new(move |argument| { let tuple = argument.as_fixed_len_tuple(2)?; let (src, val) = (tuple[0].as_number()?, tuple[1].as_number()?); @@ -244,6 +256,23 @@ pub fn init_evalexpr_func_ctx() -> LivecodeResult { ); let f = aa * (m * PI * x / a).cos() * (n * PI * y / a).cos() - bb * (n * PI * x / b).cos() * (m * PI * y / b).cos(); Ok(Value::Float(f)) + }), + "len" => Function::new(move |argument| { + let tuple = argument.as_fixed_len_tuple(2)?; + let (x1, y1) = ( + tuple[0].as_number()?, tuple[1].as_number()?, + ); + let f = vec2(x1 as f32, y1 as f32).length(); + Ok(Value::Float(f as f64)) + }), + "dist" => Function::new(move |argument| { + let tuple = argument.as_fixed_len_tuple(4)?; + let (x1, y1, x2, y2) = ( + tuple[0].as_number()?, tuple[1].as_number()?, + tuple[2].as_number()?, tuple[3].as_number()?, + ); + let f = vec2(x1 as f32, y1 as f32).distance(vec2(x2 as f32, y2 as f32)); + Ok(Value::Float(f as f64)) }) }.map_err(|err| {LivecodeError::EvalExpr("error in init_evalexpr_func_ctx!".to_string(), err)}) } diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 82fc98c..6df53e3 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -37,6 +37,12 @@ pub trait ConfCommon: CommonTrait { fn config_app_loc(&self) -> &AppConfig; } +#[derive(Debug, Clone, Copy, Livecode, MurreletGUI, Lerpable)] +pub enum SvgSaveKind { + HTML, + Inkscape, +} + #[derive(Clone, Debug)] pub struct SvgDrawConfig { size: f32, // todo, what's the difference between this and texture sizes? @@ -47,6 +53,7 @@ pub struct SvgDrawConfig { margin_size: f32, should_resize: bool, // sorry, something to force it not to resize my shapes on the web! bg_color: Option, + output_kind: SvgSaveKind, } impl SvgDrawConfig { pub fn new( @@ -55,6 +62,7 @@ impl SvgDrawConfig { capture_path: Option, target_size: f32, frame: u64, + output_kind: SvgSaveKind, ) -> SvgDrawConfig { SvgDrawConfig { size, @@ -65,6 +73,7 @@ impl SvgDrawConfig { should_resize: true, resolution, bg_color: None, + output_kind, } } @@ -120,6 +129,15 @@ impl SvgDrawConfig { pub fn bg_color(&self) -> Option { self.bg_color } + + // hrm, for now, we have two main types, html, which i don't think will need + // layers(?), and inkscape. in any case, make_layers is also very inkscape-focused + pub fn make_layers(&self) -> bool { + match self.output_kind { + SvgSaveKind::HTML => false, + SvgSaveKind::Inkscape => true, + } + } } impl TransformVec2 for SvgDrawConfig { @@ -299,25 +317,38 @@ pub struct SvgConfig { #[livecode(serde_default = "_default_svg_size")] pub size: f32, #[livecode(serde_default = "_default_svg_save")] - pub save: bool, // trigger for svg save + pub save: bool, + #[livecode(serde_default = "_default_svg_kind")] + output_kind: SvgSaveKind, // trigger for svg save } impl Default for ControlSvgConfig { fn default() -> Self { Self { size: _default_svg_size(), save: _default_svg_save(), + output_kind: _default_svg_kind(), } } } + impl Default for ControlLazySvgConfig { fn default() -> Self { Self { size: _default_svg_size_lazy(), save: _default_svg_save_lazy(), + output_kind: _default_svg_kind_lazy(), } } } +// set this in websites! +fn _default_svg_kind_lazy() -> ControlLazySvgSaveKind { + ControlLazySvgSaveKind::Inkscape +} + +fn _default_svg_kind() -> ControlSvgSaveKind { + ControlSvgSaveKind::Inkscape +} fn _default_gpu_debug_next() -> ControlBool { #[cfg(feature = "for_the_web")] { @@ -510,6 +541,7 @@ pub fn svg_save_path_with_prefix(lil_liveconfig: &LilLiveConfig, prefix: &str) - capture_path, lil_liveconfig.app_config.svg.size, lil_liveconfig.w.actual_frame_u64(), + lil_liveconfig.app_config.svg.output_kind, ) } diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 8bda410..3965f55 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -544,7 +544,7 @@ impl SvgDocCreator { let mut defs = vec![]; for (name, layer) in paths.layers.iter() { - let (g, patterns) = self.make_layer(name, layer, false); + let (g, patterns) = self.make_layer(name, layer, self.svg_draw_config.make_layers()); doc.append(g); for p in patterns { defs.push(p.to_string()); From 76a7ad1929e5d7b36e271f5b09d1db269b0a496d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 4 Jul 2025 23:27:19 -0400 Subject: [PATCH 045/149] try supporting newtypes containing structs --- murrelet_common/src/assets.rs | 2 + murrelet_common/src/geometry.rs | 25 +- murrelet_common/src/idx.rs | 2 +- murrelet_common/src/metric.rs | 77 ++ murrelet_common/src/triangulate.rs | 13 +- murrelet_draw/src/style.rs | 6 +- murrelet_draw/src/svg.rs | 3 +- murrelet_draw/src/tesselate.rs | 62 +- .../examples/tests.rs | 14 +- .../src/derive_boop.rs | 752 ------------------ .../src/derive_lazy.rs | 43 + .../src/derive_livecode.rs | 47 ++ .../src/derive_nestedit.rs | 11 + .../murrelet_livecode_derive/src/lib.rs | 14 +- .../murrelet_livecode_derive/src/parser.rs | 21 +- murrelet_perform/src/cli.rs | 2 + murrelet_src_osc/src/osc.rs | 2 +- 17 files changed, 310 insertions(+), 786 deletions(-) delete mode 100644 murrelet_livecode_macros/murrelet_livecode_derive/src/derive_boop.rs diff --git a/murrelet_common/src/assets.rs b/murrelet_common/src/assets.rs index 962ab4e..7ebf082 100644 --- a/murrelet_common/src/assets.rs +++ b/murrelet_common/src/assets.rs @@ -80,6 +80,7 @@ impl VectorLayersAssetLookup { pub trait IsColorType {} #[derive(Debug, Clone, Copy)] +#[allow(dead_code)] pub struct BlackWhite(bool); impl IsColorType for BlackWhite {} @@ -107,6 +108,7 @@ impl RasterAssetLookup { pub struct Assets { vectors: VectorLayersAssetLookup, + #[allow(dead_code)] rasters: RasterAssetLookup, } impl Assets { diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 4ec9cfb..fc882af 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -59,8 +59,6 @@ impl AnglePi { pub fn is_neg(&self) -> bool { self.0 < 0.0 } - - } impl std::ops::Neg for AnglePi { @@ -362,7 +360,6 @@ impl IsLength for PointToPoint { // Special types - // should combine this with Tangent... #[derive(Debug, Copy, Clone)] @@ -430,11 +427,15 @@ impl SpotOnCurve { } pub fn move_left_perp_dist(&self, length: L) -> Vec2 { - self.turn_left_perp().to_line(length.to_length()).to_last_point() + self.turn_left_perp() + .to_line(length.to_length()) + .to_last_point() } pub fn move_right_perp_dist(&self, length: L) -> Vec2 { - self.turn_right_perp().to_line(length.to_length()).to_last_point() + self.turn_right_perp() + .to_line(length.to_length()) + .to_last_point() } pub fn rotate(&self, rotate: AnglePi) -> Self { @@ -530,7 +531,6 @@ impl PrevCurrNextVec2 { } } - #[derive(Copy, Clone, Debug)] pub struct PointToPoint { start: Vec2, @@ -580,8 +580,7 @@ impl PointToPoint { } pub fn closest_pt_to_pt(&self, intersection: Vec2) -> PointToPoint { - let closest_point = - self.start + (intersection - self.start).dot(self.to_norm_dir()) * self.to_norm_dir(); + let closest_point = self.closest_point_to_line(intersection); PointToPoint { start: intersection, @@ -589,6 +588,12 @@ impl PointToPoint { } } + // drop a right angle down from intersection, where does it fall along + // the line extended from point to point? + pub fn closest_point_to_line(&self, intersection: Vec2) -> Vec2 { + self.start + (intersection - self.start).dot(self.to_norm_dir()) * self.to_norm_dir() + } + pub fn pct(&self, loc: f32) -> Vec2 { self.start + loc * (self.end - self.start) } @@ -669,7 +674,6 @@ pub fn tangents_between_two_circles( Some((start, end)) } - #[derive(Copy, Clone, Debug)] pub struct Tangent { pub loc: Vec2, @@ -685,5 +689,4 @@ impl Tangent { pub fn loc(&self) -> Vec2 { self.loc } - -} \ No newline at end of file +} diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index c6c5ca7..af4608b 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -4,7 +4,7 @@ use glam::{vec2, Vec2}; use itertools::Itertools; use rand::{rngs::StdRng, Rng, SeedableRng}; -use crate::{lerp, Dim2d}; +use crate::lerp; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct IdxInRange { diff --git a/murrelet_common/src/metric.rs b/murrelet_common/src/metric.rs index 1cbdd88..d7d2b51 100644 --- a/murrelet_common/src/metric.rs +++ b/murrelet_common/src/metric.rs @@ -77,6 +77,75 @@ impl Default for BoundMetricF32 { } } +#[derive(Debug, Copy, Clone)] +pub struct BoundMetricUsize { + min: usize, + max: usize, + count: usize, + sum: usize, +} +impl BoundMetricUsize { + pub fn new() -> BoundMetricUsize { + BoundMetricUsize { + min: usize::MAX, + max: usize::MIN, + count: 0, + sum: 0, + } + } + + pub fn new_init(x: usize) -> BoundMetricUsize { + let mut a = Self::new(); + a.add_point(x); + a + } + + pub fn add_point(&mut self, x: usize) { + if x < self.min { + self.min = x + } + if x > self.max { + self.max = x + } + self.count += 1; + self.sum += x; + } + + // pub fn center(&self) -> f32 { + // 0.5 * (self.right + self.left) + // } + + pub fn size(&self) -> usize { + self.max - self.min + } + + pub fn scale(&self) -> usize { + self.size() + } + + pub fn min(&self) -> usize { + self.min + } + + pub fn max(&self) -> usize { + self.max + } + + pub fn count(&self) -> usize { + self.count + } + + // pub fn avg(&self) -> f32 { + // self.sum / self.count as f32 + // } +} + +impl Default for BoundMetricUsize { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug, Copy, Clone)] pub struct BoundMetric { x_bound: BoundMetricF32, @@ -193,4 +262,12 @@ impl BoundMetric { self.add_point(other.lower_left()); self.add_point(other.upper_right()); } + + pub fn min(&self) -> Vec2 { + vec2(self.x_min(), self.y_min()) + } + + pub fn max(&self) -> Vec2 { + vec2(self.x_max(), self.y_max()) + } } diff --git a/murrelet_common/src/triangulate.rs b/murrelet_common/src/triangulate.rs index 6ed9e42..7a32f93 100644 --- a/murrelet_common/src/triangulate.rs +++ b/murrelet_common/src/triangulate.rs @@ -53,6 +53,13 @@ impl Triangulate { } } + pub fn add_many_vertices_and_offset(&mut self, vertices: Vec, indices: Vec) { + let vertex_offset = self.vertices.len() as u32; + self.vertices.extend(vertices); + self.order + .extend(indices.iter().map(|i| *i + vertex_offset)); + } + pub fn vertices(&self) -> &[VertexSimple] { &self.vertices } @@ -67,6 +74,10 @@ impl Triangulate { (self.vertices.len() - 1) as u32 } + pub fn add_tri(&mut self, tri: [u32; 3]) { + self.order.extend(tri) + } + // alternatively can add vertices and then add teh vec pub fn add_rect(&mut self, v: &[Vec3; 4], flip: bool) { let edge1 = v[0] - v[1]; @@ -89,7 +100,7 @@ impl Triangulate { self.order = u; } - fn order(&self) -> &[u32] { + pub fn order(&self) -> &[u32] { &self.order } diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index d508e82..c05c22d 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -2,7 +2,7 @@ use crate::{ curve_drawer::CurveDrawer, draw::*, - svg::{SvgPathDef, SvgShape, TransformedSvgShape}, + svg::{SvgPathDef, TransformedSvgShape}, transform2d::*, }; use glam::*; @@ -565,9 +565,9 @@ impl MurreletPath { pub fn transform_with(&self, t: &T) -> Self { match self { MurreletPath::Polyline(x) => MurreletPath::Polyline(x.transform_with(t)), - MurreletPath::Curve(mc) => todo!(), + MurreletPath::Curve(_) => todo!(), MurreletPath::Svg(_) => todo!(), - MurreletPath::MaskedCurve(murrelet_curve, murrelet_curve1) => todo!(), + MurreletPath::MaskedCurve(_, _) => todo!(), } } diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index a272158..dcb5824 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -134,7 +134,8 @@ impl SvgPathDef { } } -fn glam_to_lyon(vec: Vec2) -> Point2D { +#[allow(dead_code)] +pub fn glam_to_lyon(vec: Vec2) -> Point2D { Point::new(vec.x, vec.y) } diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 4d4e21a..68d55a8 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -136,6 +136,66 @@ pub fn cubic_bezier_path_to_lyon(path: &[CubicBezier], closed: bool) -> Option (Vec, Vec) { + let mut path_builder = Path::builder_with_attributes(5); + + // convert path to lyon + if let Some(first_vertex) = outline.first() { + path_builder.begin(vec2_to_pt(first_vertex.pos2d()), &first_vertex.attrs()); + for vertex in outline.iter().skip(1) { + path_builder.line_to(vec2_to_pt(vertex.pos2d()), &vertex.attrs()); + } + path_builder.close(); + } else { + return (Vec::new(), Vec::new()); + } + + let amount = 1e-6f32; + for s in steiner { + // now poke holes in it + let loc = s.pos2d(); + + let p0 = loc + vec2(amount, 0.0); + let p1 = loc + vec2(0.0, amount); + let p2 = loc + vec2(-amount, 0.0); + path_builder.begin(vec2_to_pt(p0), &s.attrs()); + path_builder.line_to(vec2_to_pt(p1), &s.attrs()); + path_builder.line_to(vec2_to_pt(p2), &s.attrs()); + path_builder.close() + } + + let path = path_builder.build(); + + let opts = FillOptions::default() + // .with_tolerance(0.1) // we're passing in line segments + .with_fill_rule(FillRule::EvenOdd) + .with_intersections(true); + + let mut geometry: lyon::lyon_tessellation::VertexBuffers = + lyon::lyon_tessellation::VertexBuffers::new(); + let mut tess = FillTessellator::new(); + tess.tessellate_path( + path.as_slice(), + &opts, + &mut BuffersBuilder::new(&mut geometry, |mut v: FillVertex| { + let pos = v.position(); + let attrs = v.interpolated_attributes(); + + VertexSimple { + position: [pos.x, pos.y, 0.0], + normal: [attrs[0], attrs[1], attrs[2]], + face_pos: [attrs[3], attrs[4]], + } + }), + ) + .expect("tessellation failed"); + + (geometry.indices, geometry.vertices) +} + pub fn tesselate_lyon_vertex_simple(outline: &[VertexSimple]) -> (Vec, Vec) { let mut path_builder = Path::builder_with_attributes(5); @@ -153,7 +213,7 @@ pub fn tesselate_lyon_vertex_simple(outline: &[VertexSimple]) -> (Vec, Vec< let path = path_builder.build(); let opts = FillOptions::default() - .with_tolerance(10.1) + // .with_tolerance(0.1) // we're passing in line segments .with_fill_rule(FillRule::EvenOdd) .with_intersections(true); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index 8a41711..a347307 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -4,7 +4,7 @@ use glam::*; use lerpable::Lerpable; use murrelet_common::*; use murrelet_livecode::{types::AdditionalContextNode, unitcells::*}; -use murrelet_livecode_derive::Livecode; +use murrelet_livecode_derive::{Livecode, LivecodeOnly, NestEdit}; #[derive(Debug, Clone, Livecode, Lerpable, Default)] pub struct BasicTypes { @@ -131,4 +131,16 @@ impl UnitCellCreator for SimpleSquareSequence { } } +// new type + +#[derive(Clone, Debug, Default, Livecode, Lerpable)] +pub struct NewTypeWithType(f32); + +#[derive(Clone, Debug, Default, Livecode, Lerpable)] +pub struct NewTypeWithVec(Vec); + + +#[derive(Clone, Debug, Default, LivecodeOnly)] +pub struct NewTypeWithStruct(BasicTypes); + fn main() {} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_boop.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_boop.rs deleted file mode 100644 index 0f32106..0000000 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_boop.rs +++ /dev/null @@ -1,752 +0,0 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -use crate::parser::*; - -pub(crate) fn update_to_boop_ident(name: syn::Ident) -> syn::Ident { - prefix_ident("Boop", name) -} - -pub(crate) struct BoopFieldType(ControlType); - -impl BoopFieldType { - fn to_token(&self) -> TokenStream2 { - match self.0 { - ControlType::F32 => quote! {murrelet_livecode::boop::BoopState}, - ControlType::F32_2 => quote! {murrelet_livecode::boop::BoopState2}, - ControlType::F32_3 => quote! {murrelet_livecode::boop::BoopState3}, - ControlType::Color => quote! {murrelet_livecode::boop::BoopStateHsva}, - - // nothing fancy here yet either.. - ControlType::LazyNodeF32 => quote! {murrelet_livecode::lazy::LazyNodeF32}, - - // ControlType::LinSrgbaUnclamped => quote!{[murrelet_livecode::livecode::ControlF32; 4]}, - ControlType::Bool => quote! {bool}, // nothing fancy here yet - _ => panic!("boop doesn't have {:?} yet", self.0), - } - } - - fn for_world(&self, idents: StructIdents) -> TokenStream2 { - let name = idents.name(); - let orig_ty = idents.orig_ty(); - let yaml_name = idents.name().to_string(); - let new_conf = quote! {&conf.copy_with_new_current_boop(#yaml_name)}; - - match self.0 { - ControlType::F32 => { - quote! {#name: self.#name.boop(&conf.copy_with_new_current_boop(#yaml_name), t, &(target.#name as f32)) as #orig_ty} - } - //ControlType::F32_2 => quote!{#name: self.#name[0].boop(#new_conf, t, target.#name), self.#name[1].boop(conf, t, target.#name[1]))}, - // ControlType::F32_3 => quote!{#name: vec3(self.#name[0].boop(conf, t, target.#name[0]), self.#name[1].boop(conf, t, target.#name[1]), self.#name[2].boop(conf, t, target.#name[2]))}, - // ControlType::LinSrgba => quote!{#name: hsva(self.#name[0].boop(conf, t, target.#name[0]), self.#name[1].boop(conf, t, target.#name[1]), self.#name[2].boop(conf, t, target.#name[2]), self.#name[3].boop(conf, t, target.#name[3])).into_lin_srgba()}, - // ControlType::LinSrgbaUnclamped => quote!{#name: murrelet_livecode::livecode::ControlF32::hsva_unclamped(&self.#name)}, - ControlType::Bool => quote! {#name: self.#name.clone()}, // sorry, nothing fancy for bools yet - ControlType::LazyNodeF32 => quote! {#name: self.#name.clone()}, // sorry, nothing fancy for bools yet - - // _ => quote!{#name: self.#name.boop(conf, t, &target.#name) as #orig_ty} // try to convert back to usize/etc - _ => { - let f32_out = match (idents.data.f32min, idents.data.f32max) { - (None, None) => quote! {self.#name.boop(#new_conf, t, &target.#name)}, - (None, Some(max)) => { - quote! {f32::min(self.#name.boop(#new_conf, t, &target.#name), #max)} - } - (Some(min), None) => { - quote! {f32::max(#min, self.#name.boop(#new_conf, t, &target.#name))} - } - (Some(min), Some(max)) => { - quote! {f32::min(f32::max(#min, self.#name.boop(#new_conf, t, &target.#name)), #max)} - } - }; - quote! {#name: #f32_out as #orig_ty} - } - } - } - - fn for_boop_init(&self, idents: StructIdents) -> TokenStream2 { - let name = idents.name(); - // let orig_ty = idents.orig_ty; - // let new_type = idents. - let yaml_name = idents.name().to_string(); - let new_conf = quote! {&conf.copy_with_new_current_boop(#yaml_name)}; - - match self.0 { - ControlType::F32 => { - quote! {#name: murrelet_livecode::boop::BoopState::boop_init_at_time(#new_conf, t, &(target.#name as f32))} - } - ControlType::F32_2 => { - quote! {#name: murrelet_livecode::boop::BoopState2::boop_init_at_time(#new_conf, t, &target.#name)} - } - ControlType::F32_3 => { - quote! {#name: murrelet_livecode::boop::BoopState3::boop_init_at_time(#new_conf, t, &target.#name)} - } //vec3(self.#name[0].boop(#new_conf, t, target.#name[0]), self.#name[1].boop(#new_conf, t, target.#name[1]), self.#name[2].boop(#new_conf, t, target.#name[2]))}, - ControlType::Color => { - quote! {#name: murrelet_livecode::boop::BoopStateHsva::boop_init_at_time(#new_conf, t, &target.#name)} - } //hsva(self.#name[0].boop(#new_conf, t, target.#name[0]), self.#name[1].boop(#new_conf, t, target.#name[1]), self.#name[2].boop(#new_conf, t, target.#name[2]), self.#name[3].boop(#new_conf, t, target.#name[3])).into_lin_srgba()}, - // ControlType::LinSrgbaUnclamped => quote!{#name: murrelet_livecode::livecode::ControlF32::hsva_unclamped(&self.#name)}, - ControlType::Bool => quote! {#name: target.#name}, // sorry, nothing fancy for bools yet - ControlType::LazyNodeF32 => quote! {#name: target.#name.clone()}, // sorry, nothing fancy for bools yet - // _ => quote!{#name: self.#name.boop(conf, t, &target.#name) as #orig_ty} // try to convert back to usize/etc - _ => panic!("boop doesn't have {:?} yet", self.0), - } - // quote!{#name: self.#name.boop_init_at_time(conf, t, &target.#name)} - } - - fn for_newtype_world(&self, _idents: StructIdents) -> TokenStream2 { - quote! {target.0} - // match self.0 { - // ControlType::F32 => quote!{self.0}, - // //ControlType::F32_2 => quote!{#name: self.#name[0].boop(#new_conf, t, target.#name), self.#name[1].boop(conf, t, target.#name[1]))}, - // // ControlType::F32_3 => quote!{#name: vec3(self.#name[0].boop(conf, t, target.#name[0]), self.#name[1].boop(conf, t, target.#name[1]), self.#name[2].boop(conf, t, target.#name[2]))}, - // // ControlType::LinSrgba => quote!{#name: hsva(self.#name[0].boop(conf, t, target.#name[0]), self.#name[1].boop(conf, t, target.#name[1]), self.#name[2].boop(conf, t, target.#name[2]), self.#name[3].boop(conf, t, target.#name[3])).into_lin_srgba()}, - // // ControlType::LinSrgbaUnclamped => quote!{#name: murrelet_livecode::livecode::ControlF32::hsva_unclamped(&self.#name)}, - // ControlType::Bool => quote!{self.0}, // sorry, nothing fancy for bools yet - // // _ => quote!{#name: self.#name.boop(conf, t, &target.#name) as #orig_ty} // try to convert back to usize/etc - // _ => todo!("newtype world") - // } - } - - // this might not access the right place :) - fn for_newtype_boop_init(&self, name: syn::Ident) -> TokenStream2 { - let yaml_name = name.to_string(); - let new_conf = quote! {&conf.copy_with_new_current_boop(#yaml_name)}; - - match self.0 { - ControlType::F32 => { - quote! {murrelet_livecode::boop::BoopState::boop_init_at_time(#new_conf, t, &(target.0 as f32))} - } - ControlType::F32_2 => { - quote! {murrelet_livecode::boop::BoopState2::boop_init_at_time(#new_conf, t, &target.0)} - } - ControlType::F32_3 => { - quote! {murrelet_livecode::boop::BoopState3::boop_init_at_time(#new_conf, t, &target.0)} - } //vec3(self.0[0].boop(#new_conf, t, target.0[0]), self.0[1].boop(#new_conf, t, target.0[1]), self.0[2].boop(#new_conf, t, target.0[2]))}, - ControlType::Color => { - quote! {murrelet_livecode::boop::BoopStateHsva::boop_init_at_time(#new_conf, t, &target.0)} - } //hsva(self.0[0].boop(#new_conf, t, target.0[0]), self.0[1].boop(#new_conf, t, target.0[1]), self.0[2].boop(#new_conf, t, target.0[2]), self.0[3].boop(#new_conf, t, target.0[3])).into_lin_srgba()}, - // ControlType::LinSrgbaUnclamped => quote!{0: murrelet_livecode::livecode::ControlF32::hsva_unclamped(&self.0)}, - ControlType::Bool => quote! {target.0}, // sorry, nothing fancy for bools yet - ControlType::LazyNodeF32 => quote! { target.0.clone() }, - // _ => quote!{0: self.0.boop(conf, t, &target.0) as #orig_ty} // try to convert back to usize/etc - _ => panic!("boop doesn't have {:?} yet", self.0), - } - // quote!{#name: self.#name.boop_init_at_time(conf, t, &target.#name)} - } -} - -pub(crate) struct FieldTokensBoop { - pub(crate) for_struct: TokenStream2, - pub(crate) for_world: TokenStream2, - pub(crate) for_boop_init: TokenStream2, - pub(crate) for_boop_weird: TokenStream2, -} - -impl GenFinal for FieldTokensBoop { - fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2 { - let new_ident = idents.new_ident; - let name = idents.name; - let vis = idents.vis; - - let for_struct = variants.iter().map(|x| x.for_struct.clone()); - let for_world = variants.iter().map(|x| x.for_world.clone()); - let for_boop_init = variants.iter().map(|x| x.for_boop_init.clone()); - let for_boop_weird = variants.iter().map(|x| x.for_boop_weird.clone()); - - quote! { - #[derive(Debug, Clone)] - #vis struct #new_ident { - #(#for_struct,)* - } - - impl murrelet_livecode::boop::BoopFromWorld<#name> for #new_ident { - fn boop(&mut self, conf: &murrelet_livecode::boop::BoopConf, t: f32, target: &#name) -> #name { - #name { - #(#for_world,)* - } - } - - fn boop_init_at_time(conf: &murrelet_livecode::boop::BoopConf, t: f32, target: &#name) -> Self { - #new_ident { - #(#for_boop_init,)* - } - } - - fn any_weird_states(&self) -> bool { - // todo, not the most efficient but not sure how to #(#something||)* - vec![#(#for_boop_weird,)*].iter().any(|x| *x) - } - } - } - } - - fn make_enum_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2 { - let new_ident = idents.new_ident; - let vis = idents.vis; - let name = idents.name; - - let for_struct = variants.iter().map(|x| x.for_struct.clone()); - let for_world = variants.iter().map(|x| x.for_world.clone()); - let for_boop_init = variants.iter().map(|x| x.for_boop_init.clone()); - let for_boop_weird = variants.iter().map(|x| x.for_boop_weird.clone()); - - quote! { - #[derive(Debug, Clone)] - #[allow(non_camel_case_types)] - #vis enum #new_ident { - #(#for_struct,)* - } - impl murrelet_livecode::boop::BoopFromWorld<#name> for #new_ident { - fn boop(&mut self, conf: &murrelet_livecode::boop::BoopConf, t: f32, target: &#name) -> #name { - match (self, target) { - #(#for_world,)* - _ => { - // // the enum kind changed, so reset - // *self = Self::boop_init_at_time(conf, t, &target); - target.clone() - } - } - } - - fn boop_init_at_time(conf: &murrelet_livecode::boop::BoopConf, t: f32, target: &#name) -> Self { - match target { - #(#for_boop_init,)* - } - } - - fn any_weird_states(&self) -> bool { - match self { - #(#for_boop_weird,)* - } - } - } - } - } - - fn make_newtype_struct_final( - idents: ParsedFieldIdent, - variants: Vec, - ) -> TokenStream2 { - let new_ident = idents.new_ident; - let name = idents.name; - let vis = idents.vis; - - let for_struct = variants.iter().map(|x| x.for_struct.clone()); - let for_world = variants.iter().map(|x| x.for_world.clone()); - let for_boop_init = variants.iter().map(|x| x.for_boop_init.clone()); - let for_boop_weird = variants.iter().map(|x| x.for_boop_weird.clone()); - - quote! { - #[derive(Debug, Clone)] - #vis struct #new_ident(#(#for_struct,)*); - - impl murrelet_livecode::boop::BoopFromWorld<#name> for #new_ident { - fn boop(&mut self, conf: &murrelet_livecode::boop::BoopConf, t: f32, target: &#name) -> #name { - #name(#(#for_world,)*) - } - - fn boop_init_at_time(conf: &murrelet_livecode::boop::BoopConf, t: f32, target: &#name) -> Self { - #new_ident(#(#for_boop_init,)*) - } - - fn any_weird_states(&self) -> bool { - // todo, not the most efficient but not sure how to #(#something||)* - vec![#(#for_boop_weird,)*].iter().any(|x| *x) - } - } - } - } - - fn new_ident(name: syn::Ident) -> syn::Ident { - update_to_boop_ident(name) - } - - // begin parsing the different types of fields - - fn from_newtype_struct(idents: StructIdents, parent_ident: syn::Ident) -> FieldTokensBoop { - // f32, Vec2, etc - - let ctrl = idents.control_type(); - - let for_struct = { - let t = BoopFieldType(ctrl).to_token(); - quote! {#t} - }; - let for_world = BoopFieldType(ctrl).for_newtype_world(idents.clone()); - // send parent, not ident - let for_boop_init = BoopFieldType(ctrl).for_newtype_boop_init(parent_ident.clone()); - let for_boop_weird = if ctrl == ControlType::Bool || ctrl == ControlType::LazyNodeF32 { - quote! {false} - } else { - quote! {self.0.any_weird_states()} - }; - - FieldTokensBoop { - for_struct, - for_world, - for_boop_init, - for_boop_weird, - } - } - - // e.g. TileAxisLocs::V(TileAxisVs) - fn from_unnamed_enum(idents: EnumIdents) -> FieldTokensBoop { - let variant_ident = idents.variant_ident(); - let name = idents.enum_ident(); - let new_enum_ident = Self::new_ident(name.clone()); - - let yaml_name = &format!("{}:{}", name, variant_ident); - - let unnamed = idents.data.fields.fields; - - // for struct - if unnamed.len() != 1 { - panic!("multiple fields not supported") - }; - let t = unnamed.first().unwrap().clone().ty; - let parsed_data_type = ident_from_type(&t); - - let is_lazy = parsed_data_type.main_how_to.is_lazy(); - - let (for_struct, for_world, for_boop_init, for_boop_weird) = if is_lazy { - // if it's lazy, we don't support boop on it yet, so just create a placeholder type when it's this variant - let for_struct = quote! { #variant_ident }; - - let for_world = quote! { - (#new_enum_ident::#variant_ident, #name::#variant_ident(tar)) => { - #name::#variant_ident(tar.clone()) - } - }; - - let for_boop_init = - quote! { #name::#variant_ident(targ) => #new_enum_ident::#variant_ident }; - - let for_boop_weird = quote! { #new_enum_ident::#variant_ident => false }; - - (for_struct, for_world, for_boop_init, for_boop_weird) - } else { - let new_type = update_to_boop_ident(parsed_data_type.main_type.clone()); - let for_struct = quote! { #variant_ident(#new_type) }; - - let for_world = quote! { - (#new_enum_ident::#variant_ident(s), #name::#variant_ident(tar)) => { - #name::#variant_ident(s.boop(&conf.copy_with_new_current_boop(#yaml_name), t, &tar)) - } - }; - - let for_boop_init = quote! { #name::#variant_ident(targ) => #new_enum_ident::#variant_ident(#new_type::boop_init_at_time(&conf.copy_with_new_current_boop(#yaml_name), t, &targ)) }; - - let for_boop_weird = - quote! { #new_enum_ident::#variant_ident(s) => s.any_weird_states() }; - - (for_struct, for_world, for_boop_init, for_boop_weird) - }; - - FieldTokensBoop { - for_struct, - for_world, - for_boop_init, - for_boop_weird, - } - } - - // e.g. TileAxis::Diag - fn from_unit_enum(idents: EnumIdents) -> FieldTokensBoop { - let variant_ident = idents.variant_ident(); - let name = idents.enum_ident(); - let new_enum_ident = Self::new_ident(name.clone()); - - // no-op - let for_struct = { - quote! { #variant_ident } - }; - let for_world = { - quote! { (#new_enum_ident::#variant_ident, #name::#variant_ident) => #name::#variant_ident } - }; - let for_boop_init = { - quote! { #name::#variant_ident => #new_enum_ident::#variant_ident } - }; - // is never weird - let for_boop_weird = { - quote! { #new_enum_ident::#variant_ident => false } - }; - - FieldTokensBoop { - for_struct, - for_world, - for_boop_init, - for_boop_weird, - } - } - - // s: String; - fn from_noop_struct(idents: StructIdents) -> FieldTokensBoop { - let name = idents.name(); - - let for_struct = { - quote! {#name: ()} - }; - let for_world = { - quote! {#name: target.#name.clone()} - }; - let for_boop_init = { - quote! {#name: ()} - }; - - let for_boop_weird = { - quote! {false} - }; - - FieldTokensBoop { - for_struct, - for_world, - for_boop_init, - for_boop_weird, - } - } - - // f32, Vec2, etc - fn from_type_struct(idents: StructIdents) -> FieldTokensBoop { - let name = idents.name(); - - let ctrl = idents.control_type(); - - let for_struct = { - let t = BoopFieldType(ctrl).to_token(); - quote! {#name: #t} - }; - let for_world = BoopFieldType(ctrl).for_world(idents.clone()); - let for_boop_init = BoopFieldType(ctrl).for_boop_init(idents.clone()); - let for_boop_weird = if ctrl == ControlType::Bool || ctrl == ControlType::LazyNodeF32 { - quote! {false} - } else { - quote! {self.#name.any_weird_states()} - }; - - FieldTokensBoop { - for_struct, - for_world, - for_boop_init, - for_boop_weird, - } - } - - // v: Vec - // no promises about vectors that change over time, but we try - fn from_recurse_struct_vec(idents: StructIdents) -> FieldTokensBoop { - let name = idents.name(); - let orig_ty = idents.orig_ty(); - - let yaml_name = idents.name().to_string(); - - let parsed_type_info = ident_from_type(&orig_ty); - let how_to_control_internal = parsed_type_info.how_to_control_internal(); - let wrapper = parsed_type_info.wrapper_type(); - - let for_struct = { - let internal_type = match how_to_control_internal { - HowToControlThis::WithType(_, c) => BoopFieldType(*c).to_token(), - HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => { - let original_internal_type = parsed_type_info.internal_type(); - let name = Self::new_ident(original_internal_type.clone()); - quote! {#name} - } - HowToControlThis::WithNone(_) => { - let original_internal_type = parsed_type_info.internal_type(); - quote! {#original_internal_type} - } - e => panic!("need vec something {:?}", e), - }; - - let new_ty = match wrapper { - VecDepth::NotAVec => unreachable!("huh, parsing a not-vec in the vec function"), // why is it in this function? - VecDepth::Vec => quote! {Vec<#internal_type>}, - VecDepth::VecVec => quote! {Vec>}, - }; - quote! {#name: #new_ty} - }; - - let for_world = { - if how_to_control_internal.needs_to_be_evaluated() { - match wrapper { - VecDepth::NotAVec => unreachable!(), - VecDepth::Vec => { - let new_conf = quote! {&conf.copy_with_new_current_boop(#yaml_name)}; - quote! { - #name: { - let (new_x, vals) = murrelet_livecode::boop::combine_boop_vecs_for_world(#new_conf, t, &mut self.#name, &target.#name); - self.#name = new_x; // update the values - vals - } - } - } - VecDepth::VecVec => { - let new_conf = quote! {&conf.copy_with_new_current_boop(#yaml_name)}; - quote! { - #name: { - let (new_x, vals) = murrelet_livecode::boop::combine_boop_vec_vecs_for_world(#new_conf, t, &mut self.#name, &target.#name); - self.#name = new_x; // update the values - vals - } - } - } - } - } else { - quote! {#name: target.#name.clone()} - } - }; - - let for_boop_init = { - if how_to_control_internal.needs_to_be_evaluated() { - let new_conf = quote! {&conf.copy_with_new_current_boop(#yaml_name)}; - - match wrapper { - VecDepth::NotAVec => unreachable!(), - VecDepth::Vec => quote! { - #name: { - murrelet_livecode::boop::combine_boop_vecs_for_init(#new_conf, t, &target.#name) - } - }, - VecDepth::VecVec => quote! { - #name: { - let mut result = Vec::with_capacity(self.#name.len()); - for internal_row in &target.#name { - result.push( - murrelet_livecode::boop::combine_boop_vecs_for_init(#new_conf, t, &internal_row) - ) - } - result - } - }, - } - } else { - quote! {#name: target.#name.clone()} - } - }; - - let for_boop_weird = { - if how_to_control_internal.needs_to_be_evaluated() { - match wrapper { - VecDepth::NotAVec => unreachable!(), - VecDepth::Vec => quote! { - self.#name.iter().any(|x| x.any_weird_states() ) - }, - VecDepth::VecVec => quote! { - #name: { - let mut any_weird_states = false; - for internal_row in &self.#name { - any_weird_states &= - internal_row.iter().any(|x| x.any_weird_states() ); - } - result - } - }, - } - } else { - quote! {false} - } - }; - - FieldTokensBoop { - for_struct, - for_world, - for_boop_init, - for_boop_weird, - } - } - - fn from_newtype_recurse_struct_vec(idents: StructIdents) -> Self { - let orig_ty = idents.orig_ty(); - - let (for_struct, should_o) = { - let (new_ty, should_o) = { - let (ref_lc_ident, should_o) = if let DataFromType { - second_type: Some(second_ty_ident), - .. - } = ident_from_type(&orig_ty) - { - let infer = HowToControlThis::from_type_str( - second_ty_ident.clone().to_string().as_ref(), - ); - - match infer { - HowToControlThis::WithType(_, c) => (BoopFieldType(c).to_token(), true), - HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => { - // check if this is important!! - // let name = idents.config.new_ident(second_ty_ident.clone()); - let name = Self::new_ident(second_ty_ident.clone()); - (quote! {#name}, true) - } - HowToControlThis::WithNone(_) => (quote! {#second_ty_ident}, false), - e => panic!("need vec something {:?}", e), - } - } else { - panic!("vec missing second type"); - }; - - (quote! {Vec<#ref_lc_ident>}, should_o) - }; - (quote! {#new_ty}, should_o) - }; - let for_world = { - if should_o { - let new_conf = quote! { &conf.clone() }; - quote! { - { - let (new_x, vals) = murrelet_livecode::boop::combine_boop_vecs_for_world(#new_conf, t, &mut self.0, &target.0); - self.0 = new_x; // update the values - vals - } - } - } else { - quote! {target.0.clone()} - } - }; - - let for_boop_init = { - if should_o { - let new_conf = quote! { &conf.clone() }; - quote! { - { - murrelet_livecode::boop::combine_boop_vecs_for_init(#new_conf, t, &target.0) - } - } - } else { - quote! {target.0.clone()} - } - }; - - let for_boop_weird = { - if should_o { - quote! { - self.0.iter().any(|x| x.any_weird_states() ) - } - } else { - quote! {false} - } - }; - - FieldTokensBoop { - for_struct, - for_world, - for_boop_init, - for_boop_weird, - } - } - - fn from_recurse_struct_struct(idents: StructIdents) -> FieldTokensBoop { - let name = idents.name(); - let orig_ty = idents.orig_ty(); - let yaml_name = name.to_string(); - - let new_ty = { - let DataFromType { main_type, .. } = ident_from_type(&orig_ty); - let ref_lc_ident = Self::new_ident(main_type.clone()); - quote! {#ref_lc_ident} - }; - - let for_struct = { - quote! {#name: #new_ty} - }; - let for_world = { - let new_conf = quote! {&conf.copy_with_new_current_boop(#yaml_name)}; - quote! {#name: self.#name.boop(#new_conf, t, &target.#name)} - }; - - let for_boop_init = { - let new_conf = quote! {&conf.copy_with_new_current_boop(#yaml_name)}; - quote! {#name: #new_ty::boop_init_at_time(#new_conf, t, &target.#name)} - }; - - let for_boop_weird = { - quote! {self.#name.any_weird_states()} - }; - - FieldTokensBoop { - for_struct, - for_world, - for_boop_init, - for_boop_weird, - } - } - - // UnitCells - fn from_recurse_struct_unitcell(idents: StructIdents) -> FieldTokensBoop { - // this is similar to vec, but then we rewrap with the UnitCell info - let name = idents.name(); - let orig_ty = idents.orig_ty(); - let yaml_name = name.to_string(); - - let parsed_type_info = ident_from_type(&orig_ty); - let how_to_control_internal = parsed_type_info.how_to_control_internal(); - - let for_struct: TokenStream2 = { - let new_ty = match how_to_control_internal { - HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => { - let internal_type = parsed_type_info.internal_type(); - let name = update_to_boop_ident(internal_type.clone()); - quote! {Vec<#name>} - } - - HowToControlThis::WithRecurse(_, RecursiveControlType::StructLazy) => { - quote! {()} - } - - e => panic!("need boop something {:?}", e), - }; - - quote! {#name: #new_ty} - }; - - let for_world = { - let new_conf = quote! {&conf.copy_with_new_current_boop(#yaml_name)}; - - if how_to_control_internal.is_lazy() { - quote! {#name: target.#name.clone() } - } else { - quote! { - #name: { - // // split the target into nodes and sequencer - let (targets, details): (Vec<_>, Vec<_>) = target.#name.iter().map(|x| {(*(x.node).clone(), x.detail.clone()) }).unzip(); - let (new_x, vals) = murrelet_livecode::boop::combine_boop_vecs_for_world(#new_conf, t, &mut self.#name, &targets); - self.#name = new_x; // update the values - vals.into_iter().zip(details.into_iter()).map(|(node, detail)| { - murrelet_livecode::unitcells::UnitCell::new(node, detail) - }).collect() - } - } - } - }; - - let for_boop_init = { - if how_to_control_internal.is_lazy() { - quote! {#name: ()} - } else { - let new_conf = quote! {&conf.copy_with_new_current_boop(#yaml_name)}; - - quote! { - #name: { - murrelet_livecode::boop::combine_boop_vecs_for_init(#new_conf, t, &target.#name.iter().map(|x| *(x.node).clone()).collect()) - } - } - } - }; - - let for_boop_weird = { - if how_to_control_internal.is_lazy() { - quote! {false} - } else { - quote! {self.#name.iter().any(|x| x.any_weird_states())} - } - }; - - FieldTokensBoop { - for_struct, - for_world, - for_boop_init, - for_boop_weird, - } - } - - fn from_recurse_struct_lazy(idents: StructIdents) -> Self { - Self::from_noop_struct(idents) - } -} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index 6a18683..f2787f9 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -208,6 +208,49 @@ impl GenFinal for FieldTokensLazy { } } + fn from_newtype_struct_lazy(idents: StructIdents, _parent_ident: syn::Ident) -> Self { + let orig_ty = idents.orig_ty(); + let parsed_type_info = ident_from_type(&orig_ty); + let internal_type = parsed_type_info.main_type; + + let for_struct = { + let new_inside_type = Self::new_ident(internal_type.clone()); + quote! {#new_inside_type} + }; + + let for_world = { + quote! { self.0.clone() } + }; + + FieldTokensLazy { + for_struct, + for_world, + } + } + + fn from_newtype_struct_struct( + idents: StructIdents, + _parent_ident: syn::Ident, + ) -> FieldTokensLazy { + let orig_ty = idents.orig_ty(); + let parsed_type_info = ident_from_type(&orig_ty); + let internal_type = parsed_type_info.main_type; + + let for_struct = { + let new_inside_type = Self::new_ident(internal_type.clone()); + quote! {#new_inside_type} + }; + + let for_world = { + quote! { self.0.eval_lazy(ctx)? } + }; + + FieldTokensLazy { + for_struct, + for_world, + } + } + fn from_newtype_struct(idents: StructIdents, _parent_idents: syn::Ident) -> FieldTokensLazy { let ctrl = idents.control_type(); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 8184c93..8a35f51 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -320,6 +320,53 @@ impl GenFinal for FieldTokensLivecode { } } + fn from_newtype_struct_lazy(idents: StructIdents, parent_ident: syn::Ident) -> Self { + Self::from_newtype_struct_struct(idents, parent_ident) + } + + fn from_newtype_struct_struct( + idents: StructIdents, + _parent_ident: syn::Ident, + ) -> FieldTokensLivecode { + // let serde = idents.serde(false).clone(); + // let name = idents.name().clone(); + // let _orig_type = idents.orig_ty().clone(); + + // we need to get the internal struct type + let orig_ty = idents.orig_ty(); + let parsed_type_info = ident_from_type(&orig_ty); + let internal_type = parsed_type_info.main_type; + + let for_struct = { + let t = Self::new_ident(internal_type); + quote! {#t} + }; + let for_world = { + quote! { self.0.o(w)? } + }; + + let for_to_control = { + quote! { self.0.to_control() } + }; + + let (for_variable_idents, for_function_idents) = if idents.how_to_control_this_is_none() { + (quote! { vec![] }, quote! { vec![] }) + } else { + ( + quote! {self.0.variable_identifiers()}, + quote! {self.0.function_identifiers()}, + ) + }; + + FieldTokensLivecode { + for_struct, + for_world, + for_to_control, + for_variable_idents, + for_function_idents, + } + } + // e.g. TileAxisLocs::V(TileAxisVs) fn from_unnamed_enum(idents: EnumIdents) -> FieldTokensLivecode { let variant_ident = idents.variant_ident(); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs index f0009d5..787b0ec 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs @@ -134,6 +134,17 @@ impl GenFinal for FieldTokensNestEdit { name.clone() } + fn from_newtype_struct_lazy(idents: StructIdents, parent_ident: syn::Ident) -> Self { + Self::from_newtype_struct_struct(idents, parent_ident) + } + + fn from_newtype_struct_struct( + idents: StructIdents, + parent_ident: syn::Ident, + ) -> FieldTokensNestEdit { + Self::from_newtype_struct(idents, parent_ident) + } + fn from_newtype_struct(_idents: StructIdents, parent_ident: syn::Ident) -> FieldTokensNestEdit { // let name = idents.control_type(); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs index 3c8eee5..bb3c4b0 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs @@ -5,7 +5,6 @@ extern crate proc_macro; -mod derive_boop; mod derive_graphics_trait; mod derive_lazy; mod derive_livecode; @@ -14,7 +13,7 @@ mod parser; mod toplevel; use darling::FromDeriveInput; -use derive_boop::FieldTokensBoop; +// use derive_boop::FieldTokensBoop; use derive_graphics_trait::impl_graphics_trait; use derive_lazy::FieldTokensLazy; use derive_livecode::FieldTokensLivecode; @@ -35,10 +34,6 @@ fn lazy_parse_ast(rec: LivecodeReceiver) -> TokenStream2 { FieldTokensLazy::from_ast(rec) } -fn boop_parse_ast(rec: LivecodeReceiver) -> TokenStream2 { - FieldTokensBoop::from_ast(rec) -} - fn nestedit_parse_ast(rec: LivecodeReceiver) -> TokenStream2 { FieldTokensNestEdit::from_ast(rec) } @@ -90,13 +85,6 @@ pub fn murrelet_livecode_derive_lazy(input: TokenStream) -> TokenStream { lazy_parse_ast(ast_receiver.clone()).into() } -#[proc_macro_derive(Boop, attributes(livecode))] -pub fn murrelet_livecode_derive_boop(input: TokenStream) -> TokenStream { - let ast = parse_macro_input!(input as syn::DeriveInput); - let ast_receiver = LivecodeReceiver::from_derive_input(&ast).unwrap(); - boop_parse_ast(ast_receiver.clone()).into() -} - #[proc_macro_derive(NestEdit, attributes(livecode))] pub fn murrelet_livecode_derive_nestedit(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as syn::DeriveInput); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index 842cda7..96b7c2c 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -25,6 +25,8 @@ where Self: Sized, { fn from_newtype_struct(_idents: StructIdents, parent_ident: syn::Ident) -> Self; + fn from_newtype_struct_struct(idents: StructIdents, parent_ident: syn::Ident) -> Self; + fn from_newtype_struct_lazy(idents: StructIdents, parent_ident: syn::Ident) -> Self; fn from_newtype_recurse_struct_vec(_idents: StructIdents) -> Self; fn from_unnamed_enum(idents: EnumIdents) -> Self; fn from_unit_enum(idents: EnumIdents) -> Self; @@ -38,6 +40,7 @@ where fn from_ast(ast_receiver: LivecodeReceiver) -> TokenStream2 { match ast_receiver.data { ast::Data::Enum(_) => Self::make_enum(&ast_receiver), + // it's a newtype! ast::Data::Struct(ast::Fields { style: ast::Style::Tuple, .. @@ -211,11 +214,27 @@ where } Self::from_newtype_recurse_struct_vec(idents) } + HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => { + if DEBUG_THIS { + println!("-> from_newtype_struct_struct"); + } + Self::from_newtype_struct_struct(idents, name.clone()) + } + HowToControlThis::WithRecurse(_, RecursiveControlType::StructLazy) => { + if DEBUG_THIS { + println!("-> from_newtype_struct_struct"); + } + Self::from_newtype_struct_lazy(idents, name.clone()) + } + // creating a : Something in livecode // HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => Self::from_recurse_struct_struct(idents), // dealing with UnitCell // HowToControlThis::WithRecurse(_, RecursiveControlType::UnitCell) => Self::from_recurse_struct_unitcell(idents), - _ => panic!("newtype for this kind isn't implemented yet"), + _ => panic!( + "{:?} for this kind isn't implemented yet", + field.how_to_control_this() + ), } }) .collect::>(); diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index 667325c..4d29d38 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -74,10 +74,12 @@ impl BaseConfigArgs { } } + #[allow(dead_code)] pub(crate) fn config_path(&self) -> PathBuf { self.config_path.clone() } + #[allow(dead_code)] pub(crate) fn should_capture(&self) -> bool { self.capture } diff --git a/murrelet_src_osc/src/osc.rs b/murrelet_src_osc/src/osc.rs index 350b136..e967104 100644 --- a/murrelet_src_osc/src/osc.rs +++ b/murrelet_src_osc/src/osc.rs @@ -53,7 +53,7 @@ impl IsLivecodeSrc for OscMng { let packet_data = rosc::encoder::encode(&OscPacket::Message(msg)); if let Ok(pd) = packet_data { if let Some(a) = &self.cxn.target_addr { - self.cxn.send_socket.send_to(&pd, a); + print_expect(self.cxn.send_socket.send_to(&pd, a), "failed to send osc"); } } } From e7c477a8e2791f9232b0edacb906fddc5f4e2bc2 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 5 Jul 2025 11:54:47 -0400 Subject: [PATCH 046/149] wip --- murrelet_common/src/idx.rs | 4 ++++ murrelet_draw/src/livecodetypes.rs | 7 +++++++ murrelet_draw/src/transform2d.rs | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index af4608b..662ba70 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -33,6 +33,10 @@ impl IdxInRange { .collect_vec() } + pub fn enumerate_count(total: usize) -> Vec { + (0..total).map(|i| IdxInRange::new(i, total)).collect_vec() + } + pub fn prev_i(&self) -> Option { if self.i == 0 { None diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs index e68cd0d..7c7d944 100644 --- a/murrelet_draw/src/livecodetypes.rs +++ b/murrelet_draw/src/livecodetypes.rs @@ -7,6 +7,7 @@ pub mod anglepi { use lerpable::Lerpable; use murrelet_common::{Angle, AnglePi, IsAngle}; use murrelet_gui::CanMakeGUI; + use murrelet_livecode::lazy::ControlLazyNodeF32; use murrelet_livecode_derive::Livecode; use crate::transform2d::Transform2d; @@ -65,4 +66,10 @@ pub mod anglepi { LivecodeAnglePi(new_angle) } } + + impl ControlLazyLivecodeAnglePi { + pub fn from_angle(a: AnglePi) -> Self { + Self(ControlLazyNodeF32::Float(a.angle_pi())) + } + } } diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index 1c77f6c..f373caf 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -313,6 +313,10 @@ impl Transform2dStep { Self::Scale(V2::new(vec2(scale_x, scale_y))) } + pub fn scale_p(scale_x: f32) -> Self { + Self::Scale(V2::new(vec2(scale_x, scale_x))) + } + fn transform(&self) -> Mat3 { match self { Transform2dStep::Translate(t) => Mat3::from_translation(t.v), From e2b5e90af480a95cf472376125408a58b2a24c04 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 5 Jul 2025 20:42:08 -0400 Subject: [PATCH 047/149] wip --- murrelet_common/src/triangulate.rs | 9 +++++++++ murrelet_livecode/src/unitcells.rs | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/murrelet_common/src/triangulate.rs b/murrelet_common/src/triangulate.rs index 7a32f93..dc6e818 100644 --- a/murrelet_common/src/triangulate.rs +++ b/murrelet_common/src/triangulate.rs @@ -111,4 +111,13 @@ impl Triangulate { pub fn add_order(&mut self, collect: &[u32]) { self.order.extend_from_slice(collect); } + + pub fn add_rect_simple(&mut self, ids: &[u32; 4], flip: bool) { + let [v0, v1, v3, v2] = ids; + if !flip { + self.order.extend([v0, v2, v1, v1, v2, v3]) + } else { + self.order.extend([v0, v1, v2, v1, v3, v2]) + } + } } diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index 76b449e..bbe1a11 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -262,6 +262,11 @@ impl UnitCells { None } + + // conveninence method + pub fn copy_nodes(&self) -> Vec { + self.items.iter().map(|x| *x.node.clone()).collect_vec() + } } impl FromIterator> From 098b1f3401ef4ac797941101e7ba29dda5296b3f Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 8 Jul 2025 00:22:41 -0400 Subject: [PATCH 048/149] wip --- murrelet_common/src/triangulate.rs | 22 ++++++++++++++++ murrelet_gpu/src/graphics_ref.rs | 40 +++++++++++++++++++++++++----- murrelet_perform/src/cli.rs | 3 ++- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/murrelet_common/src/triangulate.rs b/murrelet_common/src/triangulate.rs index dc6e818..a58bfd5 100644 --- a/murrelet_common/src/triangulate.rs +++ b/murrelet_common/src/triangulate.rs @@ -1,4 +1,26 @@ use glam::{vec2, Vec2, Vec3}; +use lerpable::Lerpable; + +impl Lerpable for VertexSimple { + fn lerpify(&self, other: &Self, pct: &T) -> Self { + VertexSimple { + position: [ + self.position[0].lerpify(&other.position[0], pct), + self.position[1].lerpify(&other.position[1], pct), + self.position[2].lerpify(&other.position[2], pct), + ], + normal: [ + self.normal[0].lerpify(&other.normal[0], pct), + self.normal[1].lerpify(&other.normal[1], pct), + self.normal[2].lerpify(&other.normal[2], pct), + ], + face_pos: [ + self.face_pos[0].lerpify(&other.face_pos[0], pct), + self.face_pos[1].lerpify(&other.face_pos[1], pct), + ], + } + } +} #[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)] #[repr(C)] diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index b31d3c7..50dabbb 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -223,7 +223,11 @@ impl InputVertexConf { pub fn from_triangulate_2d(t: &Triangulate) -> Self { let mut c = Self::default(); - c.vertices = t.vertices.iter().map(|x| Vertex::from_simple(x)).collect_vec(); + c.vertices = t + .vertices + .iter() + .map(|x| Vertex::from_simple(x)) + .collect_vec(); c.order = t.order.clone(); c } @@ -232,7 +236,11 @@ impl InputVertexConf { let mut c = Self::default(); c.is_3d = true; c.vs_mod = VERTEX_SHADER_3D; - c.vertices = t.vertices.iter().map(|x| Vertex::from_simple(x)).collect_vec(); + c.vertices = t + .vertices + .iter() + .map(|x| Vertex::from_simple(x)) + .collect_vec(); c.order = t.order.clone(); c } @@ -260,7 +268,11 @@ fn main(@location(0) position: vec3) -> @builtin(position) vec4 { } pub fn with_custom_vertices(mut self, tri: &Triangulate) -> Self { - self.vertices = tri.vertices.iter().map(|x| Vertex::from_simple(x)).collect_vec(); + self.vertices = tri + .vertices + .iter() + .map(|x| Vertex::from_simple(x)) + .collect_vec(); self.topology = wgpu::PrimitiveTopology::TriangleList; self.order = tri.order.clone(); self @@ -370,6 +382,7 @@ pub struct GraphicsCreator { color_blend: wgpu::BlendComponent, dst_texture: TextureCreator, input_vertex: InputVertexConf, // defaults to the square + blend_state: wgpu::BlendState, } impl Default for GraphicsCreator { fn default() -> Self { @@ -387,6 +400,7 @@ impl Default for GraphicsCreator { format: DEFAULT_TEXTURE_FORMAT, }, input_vertex: InputVertexConf::default(), + blend_state: wgpu::BlendState::REPLACE, } } } @@ -447,6 +461,11 @@ impl GraphicsCreator { self } + pub fn with_blend_state(mut self, blend_state: wgpu::BlendState) -> Self { + self.blend_state = blend_state; + self + } + pub fn to_graphics_ref<'a>( &self, c: &GraphicsWindowConf<'a>, @@ -465,6 +484,10 @@ impl GraphicsCreator { fn is_3d(&self) -> bool { self.input_vertex.is_3d } + + pub fn blend_state(&self) -> wgpu::BlendState { + self.blend_state + } } #[repr(C)] @@ -716,7 +739,11 @@ impl GraphicsRef { { let mut g = self.graphics.borrow_mut(); - g.conf.input_vertex.vertices = tri.vertices.iter().map(|x| Vertex::from_simple(x)).collect_vec(); + g.conf.input_vertex.vertices = tri + .vertices + .iter() + .map(|x| Vertex::from_simple(x)) + .collect_vec(); g.conf.input_vertex.order = tri.order.clone(); let queue = c.device.queue(); @@ -1098,9 +1125,10 @@ impl Graphics { let color_state = vec![Some(wgpu::ColorTargetState { format: dst_format, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), + // blend: None, + // blend: Some(wgpu::BlendState::ALPHA_BLENDING), // blend: Some(wgpu::BlendState::REPLACE), //None, - //Some(conf.blend_state()), // todo get this to work! + blend: Some(conf.blend_state()), // todo get this to work! write_mask: wgpu::ColorWrites::ALL, })]; diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index 4d29d38..c695d19 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -41,6 +41,7 @@ impl Default for TextureDimensions { Self { // width: 3000, // height: 2000, + // width: 1080, width: 1080, height: 1080, } @@ -57,7 +58,7 @@ pub struct BaseConfigArgs { #[arg(short, long, default_value_t = Default::default())] pub resolution: TextureDimensions, // window resolution - #[arg(long, default_value_t = 2, value_parser = clap::value_parser!(u32).range(1..=8))] + #[arg(long, default_value_t = 1, value_parser = clap::value_parser!(u32).range(1..=8))] pub texture_multiplier: u32, // controls number of pixels the shaders work on #[arg(long)] From 22c0061ac94ef1bd9bda1f691f7235cf246d2c5d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 9 Jul 2025 01:43:46 -0400 Subject: [PATCH 049/149] start using arcs in world state --- murrelet_gpu/src/shader_str.rs | 60 +++++++ murrelet_livecode/src/expr.rs | 24 ++- murrelet_livecode/src/lazy.rs | 54 ++++-- murrelet_livecode/src/livecode.rs | 16 +- murrelet_livecode/src/state.rs | 272 ++++++++++++++++++++++------- murrelet_livecode/src/types.rs | 8 +- murrelet_livecode/src/unitcells.rs | 47 ++--- murrelet_perform/src/cli.rs | 2 +- murrelet_perform/src/perform.rs | 5 +- 9 files changed, 374 insertions(+), 114 deletions(-) diff --git a/murrelet_gpu/src/shader_str.rs b/murrelet_gpu/src/shader_str.rs index 4bc95b6..691d953 100644 --- a/murrelet_gpu/src/shader_str.rs +++ b/murrelet_gpu/src/shader_str.rs @@ -136,6 +136,16 @@ fn smoothStep(edge0: vec2, edge1: vec2, x: vec2) -> vec2 { return t * t * (vec2(3.0, 3.0) - 2.0 * t); } +fn smoothStepf32(edge0: f32, edge1: f32, x: f32) -> f32 { + let t: f32 = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} + +// just basically mix but this is what i use when livecoding +fn s(pct: f32, a: f32, b: f32) -> f32 { + return mix(a, b, pct); +} + fn noise2(n: vec2) -> f32 { let d = vec2(0., 1.); @@ -223,6 +233,56 @@ fn clamp4(p: vec4, min: f32, max: f32) -> vec4 { ); } + + +fn colormap( + hue_start: f32, hue_length: f32, hue_target_offset: f32, + center_loc: f32, + sat_center: f32, sat_edges: f32, + value_center: f32, value_edges: f32, + tex_coords: vec2, +) -> vec3 { + // what is the main hue? + // let hue_start = uniforms.more_info.x; + // // how far to go across the x-axis + // let hue_length = uniforms.more_info.y; + // // how far to go along the y-axis, this one will have some effects that we'll add next + // let hue_target_offset = uniforms.more_info.z; + + // let center_loc = uniforms.more_info.a; + + // // now add in the transition variables + // let sat_center = uniforms.more_info_other.x; + // let sat_edges = uniforms.more_info_other.y; + + // let value_center = uniforms.more_info_other.z; + // let value_edges = uniforms.more_info_other.a; + + let target_hue_top = hue_start + tex_coords.x * hue_length; + let target_hue = target_hue_top + tex_coords.y * hue_target_offset; + + // now compute some things to figure out what we should show + let abs_shape = 1.0 - 2.0 * abs(tex_coords.y - 0.5); + let l_shape = 1.0 - tex_coords.y; + let v_shape = mix(abs_shape, l_shape, center_loc); + + let smoothed_dist_from_center = v_shape * v_shape * (3.0 - 2.0 * v_shape); + + let target_sat = mix(sat_edges, sat_center, smoothed_dist_from_center); + let target_value = mix(value_edges, value_center, smoothed_dist_from_center); + + // okay and convert to rgb + let rgb1 = hsv2rgb(vec3(target_hue, target_sat, target_value)); + + // if it's at the edges, set to black i guess? + let is_too_high_x = step(1.0, tex_coords.y + uniforms.dims.a); + let rgb = mix(rgb1, vec3(0.0, 0.0, 0.0), is_too_high_x); + + return rgb; + + +} + fn color_if_for_neg_color(p: vec4) -> vec4 { return vec4(step(0.0, p.r), step(0.0, p.g), step(0.0, p.b), 1.0); } diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index 42ee963..560a88a 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -1,5 +1,5 @@ #![allow(dead_code)] -use std::{f64::consts::PI, fmt::Debug}; +use std::{f64::consts::PI, fmt::Debug, sync::Arc}; use evalexpr::*; use glam::{vec2, Vec2}; @@ -413,6 +413,28 @@ impl GuideType { } } +#[derive(Debug, Clone)] +pub struct MixedEvalDefsRef(Arc); +impl MixedEvalDefsRef { + pub fn new(m: MixedEvalDefs) -> Self { + MixedEvalDefsRef(Arc::new(m)) + } + + pub fn from_ctx_node(x: AdditionalContextNode) -> Self { + let mut m = MixedEvalDefs::new(); + m.add_node(x); + Self::new(m) + } + + pub fn update_ctx(&self, ctx: &mut HashMapContext) -> LivecodeResult<()> { + self.0.update_ctx(ctx) + } + + pub(crate) fn new_from_expr(more_vals: ExprWorldContextValues) -> MixedEvalDefsRef { + Self::new(MixedEvalDefs::new_from_expr(more_vals)) + } +} + #[derive(Debug, Clone)] pub struct MixedEvalDefs { vals: ExprWorldContextValues, diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index b43729c..8051515 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use evalexpr::{HashMapContext, IterateVariablesContext, Node}; use itertools::Itertools; use lerpable::{step, Lerpable}; @@ -5,7 +7,7 @@ use murrelet_common::{IdxInRange, MurreletColor}; use serde::Deserialize; use crate::{ - expr::{ExprWorldContextValues, MixedEvalDefs}, + expr::{ExprWorldContextValues, MixedEvalDefs, MixedEvalDefsRef}, livecode::{GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeFunction, LivecodeVariable}, state::LivecodeWorldState, types::{LivecodeError, LivecodeResult}, @@ -72,49 +74,67 @@ impl GetLivecodeIdentifiers for ControlLazyNodeF32 { // todo, figure out how to only build this context once per unitcell/etc #[derive(Debug, Clone)] pub struct LazyNodeF32Inner { - n: Node, // what will be evaluated! - world: LivecodeWorldState, // (could be a reference...) - more_defs: MixedEvalDefs, + n: Node, // what will be evaluated! + world: LivecodeWorldState, // this is a reference :D + // more_defs: MixedEvalDefs, } impl LazyNodeF32Inner { pub fn new(n: Node, world: LivecodeWorldState) -> Self { Self { n, world, - more_defs: MixedEvalDefs::new(), + // more_defs: MixedEvalDefs::new(), } } // options to add more details... pub fn add_more_defs(&self, more_defs: &MixedEvalDefs) -> Self { let mut c = self.clone(); - c.more_defs = c.more_defs.combine(more_defs); + c.world + .update_with_defs(MixedEvalDefsRef::new(more_defs.clone())); c } pub fn add_expr_values(&self, more_vals: ExprWorldContextValues) -> Self { + // let mut c = self.clone(); + // c.more_defs.set_vals(more_vals); + // c let mut c = self.clone(); - c.more_defs.set_vals(more_vals); + c.world + .update_with_defs(MixedEvalDefsRef::new_from_expr(more_vals)); c } // internal function to build the ctx - fn build_ctx(&self) -> LivecodeResult { - let mut ctx = self.world.ctx().clone(); - self.more_defs.update_ctx(&mut ctx)?; - Ok(ctx) + fn build_ctx(&self) -> LivecodeResult> { + // self.world.clone_with_mixed_defs(&self.more_defs) + + // self.world.ctx()?; + + // let w = self.world.clone(); + + // let a = w.ctx().as_ref() + + self.world.ctx() + + // let copied_world = self.world.ctx().as_ref().clone(); + // let mut ctx = copied_world.clone(); + // self.more_defs.update_ctx(&mut ctx)?; + // Ok(ctx) } // what you'll use pub fn eval(&self) -> LivecodeResult { - let ctx = self.build_ctx()?; + let a = self.build_ctx()?; + let ctx = a.as_ref(); + self.n - .eval_float_with_context(&ctx) - .or_else(|_| self.n.eval_int_with_context(&ctx).map(|x| x as f64)) + .eval_float_with_context(ctx) + .or_else(|_| self.n.eval_int_with_context(ctx).map(|x| x as f64)) .or_else(|_| { self.n - .eval_boolean_with_context(&ctx) + .eval_boolean_with_context(ctx) .map(|x| if x { 1.0 } else { -1.0 }) }) .map(|x| x as f32) @@ -134,9 +154,7 @@ pub enum LazyNodeF32 { impl LazyNodeF32 { pub fn new(def: ControlLazyNodeF32, world: &LivecodeWorldState) -> Self { match def { - ControlLazyNodeF32::Expr(n) => { - Self::Node(LazyNodeF32Inner::new(n, world.clone_to_lazy())) - } + ControlLazyNodeF32::Expr(n) => Self::Node(LazyNodeF32Inner::new(n, world.clone())), _ => Self::NoCtxNode(def), } } diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index 629fa2a..03f642c 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -458,6 +458,8 @@ impl ControlF32 { } pub fn _o(&self, w: &LivecodeWorldState) -> LivecodeResult { + let a = w.ctx()?; + let ctx = a.as_ref(); match self { ControlF32::Bool(b) => { if *b { @@ -469,11 +471,11 @@ impl ControlF32 { ControlF32::Int(i) => Ok(*i as f32), ControlF32::Float(x) => Ok(*x), ControlF32::Expr(e) => e - .eval_float_with_context(w.ctx()) + .eval_float_with_context(ctx) .map(|x| x as f32) - .or_else(|_| e.eval_int_with_context(w.ctx()).map(|b| b as f32)) + .or_else(|_| e.eval_int_with_context(ctx).map(|b| b as f32)) .or_else(|_| { - e.eval_boolean_with_context(w.ctx()) + e.eval_boolean_with_context(ctx) .map(|b| if b { 1.0 } else { -1.0 }) .map_err(|err| LivecodeError::EvalExpr("evalexpr err".to_string(), err)) }), @@ -519,6 +521,8 @@ impl ControlBool { pub fn o(&self, w: &LivecodeWorldState) -> LivecodeResult { // self.to_unitcell_control().eval(w) + let a = w.ctx()?; + let ctx = a.as_ref(); match self { ControlBool::Raw(b) => Ok(*b), @@ -526,10 +530,10 @@ impl ControlBool { ControlBool::Float(x) => Ok(*x > 0.0), ControlBool::Expr(e) => e - .eval_boolean_with_context(w.ctx()) - .or_else(|_| e.eval_float_with_context(w.ctx()).map(|b| b > 0.0)) + .eval_boolean_with_context(ctx) + .or_else(|_| e.eval_float_with_context(ctx).map(|b| b > 0.0)) .or_else(|_| { - e.eval_int_with_context(w.ctx()) + e.eval_int_with_context(ctx) .map(|b| b > 0) .map_err(|err| LivecodeError::EvalExpr("evalexpr err".to_string(), err)) }), diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index ec5d19a..3a18365 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -1,10 +1,13 @@ -use std::{collections::HashSet, sync::Arc}; +use std::{ + collections::HashSet, + sync::{Arc, RwLock}, +}; use evalexpr::{HashMapContext, IterateVariablesContext}; use murrelet_common::*; use crate::{ - expr::{ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefs}, + expr::{ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefs, MixedEvalDefsRef}, types::{AdditionalContextNode, LivecodeResult}, unitcells::UnitCellContext, }; @@ -13,23 +16,170 @@ use crate::{ enum LivecodeWorldStateStage { Timeless, World(LiveCodeTimeInstantInfo), - Unit(LiveCodeTimeInstantInfo), - Lazy(LiveCodeTimeInstantInfo), + // Unit(LiveCodeTimeInstantInfo), + // Lazy(LiveCodeTimeInstantInfo), +} +// impl LivecodeWorldStateStage { +// fn add_step(&self, stage: LivecodeWorldStateStage) -> LivecodeWorldStateStage { +// // todo, i could start to represent the tree of steps.. but right now, just do the latest one +// stage +// } +// } + +#[derive(Clone, Debug)] +pub enum CachedHM { + NotCached, + Cached(Arc), } -impl LivecodeWorldStateStage { - fn add_step(&self, stage: LivecodeWorldStateStage) -> LivecodeWorldStateStage { - // todo, i could start to represent the tree of steps.. but right now, just do the latest one - stage +impl CachedHM { + fn new_rw() -> Arc> { + Arc::new(RwLock::new(CachedHM::NotCached)) + } + + fn update(&mut self, hm: HashMapContext) { + *self = CachedHM::Cached(Arc::new(hm)); + } + + fn clear(&mut self) { + *self = CachedHM::NotCached; } } -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct LivecodeWorldState { + cached: Arc>, + state: Arc, + refs: Vec, +} +impl LivecodeWorldState { + pub fn new_legacy(state: LivecodeWorldStateInner) -> LivecodeResult { + Ok(Self { + cached: CachedHM::new_rw(), + state: Arc::new(state), + refs: vec![], + }) + } + + pub fn new( + evalexpr_func_ctx: &HashMapContext, + livecode_src: &LivecodeSrc, + time: LiveCodeTimeInstantInfo, + node: AdditionalContextNode, + assets: AssetsRef, + ) -> LivecodeResult { + let state = + LivecodeWorldStateInner::new(evalexpr_func_ctx, livecode_src, time, node, assets)?; + + Self::new_legacy(state) + } + + pub fn new_timeless( + evalexpr_func_ctx: &HashMapContext, + livecode_src: &LivecodeSrc, + ) -> LivecodeResult { + let state = LivecodeWorldStateInner::new_timeless(evalexpr_func_ctx, livecode_src)?; + Self::new_legacy(state) + } + + pub fn clone_with_vals(&self, expr: ExprWorldContextValues, prefix: &str) -> Self { + let e = expr.with_prefix(prefix); + let new_info = MixedEvalDefs::new_from_expr(e); + + let mut refs = self.refs.clone(); + refs.push(MixedEvalDefsRef::new(new_info)); + + Self { + cached: CachedHM::new_rw(), + state: self.state.clone(), + refs, + } + } + + pub fn clone_to_unitcell( + &self, + unit_cell_ctx: &UnitCellContext, + prefix: &str, + maybe_node: Option<&MixedEvalDefsRef>, + ) -> LivecodeResult { + let new_info = unit_cell_ctx + .as_expr_world_context_values() + .with_prefix(prefix); + + let mut refs = self.refs.clone(); + refs.push(MixedEvalDefsRef::new(MixedEvalDefs::new_from_expr( + new_info, + ))); + if let Some(node) = maybe_node { + refs.push(node.clone()); + } + + Ok(Self { + cached: CachedHM::new_rw(), + state: self.state.clone(), + refs, + }) + } + + pub(crate) fn new_dummy() -> Self { + Self::new_legacy(LivecodeWorldStateInner::new_dummy()).unwrap() + } + + pub(crate) fn ctx(&self) -> LivecodeResult> { + let mut cache = self.cached.write().unwrap(); + + if let CachedHM::Cached(c) = cache.clone() { + return Ok(c); + } + let mut ctx = self.state.ctx().clone(); + for mixed in &self.refs { + mixed.update_ctx(&mut ctx)?; + } + cache.update(ctx); + + if let CachedHM::Cached(c) = cache.clone() { + return Ok(c); + } else { + unreachable!("we just set it?") + } + } + + pub fn actual_frame_u64(&self) -> u64 { + self.state.actual_frame_u64() + } + + pub fn vars(&self) -> HashSet { + self.state.vars() + } + + pub fn update_with_defs(&mut self, md: MixedEvalDefsRef) { + self.refs.push(md); + self.cached.write().unwrap().clear(); + } + + pub fn time(&self) -> LiveCodeTimeInstantInfo { + self.state.time() + } + + pub fn actual_frame(&self) -> f32 { + self.state.actual_frame() + } + + pub fn asset_layer(&self, key: &str, layer_idx: usize) -> Option> { + self.state.asset_layer(key, layer_idx) + } + + pub fn asset_layers_in_key(&self, key: &str) -> &[String] { + self.state.asset_layers_in_key(key) + } +} + +#[derive(Debug, Clone)] +pub struct LivecodeWorldStateInner { context: HashMapContext, stage: LivecodeWorldStateStage, assets: AssetsRef, } -impl LivecodeWorldState { +impl LivecodeWorldStateInner { pub fn vars(&self) -> HashSet { self.context.iter_variable_names().collect() } @@ -58,17 +208,17 @@ impl LivecodeWorldState { Ok(ctx) } - pub fn new<'a>( + pub fn new( evalexpr_func_ctx: &HashMapContext, livecode_src: &LivecodeSrc, time: LiveCodeTimeInstantInfo, node: AdditionalContextNode, assets: AssetsRef, - ) -> LivecodeResult { + ) -> LivecodeResult { let context = Self::clone_ctx_and_add_world(evalexpr_func_ctx, livecode_src, Some(time), Some(node))?; - Ok(LivecodeWorldState { + Ok(Self { context, stage: LivecodeWorldStateStage::World(time), assets: assets.clone(), @@ -78,10 +228,10 @@ impl LivecodeWorldState { pub fn new_timeless( evalexpr_func_ctx: &HashMapContext, livecode_src: &LivecodeSrc, - ) -> LivecodeResult { + ) -> LivecodeResult { let context = Self::clone_ctx_and_add_world(evalexpr_func_ctx, livecode_src, None, None)?; - Ok(LivecodeWorldState { + Ok(Self { context, stage: LivecodeWorldStateStage::Timeless, assets: Assets::empty_ref(), @@ -99,8 +249,8 @@ impl LivecodeWorldState { match self.stage { LivecodeWorldStateStage::Timeless => panic!("checking time in a timeless world"), LivecodeWorldStateStage::World(t) => t, - LivecodeWorldStateStage::Unit(t) => t, - LivecodeWorldStateStage::Lazy(t) => t, + // LivecodeWorldStateStage::Unit(t) => t, + // LivecodeWorldStateStage::Lazy(t) => t, } } @@ -128,50 +278,48 @@ impl LivecodeWorldState { more_defs.update_ctx(self.ctx_mut()) } - pub fn clone_with_vals( - &self, - expr: ExprWorldContextValues, - prefix: &str, - ) -> LivecodeResult { - let mut lazy = self.clone_to_lazy(); // eh just need to clone - - expr.with_prefix(prefix).update_ctx(lazy.ctx_mut())?; - - Ok(lazy) - } - - pub fn clone_to_unitcell( - &self, - unit_cell_ctx: &UnitCellContext, - prefix: &str, - ) -> LivecodeResult { - let mut context = self.context.clone(); - unit_cell_ctx - .as_expr_world_context_values() - .with_prefix(prefix) - .update_ctx(&mut context)?; - - let r = LivecodeWorldState { - context, - stage: self - .stage - .add_step(LivecodeWorldStateStage::Unit(self.time())), - assets: self.assets.clone(), - }; - - Ok(r) - } - - pub fn clone_to_lazy(&self) -> Self { - let context = self.context.clone(); - LivecodeWorldState { - context, - stage: self - .stage - .add_step(LivecodeWorldStateStage::Lazy(self.time())), - assets: self.assets.clone(), - } - } + // pub fn clone_with_vals( + // &self, + // expr: ExprWorldContextValues, + // prefix: &str, + // ) -> LivecodeResult { + // let mut lazy = self.clone_to_lazy(); // eh just need to clone + // expr.with_prefix(prefix).update_ctx(lazy.ctx_mut())?; + // Ok(lazy) + // } + + // pub fn clone_to_unitcell( + // &self, + // unit_cell_ctx: &UnitCellContext, + // prefix: &str, + // ) -> LivecodeResult { + // let mut context = self.context.clone(); + // unit_cell_ctx + // .as_expr_world_context_values() + // .with_prefix(prefix) + // .update_ctx(&mut context)?; + + // let r = LivecodeWorldState { + // context, + // stage: self + // .stage + // .add_step(LivecodeWorldStateStage::Unit(self.time())), + // assets: self.assets.clone(), + // }; + + // Ok(r) + // } + + // pub fn clone_to_lazy(&self) -> Self { + // let context = self.context.clone(); + // LivecodeWorldState { + // context, + // stage: self + // .stage + // .add_step(LivecodeWorldStateStage::Lazy(self.time())), + // assets: self.assets.clone(), + // } + // } pub fn asset_layer(&self, key: &str, layer_idx: usize) -> Option> { self.assets.asset_layer(key, layer_idx).cloned() @@ -181,7 +329,7 @@ impl LivecodeWorldState { self.assets.layer_for_key(key) } - pub fn new_dummy() -> LivecodeWorldState { + pub fn new_dummy() -> Self { let empty_ctx = HashMapContext::new(); Self::new( &empty_ctx, diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 74c2128..68dca55 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -34,7 +34,11 @@ pub enum LivecodeError { #[error("parse: {0}")] JsonParse(String), } -impl LivecodeError {} +impl LivecodeError { + pub fn raw(s: &str) -> Self { + Self::Raw(s.to_string()) + } +} // impl std::fmt::Display for LivecodeError { // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // match self { @@ -217,7 +221,7 @@ impl ControlVecElementRepeat { for idx in self.repeat.iter() { let expr = UnitCellExprWorldContext::from_idx2d(idx, 1.0).as_expr_world_context_values(); - let new_w = w.clone_with_vals(expr, &prefix)?; + let new_w = w.clone_with_vals(expr, &prefix); for src in &self.what { match src { diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index bbe1a11..409f2a3 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -8,7 +8,7 @@ use rand::{Rng, SeedableRng}; use std::fmt::Debug; use std::{any::Any, collections::HashMap, fmt}; -use crate::expr::{ExprWorldContextValues, IntoExprWorldContext}; +use crate::expr::{ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefsRef}; use crate::livecode::LivecodeFromWorld; use crate::state::LivecodeWorldState; use crate::types::AdditionalContextNode; @@ -18,7 +18,7 @@ use crate::types::LivecodeResult; pub struct TmpUnitCells { sequencer: CtxSource, node: Box>, - ctx: Option, + ctx: Option, prefix: String, } @@ -32,32 +32,32 @@ impl TmpUnitCells( - world_ctx: &'a LivecodeWorldState, - prefix: &'a str, - unit_cell_ctx: &'a UnitCellContext, - maybe_node: Option<&'a AdditionalContextNode>, -) -> LivecodeResult { - // world_ctx is currently just the World, so first attach the unit cell world state +// fn create_unit_cell<'a>( +// world_ctx: &'a LivecodeWorldState, +// prefix: &'a str, +// unit_cell_ctx: &'a UnitCellContext, +// maybe_node: Option<&'a AdditionalContextNode>, +// ) -> LivecodeResult { +// // world_ctx is currently just the World, so first attach the unit cell world state - let mut world_state = world_ctx.clone_to_unitcell(unit_cell_ctx, prefix)?; +// let mut world_state = ; - let unit_cell_world_ctx = world_state.ctx_mut(); +// // let unit_cell_world_ctx = world_state.ctx_mut(); - // now update the unit_cell context to have the node - if let Some(node) = maybe_node { - node.eval_raw(unit_cell_world_ctx)?; - } +// // now update the unit_cell context to have the node +// // if let Some(node) = maybe_node { +// // node.eval_raw(unit_cell_world_ctx)?; +// // } - // great, now we have it built. return it! - Ok(world_state) -} +// // great, now we have it built. return it! +// Ok(world_state) +// } impl TmpUnitCells where @@ -67,7 +67,7 @@ where pub fn eval_with_ctx( &self, world_ctx: &LivecodeWorldState, - unit_cell_ctx: &Option, + unit_cell_ctx: Option<&MixedEvalDefsRef>, ) -> Vec> { // right now this one doesn't usually return an error because we do stuff // to avoid returning every time, should tidy up @@ -83,8 +83,11 @@ where // - unit cell location // it doesn't have sequencer ctx yet, we'll add that next + // let unit_cell_world_ctx_result = + // create_unit_cell(world_ctx, &self.prefix, ctx, unit_cell_ctx.as_ref()); + let unit_cell_world_ctx_result = - create_unit_cell(world_ctx, &self.prefix, ctx, unit_cell_ctx.as_ref()); + world_ctx.clone_to_unitcell(ctx, &self.prefix, unit_cell_ctx); // and evaluate with this! // todo can i use the result to clean this up @@ -114,7 +117,7 @@ where } pub fn o(&self, ctx: &LivecodeWorldState) -> LivecodeResult> { - Ok(UnitCells::new(self.eval_with_ctx(ctx, &self.ctx))) + Ok(UnitCells::new(self.eval_with_ctx(ctx, self.ctx.as_ref()))) } } diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index c695d19..0188303 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -58,7 +58,7 @@ pub struct BaseConfigArgs { #[arg(short, long, default_value_t = Default::default())] pub resolution: TextureDimensions, // window resolution - #[arg(long, default_value_t = 1, value_parser = clap::value_parser!(u32).range(1..=8))] + #[arg(long, default_value_t = 4, value_parser = clap::value_parser!(u32).range(1..=8))] pub texture_multiplier: u32, // controls number of pixels the shaders work on #[arg(long)] diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 6df53e3..70f4327 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -5,7 +5,7 @@ use murrelet_common::{Assets, AssetsRef, LivecodeUsage, LivecodeValue}; use murrelet_common::{LivecodeSrc, LivecodeSrcUpdateInput, MurreletAppInput}; use murrelet_common::{MurreletColor, TransformVec2}; use murrelet_gui::MurreletGUI; -use murrelet_livecode::expr::MixedEvalDefs; +use murrelet_livecode::expr::{MixedEvalDefs, MixedEvalDefsRef}; use murrelet_livecode::lazy::ControlLazyNodeF32; use murrelet_livecode::state::{LivecodeTimingConfig, LivecodeWorldState}; use murrelet_livecode::types::{AdditionalContextNode, ControlVecElement, LivecodeResult}; @@ -896,13 +896,14 @@ where md.set_val(x, murrelet_common::LivecodeValue::Float(0.0)); } } - world.update_with_defs(&md).unwrap(); // i'm setting this so it should be okay.. + world.update_with_defs(MixedEvalDefsRef::new(md)); // i'm setting this so it should be okay.. self.cached_world = Some(world); Ok(()) } pub fn world(&self) -> &LivecodeWorldState { + // self.cached_world.as_ref().unwrap() self.cached_world.as_ref().unwrap() } From e3c37a5c0a1fe8eb36c48bbcc95783aeb19bb838 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 9 Jul 2025 18:34:13 -0400 Subject: [PATCH 050/149] wi --- murrelet_common/src/geometry.rs | 6 ++++++ murrelet_draw/src/style.rs | 2 ++ 2 files changed, 8 insertions(+) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index fc882af..1b6f01e 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -451,6 +451,12 @@ impl SpotOnCurve { SimpleTransform2dStep::translate(self.loc()), ]) } + + pub fn set_angle>(&self, new: A) -> Self { + let mut c = self.clone(); + c.angle = new.into(); + c + } } #[derive(Clone, Copy, Debug)] diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index c05c22d..84a2edf 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -388,6 +388,8 @@ pub mod styleconf { RGBAPoints(MurreletStyleRGBAPoints), } impl StyleConf { + + pub fn to_style(&self) -> MurreletStyle { match self { // StyleConf::Verbose(a) => *a, From d3cc9cd7bbe10b1e1e9df6afececf23cd1a8e161 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 11 Jul 2025 19:57:35 -0400 Subject: [PATCH 051/149] wip --- murrelet_perform/src/cli.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index 0188303..25acb0b 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -44,6 +44,8 @@ impl Default for TextureDimensions { // width: 1080, width: 1080, height: 1080, + // width: 2000, + // height: 2000, } } } @@ -58,7 +60,7 @@ pub struct BaseConfigArgs { #[arg(short, long, default_value_t = Default::default())] pub resolution: TextureDimensions, // window resolution - #[arg(long, default_value_t = 4, value_parser = clap::value_parser!(u32).range(1..=8))] + #[arg(long, default_value_t = 1, value_parser = clap::value_parser!(u32).range(1..=8))] pub texture_multiplier: u32, // controls number of pixels the shaders work on #[arg(long)] From 72f517f6dbb46bf06037c7e89c8cc7e9d6eee00d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 18 Jul 2025 18:02:59 -0700 Subject: [PATCH 052/149] wip --- murrelet_common/src/idx.rs | 1 + murrelet_draw/src/transform2d.rs | 4 ++++ murrelet_perform/src/cli.rs | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 662ba70..866f19d 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -210,6 +210,7 @@ impl IdxInRange2d { let cell_idx = vec2(self.i.i as f32, self.j.i as f32); let centering_offset = -0.5 * self.totals_vec2(); + cell_idx + vec2(0.5, 0.5) + centering_offset } diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index f373caf..5223044 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -223,6 +223,10 @@ impl Transform2d { .collect_vec(), ) } + + pub fn steps(&self) -> &Vec { + &self.0 + } } impl Default for ControlTransform2d { diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index 25acb0b..25db304 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -46,6 +46,8 @@ impl Default for TextureDimensions { height: 1080, // width: 2000, // height: 2000, + // width: 750, + // height: 750 } } } @@ -60,7 +62,7 @@ pub struct BaseConfigArgs { #[arg(short, long, default_value_t = Default::default())] pub resolution: TextureDimensions, // window resolution - #[arg(long, default_value_t = 1, value_parser = clap::value_parser!(u32).range(1..=8))] + #[arg(long, default_value_t = 4, value_parser = clap::value_parser!(u32).range(1..=8))] pub texture_multiplier: u32, // controls number of pixels the shaders work on #[arg(long)] From 8b89001e921af39181a2f002174518578ac5033f Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 20 Jul 2025 17:59:37 -0700 Subject: [PATCH 053/149] wip --- examples/foolish_guillemot/src/lib.rs | 418 +++++++++++++------------- examples/murrelet_example/src/main.rs | 252 ++++++++-------- murrelet_draw/src/cubic.rs | 21 +- murrelet_draw/src/curve_drawer.rs | 2 + murrelet_gui/examples/tests.rs | 191 ++++++------ 5 files changed, 454 insertions(+), 430 deletions(-) diff --git a/examples/foolish_guillemot/src/lib.rs b/examples/foolish_guillemot/src/lib.rs index 5a5d2d0..40659b1 100644 --- a/examples/foolish_guillemot/src/lib.rs +++ b/examples/foolish_guillemot/src/lib.rs @@ -1,212 +1,212 @@ -pub mod draw; - -use draw::{WebSDrawCtx, WebSDrawCtxUnitCell}; -use glam::*; - -use lerpable::Lerpable; -use murrelet::prelude::*; -use murrelet_common::{lerpify_vec2, mat4_from_mat3_transform}; -use murrelet_draw::{ - compass::*, - draw::*, - sequencers::*, - style::{styleconf::*, MurreletPath}, -}; -use murrelet_livecode::types::{AdditionalContextNode, LivecodeError}; -use wasm_bindgen::prelude::*; - -// from the wasm-rust tutorial, this let's you log messages to the js console -// extern crate web_sys; - -// A macro to provide `println!(..)`-style syntax for `console.log` logging. -// macro_rules! log { -// ( $( $t:tt )* ) => { -// web_sys::console::log_1(&format!( $( $t )* ).into()) +// pub mod draw; + +// use draw::{WebSDrawCtx, WebSDrawCtxUnitCell}; +// use glam::*; + +// use lerpable::Lerpable; +// use murrelet::prelude::*; +// use murrelet_common::{lerpify_vec2, mat4_from_mat3_transform}; +// use murrelet_draw::{ +// compass::*, +// draw::*, +// sequencers::*, +// style::{styleconf::*, MurreletPath}, +// }; +// use murrelet_livecode::types::{AdditionalContextNode, LivecodeError}; +// use wasm_bindgen::prelude::*; + +// // from the wasm-rust tutorial, this let's you log messages to the js console +// // extern crate web_sys; + +// // A macro to provide `println!(..)`-style syntax for `console.log` logging. +// // macro_rules! log { +// // ( $( $t:tt )* ) => { +// // web_sys::console::log_1(&format!( $( $t )* ).into()) +// // } +// // } + +// #[derive(Debug, Clone, Default, Livecode, Lerpable)] +// struct StyledShape { +// shape: MurreletCompass, +// style: StyleConf, +// #[livecode(serde_default = "false")] +// skew: bool, +// } +// impl StyledShape { +// fn draw(&self, draw_ctx: &WebSDrawCtxUnitCell) { +// // first do the simple transform + +// draw_ctx +// .with_unit_cell_skew(self.skew) +// .with_style(self.style.clone()) +// .draw_curve_path(MurreletPath::curve(self.shape.to_curve_maker())); +// } +// } + +// #[derive(Debug, Clone, Default, Livecode, Lerpable)] +// struct SimpleTile(Vec); +// impl SimpleTile { +// fn draw(&self, draw_ctx: &WebSDrawCtxUnitCell) { +// for v in &self.0 { +// v.draw(draw_ctx); +// } +// } +// } + +// #[derive(Debug, Clone, Livecode, Lerpable)] +// struct DrawingConfig { +// #[livecode(serde_default = "false")] +// debug: bool, +// sequencer: Sequencer, +// ctx: AdditionalContextNode, +// #[livecode(src = "sequencer", ctx = "ctx")] +// node: UnitCells, +// #[lerpable(func = "lerpify_vec2")] +// offset: Vec2, +// scale: f32, +// } +// impl DrawingConfig { +// fn draw(&self, draw_ctx: &WebSDrawCtx) { +// // todo, this is a little clunky.. and isn't so clunky in my other code hrm +// let transform = mat4_from_mat3_transform( +// Mat3::from_scale(Vec2::ONE * self.scale) * Mat3::from_translation(self.offset), +// ); + +// for t in self.node.iter() { +// t.node +// .draw(&draw_ctx.set_transform(transform).with_detail(&t.detail)) +// } +// } +// } + +// // set up livecoder +// #[derive(Debug, Clone, Livecode, Lerpable, TopLevelLiveCode)] +// struct LiveCodeConf { +// app: AppConfig, +// drawing: DrawingConfig, +// } + +// #[wasm_bindgen] +// pub async fn new_model(conf: String) -> WasmMurreletModelResult { +// MurreletModel::new(conf).await +// } + +// #[wasm_bindgen] +// pub struct MurreletModel { +// livecode: LiveCode, +// } +// #[wasm_bindgen] +// impl MurreletModel { +// #[wasm_bindgen(constructor)] +// pub async fn new(conf: String) -> WasmMurreletModelResult { +// // turn this on if you need to debug +// // std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + +// let livecode_src = LivecodeSrc::new(vec![Box::new(AppInputValues::new(false))]); + +// match LiveCode::new_web(conf, livecode_src, &vec![]) { +// Ok(livecode) => { +// let r = MurreletModel { livecode }; +// WasmMurreletModelResult::ok(r) +// } +// Err(e) => WasmMurreletModelResult::err(e), +// } +// } + +// #[wasm_bindgen] +// pub fn update_config(&mut self, conf: String) -> String { +// match self.livecode.update_config_to(&conf) { +// Ok(_) => "Success!".to_owned(), +// Err(e) => e, +// } +// } + +// #[wasm_bindgen] +// pub fn update_frame( +// &mut self, +// frame: u64, +// dim_x: f32, +// dim_y: f32, +// mouse_x: f32, +// mouse_y: f32, +// click: bool, +// ) { +// let app_input = +// MurreletAppInput::new_no_key(vec2(dim_x, dim_y), vec2(mouse_x, mouse_y), click, frame); +// // todo, show an error about this? +// self.livecode.update(&app_input, false).ok(); +// } + +// // useful if you have shaders, this will list what canvases to draw to +// #[wasm_bindgen] +// pub fn canvas_ids(&self) -> Vec { +// Vec::new() // } + +// // useful if you have shaders, this should generate the DOM for image textures in the svg +// #[wasm_bindgen] +// pub fn make_img_defs(&self) -> String { +// String::new() +// } + +// #[wasm_bindgen] +// pub fn draw(&self) -> Vec { +// let svg_draw_config = self.livecode.svg_save_path(); +// let draw_ctx = WebSDrawCtx::new(&svg_draw_config); + +// self.livecode.config().drawing.draw(&draw_ctx); + +// draw_ctx.make_html() +// } + +// #[wasm_bindgen] +// pub fn fps(&self) -> f32 { +// self.livecode.app_config().time.fps +// } + +// #[wasm_bindgen] +// pub fn bg_color(&self) -> String { +// // it's fine that this one drops transparency +// self.livecode.app_config().bg_color.to_svg_rgb() +// } +// } + +// // just creating a Result that we can send to javascript +// #[wasm_bindgen] +// pub struct WasmMurreletModelResult { +// m: Option, +// err: String, // } -#[derive(Debug, Clone, Default, Livecode, Lerpable)] -struct StyledShape { - shape: MurreletCompass, - style: StyleConf, - #[livecode(serde_default = "false")] - skew: bool, -} -impl StyledShape { - fn draw(&self, draw_ctx: &WebSDrawCtxUnitCell) { - // first do the simple transform - - draw_ctx - .with_unit_cell_skew(self.skew) - .with_style(self.style.clone()) - .draw_curve_path(MurreletPath::curve(self.shape.to_curve_maker())); - } -} - -#[derive(Debug, Clone, Default, Livecode, Lerpable)] -struct SimpleTile(Vec); -impl SimpleTile { - fn draw(&self, draw_ctx: &WebSDrawCtxUnitCell) { - for v in &self.0 { - v.draw(draw_ctx); - } - } -} - -#[derive(Debug, Clone, Livecode, Lerpable)] -struct DrawingConfig { - #[livecode(serde_default = "false")] - debug: bool, - sequencer: Sequencer, - ctx: AdditionalContextNode, - #[livecode(src = "sequencer", ctx = "ctx")] - node: UnitCells, - #[lerpable(func = "lerpify_vec2")] - offset: Vec2, - scale: f32, -} -impl DrawingConfig { - fn draw(&self, draw_ctx: &WebSDrawCtx) { - // todo, this is a little clunky.. and isn't so clunky in my other code hrm - let transform = mat4_from_mat3_transform( - Mat3::from_scale(Vec2::ONE * self.scale) * Mat3::from_translation(self.offset), - ); - - for t in self.node.iter() { - t.node - .draw(&draw_ctx.set_transform(transform).with_detail(&t.detail)) - } - } -} - -// set up livecoder -#[derive(Debug, Clone, Livecode, Lerpable, TopLevelLiveCode)] -struct LiveCodeConf { - app: AppConfig, - drawing: DrawingConfig, -} - -#[wasm_bindgen] -pub async fn new_model(conf: String) -> WasmMurreletModelResult { - MurreletModel::new(conf).await -} - -#[wasm_bindgen] -pub struct MurreletModel { - livecode: LiveCode, -} -#[wasm_bindgen] -impl MurreletModel { - #[wasm_bindgen(constructor)] - pub async fn new(conf: String) -> WasmMurreletModelResult { - // turn this on if you need to debug - // std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - - let livecode_src = LivecodeSrc::new(vec![Box::new(AppInputValues::new(false))]); - - match LiveCode::new_web(conf, livecode_src, &vec![]) { - Ok(livecode) => { - let r = MurreletModel { livecode }; - WasmMurreletModelResult::ok(r) - } - Err(e) => WasmMurreletModelResult::err(e), - } - } - - #[wasm_bindgen] - pub fn update_config(&mut self, conf: String) -> String { - match self.livecode.update_config_to(&conf) { - Ok(_) => "Success!".to_owned(), - Err(e) => e, - } - } - - #[wasm_bindgen] - pub fn update_frame( - &mut self, - frame: u64, - dim_x: f32, - dim_y: f32, - mouse_x: f32, - mouse_y: f32, - click: bool, - ) { - let app_input = - MurreletAppInput::new_no_key(vec2(dim_x, dim_y), vec2(mouse_x, mouse_y), click, frame); - // todo, show an error about this? - self.livecode.update(&app_input, false).ok(); - } - - // useful if you have shaders, this will list what canvases to draw to - #[wasm_bindgen] - pub fn canvas_ids(&self) -> Vec { - Vec::new() - } - - // useful if you have shaders, this should generate the DOM for image textures in the svg - #[wasm_bindgen] - pub fn make_img_defs(&self) -> String { - String::new() - } - - #[wasm_bindgen] - pub fn draw(&self) -> Vec { - let svg_draw_config = self.livecode.svg_save_path(); - let draw_ctx = WebSDrawCtx::new(&svg_draw_config); - - self.livecode.config().drawing.draw(&draw_ctx); - - draw_ctx.make_html() - } - - #[wasm_bindgen] - pub fn fps(&self) -> f32 { - self.livecode.app_config().time.fps - } - - #[wasm_bindgen] - pub fn bg_color(&self) -> String { - // it's fine that this one drops transparency - self.livecode.app_config().bg_color.to_svg_rgb() - } -} - -// just creating a Result that we can send to javascript -#[wasm_bindgen] -pub struct WasmMurreletModelResult { - m: Option, - err: String, -} - -#[wasm_bindgen] -impl WasmMurreletModelResult { - fn ok(m: MurreletModel) -> WasmMurreletModelResult { - WasmMurreletModelResult { - m: Some(m), - err: String::new(), - } - } - - fn err(err: LivecodeError) -> WasmMurreletModelResult { - WasmMurreletModelResult { - m: None, - err: err.to_string(), - } - } - - #[wasm_bindgen] - pub fn is_err(&self) -> bool { - self.m.is_none() - } - - #[wasm_bindgen] - pub fn err_msg(self) -> String { - self.err - } - - #[wasm_bindgen] - pub fn to_model(self) -> MurreletModel { - // panics if you don't check is error first - self.m.unwrap() - } -} +// #[wasm_bindgen] +// impl WasmMurreletModelResult { +// fn ok(m: MurreletModel) -> WasmMurreletModelResult { +// WasmMurreletModelResult { +// m: Some(m), +// err: String::new(), +// } +// } + +// fn err(err: LivecodeError) -> WasmMurreletModelResult { +// WasmMurreletModelResult { +// m: None, +// err: err.to_string(), +// } +// } + +// #[wasm_bindgen] +// pub fn is_err(&self) -> bool { +// self.m.is_none() +// } + +// #[wasm_bindgen] +// pub fn err_msg(self) -> String { +// self.err +// } + +// #[wasm_bindgen] +// pub fn to_model(self) -> MurreletModel { +// // panics if you don't check is error first +// self.m.unwrap() +// } +// } diff --git a/examples/murrelet_example/src/main.rs b/examples/murrelet_example/src/main.rs index 196c4f5..cac5f34 100644 --- a/examples/murrelet_example/src/main.rs +++ b/examples/murrelet_example/src/main.rs @@ -1,131 +1,131 @@ -use lerpable::Lerpable; -use murrelet::prelude::*; -use murrelet_draw::{compass::*, sequencers::*, style::MurreletPath}; -use murrelet_livecode::types::*; -use murrelet_src_audio::audio_src::AudioMng; -//use murrelet_src_blte::blte::BlteMng; -use murrelet_src_midi::midi::MidiMng; -use murrelet_svg::svg::ToSvgData; - -#[derive(Debug, Clone, Livecode, Lerpable, Default)] -struct SimpleTile { - val: f32, - curve: MurreletCompass, -} -impl SimpleTile { - fn draw(&self, _ctx: &UnitCellContext) { - // very simple program that outputs the value in test - - let m = MurreletPath::curve(self.curve.to_curve_maker()).to_svg(); - - println!("val {:?}", self.val); - println!("m {:?}", m); - //println!("ctx {:?}", ctx); - } -} - -#[derive(Debug, Clone, Livecode, Lerpable)] -struct DrawingConfig { - #[livecode(serde_default = "false")] - debug: bool, - sequencer: Sequencer, - ctx: AdditionalContextNode, - #[livecode(src = "sequencer", ctx = "ctx")] - node: UnitCells, -} -impl DrawingConfig { - fn output(&self) { - for t in self.node.iter() { - t.node.draw(&t.detail) - } - } -} - -// set up livecoder -#[derive(Debug, Clone, Livecode, Lerpable, TopLevelLiveCode)] -struct LiveCodeConf { - // global things - app: AppConfig, - drawing: DrawingConfig, -} - -struct Model { - livecode: LiveCode, - curr_frame: u64, -} -impl Model { - fn new() -> Model { - let capture_path_prefix = std::env::current_dir() - .expect("failed to locate `project_path`") - .join("_recordings"); - - // here is where you connect the livecode srcs (time is always included) - let livecode_src = LivecodeSrc::new(vec![ - Box::new(AppInputValues::new(true)), - Box::new(AudioMng::new()), - Box::new(MidiMng::new()), - //Box::new(BlteMng::new()), - ]); - - let livecode = LiveCode::new(capture_path_prefix, livecode_src, &[]); - // let drawing_state = { - // Drawing::new(&livecode.config().drawing) - // }; - - Model { - livecode, - curr_frame: 0, - } - } - - fn update(&mut self) { - self.curr_frame += 1; - - let app_input = MurreletAppInput::default_with_frames(self.curr_frame); - - self.livecode.update(&app_input, true).ok(); - - // this is also where you could update state - // instead of having the model hold the config directly, you can have - // it hold state - // if self.livecode.app_config().reload { - // self.drawing_state = Drawing::new(&self.livecode.config().drawing); - // } - } - - fn should_update(&self) -> bool { - let w = self.livecode.world(); - w.actual_frame() as u64 % self.livecode.app_config().redraw != 0 - } - - fn draw(&self) { - let _svg_draw_config = self.livecode.svg_save_path(); - - if !self.livecode.app_config().svg.save && self.should_update() { - return; - } - - // let draw_ctx = SDrawCtx::new( - // draw, - // &svg_draw_config, - // self.livecode.should_save_svg() - // ); - - // self.livecode.draw(&draw_ctx); // clear bg, etc - - // draw your stuff - // self.draw(&draw_ctx); - self.livecode.config().drawing.output() - } -} +// use lerpable::Lerpable; +// use murrelet::prelude::*; +// use murrelet_draw::{compass::*, sequencers::*, style::MurreletPath}; +// use murrelet_livecode::types::*; +// use murrelet_src_audio::audio_src::AudioMng; +// //use murrelet_src_blte::blte::BlteMng; +// use murrelet_src_midi::midi::MidiMng; +// use murrelet_svg::svg::ToSvgData; + +// #[derive(Debug, Clone, Livecode, Lerpable, Default)] +// struct SimpleTile { +// val: f32, +// curve: MurreletCompass, +// } +// impl SimpleTile { +// fn draw(&self, _ctx: &UnitCellContext) { +// // very simple program that outputs the value in test + +// let m = MurreletPath::curve(self.curve.to_curve_maker()).to_svg(); + +// println!("val {:?}", self.val); +// println!("m {:?}", m); +// //println!("ctx {:?}", ctx); +// } +// } + +// #[derive(Debug, Clone, Livecode, Lerpable)] +// struct DrawingConfig { +// #[livecode(serde_default = "false")] +// debug: bool, +// sequencer: Sequencer, +// ctx: AdditionalContextNode, +// #[livecode(src = "sequencer", ctx = "ctx")] +// node: UnitCells, +// } +// impl DrawingConfig { +// fn output(&self) { +// for t in self.node.iter() { +// t.node.draw(&t.detail) +// } +// } +// } + +// // set up livecoder +// #[derive(Debug, Clone, Livecode, Lerpable, TopLevelLiveCode)] +// struct LiveCodeConf { +// // global things +// app: AppConfig, +// drawing: DrawingConfig, +// } + +// struct Model { +// livecode: LiveCode, +// curr_frame: u64, +// } +// impl Model { +// fn new() -> Model { +// let capture_path_prefix = std::env::current_dir() +// .expect("failed to locate `project_path`") +// .join("_recordings"); + +// // here is where you connect the livecode srcs (time is always included) +// let livecode_src = LivecodeSrc::new(vec![ +// Box::new(AppInputValues::new(true)), +// Box::new(AudioMng::new()), +// Box::new(MidiMng::new()), +// //Box::new(BlteMng::new()), +// ]); + +// let livecode = LiveCode::new(capture_path_prefix, livecode_src, &[]); +// // let drawing_state = { +// // Drawing::new(&livecode.config().drawing) +// // }; + +// Model { +// livecode, +// curr_frame: 0, +// } +// } + +// fn update(&mut self) { +// self.curr_frame += 1; + +// let app_input = MurreletAppInput::default_with_frames(self.curr_frame); + +// self.livecode.update(&app_input, true).ok(); + +// // this is also where you could update state +// // instead of having the model hold the config directly, you can have +// // it hold state +// // if self.livecode.app_config().reload { +// // self.drawing_state = Drawing::new(&self.livecode.config().drawing); +// // } +// } + +// fn should_update(&self) -> bool { +// let w = self.livecode.world(); +// w.actual_frame() as u64 % self.livecode.app_config().redraw != 0 +// } + +// fn draw(&self) { +// let _svg_draw_config = self.livecode.svg_save_path(); + +// if !self.livecode.app_config().svg.save && self.should_update() { +// return; +// } + +// // let draw_ctx = SDrawCtx::new( +// // draw, +// // &svg_draw_config, +// // self.livecode.should_save_svg() +// // ); + +// // self.livecode.draw(&draw_ctx); // clear bg, etc + +// // draw your stuff +// // self.draw(&draw_ctx); +// self.livecode.config().drawing.output() +// } +// } fn main() { // normally you call this on start up - let mut model = Model::new(); + // let mut model = Model::new(); - for _ in 0..10 { - model.update(); - model.draw(); - std::thread::sleep(std::time::Duration::from_secs(1)); - } + // for _ in 0..10 { + // model.update(); + // model.draw(); + // std::thread::sleep(std::time::Duration::from_secs(1)); + // } } diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index 4193c3c..7fe5483 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -1,5 +1,5 @@ use glam::Vec2; -use murrelet_common::{Angle, IsAngle, Tangent}; +use murrelet_common::{Angle, IsAngle, SpotOnCurve, Tangent}; #[derive(Debug, Clone)] pub struct CubicBezier { @@ -9,6 +9,25 @@ pub struct CubicBezier { pub to: Vec2, } impl CubicBezier { + pub fn from_spots( + in_spot: SpotOnCurve, + in_strength: f32, + out_spot: SpotOnCurve, + out_strength: f32, + ) -> Self { + let norm_dist = in_spot.loc().distance(out_spot.loc()); + + let ctrl1 = in_spot.loc() + in_spot.angle().to_norm_dir() * in_strength * norm_dist; + let ctrl2 = out_spot.loc() + out_spot.angle().to_norm_dir() * out_strength * norm_dist; + + Self { + from: in_spot.loc(), + ctrl1, + ctrl2, + to: out_spot.loc(), + } + } + pub fn new(from: Vec2, ctrl1: Vec2, ctrl2: Vec2, to: Vec2) -> Self { Self { from, diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index e6c401c..8e09e77 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -91,6 +91,7 @@ impl CurveDrawer { pub enum CurveSegment { Arc(CurveArc), Points(CurvePoints), + // Bezier(CubicBezier), } impl CurveSegment { @@ -98,6 +99,7 @@ impl CurveSegment { match self { CurveSegment::Arc(c) => c.first_point(), CurveSegment::Points(c) => c.first_point(), + // CurveSegment::Bezier(_) => todo!(), } } diff --git a/murrelet_gui/examples/tests.rs b/murrelet_gui/examples/tests.rs index a29bd01..bd4f632 100644 --- a/murrelet_gui/examples/tests.rs +++ b/murrelet_gui/examples/tests.rs @@ -1,96 +1,99 @@ -use std::collections::HashMap; - -use murrelet_gui::{CanMakeGUI, MurreletEnumValGUI, MurreletGUI, MurreletGUISchema, ValueGUI}; - -#[derive(MurreletGUI)] -pub struct BasicTypes { - a_number: f32, - b_number: usize, - c_number: u64, - d_number: i32, - bool: bool, - something: Vec, - s: String, - #[murrelet_gui(reference = "test")] - referenced_string: String, -} - -fn custom_func() -> MurreletGUISchema { - MurreletGUISchema::Val(ValueGUI::Num) -} - -#[derive(MurreletGUI)] -pub struct OverridesAndRecursive { - a_number: f32, - something: Vec, - #[murrelet_gui(func = "custom_func")] - label: String, - #[murrelet_gui(kind = "skip")] - b: HashMap, -} - -#[derive(MurreletGUI)] -enum EnumTest { - A, - B(OverridesAndRecursive), -} - -#[derive(MurreletGUI)] -struct SimpleNewtype(f32); - -// fn lerp_partial(&self, pct: T) -> Self { -// SimpleNewtype(pct.lerp_pct() as f32) -// } +// use std::collections::HashMap; + +// use murrelet_gui::{CanMakeGUI, MurreletEnumValGUI, MurreletGUI, MurreletGUISchema, ValueGUI}; + +// #[derive(MurreletGUI)] +// pub struct BasicTypes { +// a_number: f32, +// b_number: usize, +// c_number: u64, +// d_number: i32, +// bool: bool, +// something: Vec, +// s: String, +// #[murrelet_gui(reference = "test")] +// referenced_string: String, +// } + +// fn custom_func() -> MurreletGUISchema { +// MurreletGUISchema::Val(ValueGUI::Num) +// } + +// #[derive(MurreletGUI)] +// pub struct OverridesAndRecursive { +// a_number: f32, +// something: Vec, +// #[murrelet_gui(func = "custom_func")] +// label: String, +// #[murrelet_gui(kind = "skip")] +// b: HashMap, +// } + +// #[derive(MurreletGUI)] +// enum EnumTest { +// A, +// B(OverridesAndRecursive), +// } + +// #[derive(MurreletGUI)] +// struct SimpleNewtype(f32); + +// // fn lerp_partial(&self, pct: T) -> Self { +// // SimpleNewtype(pct.lerp_pct() as f32) +// // } +// // } + +// // #[derive(Debug, Clone, MurreletUX)] + +// fn main() { +// // let b = BasicTypes{ +// // a_number: 1.0, +// // b_number: -10.0, +// // }; +// let test_val = BasicTypes::make_gui(); + +// let basic_types_schema = MurreletGUISchema::Struct(vec![ +// ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), +// ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), +// ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), +// ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), +// ("bool".to_owned(), MurreletGUISchema::Val(ValueGUI::Bool)), +// ( +// "something".to_owned(), +// MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), +// ), +// ("s".to_owned(), MurreletGUISchema::Skip), +// ( +// "referenced_string".to_owned(), +// MurreletGUISchema::Val(ValueGUI::Name("test".to_owned())), +// ), +// ]); + +// assert_eq!(test_val, basic_types_schema); + +// let test_val = OverridesAndRecursive::make_gui(); + +// let overrides_and_recursive_schema = MurreletGUISchema::Struct(vec![ +// ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), +// ( +// "something".to_owned(), +// MurreletGUISchema::list(basic_types_schema), +// ), +// ("label".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), // make sure it calls the override +// ("b".to_owned(), MurreletGUISchema::Skip), +// ]); +// assert_eq!(test_val, overrides_and_recursive_schema); + +// let test_val = EnumTest::make_gui(); + +// assert_eq!( +// test_val, +// MurreletGUISchema::Enum(vec![ +// (MurreletEnumValGUI::Unit("A".to_owned())), +// (MurreletEnumValGUI::Unnamed("B".to_owned(), overrides_and_recursive_schema)), +// ]) +// ); // } -// #[derive(Debug, Clone, MurreletUX)] - -fn main() { - // let b = BasicTypes{ - // a_number: 1.0, - // b_number: -10.0, - // }; - let test_val = BasicTypes::make_gui(); - - let basic_types_schema = MurreletGUISchema::Struct(vec![ - ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ("bool".to_owned(), MurreletGUISchema::Val(ValueGUI::Bool)), - ( - "something".to_owned(), - MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), - ), - ("s".to_owned(), MurreletGUISchema::Skip), - ( - "referenced_string".to_owned(), - MurreletGUISchema::Val(ValueGUI::Name("test".to_owned())), - ), - ]); - - assert_eq!(test_val, basic_types_schema); - - let test_val = OverridesAndRecursive::make_gui(); - - let overrides_and_recursive_schema = MurreletGUISchema::Struct(vec![ - ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ( - "something".to_owned(), - MurreletGUISchema::list(basic_types_schema), - ), - ("label".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), // make sure it calls the override - ("b".to_owned(), MurreletGUISchema::Skip), - ]); - assert_eq!(test_val, overrides_and_recursive_schema); - - let test_val = EnumTest::make_gui(); - - assert_eq!( - test_val, - MurreletGUISchema::Enum(vec![ - (MurreletEnumValGUI::Unit("A".to_owned())), - (MurreletEnumValGUI::Unnamed("B".to_owned(), overrides_and_recursive_schema)), - ]) - ); -} + +fn main() {} \ No newline at end of file From 729202a3907ad6304056e4c620ca3ae278e4ef8d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 21 Jul 2025 12:17:11 -0700 Subject: [PATCH 054/149] wip --- examples/foolish_guillemot/Cargo.toml | 2 +- examples/murrelet_example/Cargo.toml | 2 +- murrelet_common/Cargo.toml | 2 +- murrelet_common/src/geometry.rs | 6 ++ murrelet_draw/Cargo.toml | 2 +- murrelet_gpu/Cargo.toml | 2 +- murrelet_livecode/Cargo.toml | 2 +- murrelet_livecode/src/livecode.rs | 24 +++++ murrelet_livecode/src/nestedit.rs | 10 ++ .../murrelet_livecode_derive/Cargo.toml | 10 +- .../examples/tests.rs | 2 + .../src/derive_lazy.rs | 72 +++++++++++++++ .../src/derive_livecode.rs | 92 +++++++++++++++++++ .../src/derive_nestedit.rs | 34 +++++++ .../murrelet_livecode_derive/src/parser.rs | 16 +++- murrelet_perform/Cargo.toml | 2 +- 16 files changed, 267 insertions(+), 13 deletions(-) diff --git a/examples/foolish_guillemot/Cargo.toml b/examples/foolish_guillemot/Cargo.toml index bfbc650..e21fb45 100644 --- a/examples/foolish_guillemot/Cargo.toml +++ b/examples/foolish_guillemot/Cargo.toml @@ -37,7 +37,7 @@ murrelet_gpu = { version = "0.1.2", path = "../../murrelet_gpu/", features = [ "no_nannou", ] } -lerpable = "0.0.2" +lerpable = { version = "0.0.2", path = "../../../../../lerpable/lerpable" } wgpu = { version = "0.20.1", features = ["webgpu", "webgl"] } anyhow = "1.0.86" diff --git a/examples/murrelet_example/Cargo.toml b/examples/murrelet_example/Cargo.toml index 12695e5..ba9cb4a 100644 --- a/examples/murrelet_example/Cargo.toml +++ b/examples/murrelet_example/Cargo.toml @@ -23,5 +23,5 @@ murrelet_svg = { version = "0.1.1", path = "../../murrelet_svg/" } murrelet_src_audio = { version = "0.1.1", path = "../../murrelet_src_audio/" } murrelet_src_midi = { version = "0.1.1", path = "../../murrelet_src_midi/" } -lerpable = "0.0.2" +lerpable = { version = "0.0.2", path = "../../../../../lerpable/lerpable" } schemars = "0.8.21" \ No newline at end of file diff --git a/murrelet_common/Cargo.toml b/murrelet_common/Cargo.toml index 37b54de..7b92c22 100644 --- a/murrelet_common/Cargo.toml +++ b/murrelet_common/Cargo.toml @@ -13,7 +13,7 @@ glam = "0.28.0" palette = "0.7.6" rand = "0.8" num-traits = "0.2.19" -lerpable = "0.0.2" +lerpable = { version = "0.0.2", path = "../../../../lerpable/lerpable" } murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } serde = { version = "1.0.104", features = ["derive"] } bytemuck = { version = "1.15", features = ["derive"] } diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 1b6f01e..b16d716 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -59,6 +59,12 @@ impl AnglePi { pub fn is_neg(&self) -> bool { self.0 < 0.0 } + + pub fn transform_vec2(&self, v: Vec2) -> Vec2 { + SimpleTransform2d::rotate_pi(self.angle_pi()) + .to_mat3() + .transform_vector2(v) + } } impl std::ops::Neg for AnglePi { diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index b3c1c47..890457c 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -18,7 +18,7 @@ murrelet_livecode = { version = "0.1.2", path = "../murrelet_livecode/", default murrelet_livecode_macros = { version = "0.1.2", path = "../murrelet_livecode_macros/", default-features = false } murrelet_livecode_derive = { version = "0.1.2", path = "../murrelet_livecode_macros/murrelet_livecode_derive/", default-features = false } -lerpable = "0.0.2" +lerpable = { version = "0.0.2", path = "../../../../lerpable/lerpable" } serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" diff --git a/murrelet_gpu/Cargo.toml b/murrelet_gpu/Cargo.toml index fd8c3ea..bd915d4 100644 --- a/murrelet_gpu/Cargo.toml +++ b/murrelet_gpu/Cargo.toml @@ -35,7 +35,7 @@ murrelet_livecode_macros = { version = "0.1.2", path = "../murrelet_livecode_mac murrelet_livecode_derive = { version = "0.1.2", path = "../murrelet_livecode_macros/murrelet_livecode_derive/" } murrelet_perform = { version = "0.1.2", path = "../murrelet_perform/", default-features = false } -lerpable = "0.0.2" +lerpable = { version = "0.0.2", path = "../../../../lerpable/lerpable" } glam = "0.28.0" palette = "0.7.6" diff --git a/murrelet_livecode/Cargo.toml b/murrelet_livecode/Cargo.toml index 23b3bc7..51ab1c7 100644 --- a/murrelet_livecode/Cargo.toml +++ b/murrelet_livecode/Cargo.toml @@ -14,7 +14,7 @@ schemars = ["dep:schemars"] murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } -lerpable = "0.0.2" +lerpable = { version = "0.0.2", path = "../../../../lerpable/lerpable" } itertools = "0.10.5" regex = "1.7.3" diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index 03f642c..7558a0b 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -141,6 +141,16 @@ impl LivecodeToControl for LazyNodeF32 { } } +impl LivecodeToControl> for Option +where + T: LivecodeToControl + Clone, + ControlType: LivecodeFromWorld, +{ + fn to_control(&self) -> Option { + self.as_ref().map(|s| s.to_control()) + } +} + // wrappers around identifiers evalexpr gives us, right now // just to control midi controller #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] @@ -315,6 +325,20 @@ impl GetLivecodeIdentifiers for HashMap { } } +impl GetLivecodeIdentifiers for Option { + fn variable_identifiers(&self) -> Vec { + self.as_ref() + .map(|x| x.variable_identifiers()) + .unwrap_or(vec![]) + } + + fn function_identifiers(&self) -> Vec { + self.as_ref() + .map(|x| x.function_identifiers()) + .unwrap_or(vec![]) + } +} + impl GetLivecodeIdentifiers for ControlBool { fn variable_identifiers(&self) -> Vec { match self { diff --git a/murrelet_livecode/src/nestedit.rs b/murrelet_livecode/src/nestedit.rs index 6d78188..a0b7a6a 100644 --- a/murrelet_livecode/src/nestedit.rs +++ b/murrelet_livecode/src/nestedit.rs @@ -288,6 +288,16 @@ impl NestEditable for Vec { } } +impl NestEditable for Option { + fn nest_update(&self, _mods: NestedMod) -> Self { + self.clone() // noop + } + + fn nest_get(&self, _getter: &[&str]) -> LivecodeResult { + Err(LivecodeError::NestGetExtra("Option".to_owned())) // maybe in the future! + } +} + impl NestEditable for HashMap { fn nest_update(&self, _mods: NestedMod) -> Self { self.clone() // noop diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml index b8e3f1f..cb012eb 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml +++ b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml @@ -20,16 +20,18 @@ quote = "1.0.18" proc-macro2 = "1.0.37" darling = "0.20.3" serde = { version = "1.0.104", features = ["derive"] } -murrelet_livecode = { version = "0.1.2", path = "../../murrelet_livecode", default-features = false} +murrelet_livecode = { version = "0.1.2", path = "../../murrelet_livecode", default-features = false } # just for examples... [dev-dependencies] -murrelet_common = { version = "0.1.2", path = "../../murrelet_common"} +murrelet_common = { version = "0.1.2", path = "../../murrelet_common" } glam = "0.28.0" palette = "0.7.6" -lerpable = "0.0.2" +lerpable = { version = "0.0.2", path = "../../../../../lerpable/lerpable" } schemars = "0.8.21" -murrelet_gui = { version = "0.1.2", path = "../../murrelet_gui", features = ["glam"] } +murrelet_gui = { version = "0.1.2", path = "../../murrelet_gui", features = [ + "glam", +] } [[example]] name = "tests" diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index a347307..cf56833 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -15,6 +15,8 @@ pub struct BasicTypes { something: Vec, #[lerpable(func = "lerpify_vec_vec2")] list_of_vec2: Vec, + // option_f32: Option, + // option_vec2: Option, } fn empty_string() -> String { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index f2787f9..d160274 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -94,6 +94,57 @@ impl LazyFieldType { } } + fn for_world_option(&self, idents: StructIdents) -> TokenStream2 { + let name = idents.name(); + let orig_ty = idents.orig_ty(); + match self.0 { + ControlType::F32_2 => { + quote! { #name: self.#name.map(|name| glam::vec2(name[0].eval_lazy(ctx)? as f32, name[1].eval_lazy(ctx)? as f32))} + } + ControlType::F32_3 => { + quote! {#name: self.#name.map(|name| glam::vec3(name[0].eval_lazy(ctx)? as f32, name[1].eval_lazy(ctx)? as f32, name[2].eval_lazy(ctx)? as f32))} + } + ControlType::Color => { + quote! {#name: self.#name.map(|name| murrelet_common::MurreletColor::hsva(name[0].eval_lazy(ctx)? as f32, name[1].eval_lazy(ctx)? as f32, name[2].eval_lazy(ctx)? as f32, name[3].eval_lazy(ctx)? as f32))} + } + ControlType::Bool => quote! {#name: self.#name.map(|name| name.eval_lazy(ctx)? > 0.0)}, + ControlType::LazyNodeF32 => { + quote! {#name: { + if let Some(name) = &self.#name { + let a = name.add_more_defs(ctx)?; + Some(a) + } else { + None + } + } + } + } + _ => { + // for number-like things, we also enable clamping! (it's a bit experimental though, be careful) + let f32_out = match (idents.data.f32min, idents.data.f32max) { + (None, None) => quote! { + if let Some(name) = &self.#name { + let n = name.eval_lazy(ctx)?; + Some(n) + } else { + None + } + }, + (None, Some(max)) => { + quote! {f32::min(self.#name.map(|name| name.eval_lazy(ctx)?, #max))} + } + (Some(min), None) => { + quote! {f32::max(#min, self.#name.map(|name| name.eval_lazy(ctx)?))} + } + (Some(min), Some(max)) => { + quote! {f32::min(f32::max(#min, self.#name.map(|name| name.eval_lazy(ctx)?), #max))} + } + }; + quote! {#name: #f32_out as #orig_ty} + } + } + } + fn for_newtype_world(&self, idents: StructIdents) -> TokenStream2 { let orig_ty = idents.orig_ty(); match self.0 { @@ -363,6 +414,27 @@ impl GenFinal for FieldTokensLazy { } } + fn from_option(idents: StructIdents) -> Self { + let name = idents.name(); + let back_to_quote = idents.back_to_quote(); + + let s = ident_from_type(&idents.orig_ty()); + + let ctrl = s.second_how_to.unwrap().get_control_type(); + + let for_struct = { + let t = LazyFieldType(ctrl).to_token(); + quote! {#back_to_quote #name: Option<#t>} + }; + + let for_world = LazyFieldType(ctrl).for_world_option(idents.clone()); + + FieldTokensLazy { + for_struct, + for_world, + } + } + // Vec, Vec fn from_recurse_struct_vec(idents: StructIdents) -> FieldTokensLazy { let name = idents.name(); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 8a35f51..8cb207a 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -55,6 +55,52 @@ impl LivecodeFieldType { } } + // usually can call for_world directly, but this is useful in Vec<> + pub(crate) fn for_world_no_name_option( + &self, + name: syn::Ident, + orig_ty: syn::Type, + f32min: Option, + f32max: Option, + ) -> TokenStream2 { + // let name = idents.name(); + // let orig_ty = idents.orig_ty(); + match self.0 { + ControlType::F32_2 => quote! {self.#name.map(|name| name.o(w)?)}, + ControlType::F32_3 => quote! {self.#name.map(|name| name.o(w)?)}, + ControlType::Color => quote! {self.#name.map(|name| name.o(w)?)}, + ControlType::ColorUnclamped => { + quote! {self.#name.map(|name| murrelet_livecode::livecode::ControlF32::hsva_unclamped(&name, w)?)} + } + ControlType::LazyNodeF32 => quote! { + if let Some(name) = &self.#name { + let a = name.o(w)?; + Some(a) + } else { + None + } + }, + _ => { + let f32_out = match (f32min, f32max) { + (None, None) => quote! { + if let Some(name) = &self.#name { + let n = name.o(w)?; + Some(n) + } else { + None + } + }, + (None, Some(max)) => quote! {f32::min(self.#name.map(|name| name.o(w)?), #max)}, + (Some(min), None) => quote! {f32::max(#min, self.#name.map(|name| name.o(w)?))}, + (Some(min), Some(max)) => { + quote! {f32::min(f32::max(#min, self.#name.map(|name| name.o(w)?)), #max)} + } + }; + quote! {#f32_out as #orig_ty} + } + } + } + pub(crate) fn for_world(&self, idents: StructIdents) -> TokenStream2 { let name = idents.name(); let rest = self.for_world_no_name( @@ -66,6 +112,17 @@ impl LivecodeFieldType { quote! {#name: #rest} } + pub(crate) fn for_world_option(&self, idents: StructIdents) -> TokenStream2 { + let name = idents.name(); + let rest = self.for_world_no_name_option( + idents.name(), + idents.orig_ty(), + idents.data.f32min, + idents.data.f32max, + ); + quote! {#name: #rest} + } + pub(crate) fn for_newtype_world(&self, idents: StructIdents) -> TokenStream2 { let orig_ty = idents.orig_ty(); match self.0 { @@ -494,6 +551,41 @@ impl GenFinal for FieldTokensLivecode { } } + fn from_option(idents: StructIdents) -> FieldTokensLivecode { + let serde = idents.serde().clone(); + let name = idents.name().clone(); + // let _orig_type = idents.orig_ty().clone(); + + let s = ident_from_type(&idents.orig_ty()); + + let ctrl = s.second_how_to.unwrap().get_control_type(); + let for_struct = { + let t = LivecodeFieldType(ctrl).to_token(); + quote! {#serde #name: Option<#t>} + }; + let for_world = LivecodeFieldType(ctrl).for_world_option(idents.clone()); + + let for_to_control = LivecodeFieldType(ctrl).for_control(idents.clone()); + + // we'll just use the trait (i want to try it for the above, but we'll come back to that!) + let (for_variable_idents, for_function_idents) = if idents.how_to_control_this_is_none() { + (quote! { vec![] }, quote! { vec![] }) + } else { + ( + quote! {self.#name.variable_identifiers()}, + quote! {self.#name.function_identifiers()}, + ) + }; + + FieldTokensLivecode { + for_struct, + for_world, + for_to_control, + for_variable_idents, + for_function_idents, + } + } + // Vec, Vec fn from_recurse_struct_vec(idents: StructIdents) -> FieldTokensLivecode { let serde = idents.serde(); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs index 787b0ec..766dace 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs @@ -298,6 +298,40 @@ impl GenFinal for FieldTokensNestEdit { } } + fn from_option(idents: StructIdents) -> Self { + let name = idents.name(); + let yaml_name = name.to_string(); + + // let s = ident_from_type(&idents.orig_ty()); + // let ctrl = s.second_how_to.unwrap().get_control_type(); + + // we'll just use the trait! (unless it's none, then we bail + let for_nestedit = match idents.how_to_control_this() { + HowToControlThis::WithNone(_) => quote! { + #name: self.#name.clone() + }, + _ => quote! { + #name: self.#name.as_ref().map(|name| name.nest_update(mods.next_loc(#yaml_name))) + }, + }; + + let for_nestedit_get = quote! { + [#yaml_name, rest @ ..] => self.#name.as_ref().map(|name| name.nest_get(rest)).unwrap_or(Ok("".to_string())) + }; + + let for_nestedit_get_newtype = quote! { + _ => self.#name.map(|name| name.nest_get(getter)).unwrap_or(Ok("".to_string())) + }; + + FieldTokensNestEdit { + kind: "type struct".to_owned(), + for_nestedit, + for_nestedit_get, + for_nestedit_get_newtype: Some(for_nestedit_get_newtype), + for_nestedit_get_flatten: None, + } + } + // v: Vec fn from_recurse_struct_vec(idents: StructIdents) -> FieldTokensNestEdit { let name = idents.name(); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index 96b7c2c..1c2c2db 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -36,6 +36,7 @@ where fn from_recurse_struct_struct(idents: StructIdents) -> Self; fn from_recurse_struct_unitcell(idents: StructIdents) -> Self; fn from_recurse_struct_lazy(idents: StructIdents) -> Self; + fn from_option(idents: StructIdents) -> Self; fn from_ast(ast_receiver: LivecodeReceiver) -> TokenStream2 { match ast_receiver.data { @@ -116,6 +117,12 @@ where } Self::from_recurse_struct_unitcell(idents) } + HowToControlThis::WithRecurse(_, RecursiveControlType::Option) => { + if DEBUG_THIS { + println!("-> from_option"); + } + Self::from_option(idents) + } } }) .collect::>(); @@ -539,8 +546,9 @@ pub(crate) enum RecursiveControlType { Struct, StructLazy, // just a way to stop some features from propogating.. Vec, - UnitCell, // special type that builds up an expression context - // Array, + UnitCell, + Option, // special type that builds up an expression context + // Array, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -629,6 +637,10 @@ impl HowToControlThis { OverrideOrInferred::Inferred, RecursiveControlType::Vec, ), + "Option" => HowToControlThis::WithRecurse( + OverrideOrInferred::Inferred, + RecursiveControlType::Option, + ), "Vec3" => HowToControlThis::WithType(OverrideOrInferred::Inferred, ControlType::F32_3), "String" => HowToControlThis::WithNone(OverrideOrInferred::Inferred), // some special types from this library diff --git a/murrelet_perform/Cargo.toml b/murrelet_perform/Cargo.toml index e35f538..cb38d5e 100644 --- a/murrelet_perform/Cargo.toml +++ b/murrelet_perform/Cargo.toml @@ -30,7 +30,7 @@ murrelet_livecode = { version = "0.1.2", path = "../murrelet_livecode/", default murrelet_draw = { version = "0.1.2", path = "../murrelet_draw/", default-features = false } murrelet_livecode_macros = { version = "0.1.2", path = "../murrelet_livecode_macros/", default-features = false } murrelet_livecode_derive = { version = "0.1.2", path = "../murrelet_livecode_macros/murrelet_livecode_derive/", default-features = false } -lerpable = "0.0.2" +lerpable = { version = "0.0.2", path = "../../../../lerpable/lerpable" } serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" evalexpr = "11.1.0" From fcf08942c8484edf17fc5cfc0dfb659899a20de5 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 24 Jul 2025 22:39:25 -0400 Subject: [PATCH 055/149] update path --- examples/foolish_guillemot/Cargo.toml | 20 +++++++------- examples/murrelet_example/Cargo.toml | 24 ++++++++--------- murrelet/Cargo.toml | 12 ++++----- murrelet_common/Cargo.toml | 4 +-- murrelet_common/src/geometry.rs | 4 +-- murrelet_draw/Cargo.toml | 21 +++++++++------ murrelet_draw/src/curve_drawer.rs | 26 +++++++++++++++++++ murrelet_gen/Cargo.toml | 4 +-- murrelet_gen_derive/src/gen_methods.rs | 14 ++++++---- murrelet_gen_derive/src/parser.rs | 1 + murrelet_gpu/Cargo.toml | 17 ++++++------ murrelet_livecode/Cargo.toml | 6 ++--- murrelet_livecode_macros/Cargo.toml | 4 +-- .../murrelet_livecode_derive/Cargo.toml | 10 +++---- murrelet_perform/Cargo.toml | 14 +++++----- murrelet_src_audio/Cargo.toml | 2 +- murrelet_src_midi/Cargo.toml | 2 +- murrelet_src_osc/Cargo.toml | 2 +- murrelet_svg/Cargo.toml | 7 +++-- 19 files changed, 113 insertions(+), 81 deletions(-) diff --git a/examples/foolish_guillemot/Cargo.toml b/examples/foolish_guillemot/Cargo.toml index e21fb45..9b5b1b6 100644 --- a/examples/foolish_guillemot/Cargo.toml +++ b/examples/foolish_guillemot/Cargo.toml @@ -23,21 +23,21 @@ itertools = "0.10.5" serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" -murrelet = { version = "0.1.2", path = "../../murrelet/" } -murrelet_common = { version = "0.1.2", path = "../../murrelet_common/" } -murrelet_livecode = { version = "0.1.2", path = "../../murrelet_livecode/" } -murrelet_livecode_macros = { version = "0.1.2", path = "../../murrelet_livecode_macros/" } -murrelet_livecode_derive = { version = "0.1.2", path = "../../murrelet_livecode_macros/murrelet_livecode_derive/" } -murrelet_perform = { version = "0.1.2", path = "../../murrelet_perform/", features = [ +murrelet = "0.1.2" +murrelet_common = "0.1.2" +murrelet_livecode = "0.1.2" +murrelet_livecode_macros = "0.1.2" +murrelet_livecode_derive = "0.1.2" +murrelet_perform = { version = "0.1.2", features = [ "for_the_web", ] } -murrelet_draw = { version = "0.1.2", path = "../../murrelet_draw/" } -murrelet_svg = { version = "0.1.2", path = "../../murrelet_svg/" } -murrelet_gpu = { version = "0.1.2", path = "../../murrelet_gpu/", features = [ +murrelet_draw = "0.1.2" +murrelet_svg = "0.1.2" +murrelet_gpu = { version = "0.1.2", features = [ "no_nannou", ] } -lerpable = { version = "0.0.2", path = "../../../../../lerpable/lerpable" } +lerpable = { version = "0.0.2" } wgpu = { version = "0.20.1", features = ["webgpu", "webgl"] } anyhow = "1.0.86" diff --git a/examples/murrelet_example/Cargo.toml b/examples/murrelet_example/Cargo.toml index ba9cb4a..9106257 100644 --- a/examples/murrelet_example/Cargo.toml +++ b/examples/murrelet_example/Cargo.toml @@ -11,17 +11,17 @@ itertools = "0.10.5" serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" -murrelet = { version = "0.1.1", path = "../../murrelet/" } -murrelet_livecode = { version = "0.1.1", path = "../../murrelet_livecode/" } -murrelet_livecode_macros = { version = "0.1.1", path = "../../murrelet_livecode_macros/" } -murrelet_common = { version = "0.1.1", path = "../../murrelet_common/" } -murrelet_livecode_derive = { version = "0.1.1", path = "../../murrelet_livecode_macros/murrelet_livecode_derive/" } -murrelet_perform = { version = "0.1.1", path = "../../murrelet_perform/" } -murrelet_draw = { version = "0.1.1", path = "../../murrelet_draw/" } -murrelet_svg = { version = "0.1.1", path = "../../murrelet_svg/" } +murrelet = "0.1.1" +murrelet_livecode = "0.1.1" +murrelet_livecode_macros = "0.1.1" +murrelet_common = "0.1.1" +murrelet_livecode_derive = "0.1.1" +murrelet_perform = "0.1.1" +murrelet_draw = "0.1.1" +murrelet_svg = "0.1.1" -murrelet_src_audio = { version = "0.1.1", path = "../../murrelet_src_audio/" } -murrelet_src_midi = { version = "0.1.1", path = "../../murrelet_src_midi/" } +murrelet_src_audio = "0.1.1" +murrelet_src_midi = "0.1.1" -lerpable = { version = "0.0.2", path = "../../../../../lerpable/lerpable" } -schemars = "0.8.21" \ No newline at end of file +lerpable = "0.0.2" +schemars = "0.8.21" diff --git a/murrelet/Cargo.toml b/murrelet/Cargo.toml index 096bf9b..97780f5 100644 --- a/murrelet/Cargo.toml +++ b/murrelet/Cargo.toml @@ -32,9 +32,9 @@ itertools = "0.10.5" serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" -murrelet_common = { version = "0.1.2", path = "../murrelet_common" } -murrelet_draw = { version = "0.1.2", path = "../murrelet_draw", default-features = false } -murrelet_livecode = { version = "0.1.2", path = "../murrelet_livecode", default-features = false } -murrelet_livecode_macros = { version = "0.1.2", path = "../murrelet_livecode_macros" , default-features = false } -murrelet_livecode_derive = { version = "0.1.2", path = "../murrelet_livecode_macros/murrelet_livecode_derive/" , default-features = false } -murrelet_perform = { version = "0.1.2", path = "../murrelet_perform", default-features = false } +murrelet_common = "0.1.2" +murrelet_draw = { version = "0.1.2", default-features = false } +murrelet_livecode = { version = "0.1.2", default-features = false } +murrelet_livecode_macros = { version = "0.1.2", default-features = false } +murrelet_livecode_derive = { version = "0.1.2", default-features = false } +murrelet_perform = { version = "0.1.2", default-features = false } diff --git a/murrelet_common/Cargo.toml b/murrelet_common/Cargo.toml index 7b92c22..45fdfb6 100644 --- a/murrelet_common/Cargo.toml +++ b/murrelet_common/Cargo.toml @@ -13,8 +13,8 @@ glam = "0.28.0" palette = "0.7.6" rand = "0.8" num-traits = "0.2.19" -lerpable = { version = "0.0.2", path = "../../../../lerpable/lerpable" } -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } +lerpable = "0.0.2" +murrelet_gui = { version = "0.1.2", features = ["glam"] } serde = { version = "1.0.104", features = ["derive"] } bytemuck = { version = "1.15", features = ["derive"] } diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index b16d716..10f552a 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -370,8 +370,8 @@ impl IsLength for PointToPoint { #[derive(Debug, Copy, Clone)] pub struct SpotOnCurve { - loc: Vec2, - angle: Angle, + pub loc: Vec2, + pub angle: Angle, } impl SpotOnCurve { diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index 890457c..534087b 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -8,17 +8,22 @@ description = "drawing functions for murrelet, a livecode framework" license = "AGPL-3.0-or-later" [features] -schemars = ["dep:schemars", "murrelet_livecode/schemars", "murrelet_livecode_macros/schemars", "murrelet_livecode_derive/schemars"] +schemars = [ + "dep:schemars", + "murrelet_livecode/schemars", + "murrelet_livecode_macros/schemars", + "murrelet_livecode_derive/schemars", +] # murrelet_gui = ["dep:murrelet_gui", "murrelet_livecode/murrelet_gui", "murrelet_livecode_macros/murrelet_gui", "murrelet_livecode_derive/murrelet_gui"] [dependencies] -murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } +murrelet_common = "0.1.2" -murrelet_livecode = { version = "0.1.2", path = "../murrelet_livecode/", default-features = false } -murrelet_livecode_macros = { version = "0.1.2", path = "../murrelet_livecode_macros/", default-features = false } -murrelet_livecode_derive = { version = "0.1.2", path = "../murrelet_livecode_macros/murrelet_livecode_derive/", default-features = false } +murrelet_livecode = { version = "0.1.2", default-features = false } +murrelet_livecode_macros = { version = "0.1.2", default-features = false } +murrelet_livecode_derive = { version = "0.1.2", default-features = false } -lerpable = { version = "0.0.2", path = "../../../../lerpable/lerpable" } +lerpable = "0.0.2" serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" @@ -33,8 +38,8 @@ md-5 = "0.10.6" hex = "0.4.3" schemars = { version = "0.8.21", optional = true } -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } +murrelet_gui = { version = "0.1.2", features = ["glam"] } kurbo = "0.11" svg = "0.10.0" -lyon = "0.17" \ No newline at end of file +lyon = "0.17" diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 8e09e77..444b26f 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -176,6 +176,32 @@ impl CurveSegment { CurveSegment::Points(p) => Some(p.points.len()), } } + + pub fn first_angle(&self) -> Option { + match self { + CurveSegment::Arc(curve_arc) => { + Some(curve_arc.start_angle().perp_to_left().as_angle_pi()) + } + CurveSegment::Points(_) => None, + } + } + + pub fn first_spot(&self) -> SpotOnCurve { + SpotOnCurve::new(self.first_point(), self.first_angle().unwrap()) + } + + pub fn last_angle(&self) -> Option { + match self { + CurveSegment::Arc(curve_arc) => { + Some(curve_arc.end_angle().perp_to_right().as_angle_pi()) + } + CurveSegment::Points(_) => None, + } + } + + pub fn last_spot(&self) -> SpotOnCurve { + SpotOnCurve::new(self.last_point(), self.last_angle().unwrap()) + } } #[derive(Debug, Clone, Livecode, Lerpable)] diff --git a/murrelet_gen/Cargo.toml b/murrelet_gen/Cargo.toml index f4a6107..e8bd5da 100644 --- a/murrelet_gen/Cargo.toml +++ b/murrelet_gen/Cargo.toml @@ -7,14 +7,14 @@ edition = "2021" default = [] [dependencies] -murrelet_gen_derive = { path = "../murrelet_gen_derive/" } +murrelet_gen_derive = "0.1.2" thiserror = "2.0.11" serde = { version = "1.0.104", features = ["derive"] } serde_json = "1.0.48" rand = "0.8" glam = "0.28.0" -murrelet_common = { path = "../murrelet_common/" } +murrelet_common = "0.1.2" [[example]] diff --git a/murrelet_gen_derive/src/gen_methods.rs b/murrelet_gen_derive/src/gen_methods.rs index b2555a8..d7ef4e7 100644 --- a/murrelet_gen_derive/src/gen_methods.rs +++ b/murrelet_gen_derive/src/gen_methods.rs @@ -49,6 +49,9 @@ pub enum GenMethod { StringChoice { choices: HashMap, // string mapped to its weight }, + F32Lazy, // eval data is passed in through another method. + // BoolLazy, + // Vec2Lazy, } impl GenMethod { pub(crate) fn to_methods( @@ -280,11 +283,11 @@ impl GenMethod { } }; let for_to_dist_choices = choices - .iter() - .map(|(key, _)| { - quote! { if #key.clone() == #name { result.push(1.0) } else { result.push(0.0) } } - }) - .collect::>(); + .iter() + .map(|(key, _)| { + quote! { if #key.clone() == #name { result.push(1.0) } else { result.push(0.0) } } + }) + .collect::>(); let for_to_dist = quote! { { let mut result = vec![]; @@ -307,6 +310,7 @@ impl GenMethod { (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } + GenMethod::F32Lazy => todo!(), } } diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index c64376b..afdeb1d 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -233,6 +233,7 @@ pub(crate) enum HowToControlThis { // Override(String, Vec, usize), } +#[allow(dead_code)] pub fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { match t { syn::Type::Path(syn::TypePath { path, .. }) => { diff --git a/murrelet_gpu/Cargo.toml b/murrelet_gpu/Cargo.toml index bd915d4..56a13f9 100644 --- a/murrelet_gpu/Cargo.toml +++ b/murrelet_gpu/Cargo.toml @@ -28,14 +28,14 @@ schemars = [ wgpu_for_latest = { package = "wgpu", version = "0.20.1", optional = true } wgpu_for_nannou = { package = "wgpu", version = "0.17.1", optional = true } -murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } -murrelet_livecode = { version = "0.1.2", path = "../murrelet_livecode/", default-features = false } -murrelet_draw = { version = "0.1.2", path = "../murrelet_draw/", default-features = false } -murrelet_livecode_macros = { version = "0.1.2", path = "../murrelet_livecode_macros/" } -murrelet_livecode_derive = { version = "0.1.2", path = "../murrelet_livecode_macros/murrelet_livecode_derive/" } -murrelet_perform = { version = "0.1.2", path = "../murrelet_perform/", default-features = false } +murrelet_common = "0.1.2" +murrelet_livecode = { version = "0.1.2", default-features = false } +murrelet_draw = { version = "0.1.2", default-features = false } +murrelet_livecode_macros = { version = "0.1.2" } +murrelet_livecode_derive = { version = "0.1.2" } +murrelet_perform = { version = "0.1.2", default-features = false } -lerpable = { version = "0.0.2", path = "../../../../lerpable/lerpable" } +lerpable = { version = "0.0.2" } glam = "0.28.0" palette = "0.7.6" @@ -54,5 +54,4 @@ image = "0.25.2" bytemuck = { version = "1.16.1", features = ["derive"] } schemars = { version = "0.8.21", optional = true } -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } - +murrelet_gui = { version = "0.1.2", features = ["glam"] } diff --git a/murrelet_livecode/Cargo.toml b/murrelet_livecode/Cargo.toml index 51ab1c7..0ba2e02 100644 --- a/murrelet_livecode/Cargo.toml +++ b/murrelet_livecode/Cargo.toml @@ -11,10 +11,10 @@ license = "AGPL-3.0-or-later" schemars = ["dep:schemars"] [dependencies] -murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } +murrelet_common = "0.1.2" +murrelet_gui = { version = "0.1.2", features = ["glam"] } -lerpable = { version = "0.0.2", path = "../../../../lerpable/lerpable" } +lerpable = "0.0.2" itertools = "0.10.5" regex = "1.7.3" diff --git a/murrelet_livecode_macros/Cargo.toml b/murrelet_livecode_macros/Cargo.toml index e1407fd..6202438 100644 --- a/murrelet_livecode_macros/Cargo.toml +++ b/murrelet_livecode_macros/Cargo.toml @@ -13,5 +13,5 @@ schemars = ["murrelet_livecode/schemars", "murrelet_livecode_derive/schemars"] [dependencies] -murrelet_livecode_derive = { version = "0.1.2", path = "murrelet_livecode_derive", default-features = false } -murrelet_livecode = { version = "0.1.2", path = "../murrelet_livecode", default-features = false } +murrelet_livecode_derive = { version = "0.1.2", default-features = false } +murrelet_livecode = { version = "0.1.2", default-features = false } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml index cb012eb..3560714 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml +++ b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml @@ -20,18 +20,16 @@ quote = "1.0.18" proc-macro2 = "1.0.37" darling = "0.20.3" serde = { version = "1.0.104", features = ["derive"] } -murrelet_livecode = { version = "0.1.2", path = "../../murrelet_livecode", default-features = false } +murrelet_livecode = { version = "0.1.2", default-features = false } # just for examples... [dev-dependencies] -murrelet_common = { version = "0.1.2", path = "../../murrelet_common" } +murrelet_common = "0.1.2" glam = "0.28.0" palette = "0.7.6" -lerpable = { version = "0.0.2", path = "../../../../../lerpable/lerpable" } +lerpable = "0.0.2" schemars = "0.8.21" -murrelet_gui = { version = "0.1.2", path = "../../murrelet_gui", features = [ - "glam", -] } +murrelet_gui = { version = "0.1.2", features = ["glam"] } [[example]] name = "tests" diff --git a/murrelet_perform/Cargo.toml b/murrelet_perform/Cargo.toml index cb38d5e..80bec7a 100644 --- a/murrelet_perform/Cargo.toml +++ b/murrelet_perform/Cargo.toml @@ -25,12 +25,12 @@ schemars = [ # ] [dependencies] -murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } -murrelet_livecode = { version = "0.1.2", path = "../murrelet_livecode/", default-features = false } -murrelet_draw = { version = "0.1.2", path = "../murrelet_draw/", default-features = false } -murrelet_livecode_macros = { version = "0.1.2", path = "../murrelet_livecode_macros/", default-features = false } -murrelet_livecode_derive = { version = "0.1.2", path = "../murrelet_livecode_macros/murrelet_livecode_derive/", default-features = false } -lerpable = { version = "0.0.2", path = "../../../../lerpable/lerpable" } +murrelet_common = "0.1.2" +murrelet_livecode = { version = "0.1.2", default-features = false } +murrelet_draw = { version = "0.1.2", default-features = false } +murrelet_livecode_macros = { version = "0.1.2", default-features = false } +murrelet_livecode_derive = { version = "0.1.2", default-features = false } +lerpable = { version = "0.0.2" } serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" evalexpr = "11.1.0" @@ -42,6 +42,6 @@ palette = "0.7.6" anyhow = "1.0.86" schemars = { version = "0.8.21", optional = true } -murrelet_gui = { version = "0.1.2", path = "../murrelet_gui/", features = ["glam"] } +murrelet_gui = { version = "0.1.2", features = ["glam"] } clap = { version = "4.5.23", features = ["derive"] } diff --git a/murrelet_src_audio/Cargo.toml b/murrelet_src_audio/Cargo.toml index b56fa83..ae6a5ff 100644 --- a/murrelet_src_audio/Cargo.toml +++ b/murrelet_src_audio/Cargo.toml @@ -8,7 +8,7 @@ description = "audio input functions for murrelet, a livecode framework" license = "AGPL-3.0-or-later" [dependencies] -murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } +murrelet_common = "0.1.2" cpal = "0.14" rustfft = "6.1.0" glam = "0.28.0" diff --git a/murrelet_src_midi/Cargo.toml b/murrelet_src_midi/Cargo.toml index 3b64af9..277ef2d 100644 --- a/murrelet_src_midi/Cargo.toml +++ b/murrelet_src_midi/Cargo.toml @@ -8,7 +8,7 @@ description = "MIDI input functions for murrelet, a livecode framework" license = "AGPL-3.0-or-later" [dependencies] -murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } +murrelet_common = "0.1.2" midir = "0.8.0" diff --git a/murrelet_src_osc/Cargo.toml b/murrelet_src_osc/Cargo.toml index 9245ed3..454783a 100644 --- a/murrelet_src_osc/Cargo.toml +++ b/murrelet_src_osc/Cargo.toml @@ -9,7 +9,7 @@ license = "AGPL-3.0-or-later" [dependencies] -murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } +murrelet_common = "0.1.2" uuid = "1.8.0" rosc = "~0.10" diff --git a/murrelet_svg/Cargo.toml b/murrelet_svg/Cargo.toml index 4935834..a67be59 100644 --- a/murrelet_svg/Cargo.toml +++ b/murrelet_svg/Cargo.toml @@ -12,10 +12,9 @@ schemars = ["murrelet_perform/schemars", "murrelet_draw/schemars"] # murrelet_gui = ["murrelet_perform/murrelet_gui", "murrelet_draw/murrelet_gui"] [dependencies] -murrelet_common = { version = "0.1.2", path = "../murrelet_common/" } -murrelet_perform = { version = "0.1.2", path = "../murrelet_perform/", default-features = false} -murrelet_draw = { version = "0.1.2", path = "../murrelet_draw/", default-features = false} +murrelet_common = "0.1.2" +murrelet_perform = { version = "0.1.2", default-features = false } +murrelet_draw = { version = "0.1.2", default-features = false } glam = "0.28.0" itertools = "0.10.5" svg = "0.10.0" - From ddfb5849e6469004a26ebd1541296698e8dbbe8f Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 25 Jul 2025 12:52:26 -0400 Subject: [PATCH 056/149] consolidate birdcurve and curvesegment --- murrelet_common/src/geometry.rs | 17 ----- murrelet_draw/src/cubic.rs | 25 ++++--- murrelet_draw/src/curve_drawer.rs | 111 +++++++++++++++++++++++++----- murrelet_svg/src/svg.rs | 1 + 4 files changed, 113 insertions(+), 41 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 10f552a..417de8b 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -685,20 +685,3 @@ pub fn tangents_between_two_circles( Some((start, end)) } - -#[derive(Copy, Clone, Debug)] -pub struct Tangent { - pub loc: Vec2, - pub dir: AnglePi, - // strength: f32, -} - -impl Tangent { - pub fn dir(&self) -> AnglePi { - self.dir - } - - pub fn loc(&self) -> Vec2 { - self.loc - } -} diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index 7fe5483..3d62393 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -1,5 +1,5 @@ use glam::Vec2; -use murrelet_common::{Angle, IsAngle, SpotOnCurve, Tangent}; +use murrelet_common::{Angle, IsAngle, SpotOnCurve}; #[derive(Debug, Clone)] pub struct CubicBezier { @@ -72,7 +72,7 @@ impl CubicBezier { ) } - pub fn start_to_tangent(&self) -> (Tangent, f32) { + pub fn start_to_tangent(&self) -> (SpotOnCurve, f32) { // let side_len = self.from.distance(self.to); let ctrl_line = self.from - self.ctrl1; @@ -83,16 +83,16 @@ impl CubicBezier { // let strength = ctrl_line.length() / side_len; ( - Tangent { + SpotOnCurve { loc: self.from, - dir: dir.into(), + angle: dir.into(), // strength, }, ctrl_line.length(), ) } - pub fn end_to_tangent(&self) -> (Tangent, f32) { + pub fn end_to_tangent(&self) -> (SpotOnCurve, f32) { // let side_len = self.from.distance(self.to); let ctrl_line = self.ctrl2 - self.to; @@ -103,18 +103,27 @@ impl CubicBezier { // let strength = ctrl_line.length() / side_len; ( - Tangent { + SpotOnCurve { loc: self.to, - dir: dir.into(), + angle: dir.into(), // strength, }, ctrl_line.length(), ) } - pub fn tangent_at_pct(&self, pct: f32) -> Tangent { + pub fn tangent_at_pct(&self, pct: f32) -> SpotOnCurve { let (start, _) = self.split(pct); let (t, _a) = start.end_to_tangent(); t } + + pub fn reverse(&self) -> CubicBezier { + CubicBezier { + from: self.to, + ctrl1: self.ctrl2, + ctrl2: self.ctrl1, + to: self.from, + } + } } diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 444b26f..574d34c 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -4,7 +4,7 @@ use lerpable::Lerpable; use murrelet_common::*; use murrelet_livecode_derive::*; -use crate::livecodetypes::anglepi::*; +use crate::{cubic::CubicBezier, livecodetypes::anglepi::*, tesselate::ToVecVec2}; #[derive(Debug, Clone, Livecode, Lerpable)] pub struct CurveDrawer { @@ -91,15 +91,24 @@ impl CurveDrawer { pub enum CurveSegment { Arc(CurveArc), Points(CurvePoints), - // Bezier(CubicBezier), + CubicBezier(CurveCubicBezier), } impl CurveSegment { + pub fn arc(loc: Vec2, radius: f32, start_pi: A, end_pi: A) -> Self { + Self::Arc(CurveArc { + loc, + radius, + start_pi: LivecodeAnglePi::new(start_pi), + end_pi: LivecodeAnglePi::new(end_pi), + }) + } + pub fn first_point(&self) -> Vec2 { match self { CurveSegment::Arc(c) => c.first_point(), CurveSegment::Points(c) => c.first_point(), - // CurveSegment::Bezier(_) => todo!(), + CurveSegment::CubicBezier(c) => c.first_point(), } } @@ -107,6 +116,7 @@ impl CurveSegment { match self { CurveSegment::Arc(c) => c.last_point(), CurveSegment::Points(c) => c.last_point(), + CurveSegment::CubicBezier(c) => c.last_point(), } } @@ -114,6 +124,7 @@ impl CurveSegment { match self { CurveSegment::Arc(curve_arc) => CurveSegment::Arc(curve_arc.reverse()), CurveSegment::Points(curve_points) => CurveSegment::Points(curve_points.reverse()), + CurveSegment::CubicBezier(c) => CurveSegment::CubicBezier(c.reverse()), } } @@ -174,33 +185,54 @@ impl CurveSegment { match self { CurveSegment::Arc(_) => None, CurveSegment::Points(p) => Some(p.points.len()), + CurveSegment::CubicBezier(_) => None, } } pub fn first_angle(&self) -> Option { - match self { - CurveSegment::Arc(curve_arc) => { - Some(curve_arc.start_angle().perp_to_left().as_angle_pi()) - } - CurveSegment::Points(_) => None, - } + Some(self.first_spot().angle().into()) // todo, return none if it's one point } pub fn first_spot(&self) -> SpotOnCurve { - SpotOnCurve::new(self.first_point(), self.first_angle().unwrap()) + match self { + CurveSegment::Arc(arc) => { + let a = CurveArc::new(arc.loc, arc.radius, arc.start_pi, arc.end_pi); + SpotOnCurve::new(a.first_point(), a.start_tangent_angle()) + } + CurveSegment::Points(points) => { + let vec2s = points.points(); + if vec2s.len() >= 2 { + let first = vec2s[0]; + let second = vec2s[1]; + + let angle = PointToPoint::new(first, second).angle().perp_to_left(); + SpotOnCurve::new(first, angle) + } else { + todo!() + } + } + CurveSegment::CubicBezier(cubic_bezier) => cubic_bezier.to_cubic().start_to_tangent().0, + } } - pub fn last_angle(&self) -> Option { + pub fn last_spot(&self) -> SpotOnCurve { match self { - CurveSegment::Arc(curve_arc) => { - Some(curve_arc.end_angle().perp_to_right().as_angle_pi()) + CurveSegment::Arc(arc) => { + let a = CurveArc::new(arc.loc, arc.radius, arc.start_pi, arc.end_pi); + SpotOnCurve::new(a.last_point(), a.end_tangent_angle()) } - CurveSegment::Points(_) => None, + CurveSegment::Points(_) => todo!(), + CurveSegment::CubicBezier(c) => c.to_cubic().end_to_tangent().0, } } - pub fn last_spot(&self) -> SpotOnCurve { - SpotOnCurve::new(self.last_point(), self.last_angle().unwrap()) + pub fn cubic_bezier(from: Vec2, ctrl1: Vec2, ctrl2: Vec2, to: Vec2) -> Self { + Self::CubicBezier(CurveCubicBezier { + from, + ctrl1, + ctrl2, + to, + }) } } @@ -304,6 +336,53 @@ impl CurveArc { } } +#[derive(Debug, Clone, Livecode, Lerpable)] +pub struct CurveCubicBezier { + #[lerpable(func = "lerpify_vec2")] + from: Vec2, + #[lerpable(func = "lerpify_vec2")] + ctrl1: Vec2, + #[lerpable(func = "lerpify_vec2")] + ctrl2: Vec2, + #[lerpable(func = "lerpify_vec2")] + to: Vec2, +} +impl CurveCubicBezier { + pub fn to_cubic(&self) -> CubicBezier { + CubicBezier { + from: self.from, + ctrl1: self.ctrl1, + ctrl2: self.ctrl2, + to: self.to, + } + } + + pub fn from_cubic(c: CubicBezier) -> Self { + Self { + from: c.from, + ctrl1: c.ctrl1, + ctrl2: c.ctrl2, + to: c.to, + } + } + + pub fn first_point(&self) -> Vec2 { + self.from + } + + pub fn last_point(&self) -> Vec2 { + self.to + } + + pub fn reverse(&self) -> CurveCubicBezier { + Self::from_cubic(self.to_cubic().reverse()) + } + + pub fn as_points(&self) -> CurvePoints { + CurvePoints::new(self.to_cubic().to_vec2()) + } +} + #[derive(Debug, Clone, Livecode, Lerpable)] pub struct CurvePoints { #[lerpable(func = "lerpify_vec_vec2")] diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 3965f55..13f6b64 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -893,6 +893,7 @@ impl ToSvgData for CurveDrawer { } match curve { + murrelet_draw::curve_drawer::CurveSegment::CubicBezier(_) => {todo!()} murrelet_draw::curve_drawer::CurveSegment::Arc(a) => { let f = a.first_point(); // first make sure we're at the first point From c5b60f2e2348219ca6af94be4695b8de2c387a27 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 25 Jul 2025 20:27:27 -0400 Subject: [PATCH 057/149] wip --- murrelet_draw/src/curve_drawer.rs | 5 ++++- murrelet_draw/src/livecodetypes.rs | 21 ++++++++++++++++++++- murrelet_draw/src/style.rs | 3 +++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 574d34c..f4a7172 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -124,7 +124,10 @@ impl CurveSegment { match self { CurveSegment::Arc(curve_arc) => CurveSegment::Arc(curve_arc.reverse()), CurveSegment::Points(curve_points) => CurveSegment::Points(curve_points.reverse()), - CurveSegment::CubicBezier(c) => CurveSegment::CubicBezier(c.reverse()), + CurveSegment::CubicBezier(c) => { + // CurveSegment::CubicBezier(c.reverse()) + CurveSegment::Points(c.as_points().reverse()) + } } } diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs index 7c7d944..b4c3d6a 100644 --- a/murrelet_draw/src/livecodetypes.rs +++ b/murrelet_draw/src/livecodetypes.rs @@ -7,13 +7,21 @@ pub mod anglepi { use lerpable::Lerpable; use murrelet_common::{Angle, AnglePi, IsAngle}; use murrelet_gui::CanMakeGUI; - use murrelet_livecode::lazy::ControlLazyNodeF32; + use murrelet_livecode::{lazy::ControlLazyNodeF32, livecode::ControlF32}; use murrelet_livecode_derive::Livecode; use crate::transform2d::Transform2d; #[derive(Clone, Copy, Debug, Livecode, Lerpable, Default, PartialEq)] pub struct LivecodeAnglePi(f32); + + impl std::ops::Add for LivecodeAnglePi { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + Self(self.0 + other.0) + } + } impl LivecodeAnglePi { pub const ZERO: Self = LivecodeAnglePi(0.0); @@ -38,6 +46,11 @@ pub mod anglepi { .to_mat3() .transform_vector2(v) } + + pub fn add(&self, f: A) -> LivecodeAnglePi { + Self(self.0 + f.angle_pi()) + } + } impl From for Angle { @@ -67,6 +80,12 @@ pub mod anglepi { } } + impl ControlLivecodeAnglePi { + pub fn from_angle(a: AnglePi) -> Self { + Self(ControlF32::Float(a.angle_pi())) + } + } + impl ControlLazyLivecodeAnglePi { pub fn from_angle(a: AnglePi) -> Self { Self(ControlLazyNodeF32::Float(a.angle_pi())) diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 84a2edf..9ad6da9 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -389,6 +389,9 @@ pub mod styleconf { } impl StyleConf { + pub fn black_fill() -> Self { + Self::fill(MurreletColor::black()) + } pub fn to_style(&self) -> MurreletStyle { match self { From eff088f03ae21e8d03b64b353abefd057e8f4bdc Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 27 Jul 2025 23:30:24 -0400 Subject: [PATCH 058/149] wip --- murrelet_draw/src/curve_drawer.rs | 12 ++ murrelet_draw/src/draw.rs | 13 ++- murrelet_draw/src/style.rs | 41 ++++--- murrelet_draw/src/svg.rs | 176 ++++++++++++++++++++++++++++++ murrelet_draw/src/tesselate.rs | 4 + murrelet_draw/src/transform2d.rs | 22 ++++ murrelet_svg/src/svg.rs | 28 ++++- 7 files changed, 274 insertions(+), 22 deletions(-) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index f4a7172..4f60ab2 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -384,6 +384,18 @@ impl CurveCubicBezier { pub fn as_points(&self) -> CurvePoints { CurvePoints::new(self.to_cubic().to_vec2()) } + + pub fn ctrl1(&self) -> Vec2 { + self.ctrl1 + } + + pub fn ctrl2(&self) -> Vec2 { + self.ctrl2 + } + + pub fn to(&self) -> Vec2 { + self.to + } } #[derive(Debug, Clone, Livecode, Lerpable)] diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index b9b6f5a..8aac732 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -11,6 +11,7 @@ use palette::{named::AQUAMARINE, LinSrgba, Srgb}; use crate::{ newtypes::RGBandANewtype, style::{styleconf::*, StyledPathSvgFill}, + transform2d::ToMat4, }; #[derive(Debug, Clone, Copy, Livecode, Lerpable, Default)] @@ -82,7 +83,6 @@ impl MurreletColorStyle { fn hue(hue: f32) -> MurreletColorStyle { MurreletColorStyle::Color(MurreletColor::hsva(hue, 1.0, 1.0, 1.0)) } - } #[derive(Debug, Clone)] @@ -345,7 +345,8 @@ pub trait Sdraw: Sized { } fn transform(&self) -> Mat4; - fn set_transform(&self, m: Mat4) -> Self; + // fn set_transform(&self, m: Mat4) -> Self; + fn set_transform(&self, m: M) -> Self; fn add_transform_after(&self, t: Mat4) -> Self { let m = t * self.transform(); @@ -412,9 +413,9 @@ impl Sdraw for CoreSDrawCtxUnitCell { self.sdraw.transform } - fn set_transform(&self, m: Mat4) -> Self { + fn set_transform(&self, m: M) -> Self { let mut ctx = self.clone(); - ctx.sdraw.transform = m; + ctx.sdraw.transform = m.change_to_mat4(); ctx } } @@ -492,9 +493,9 @@ impl Sdraw for CoreSDrawCtx { fn transform(&self) -> Mat4 { self.transform } - fn set_transform(&self, m: Mat4) -> Self { + fn set_transform(&self, m: M) -> Self { let mut sdraw = self.clone(); - sdraw.transform = m; + sdraw.transform = m.change_to_mat4(); sdraw } diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 9ad6da9..32832a9 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -3,6 +3,7 @@ use crate::{ curve_drawer::CurveDrawer, draw::*, svg::{SvgPathDef, TransformedSvgShape}, + tesselate::parse_svg_path_as_vec2, transform2d::*, }; use glam::*; @@ -388,7 +389,6 @@ pub mod styleconf { RGBAPoints(MurreletStyleRGBAPoints), } impl StyleConf { - pub fn black_fill() -> Self { Self::fill(MurreletColor::black()) } @@ -518,14 +518,15 @@ impl MurreletCurve { self.t } - // pub fn curve(&self) -> &CurveDrawer { - // match &self.cd { - // MurreletCurveKinds::CD(cd) => cd, - // MurreletCurveKinds::Svg(pts) => { - // &CurveDrawer::new_simple_points(parse_svg_path_as_vec2(pts), false) - // }, - // } - // } + pub fn curve(&self) -> CurveDrawer { + match &self.cd { + MurreletCurveKinds::CD(cd) => cd.clone(), + MurreletCurveKinds::Svg(pts) => { + let s = parse_svg_path_as_vec2(pts, 1.0); + CurveDrawer::new_simple_points(s, false) + } + } + } fn new_transformed_svg(svg: &TransformedSvgShape) -> MurreletCurve { Self { @@ -537,6 +538,16 @@ impl MurreletCurve { pub fn cd(&self) -> &MurreletCurveKinds { &self.cd } + + fn to_svg(&self) -> TransformedSvgShape { + let c = match &self.cd { + MurreletCurveKinds::CD(cd) => TransformedSvgShape::from_cd(cd), + MurreletCurveKinds::Svg(pts) => { + TransformedSvgShape::from_shape(crate::svg::SvgShape::Path(pts.clone())) + } + }; + c.transform_with_mat4_after(self.mat4()) + } } #[derive(Debug, Clone)] @@ -567,11 +578,15 @@ impl MurreletPath { } } - pub fn transform_with(&self, t: &T) -> Self { + pub fn transform_with(&self, t: &T) -> Self { match self { MurreletPath::Polyline(x) => MurreletPath::Polyline(x.transform_with(t)), - MurreletPath::Curve(_) => todo!(), - MurreletPath::Svg(_) => todo!(), + MurreletPath::Curve(cd) => { + MurreletPath::Svg(cd.to_svg().transform_with_mat4_after(t.change_to_mat4())) + } + MurreletPath::Svg(svg) => { + MurreletPath::Svg(svg.transform_with_mat4_after(t.change_to_mat4())) + } MurreletPath::MaskedCurve(_, _) => todo!(), } } @@ -669,7 +684,7 @@ impl StyledPath { } } - pub fn transform_path(&self, t: &T) -> Self { + pub fn transform_path(&self, t: &T) -> Self { StyledPath { path: self.path.transform_with(t), ..self.clone() diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index dcb5824..04ed941 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -4,8 +4,11 @@ use glam::{Mat4, Vec2}; use lerpable::Lerpable; use lyon::geom::{euclid::Point2D, Point}; use murrelet_gui::MurreletGUI; +use murrelet_livecode::types::LivecodeResult; use murrelet_livecode_derive::Livecode; +use crate::curve_drawer::{CurveArc, CurveDrawer}; + #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] pub struct SvgRect { #[livecode(serde_default = "0")] @@ -63,6 +66,10 @@ impl SvgTo { pub fn params(&self) -> (f32, f32) { (self.x, self.y) } + + pub fn to(&self) -> Vec2 { + (self.x, self.y).into() + } } #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] @@ -75,6 +82,10 @@ pub struct SvgCubicBezier { to_y: f32, } impl SvgCubicBezier { + pub fn to(&self) -> Vec2 { + (self.to_x, self.to_y).into() + } + pub fn params(&self) -> (f32, f32, f32, f32, f32, f32) { ( self.ctrl1_x, @@ -87,10 +98,83 @@ impl SvgCubicBezier { } } +#[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] +pub struct SvgArc { + radius_x: f32, + radius_y: f32, + x_axis_rotation: f32, + large_arc_flag: f32, + sweep_flag: f32, + to_x: f32, + to_y: f32, +} + +impl SvgArc { + pub fn to(&self) -> Vec2 { + (self.to_x, self.to_y).into() + } + + pub fn new( + radius_x: f32, + radius_y: f32, + x_axis_rotation: f32, + large_arc_flag: f32, + sweep_flag: f32, + to_x: f32, + to_y: f32, + ) -> Self { + Self { + radius_x, + radius_y, + x_axis_rotation, + large_arc_flag, + sweep_flag, + to_x, + to_y, + } + } + + pub fn from_arc(a: &CurveArc) -> Self { + let last_point = a.last_point(); + + SvgArc::new( + a.radius, + a.radius, // same as other rad because it's a circle + 0.0, // angle of ellipse doesn't matter, so 0 + if a.is_large_arc() { 1.0 } else { 0.0 }, // large arc flag + if a.is_ccw() { 1.0 } else { 0.0 }, // sweep-flag + last_point.x, + last_point.y, + ) + } + + pub fn params(&self) -> (f32, f32, f32, f32, f32, f32, f32) { + ( + self.radius_x, + self.radius_y, + self.x_axis_rotation, + self.large_arc_flag, + self.sweep_flag, + self.to_x, + self.to_y, + ) + } +} + #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] pub enum SvgCmd { Line(SvgTo), CubicBezier(SvgCubicBezier), + ArcTo(SvgArc), +} +impl SvgCmd { + fn to(&self) -> Vec2{ + match self { + SvgCmd::Line(svg_to) => svg_to.to(), + SvgCmd::CubicBezier(svg_cubic_bezier) => svg_cubic_bezier.to(), + SvgCmd::ArcTo(svg_arc) => svg_arc.to(), + } + } } #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] @@ -100,6 +184,11 @@ pub struct SvgPathDef { } impl SvgPathDef { + + pub fn start(&self) -> Vec2 { + (self.start.x, self.start.y).into() + } + pub fn new(start: Vec2) -> Self { Self { start: SvgTo { @@ -114,6 +203,12 @@ impl SvgPathDef { self.v.push(SvgCmd::Line(SvgTo { x: to.x, y: to.y })); } + fn add_curve_points(&mut self, curve_points: &crate::curve_drawer::CurvePoints) { + for c in curve_points.points() { + self.add_line(*c); + } + } + pub fn add_cubic_bezier(&mut self, ctrl1: Vec2, ctrl2: Vec2, to: Vec2) { self.v.push(SvgCmd::CubicBezier(SvgCubicBezier { ctrl1_x: ctrl1.x, @@ -125,6 +220,18 @@ impl SvgPathDef { })) } + fn add_curve_bezier(&mut self, curve_cubic_bezier: &crate::curve_drawer::CurveCubicBezier) { + self.add_cubic_bezier( + curve_cubic_bezier.ctrl1(), + curve_cubic_bezier.ctrl2(), + curve_cubic_bezier.to(), + ); + } + + fn add_curve_arc(&mut self, curve_arc: &CurveArc) { + self.v.push(SvgCmd::ArcTo(SvgArc::from_arc(curve_arc))); + } + pub fn svg_move_to(&self) -> (f32, f32) { (self.start.x, self.start.y) } @@ -132,6 +239,35 @@ impl SvgPathDef { pub fn cmds(&self) -> &[SvgCmd] { &self.v } + + fn from_cd(cd: &CurveDrawer) -> SvgPathDef { + // if it's a weird cd, then just draw a dot at 0, 0 and be done... + // probably want to do better error handling here + let mut curr = cd.first_point().unwrap_or_default(); + let mut path = SvgPathDef::new(curr); + + for s in cd.segments() { + if curr != s.first_point() { + path.add_line(s.first_point()); + } + + match s { + crate::curve_drawer::CurveSegment::Arc(curve_arc) => { + path.add_curve_arc(curve_arc); + } + crate::curve_drawer::CurveSegment::Points(curve_points) => { + path.add_curve_points(curve_points); + } + crate::curve_drawer::CurveSegment::CubicBezier(curve_cubic_bezier) => { + path.add_curve_bezier(curve_cubic_bezier); + } + } + + curr = s.last_point(); + } + + path + } } #[allow(dead_code)] @@ -198,6 +334,15 @@ impl TransformedSvgShape { } } + pub fn from_cd(cd: &CurveDrawer) -> Self { + let shape = SvgShape::Path(SvgPathDef::from_cd(&cd)); + + Self { + shape, + t: Mat4::IDENTITY, + } + } + pub fn transform_with_mat4_after(&self, t: Mat4) -> Self { Self { shape: self.shape.clone(), @@ -212,3 +357,34 @@ impl TransformedSvgShape { } } } + +#[cfg(test)] +mod tests { + use glam::vec2; + use murrelet_common::AnglePi; + + use super::*; + + #[test] + fn test_curve_drawer_to_svg_to_curve_drawer_arc() { + let cd = CurveDrawer::new_simple_arc(Vec2::new(20.0, 20.0), 5.0, AnglePi::new(-1.0), AnglePi::new(0.0)); + + let first_point_before = cd.first_point().unwrap(); + let last_point_before = cd.last_point().unwrap(); + + assert_eq!(first_point_before, vec2(15.0, 20.0)); + assert_eq!(last_point_before, vec2(25.0, 20.0)); + + // Convert to SvgPathDef + let svg_path = SvgPathDef::from_cd(&cd); + + assert_eq!(svg_path.start(), first_point_before); + let cmds = svg_path.cmds().to_vec(); + assert_eq!(cmds.len(), 1); + + + assert_eq!(cmds[0].to(), last_point_before); + + + } +} diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 68d55a8..adc9fe1 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -763,6 +763,10 @@ pub fn parse_svg_path_as_vec2(data: &SvgPathDef, tolerance: f32) -> Vec { let (a, b, c, d, e, f) = svg_cubic_bezier.params(); cmds = cmds.cubic_curve_to(vec![a, b, c, d, e, f]); } + crate::svg::SvgCmd::ArcTo(svg_arc) => { + let (a, b, c, d, e, f, g) = svg_arc.params(); + cmds = cmds.elliptical_arc_to(vec![a, b, c, d, e, f, g]); + }, } } diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index 5223044..bf4517c 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -11,6 +11,28 @@ use murrelet_common::{ }; use murrelet_livecode_derive::Livecode; +pub trait ToMat4 { + fn change_to_mat4(&self) -> Mat4; +} + +impl ToMat4 for Transform2d { + fn change_to_mat4(&self) -> Mat4 { + self.to_mat4() + } +} + +impl ToMat4 for SimpleTransform2d { + fn change_to_mat4(&self) -> Mat4 { + self.to_mat4() + } +} + +impl ToMat4 for Mat4 { + fn change_to_mat4(&self) -> Mat4 { + self.clone() + } +} + #[derive(Clone, Debug, Livecode, Lerpable, Default)] pub struct Transform2d(Vec); impl Transform2d { diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 13f6b64..e9614a3 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -133,8 +133,8 @@ impl ToStyledGroup for T { impl ToSvgData for MurreletCurve { fn to_svg_data(&self) -> Option { - // self.curve().to_svg_data() - todo!() + self.curve().to_svg_data() + // todo!() } fn transform(&self) -> Option { @@ -866,6 +866,9 @@ impl ToSvgData for SvgPathDef { murrelet_draw::svg::SvgCmd::CubicBezier(svg_cubic_bezier) => { path = path.cubic_curve_to(svg_cubic_bezier.params()) } + murrelet_draw::svg::SvgCmd::ArcTo(svg_arc) => { + path = path.elliptical_arc_to(svg_arc.params()) + } } } @@ -893,7 +896,26 @@ impl ToSvgData for CurveDrawer { } match curve { - murrelet_draw::curve_drawer::CurveSegment::CubicBezier(_) => {todo!()} + murrelet_draw::curve_drawer::CurveSegment::CubicBezier(cb) => { + let f = cb.first_point(); + // first make sure we're at the first point + if curr_point != Some(f) { + path = path.line_to((f.x, f.y)); + } + + let params = ( + cb.ctrl1().x, + cb.ctrl1().y, + cb.ctrl2().x, + cb.ctrl2().y, + cb.to().x, + cb.to().y, + ); + + path = path.cubic_curve_to(params); + + curr_point = Some(cb.to()) + } murrelet_draw::curve_drawer::CurveSegment::Arc(a) => { let f = a.first_point(); // first make sure we're at the first point From 2fa3efd299f25024e8a8538e21e57c77bb551910 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 3 Aug 2025 14:54:36 -0400 Subject: [PATCH 059/149] wip --- murrelet_common/src/geometry.rs | 4 ++ murrelet_draw/src/curve_drawer.rs | 65 ++++++++++++++++++++++++++++++- murrelet_draw/src/svg.rs | 38 +++++++++--------- murrelet_svg/src/svg.rs | 39 +++++++++++++------ 4 files changed, 113 insertions(+), 33 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 417de8b..c92b154 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -629,6 +629,10 @@ impl LineFromVecAndLen { pub fn to_last_point(&self) -> Vec2 { self.start + self.length.len() * self.angle.to_norm_dir() } + + pub fn to_vec(&self) -> Vec { + vec![self.start, self.to_last_point()] + } } // more curve things diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 4f60ab2..8cfeffd 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -17,6 +17,13 @@ impl CurveDrawer { Self { segments, closed } } + pub fn new_closed(segments: Vec) -> Self { + Self { + segments, + closed: true, + } + } + pub fn is_closed(&self) -> bool { self.closed } @@ -224,7 +231,17 @@ impl CurveSegment { let a = CurveArc::new(arc.loc, arc.radius, arc.start_pi, arc.end_pi); SpotOnCurve::new(a.last_point(), a.end_tangent_angle()) } - CurveSegment::Points(_) => todo!(), + CurveSegment::Points(p) => { + if p.points().len() >= 2 { + let points = p.points(); + let end = *points.last().unwrap(); + let prev = *points.get(points.len() - 2).unwrap(); + let angle = PointToPoint::new(prev, end).angle().perp_to_left(); + SpotOnCurve::new(end, angle) + } else { + unimplemented!() // need to look at the val before... + } + } CurveSegment::CubicBezier(c) => c.to_cubic().end_to_tangent().0, } } @@ -290,7 +307,18 @@ impl CurveArc { // useful for svg pub fn is_large_arc(&self) -> bool { - (self.end_pi.angle_pi() - self.start_pi.angle_pi()).abs() > 1.0 + // (self.end_pi.angle_pi() - self.start_pi.angle_pi()).abs() > 1.0 + // (self.end_pi.angle_pi() - self.start_pi.angle_pi()).rem_euclid(2.0) > 1.0 + + let ccw_angular_distance = + (self.end_pi.angle_pi() - self.start_pi.angle_pi()).rem_euclid(2.0); + + if self.is_ccw() { + ccw_angular_distance > 1.0 + } else { + // for CW, the distance is the other way around the circle + (2.0 - ccw_angular_distance) > 1.0 + } } pub fn last_point(&self) -> Vec2 { @@ -337,6 +365,39 @@ impl CurveArc { m.radius = radius; m } + + pub fn svg_params(&self) -> [f32; 7] { + // assumes you've already moved to first_point + let last_point = self.last_point(); + [ + self.radius, + self.radius, // same as other rad because it's a circle + 0.0, // angle of ellipse doesn't matter, so 0 + if self.is_large_arc() { 1.0 } else { 0.0 }, // large arc flag + if self.is_ccw() { 1.0 } else { 0.0 }, // sweep-flag + last_point.x, + last_point.y, + ] + } + + pub fn is_full_circle(&self) -> bool { + (self.end_pi - self.start_pi).angle_pi().abs() >= 0.0 + && (self.end_pi - self.start_pi).angle_pi().rem_euclid(2.0) < 1e-3f32 + } + + pub fn is_full_circle_then_split(&self) -> Option<(CurveArc, CurveArc)> { + if self.is_full_circle() { + let start_angle = self.start_angle(); + let mid_angle = self.start_angle() + (self.end_angle() - self.start_angle()).scale(0.5); + let end_angle = self.end_angle(); + + let semi_circle1 = CurveArc::new(self.loc, self.radius, start_angle, mid_angle); + let semi_circle2 = CurveArc::new(self.loc, self.radius, mid_angle, end_angle); + Some((semi_circle1, semi_circle2)) + } else { + None + } + } } #[derive(Debug, Clone, Livecode, Lerpable)] diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index 04ed941..08b3f1f 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -4,7 +4,6 @@ use glam::{Mat4, Vec2}; use lerpable::Lerpable; use lyon::geom::{euclid::Point2D, Point}; use murrelet_gui::MurreletGUI; -use murrelet_livecode::types::LivecodeResult; use murrelet_livecode_derive::Livecode; use crate::curve_drawer::{CurveArc, CurveDrawer}; @@ -135,17 +134,8 @@ impl SvgArc { } pub fn from_arc(a: &CurveArc) -> Self { - let last_point = a.last_point(); - - SvgArc::new( - a.radius, - a.radius, // same as other rad because it's a circle - 0.0, // angle of ellipse doesn't matter, so 0 - if a.is_large_arc() { 1.0 } else { 0.0 }, // large arc flag - if a.is_ccw() { 1.0 } else { 0.0 }, // sweep-flag - last_point.x, - last_point.y, - ) + let [a, b, c, d, e, f, g] = a.svg_params(); + SvgArc::new(a, b, c, d, e, f, g) } pub fn params(&self) -> (f32, f32, f32, f32, f32, f32, f32) { @@ -168,7 +158,7 @@ pub enum SvgCmd { ArcTo(SvgArc), } impl SvgCmd { - fn to(&self) -> Vec2{ + pub fn to(&self) -> Vec2 { match self { SvgCmd::Line(svg_to) => svg_to.to(), SvgCmd::CubicBezier(svg_cubic_bezier) => svg_cubic_bezier.to(), @@ -184,7 +174,6 @@ pub struct SvgPathDef { } impl SvgPathDef { - pub fn start(&self) -> Vec2 { (self.start.x, self.start.y).into() } @@ -229,9 +218,18 @@ impl SvgPathDef { } fn add_curve_arc(&mut self, curve_arc: &CurveArc) { - self.v.push(SvgCmd::ArcTo(SvgArc::from_arc(curve_arc))); + if let Some((hemi1, hemi2)) = curve_arc.is_full_circle_then_split() { + self.v.push(SvgCmd::ArcTo(SvgArc::from_arc(&hemi1))); + self.v.push(SvgCmd::ArcTo(SvgArc::from_arc(&hemi2))); + } else { + self.v.push(SvgCmd::ArcTo(SvgArc::from_arc(curve_arc))); + } } + // fn add_curve_arc(&mut self, curve_arc: &CurveArc) { + // self.v.push(SvgCmd::ArcTo(SvgArc::from_arc(curve_arc))); + // } + pub fn svg_move_to(&self) -> (f32, f32) { (self.start.x, self.start.y) } @@ -367,7 +365,12 @@ mod tests { #[test] fn test_curve_drawer_to_svg_to_curve_drawer_arc() { - let cd = CurveDrawer::new_simple_arc(Vec2::new(20.0, 20.0), 5.0, AnglePi::new(-1.0), AnglePi::new(0.0)); + let cd = CurveDrawer::new_simple_arc( + Vec2::new(20.0, 20.0), + 5.0, + AnglePi::new(-1.0), + AnglePi::new(0.0), + ); let first_point_before = cd.first_point().unwrap(); let last_point_before = cd.last_point().unwrap(); @@ -382,9 +385,6 @@ mod tests { let cmds = svg_path.cmds().to_vec(); assert_eq!(cmds.len(), 1); - assert_eq!(cmds[0].to(), last_point_before); - - } } diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index e9614a3..6f9e5d7 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -922,20 +922,35 @@ impl ToSvgData for CurveDrawer { if curr_point != Some(f) { path = path.line_to((f.x, f.y)); } - let last_point = a.last_point(); - let params = ( - a.radius, - a.radius, // same as other rad because it's a circle - 0.0, // angle of ellipse doesn't matter, so 0 - if a.is_large_arc() { 1 } else { 0 }, // large arc flag - if a.is_ccw() { 1 } else { 0 }, // sweep-flag - last_point.x, - last_point.y, - ); - - path = path.elliptical_arc_to(params); + // special cases for circles! + if let Some((hemi1, hemi2)) = a.is_full_circle_then_split() { + // let params = ( + // a.radius, + // a.radius, // same as other rad because it's a circle + // 0.0, // angle of ellipse doesn't matter, so 0 + // if a.is_large_arc() { 1 } else { 0 }, // large arc flag + // if a.is_ccw() { 1 } else { 0 }, // sweep-flag + // last_point.x, + // last_point.y, + // ); + + path = path.elliptical_arc_to(hemi1.svg_params().to_vec()); + path = path.elliptical_arc_to(hemi2.svg_params().to_vec()); + } else { + // let params = ( + // a.radius, + // a.radius, // same as other rad because it's a circle + // 0.0, // angle of ellipse doesn't matter, so 0 + // if a.is_large_arc() { 1 } else { 0 }, // large arc flag + // if a.is_ccw() { 1 } else { 0 }, // sweep-flag + // last_point.x, + // last_point.y, + // ); + + path = path.elliptical_arc_to(a.svg_params().to_vec()); + } curr_point = Some(last_point) } From 9ff45790cc62c38630df4246099297ee7ccfdb59 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 4 Aug 2025 15:57:14 -0400 Subject: [PATCH 060/149] add murrelet schema --- Cargo.toml | 11 +- murrelet_schema/Cargo.toml | 21 ++ murrelet_schema/examples/tests.rs | 127 ++++++++++ murrelet_schema/src/lib.rs | 118 ++++++++++ murrelet_schema_derive/Cargo.toml | 19 ++ murrelet_schema_derive/src/derive_schema.rs | 186 +++++++++++++++ murrelet_schema_derive/src/lib.rs | 16 ++ murrelet_schema_derive/src/parser.rs | 244 ++++++++++++++++++++ 8 files changed, 739 insertions(+), 3 deletions(-) create mode 100644 murrelet_schema/Cargo.toml create mode 100644 murrelet_schema/examples/tests.rs create mode 100644 murrelet_schema/src/lib.rs create mode 100644 murrelet_schema_derive/Cargo.toml create mode 100644 murrelet_schema_derive/src/derive_schema.rs create mode 100644 murrelet_schema_derive/src/lib.rs create mode 100644 murrelet_schema_derive/src/parser.rs diff --git a/Cargo.toml b/Cargo.toml index a9118c9..0a7b5b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,13 @@ members = [ "murrelet_svg", "examples/murrelet_example", "examples/foolish_guillemot", - "tinylivecode" -, "murrelet_gui", "murrelet_gui_derive" -, "murrelet_gen", "murrelet_gen_derive"] + "tinylivecode", + "murrelet_schema", + "murrelet_schema_derive", + "murrelet_gui", + "murrelet_gui_derive", + "murrelet_gen", + "murrelet_gen_derive", +] resolver = "2" diff --git a/murrelet_schema/Cargo.toml b/murrelet_schema/Cargo.toml new file mode 100644 index 0000000..1b700f0 --- /dev/null +++ b/murrelet_schema/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "murrelet_schema" +version = "0.1.2" +edition = "2021" + +[features] +default = [] +glam = ["dep:glam"] + +[dependencies] +murrelet_schema_derive = "0.1.2" + +thiserror = "2.0.11" +serde = { version = "1.0.104", features = ["derive"] } +serde_json = "1.0.48" + +glam = { version = "0.23", optional = true } + +[[example]] +name = "tests" +path = "examples/tests.rs" diff --git a/murrelet_schema/examples/tests.rs b/murrelet_schema/examples/tests.rs new file mode 100644 index 0000000..6ceac73 --- /dev/null +++ b/murrelet_schema/examples/tests.rs @@ -0,0 +1,127 @@ +use std::collections::HashMap; + +use murrelet_schema::*; +use murrelet_schema_derive::MurreletSchema; + +#[derive(MurreletSchema)] +pub struct BasicTypes { + a_number: f32, + b_number: usize, + c_number: u64, + d_number: i32, + bool: bool, + something: Vec, + s: String, +} + +fn custom_func() -> MurreletSchema { + MurreletSchema::Val(MurreletPrimitive::Num) +} + +#[derive(MurreletSchema)] +pub struct OverridesAndRecursive { + a_number: f32, + something: Vec, + #[murrelet_schema(func = "custom_func")] + label: String, + #[murrelet_schema(kind = "skip")] + b: HashMap, +} + +#[derive(MurreletSchema)] +enum EnumTest { + A, + B(OverridesAndRecursive), +} + +#[derive(MurreletSchema)] +struct SimpleNewtype(f32); + +// // fn lerp_partial(&self, pct: T) -> Self { +// // SimpleNewtype(pct.lerp_pct() as f32) +// // } +// // } + +// // #[derive(Debug, Clone, MurreletUX)] + +fn main() { + // let b = BasicTypes{ + // a_number: 1.0, + // b_number: -10.0, + // }; + let test_val = BasicTypes::make_schema(); + + let basic_types_schema = MurreletSchema::Struct( + "BasicTypes".to_string(), + vec![ + ( + "a_number".to_owned(), + MurreletSchema::Val(MurreletPrimitive::Num), + ), + ( + "b_number".to_owned(), + MurreletSchema::Val(MurreletPrimitive::Num), + ), + ( + "c_number".to_owned(), + MurreletSchema::Val(MurreletPrimitive::Num), + ), + ( + "d_number".to_owned(), + MurreletSchema::Val(MurreletPrimitive::Num), + ), + ( + "bool".to_owned(), + MurreletSchema::Val(MurreletPrimitive::Bool), + ), + ( + "something".to_owned(), + MurreletSchema::list(MurreletSchema::Val(MurreletPrimitive::Num)), + ), + ( + "s".to_owned(), + MurreletSchema::Val(MurreletPrimitive::String), + ), + ], + ); + + assert_eq!(test_val, basic_types_schema); + + let test_val = OverridesAndRecursive::make_schema(); + + let overrides_and_recursive_schema = MurreletSchema::Struct( + "OverridesAndRecursive".to_string(), + vec![ + ( + "a_number".to_owned(), + MurreletSchema::Val(MurreletPrimitive::Num), + ), + ( + "something".to_owned(), + MurreletSchema::list(basic_types_schema), + ), + ( + "label".to_owned(), + MurreletSchema::Val(MurreletPrimitive::Num), + ), // make sure it calls the override + ("b".to_owned(), MurreletSchema::Skip), + ], + ); + assert_eq!(test_val, overrides_and_recursive_schema); + + let test_val = EnumTest::make_schema(); + + assert_eq!( + test_val, + MurreletSchema::Enum( + "EnumTest".to_string(), + vec![ + (MurreletEnumVal::Unit("A".to_owned())), + (MurreletEnumVal::Unnamed("B".to_owned(), overrides_and_recursive_schema)), + ], + false + ) + ); +} + +// fn main() {} diff --git a/murrelet_schema/src/lib.rs b/murrelet_schema/src/lib.rs new file mode 100644 index 0000000..da15724 --- /dev/null +++ b/murrelet_schema/src/lib.rs @@ -0,0 +1,118 @@ +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub enum MurreletPrimitive { + Bool, // should be a ControlBool + Num, // should be a ControlF32 + Color, // expected to give h s v a + Defs, // make a ctx node + Vec2, // arbitrary vec2, also see Coords + Vec3, // arbitrary vec3 + Style, // murrelet style + Angle, // angle pi + Coords, // global coords, so the user can click things + String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub enum MurreletEnumVal { + Unnamed(String, MurreletSchema), + Unit(String), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub enum MurreletSchema { + NewType(String, Box), + Struct(String, Vec<(String, MurreletSchema)>), + Enum(String, Vec, bool), + List(Box), + Val(MurreletPrimitive), + Skip, +} +impl MurreletSchema { + pub fn new_type(name: String, m: MurreletSchema) -> Self { + Self::NewType(name, Box::new(m)) + } + + pub fn list(m: MurreletSchema) -> Self { + Self::List(Box::new(m)) + } + + pub fn as_enum(&self) -> Option<&Vec> { + if let Self::Enum(_, v, _) = self { + Some(v) + } else { + None + } + } + + pub fn as_new_type(&self) -> Option<&Box> { + if let Self::NewType(_, v) = self { + Some(v) + } else { + None + } + } + + pub fn unwrap_to_struct_fields(self) -> Vec<(String, MurreletSchema)> { + match self { + MurreletSchema::Struct(_, items) => items, + _ => unreachable!("tried to flatten a struct that wasn't a struct"), + } + } +} + +// this should be on the Control version +pub trait CanMakeSchema: Sized { + fn make_schema() -> MurreletSchema; +} + +macro_rules! impl_can_make_schema_for_num { + ($ty:ty) => { + impl CanMakeSchema for $ty { + fn make_schema() -> MurreletSchema { + MurreletSchema::Val(MurreletPrimitive::Num) + } + } + }; +} + +impl_can_make_schema_for_num!(f32); +impl_can_make_schema_for_num!(f64); +impl_can_make_schema_for_num!(u32); +impl_can_make_schema_for_num!(u64); +impl_can_make_schema_for_num!(i32); +impl_can_make_schema_for_num!(i64); +impl_can_make_schema_for_num!(usize); + +impl CanMakeSchema for Vec { + fn make_schema() -> MurreletSchema { + MurreletSchema::List(Box::new(T::make_schema())) + } +} + +impl CanMakeSchema for String { + fn make_schema() -> MurreletSchema { + MurreletSchema::Val(MurreletPrimitive::String) + } +} + +impl CanMakeSchema for bool { + fn make_schema() -> MurreletSchema { + MurreletSchema::Val(MurreletPrimitive::Bool) + } +} + +#[cfg(feature = "glam")] +impl CanMakeSchema for glam::Vec2 { + fn make_schema() -> MurreletSchema { + MurreletSchema::Val(MurreletPrimitive::Vec2) + } +} + +#[cfg(feature = "glam")] +impl CanMakeSchema for glam::Vec3 { + fn make_schema() -> MurreletSchema { + MurreletSchema::Val(MurreletPrimitive::Vec3) + } +} diff --git a/murrelet_schema_derive/Cargo.toml b/murrelet_schema_derive/Cargo.toml new file mode 100644 index 0000000..da45749 --- /dev/null +++ b/murrelet_schema_derive/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "murrelet_schema_derive" +version = "0.1.2" +edition = "2021" + +[lib] +proc-macro = true + +[features] +debug_logging = ["log"] + +[dependencies] +syn = "2.0.15" +quote = "1.0.18" +proc-macro2 = "1.0.37" +darling = "0.20.3" + +log = { version = "0.4.25", optional = true } + diff --git a/murrelet_schema_derive/src/derive_schema.rs b/murrelet_schema_derive/src/derive_schema.rs new file mode 100644 index 0000000..65a28ac --- /dev/null +++ b/murrelet_schema_derive/src/derive_schema.rs @@ -0,0 +1,186 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::*; + +pub(crate) struct FieldTokensSchema { + pub(crate) for_make_schema: TokenStream2, +} +impl GenFinal for FieldTokensSchema { + // Something(f32) + fn make_newtype_struct_final( + idents: ParsedFieldIdent, + variants: Vec, + ) -> TokenStream2 { + let name = idents.name; + let name_str = name.to_string(); + + let for_make_schema = variants.iter().map(|x| x.for_make_schema.clone()); + + quote! { + impl murrelet_schema::CanMakeSchema for #name { + fn make_schema() -> murrelet_schema::MurreletSchema { + murrelet_schema::MurreletSchema::new_type(#name_str.to_owned(), #(#for_make_schema,)*) + } + } + } + } + + fn make_struct_final( + idents: ParsedFieldIdent, + variants: Vec, + ) -> TokenStream2 { + let name = idents.name; + let name_str = name.to_string(); + + let for_make_schema = variants.iter().map(|x| x.for_make_schema.clone()); + + quote! { + impl murrelet_schema::CanMakeSchema for #name { + fn make_schema() -> murrelet_schema::MurreletSchema { + + let mut v = vec![]; + #(#for_make_schema;)* + + murrelet_schema::MurreletSchema::Struct(#name_str.to_owned(), v) + } + + } + } + } + + fn make_enum_final( + idents: ParsedFieldIdent, + variants: Vec, + is_untagged: bool, + ) -> TokenStream2 { + let name = idents.name; + let name_str = name.to_string(); + + let for_make_schema = variants.iter().map(|x| x.for_make_schema.clone()); + + quote! { + impl murrelet_schema::CanMakeSchema for #name { + fn make_schema() -> murrelet_schema::MurreletSchema { + murrelet_schema::MurreletSchema::Enum(#name_str.to_owned(), vec![#(#for_make_schema,)*], #is_untagged) + } + + } + } + } + + fn from_override_enum(func: &str) -> FieldTokensSchema { + let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); + + let for_make_schema = quote! { + #method() + }; + + FieldTokensSchema { for_make_schema } + } + + fn from_newtype_struct(idents: StructIdents, _parent_ident: syn::Ident) -> FieldTokensSchema { + let ty = convert_vec_type(&idents.data.ty); + + let for_make_schema = quote! { + #ty::make_schema() + }; + + FieldTokensSchema { for_make_schema } + } + + // e.g. TileAxisLocs::V(TileAxisVs) + fn from_unnamed_enum(idents: EnumIdents) -> FieldTokensSchema { + let variant_ident = idents.data.ident; + let ty = convert_vec_type(&idents.data.fields.fields.first().unwrap().ty); + let variant_ident_str = variant_ident.to_string(); + + let for_make_schema = quote! { (murrelet_schema::MurreletEnumVal::Unnamed(#variant_ident_str.to_string(), #ty::make_schema())) }; + + FieldTokensSchema { for_make_schema } + } + + // e.g. TileAxis::Diag + fn from_unit_enum(idents: EnumIdents) -> FieldTokensSchema { + let variant_ident = idents.data.ident; + let variant_ident_str = variant_ident.to_string(); + + let for_make_schema = + quote! { murrelet_schema::MurreletEnumVal::Unit(#variant_ident_str.to_owned()) }; + + FieldTokensSchema { for_make_schema } + } + + // // s: String with reference + // fn from_name(idents: StructIdents) -> FieldTokensSchema { + // let field_name = idents.data.ident.unwrap().to_string(); + + // let name_reference = idents + // .data + // .reference + // .expect("from name called without a reference!"); + + // let is_main = idents.data.is_ref_def.unwrap_or(false); + + // let for_make_schema = quote! { v.push((#field_name.to_owned(), murrelet_schema::MurreletSchema::Val(murrelet_schema::Value::Name(#name_reference.to_owned(), #is_main)))) }; + + // FieldTokensSchema { for_make_schema } + // } + + // skip + fn from_noop_struct(idents: StructIdents) -> FieldTokensSchema { + let field_name = idents.data.ident.unwrap().to_string(); + + let for_make_schema = + quote! { v.push((#field_name.to_owned(), murrelet_schema::MurreletSchema::Skip)) }; + + FieldTokensSchema { for_make_schema } + } + + // f32, Vec2, etc + fn from_type_struct(idents: StructIdents) -> FieldTokensSchema { + let field_name = idents.data.ident.unwrap(); + let field_name_str = field_name.to_string(); + // to call a static function, we need to + let kind = convert_vec_type(&idents.data.ty); + + let is_flat = idents.data.flatten.unwrap_or(false); + + let for_make_schema = if is_flat { + quote! { v.extend(#kind::make_schema().unwrap_to_struct_fields().into_iter()) } + } else { + quote! { v.push((#field_name_str.to_owned(), #kind::make_schema())) } + }; + + FieldTokensSchema { for_make_schema } + } + + fn from_override_struct(idents: StructIdents, func: &str) -> FieldTokensSchema { + let field_name = idents.data.ident.unwrap(); + let field_name_str = field_name.to_string(); + + let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); + + let for_make_schema = quote! { v.push((#field_name_str.to_owned(), #method())) }; + + FieldTokensSchema { for_make_schema } + } +} + +// we need to use turbofish to call an associated function +fn convert_vec_type(ty: &syn::Type) -> TokenStream2 { + if let syn::Type::Path(type_path) = ty { + if let Some(last_segment) = type_path.path.segments.last() { + if last_segment.ident == "Vec" { + if let syn::PathArguments::AngleBracketed(angle_bracketed) = &last_segment.arguments + { + if let Some(inner_arg) = angle_bracketed.args.first() { + return quote! { Vec:: < #inner_arg > }; + } + } + } + } + } + + quote! { #ty } +} diff --git a/murrelet_schema_derive/src/lib.rs b/murrelet_schema_derive/src/lib.rs new file mode 100644 index 0000000..adf009d --- /dev/null +++ b/murrelet_schema_derive/src/lib.rs @@ -0,0 +1,16 @@ +extern crate proc_macro; + +use darling::FromDeriveInput; +use derive_schema::FieldTokensSchema; +use parser::{GenFinal, LivecodeReceiver}; +use proc_macro::TokenStream; + +mod derive_schema; +mod parser; + +#[proc_macro_derive(MurreletSchema, attributes(murrelet_schema))] +pub fn murrelet_livecode_derive_murrelet_gui(input: TokenStream) -> TokenStream { + let ast = syn::parse_macro_input!(input as syn::DeriveInput); + let ast_receiver = LivecodeReceiver::from_derive_input(&ast).unwrap(); + FieldTokensSchema::from_ast(ast_receiver).into() +} diff --git a/murrelet_schema_derive/src/parser.rs b/murrelet_schema_derive/src/parser.rs new file mode 100644 index 0000000..f7b9303 --- /dev/null +++ b/murrelet_schema_derive/src/parser.rs @@ -0,0 +1,244 @@ +use darling::{ast, FromDeriveInput, FromField, FromVariant}; +use proc_macro2::TokenStream as TokenStream2; + +#[derive(Debug)] +pub(crate) struct ParsedFieldIdent { + pub(crate) name: syn::Ident, +} + +// trait and helpers needed to parse a variety of objects +pub(crate) trait GenFinal +where + Self: Sized, +{ + fn from_newtype_struct(_idents: StructIdents, parent_ident: syn::Ident) -> Self; + fn from_unnamed_enum(idents: EnumIdents) -> Self; + fn from_unit_enum(idents: EnumIdents) -> Self; + fn from_noop_struct(idents: StructIdents) -> Self; + // fn from_name(idents: StructIdents) -> Self; + fn from_type_struct(idents: StructIdents) -> Self; + // fn from_recurse_struct_vec(idents: StructIdents) -> Self; + + fn from_ast(ast_receiver: LivecodeReceiver) -> TokenStream2 { + match ast_receiver.data { + ast::Data::Enum(_) => Self::make_enum(&ast_receiver), + ast::Data::Struct(ast::Fields { + style: ast::Style::Tuple, + .. + }) => Self::make_newtype(&ast_receiver), + ast::Data::Struct(_) => Self::make_struct(&ast_receiver), + } + } + fn from_override_struct(idents: StructIdents, func: &str) -> Self; + fn from_override_enum(func: &str) -> Self; + + fn make_enum_final( + idents: ParsedFieldIdent, + variants: Vec, + is_untagged: bool, + ) -> TokenStream2; + fn make_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; + fn make_newtype_struct_final(idents: ParsedFieldIdent, variants: Vec) -> TokenStream2; + + fn make_struct(s: &LivecodeReceiver) -> TokenStream2 { + let name = s.ident.clone(); + + #[cfg(feature = "debug_logging")] + log::info!("{}::make_struct {}", Self::classname(), name.to_string()); + + // shouldn't be calling this with something that's not a struct.. + let fields = s.data.clone().take_struct().unwrap(); + + let livecodable_fields = fields + .iter() + .map(|field| { + let idents = StructIdents { + data: field.clone(), + }; + + match field.how_to_control_this() { + HowToControlThis::Skip => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_noop_struct"); + Self::from_noop_struct(idents) + } + // HowToControlThis::Name => { + // #[cfg(feature = "debug_logging")] + // log::info!("-> from_name"); + // Self::from_name(idents) + // } + HowToControlThis::SchemaType => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_type_struct"); + Self::from_type_struct(idents) + } + HowToControlThis::Override(func) => Self::from_override_struct(idents, &func), + } + }) + .collect::>(); + + let idents = ParsedFieldIdent { name: name.clone() }; + + Self::make_struct_final(idents, livecodable_fields) + } + + fn make_enum(e: &LivecodeReceiver) -> TokenStream2 { + let name = e.ident.clone(); + + #[cfg(feature = "debug_logging")] + log::info!("{}::make_enum {}", Self::classname(), name.to_string()); + + let variants = e.data.clone().take_enum().unwrap(); + + // just go through and find ones that wrap around a type, and make sure those types are + let variants = variants + .iter() + .map(|variant| { + let ident = EnumIdents { + // enum_name: name.clone(), + data: variant.clone(), + }; + + match variant.fields.style { + ast::Style::Tuple => Self::from_unnamed_enum(ident), + ast::Style::Struct => panic!("enum named fields not supported yet"), + ast::Style::Unit => Self::from_unit_enum(ident), + } + }) + .collect::>(); + + let idents = ParsedFieldIdent { name: name.clone() }; + + // "external" => quote! {}, + // "internal" => default, + // "untagged" => quote! {#[serde(untagged)]}, + // _ => default, + // let is_external = match &e.enum_tag.map(|x| x.as_str()) { + // Some("external") => true, + // None => false, + // _ => unimplemented!("enum type not implemented") + // }; + let is_untagged = if let Some(enum_tag) = &e.enum_tag { + if enum_tag.as_str() == "external" { + true + } else { + false + } + } else { + false + }; + + Self::make_enum_final(idents, variants, is_untagged) + } + + fn make_newtype(s: &LivecodeReceiver) -> TokenStream2 { + let name = s.ident.clone(); + + #[cfg(feature = "debug_logging")] + log::info!("{}::make_newtype {}", Self::classname(), name.to_string()); + + // shouldn't be calling this with something that's not a struct.. + let fields = s.data.clone().take_struct().unwrap(); + + let livecodable_fields = fields + .iter() + .map(|field| { + let idents = StructIdents { + data: field.clone(), + }; + + match field.how_to_control_this() { + HowToControlThis::SchemaType => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_newtype_struct"); + Self::from_newtype_struct(idents, name.clone()) + } + HowToControlThis::Skip => { + #[cfg(feature = "debug_logging")] + log::info!("-> from_newtype_recurse_struct_vec"); + Self::from_noop_struct(idents) + } + // HowToControlThis::Name => { + // #[cfg(feature = "debug_logging")] + // log::info!("-> from_name"); + // Self::from_name(idents) + // } + HowToControlThis::Override(func) => Self::from_override_enum(&func), + } + }) + .collect::>(); + + let idents = ParsedFieldIdent { name: name.clone() }; + + Self::make_newtype_struct_final(idents, livecodable_fields) + } +} + +#[derive(Debug, FromField, Clone)] +#[darling(attributes(murrelet_schema))] +pub(crate) struct LivecodeFieldReceiver { + pub(crate) ident: Option, + pub(crate) ty: syn::Type, + pub(crate) kind: Option, + pub(crate) func: Option, + pub(crate) flatten: Option, +} +impl LivecodeFieldReceiver { + fn how_to_control_this(&self) -> HowToControlThis { + if let Some(kind_val) = &self.kind { + if kind_val == "skip" { + HowToControlThis::Skip + // } else if kind_val == "reference" { + // HowToControlThis::Name + } else { + panic!("unexpected kind") + } + // } else if let Some(_) = &self.reference { + // HowToControlThis::Name + } else if let Some(func) = &self.func { + HowToControlThis::Override(func.to_owned()) + } else { + HowToControlThis::SchemaType + } + } +} + +// for enums +#[derive(Debug, FromVariant, Clone)] +#[darling(attributes(murrelet_schema))] +pub(crate) struct LivecodeVariantReceiver { + pub(crate) ident: syn::Ident, + pub(crate) fields: ast::Fields, +} + +#[derive(Debug, Clone, FromDeriveInput)] +#[darling(attributes(murrelet_schema), supports(any))] +pub(crate) struct LivecodeReceiver { + ident: syn::Ident, + data: ast::Data, + enum_tag: Option, +} +impl LivecodeReceiver {} + +// represents an enum +pub(crate) struct EnumIdents { + // pub(crate) enum_name: syn::Ident, + pub(crate) data: LivecodeVariantReceiver, +} + +impl EnumIdents {} + +#[derive(Clone, Debug)] +pub struct StructIdents { + pub(crate) data: LivecodeFieldReceiver, +} +impl StructIdents {} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) enum HowToControlThis { + Skip, // just do the default values + SchemaType, // build a gui for this type + // GUIVec, // GUI for a list + // Name, // a referenced thing, + Override(String), +} From c1ed1981e3434bf95e397a354bc3934f6151746f Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 4 Aug 2025 16:00:01 -0400 Subject: [PATCH 061/149] fix gui tests --- murrelet_gui/Cargo.toml | 4 +- murrelet_gui/examples/tests.rs | 201 +++++++++++++++++---------------- murrelet_gui/out | 180 ----------------------------- 3 files changed, 107 insertions(+), 278 deletions(-) delete mode 100644 murrelet_gui/out diff --git a/murrelet_gui/Cargo.toml b/murrelet_gui/Cargo.toml index b77436b..e8b223c 100644 --- a/murrelet_gui/Cargo.toml +++ b/murrelet_gui/Cargo.toml @@ -8,7 +8,9 @@ default = [] glam = ["dep:glam"] [dependencies] -murrelet_gui_derive = { path = "../murrelet_gui_derive/" } +murrelet_gui_derive = "0.1.2" +murrelet_schema = "0.1.2" +murrelet_schema_derive = "0.1.2" thiserror = "2.0.11" serde = { version = "1.0.104", features = ["derive"] } diff --git a/murrelet_gui/examples/tests.rs b/murrelet_gui/examples/tests.rs index bd4f632..c4f60d3 100644 --- a/murrelet_gui/examples/tests.rs +++ b/murrelet_gui/examples/tests.rs @@ -1,99 +1,106 @@ -// use std::collections::HashMap; - -// use murrelet_gui::{CanMakeGUI, MurreletEnumValGUI, MurreletGUI, MurreletGUISchema, ValueGUI}; - -// #[derive(MurreletGUI)] -// pub struct BasicTypes { -// a_number: f32, -// b_number: usize, -// c_number: u64, -// d_number: i32, -// bool: bool, -// something: Vec, -// s: String, -// #[murrelet_gui(reference = "test")] -// referenced_string: String, -// } - -// fn custom_func() -> MurreletGUISchema { -// MurreletGUISchema::Val(ValueGUI::Num) -// } - -// #[derive(MurreletGUI)] -// pub struct OverridesAndRecursive { -// a_number: f32, -// something: Vec, -// #[murrelet_gui(func = "custom_func")] -// label: String, -// #[murrelet_gui(kind = "skip")] -// b: HashMap, -// } - -// #[derive(MurreletGUI)] -// enum EnumTest { -// A, -// B(OverridesAndRecursive), -// } - -// #[derive(MurreletGUI)] -// struct SimpleNewtype(f32); - -// // fn lerp_partial(&self, pct: T) -> Self { -// // SimpleNewtype(pct.lerp_pct() as f32) -// // } -// // } - -// // #[derive(Debug, Clone, MurreletUX)] - -// fn main() { -// // let b = BasicTypes{ -// // a_number: 1.0, -// // b_number: -10.0, -// // }; -// let test_val = BasicTypes::make_gui(); - -// let basic_types_schema = MurreletGUISchema::Struct(vec![ -// ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), -// ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), -// ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), -// ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), -// ("bool".to_owned(), MurreletGUISchema::Val(ValueGUI::Bool)), -// ( -// "something".to_owned(), -// MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), -// ), -// ("s".to_owned(), MurreletGUISchema::Skip), -// ( -// "referenced_string".to_owned(), -// MurreletGUISchema::Val(ValueGUI::Name("test".to_owned())), -// ), -// ]); - -// assert_eq!(test_val, basic_types_schema); - -// let test_val = OverridesAndRecursive::make_gui(); - -// let overrides_and_recursive_schema = MurreletGUISchema::Struct(vec![ -// ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), -// ( -// "something".to_owned(), -// MurreletGUISchema::list(basic_types_schema), -// ), -// ("label".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), // make sure it calls the override -// ("b".to_owned(), MurreletGUISchema::Skip), -// ]); -// assert_eq!(test_val, overrides_and_recursive_schema); - -// let test_val = EnumTest::make_gui(); - -// assert_eq!( -// test_val, -// MurreletGUISchema::Enum(vec![ -// (MurreletEnumValGUI::Unit("A".to_owned())), -// (MurreletEnumValGUI::Unnamed("B".to_owned(), overrides_and_recursive_schema)), -// ]) -// ); +use std::collections::HashMap; + +use murrelet_gui::{CanMakeGUI, MurreletEnumValGUI, MurreletGUI, MurreletGUISchema, ValueGUI}; + +#[derive(MurreletGUI)] +pub struct BasicTypes { + a_number: f32, + b_number: usize, + c_number: u64, + d_number: i32, + bool: bool, + something: Vec, + s: String, + #[murrelet_gui(reference = "test")] + referenced_string: String, +} + +fn custom_func() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Num) +} + +#[derive(MurreletGUI)] +pub struct OverridesAndRecursive { + a_number: f32, + something: Vec, + #[murrelet_gui(func = "custom_func")] + label: String, + #[murrelet_gui(kind = "skip")] + b: HashMap, +} + +#[derive(MurreletGUI)] +enum EnumTest { + A, + B(OverridesAndRecursive), +} + +#[derive(MurreletGUI)] +struct SimpleNewtype(f32); + +// fn lerp_partial(&self, pct: T) -> Self { +// SimpleNewtype(pct.lerp_pct() as f32) +// } // } - -fn main() {} \ No newline at end of file +// #[derive(Debug, Clone, MurreletUX)] + +fn main() { + // let b = BasicTypes{ + // a_number: 1.0, + // b_number: -10.0, + // }; + let test_val = BasicTypes::make_gui(); + + let basic_types_schema = MurreletGUISchema::Struct( + "BasicTypes".to_string(), + vec![ + ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("bool".to_owned(), MurreletGUISchema::Val(ValueGUI::Bool)), + ( + "something".to_owned(), + MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), + ), + ("s".to_owned(), MurreletGUISchema::Skip), + ( + "referenced_string".to_owned(), + MurreletGUISchema::Val(ValueGUI::Name("test".to_owned(), false)), + ), + ], + ); + + assert_eq!(test_val, basic_types_schema); + + let test_val = OverridesAndRecursive::make_gui(); + + let overrides_and_recursive_schema = MurreletGUISchema::Struct( + "OverridesAndRecursive".to_string(), + vec![ + ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ( + "something".to_owned(), + MurreletGUISchema::list(basic_types_schema), + ), + ("label".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), // make sure it calls the override + ("b".to_owned(), MurreletGUISchema::Skip), + ], + ); + assert_eq!(test_val, overrides_and_recursive_schema); + + let test_val = EnumTest::make_gui(); + + assert_eq!( + test_val, + MurreletGUISchema::Enum( + "EnumTest".to_string(), + vec![ + (MurreletEnumValGUI::Unit("A".to_owned())), + (MurreletEnumValGUI::Unnamed("B".to_owned(), overrides_and_recursive_schema)), + ], + false + ) + ); +} diff --git a/murrelet_gui/out b/murrelet_gui/out deleted file mode 100644 index a9bdc5a..0000000 --- a/murrelet_gui/out +++ /dev/null @@ -1,180 +0,0 @@ -#![feature(prelude_import)] -#[prelude_import] -use std::prelude::rust_2021::*; -#[macro_use] -extern crate std; -use std::collections::HashMap; -use murrelet_gui::{ - CanMakeGUI, MurreletEnumValGUI, MurreletGUI, MurreletGUISchema, ValueGUI, -}; -pub struct BasicTypes { - a_number: f32, - b_number: usize, - c_number: u64, - d_number: i32, - bool: bool, - something: Vec, - s: String, - #[murrelet_gui(reference = "test")] - referenced_string: String, -} -impl murrelet_gui::CanMakeGUI for BasicTypes { - fn make_gui() -> murrelet_gui::MurreletGUISchema { - let mut v = ::alloc::vec::Vec::new(); - v.push(("a_number".to_owned(), f32::make_gui())); - v.push(("b_number".to_owned(), usize::make_gui())); - v.push(("c_number".to_owned(), u64::make_gui())); - v.push(("d_number".to_owned(), i32::make_gui())); - v.push(("bool".to_owned(), bool::make_gui())); - v.push(("something".to_owned(), Vec::::make_gui())); - v.push(("s".to_owned(), String::make_gui())); - v.push(( - "referenced_string".to_owned(), - murrelet_gui::MurreletGUISchema::Val( - murrelet_gui::ValueGUI::Name("test".to_owned(), false), - ), - )); - murrelet_gui::MurreletGUISchema::Struct("BasicTypes".to_owned(), v) - } -} -fn custom_func() -> MurreletGUISchema { - MurreletGUISchema::Val(ValueGUI::Num) -} -pub struct OverridesAndRecursive { - a_number: f32, - something: Vec, - #[murrelet_gui(func = "custom_func")] - label: String, - #[murrelet_gui(kind = "skip")] - b: HashMap, -} -impl murrelet_gui::CanMakeGUI for OverridesAndRecursive { - fn make_gui() -> murrelet_gui::MurreletGUISchema { - let mut v = ::alloc::vec::Vec::new(); - v.push(("a_number".to_owned(), f32::make_gui())); - v.push(("something".to_owned(), Vec::::make_gui())); - v.push(("label".to_owned(), custom_func())); - v.push(("b".to_owned(), murrelet_gui::MurreletGUISchema::Skip)); - murrelet_gui::MurreletGUISchema::Struct("OverridesAndRecursive".to_owned(), v) - } -} -enum EnumTest { - A, - B(OverridesAndRecursive), -} -impl murrelet_gui::CanMakeGUI for EnumTest { - fn make_gui() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::Enum( - "EnumTest".to_owned(), - <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([ - murrelet_gui::MurreletEnumValGUI::Unit("A".to_owned()), - (murrelet_gui::MurreletEnumValGUI::Unnamed( - "B".to_string(), - OverridesAndRecursive::make_gui(), - )), - ]), - ), - ) - } -} -struct SimpleNewtype(f32); -impl murrelet_gui::CanMakeGUI for SimpleNewtype { - fn make_gui() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::new_type( - "SimpleNewtype".to_owned(), - f32::make_gui(), - ) - } -} -fn main() { - let test_val = BasicTypes::make_gui(); - let basic_types_schema = MurreletGUISchema::Struct( - <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([ - ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ("bool".to_owned(), MurreletGUISchema::Val(ValueGUI::Bool)), - ( - "something".to_owned(), - MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), - ), - ("s".to_owned(), MurreletGUISchema::Skip), - ( - "referenced_string".to_owned(), - MurreletGUISchema::Val(ValueGUI::Name("test".to_owned())), - ), - ]), - ), - ); - match (&test_val, &basic_types_schema) { - (left_val, right_val) => { - if !(*left_val == *right_val) { - let kind = ::core::panicking::AssertKind::Eq; - ::core::panicking::assert_failed( - kind, - &*left_val, - &*right_val, - ::core::option::Option::None, - ); - } - } - }; - let test_val = OverridesAndRecursive::make_gui(); - let overrides_and_recursive_schema = MurreletGUISchema::Struct( - <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([ - ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ("something".to_owned(), MurreletGUISchema::list(basic_types_schema)), - ("label".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), - ("b".to_owned(), MurreletGUISchema::Skip), - ]), - ), - ); - match (&test_val, &overrides_and_recursive_schema) { - (left_val, right_val) => { - if !(*left_val == *right_val) { - let kind = ::core::panicking::AssertKind::Eq; - ::core::panicking::assert_failed( - kind, - &*left_val, - &*right_val, - ::core::option::Option::None, - ); - } - } - }; - let test_val = EnumTest::make_gui(); - match ( - &test_val, - &MurreletGUISchema::Enum( - <[_]>::into_vec( - #[rustc_box] - ::alloc::boxed::Box::new([ - (MurreletEnumValGUI::Unit("A".to_owned())), - (MurreletEnumValGUI::Unnamed( - "B".to_owned(), - overrides_and_recursive_schema, - )), - ]), - ), - ), - ) { - (left_val, right_val) => { - if !(*left_val == *right_val) { - let kind = ::core::panicking::AssertKind::Eq; - ::core::panicking::assert_failed( - kind, - &*left_val, - &*right_val, - ::core::option::Option::None, - ); - } - } - }; -} From 5dc1ae7d8450650524d08d5e8806136970d6c333 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 4 Aug 2025 23:12:08 -0400 Subject: [PATCH 062/149] swap dependency ordering of gui and common --- murrelet_common/Cargo.toml | 2 +- murrelet_common/src/color.rs | 12 ++--- murrelet_common/src/lib.rs | 11 ----- murrelet_draw/Cargo.toml | 2 +- murrelet_draw/src/compass.rs | 1 + murrelet_gui/Cargo.toml | 7 +++ murrelet_gui/examples/tests.rs | 2 + murrelet_gui/src/lib.rs | 85 ++++++++++++++++++++++++++++++++++ murrelet_schema/Cargo.toml | 2 + murrelet_schema/src/lib.rs | 57 +++++++++++++++++++++++ 10 files changed, 162 insertions(+), 19 deletions(-) diff --git a/murrelet_common/Cargo.toml b/murrelet_common/Cargo.toml index 45fdfb6..a7c40c0 100644 --- a/murrelet_common/Cargo.toml +++ b/murrelet_common/Cargo.toml @@ -14,7 +14,7 @@ palette = "0.7.6" rand = "0.8" num-traits = "0.2.19" lerpable = "0.0.2" -murrelet_gui = { version = "0.1.2", features = ["glam"] } +# murrelet_gui = { version = "0.1.2", features = ["glam"] } serde = { version = "1.0.104", features = ["derive"] } bytemuck = { version = "1.15", features = ["derive"] } diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index db67ccc..79c41d4 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -4,7 +4,7 @@ use std::{ }; use lerpable::{IsLerpingMethod, Lerpable}; -use murrelet_gui::CanMakeGUI; +// use murrelet_gui::CanMakeGUI; use palette::{rgb::Rgb, FromColor, Hsva, IntoColor, LinSrgb, LinSrgba, Srgb, Srgba, WithAlpha}; use crate::rgb_to_hex; @@ -155,11 +155,11 @@ impl MurreletColor { } } -impl CanMakeGUI for MurreletColor { - fn make_gui() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Color) - } -} +// impl CanMakeGUI for MurreletColor { +// fn make_gui() -> murrelet_gui::MurreletGUISchema { +// murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Color) +// } +// } pub trait MurreletIntoLinSrgba { fn into_murrelet_color(&self) -> MurreletColor; diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 9507c21..44792a4 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -922,17 +922,6 @@ pub fn lerpify_vec_vec3( lerped.into_iter().map(|v| v.into()).collect_vec() } -pub fn make_gui_vec2() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Vec2) -} - -pub fn make_gui_vec2_coords() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Coords) -} - -pub fn make_gui_vec3() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Vec3) -} #[derive(Clone, Copy, Debug)] pub struct Dim2d { diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index 534087b..813c399 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -38,7 +38,7 @@ md-5 = "0.10.6" hex = "0.4.3" schemars = { version = "0.8.21", optional = true } -murrelet_gui = { version = "0.1.2", features = ["glam"] } +murrelet_gui = { version = "0.1.2", features = ["glam", "murrelet"] } kurbo = "0.11" svg = "0.10.0" diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index a3e3bf4..1daf89c 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -3,6 +3,7 @@ use glam::Vec2; use lerpable::Lerpable; use murrelet_common::*; use murrelet_gui::MurreletGUI; +use murrelet_gui::make_gui_vec2; use murrelet_livecode_derive::Livecode; use crate::{ diff --git a/murrelet_gui/Cargo.toml b/murrelet_gui/Cargo.toml index e8b223c..6376820 100644 --- a/murrelet_gui/Cargo.toml +++ b/murrelet_gui/Cargo.toml @@ -6,18 +6,25 @@ edition = "2021" [features] default = [] glam = ["dep:glam"] +murrelet = ["dep:murrelet_common"] [dependencies] murrelet_gui_derive = "0.1.2" murrelet_schema = "0.1.2" murrelet_schema_derive = "0.1.2" +murrelet_common = { version = "0.1.2", optional = true } # used to derive types thiserror = "2.0.11" serde = { version = "1.0.104", features = ["derive"] } serde_json = "1.0.48" +itertools = "0.10.5" glam = { version = "0.23", optional = true } [[example]] name = "tests" path = "examples/tests.rs" + +[[example]] +name = "tests_schema" +path = "examples/tests_schema.rs" diff --git a/murrelet_gui/examples/tests.rs b/murrelet_gui/examples/tests.rs index c4f60d3..8870ad5 100644 --- a/murrelet_gui/examples/tests.rs +++ b/murrelet_gui/examples/tests.rs @@ -1,3 +1,5 @@ +mod tests_schema; + use std::collections::HashMap; use murrelet_gui::{CanMakeGUI, MurreletEnumValGUI, MurreletGUI, MurreletGUISchema, ValueGUI}; diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs index 9238744..394a75a 100644 --- a/murrelet_gui/src/lib.rs +++ b/murrelet_gui/src/lib.rs @@ -1,4 +1,8 @@ +use itertools::Itertools; +#[cfg(feature = "murrelet")] +use murrelet_common::MurreletColor; pub use murrelet_gui_derive::MurreletGUI; +use murrelet_schema::{MurreletEnumVal, MurreletPrimitive, MurreletSchema}; use serde::Serialize; // #[derive(Debug, Error)] @@ -21,6 +25,7 @@ pub enum ValueGUI { Style, // murrelet style Angle, // angle pi Coords, // global coords, so the user can click things + String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize)] @@ -125,3 +130,83 @@ impl CanMakeGUI for glam::Vec3 { MurreletGUISchema::Val(ValueGUI::Vec3) } } + +#[cfg(feature = "murrelet")] +impl CanMakeGUI for MurreletColor { + fn make_gui() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Color) + } +} + +#[cfg(feature = "murrelet")] +pub fn make_gui_vec2() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Vec2) +} + +#[cfg(feature = "murrelet")] +pub fn make_gui_vec2_coords() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Coords) +} + +#[cfg(feature = "murrelet")] +pub fn make_gui_vec3() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Vec3) +} + +// if you already have a schema, you can transform it +pub trait CanChangeToGUI: Sized { + fn change_to_gui(&self) -> MurreletGUISchema; +} + +impl CanChangeToGUI for MurreletSchema { + fn change_to_gui(&self) -> MurreletGUISchema { + match self { + MurreletSchema::NewType(name, murrelet_schema) => { + MurreletGUISchema::NewType(name.clone(), Box::new(murrelet_schema.change_to_gui())) + } + MurreletSchema::Struct(name, items) => MurreletGUISchema::Struct( + name.clone(), + items + .iter() + .map(|(k, v)| (k.clone(), v.change_to_gui())) + .collect::>(), + ), + MurreletSchema::Enum(name, items, b) => MurreletGUISchema::Enum( + name.clone(), + items.iter().map(|x| change_enum_to_gui(x)).collect_vec(), + *b, + ), + MurreletSchema::List(murrelet_schema) => { + MurreletGUISchema::List(Box::new(murrelet_schema.change_to_gui())) + } + MurreletSchema::Val(murrelet_primitive) => { + MurreletGUISchema::Val(change_primitive_to_gui(murrelet_primitive)) + } + MurreletSchema::Skip => MurreletGUISchema::Skip, + } + } +} + +fn change_enum_to_gui(a: &MurreletEnumVal) -> MurreletEnumValGUI { + match a { + MurreletEnumVal::Unnamed(a, murrelet_schema) => { + MurreletEnumValGUI::Unnamed(a.clone(), murrelet_schema.change_to_gui()) + } + MurreletEnumVal::Unit(a) => MurreletEnumValGUI::Unit(a.clone()), + } +} + +fn change_primitive_to_gui(a: &MurreletPrimitive) -> ValueGUI { + match a { + MurreletPrimitive::Bool => ValueGUI::Bool, + MurreletPrimitive::Num => ValueGUI::Num, + MurreletPrimitive::Color => ValueGUI::Color, + MurreletPrimitive::Defs => ValueGUI::Defs, + MurreletPrimitive::Vec2 => ValueGUI::Vec2, + MurreletPrimitive::Vec3 => ValueGUI::Vec3, + MurreletPrimitive::Style => ValueGUI::Style, + MurreletPrimitive::Angle => ValueGUI::Angle, + MurreletPrimitive::Coords => ValueGUI::Coords, + MurreletPrimitive::String => ValueGUI::String, + } +} diff --git a/murrelet_schema/Cargo.toml b/murrelet_schema/Cargo.toml index 1b700f0..4a3a7f9 100644 --- a/murrelet_schema/Cargo.toml +++ b/murrelet_schema/Cargo.toml @@ -9,7 +9,9 @@ glam = ["dep:glam"] [dependencies] murrelet_schema_derive = "0.1.2" +murrelet_common = "0.1.2" +anyhow = "1.0.86" thiserror = "2.0.11" serde = { version = "1.0.104", features = ["derive"] } serde_json = "1.0.48" diff --git a/murrelet_schema/src/lib.rs b/murrelet_schema/src/lib.rs index da15724..d567b3f 100644 --- a/murrelet_schema/src/lib.rs +++ b/murrelet_schema/src/lib.rs @@ -1,3 +1,4 @@ +use anyhow::{anyhow, Result}; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Eq, Serialize)] @@ -60,6 +61,62 @@ impl MurreletSchema { _ => unreachable!("tried to flatten a struct that wasn't a struct"), } } + + pub fn update_with_hints(&self, s: &std::collections::HashMap) -> Result { + // basically we go through s and then traverse until we find the spot we need to update + + let mut n = self.clone(); + + for (key, new_type) in s.iter() { + let location = key.split(".").collect::>(); + + let kind = match new_type.as_str() { + "Vec2" => Ok(MurreletPrimitive::Vec2), + _ => Result::Err(anyhow!(format!("unsupported schema hint, {}", new_type))), + }?; + + n.update_with_one_hint(&location, &kind)?; + } + + Ok(n) + } + + pub fn update_with_one_hint( + &mut self, + location: &[&str], + value: &MurreletPrimitive, + ) -> Result<()> { + // basically we go through s and then traverse until we find the spot we need to update + + match self { + MurreletSchema::NewType(_, murrelet_schema) => { + murrelet_schema.update_with_one_hint(location, value) + } + MurreletSchema::Struct(_, items) => { + if let Some((first_name, rest)) = location.split_first() { + let mut found_match = false; + for (name, schema) in items { + if name == first_name { + found_match = true; + schema.update_with_one_hint(rest, value)?; + break; + } + } + if !found_match { + Result::Err(anyhow!(format!("{} didn't match", first_name))) + } else { + Ok(()) + } + } else { + Result::Err(anyhow!("missing")) + } + }, + MurreletSchema::Enum(_, murrelet_enum_vals, _) => todo!(), + MurreletSchema::List(murrelet_schema) => todo!(), + MurreletSchema::Val(murrelet_primitive) => todo!(), + MurreletSchema::Skip => Result::Err(anyhow!("hm, trying to edit a skip")), + } + } } // this should be on the Control version From 35b90d325062e2f8c1ed9d65a80682e9f8065a3a Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 22 Aug 2025 18:05:34 -0400 Subject: [PATCH 063/149] wip --- examples/foolish_guillemot/Cargo.toml | 2 +- examples/murrelet_example/Cargo.toml | 2 +- murrelet/Cargo.toml | 2 +- murrelet_common/Cargo.toml | 2 +- murrelet_common/src/assets.rs | 27 +- murrelet_common/src/color.rs | 9 +- murrelet_common/src/idx.rs | 9 +- murrelet_common/src/lib.rs | 35 +- murrelet_draw/Cargo.toml | 2 + murrelet_draw/src/cubic.rs | 3 + murrelet_draw/src/curve_drawer.rs | 200 ++++++- murrelet_draw/src/draw.rs | 10 +- murrelet_draw/src/lib.rs | 2 + murrelet_draw/src/livecodetypes.rs | 3 +- murrelet_draw/src/newtypes.rs | 4 +- murrelet_draw/src/scaffold.rs | 55 ++ murrelet_draw/src/sequencers.rs | 59 ++- murrelet_draw/src/serialize.rs | 32 ++ murrelet_draw/src/style.rs | 6 +- murrelet_draw/src/svg.rs | 15 +- murrelet_draw/src/tesselate.rs | 143 ++++- murrelet_gen/Cargo.toml | 2 +- murrelet_gen/src/lib.rs | 4 + murrelet_gen_derive/src/gen_methods.rs | 487 ++++++++++-------- murrelet_gpu/Cargo.toml | 2 +- murrelet_gui/examples/tests_schema.rs | 107 ++++ murrelet_gui/src/lib.rs | 5 +- murrelet_livecode/Cargo.toml | 2 +- murrelet_livecode/src/app_src.rs | 9 +- murrelet_livecode/src/state.rs | 8 +- murrelet_livecode/src/types.rs | 33 +- .../murrelet_livecode_derive/Cargo.toml | 2 +- murrelet_perform/Cargo.toml | 2 +- murrelet_perform/src/asset_loader.rs | 27 +- murrelet_perform/src/perform.rs | 137 ++++- murrelet_schema/src/lib.rs | 46 +- murrelet_src_audio/Cargo.toml | 2 +- murrelet_svg/Cargo.toml | 2 +- murrelet_svg/src/svg.rs | 21 +- 39 files changed, 1216 insertions(+), 304 deletions(-) create mode 100644 murrelet_draw/src/scaffold.rs create mode 100644 murrelet_draw/src/serialize.rs create mode 100644 murrelet_gui/examples/tests_schema.rs diff --git a/examples/foolish_guillemot/Cargo.toml b/examples/foolish_guillemot/Cargo.toml index 9b5b1b6..ff5a52f 100644 --- a/examples/foolish_guillemot/Cargo.toml +++ b/examples/foolish_guillemot/Cargo.toml @@ -17,7 +17,7 @@ wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4.42" console_error_panic_hook = "0.1" -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } itertools = "0.10.5" serde = { version = "1.0.104", features = ["derive"] } diff --git a/examples/murrelet_example/Cargo.toml b/examples/murrelet_example/Cargo.toml index 9106257..dd5c524 100644 --- a/examples/murrelet_example/Cargo.toml +++ b/examples/murrelet_example/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Jessica Stringham "] repository = "https://github.com/jessstringham/murrelet.git" [dependencies] -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } itertools = "0.10.5" serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" diff --git a/murrelet/Cargo.toml b/murrelet/Cargo.toml index 97780f5..6d6506e 100644 --- a/murrelet/Cargo.toml +++ b/murrelet/Cargo.toml @@ -26,7 +26,7 @@ schemars = [ # ] [dependencies] -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } itertools = "0.10.5" serde = { version = "1.0.104", features = ["derive"] } diff --git a/murrelet_common/Cargo.toml b/murrelet_common/Cargo.toml index a7c40c0..bf16313 100644 --- a/murrelet_common/Cargo.toml +++ b/murrelet_common/Cargo.toml @@ -9,7 +9,7 @@ license = "AGPL-3.0-or-later" [dependencies] itertools = "0.10.5" -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } palette = "0.7.6" rand = "0.8" num-traits = "0.2.19" diff --git a/murrelet_common/src/assets.rs b/murrelet_common/src/assets.rs index 7ebf082..14efe06 100644 --- a/murrelet_common/src/assets.rs +++ b/murrelet_common/src/assets.rs @@ -104,16 +104,38 @@ impl RasterAssetLookup { } } +#[derive(Debug, Clone)] +pub struct JsonAssetLookup(HashMap); +impl JsonAssetLookup { + pub fn empty() -> Self { + Self(HashMap::new()) + } + + pub fn insert(&mut self, filename: String, img: String) { + self.0.insert(filename, img); + } +} + #[derive(Debug, Clone)] pub struct Assets { vectors: VectorLayersAssetLookup, #[allow(dead_code)] rasters: RasterAssetLookup, + #[allow(dead_code)] + json: JsonAssetLookup, } impl Assets { - pub fn new(vectors: VectorLayersAssetLookup, rasters: RasterAssetLookup) -> Self { - Self { vectors, rasters } + pub fn new( + vectors: VectorLayersAssetLookup, + rasters: RasterAssetLookup, + json: JsonAssetLookup, + ) -> Self { + Self { + vectors, + rasters, + json, + } } pub fn empty_ref() -> AssetsRef { @@ -124,6 +146,7 @@ impl Assets { Self { vectors: VectorLayersAssetLookup::empty(), rasters: RasterAssetLookup::empty(), + json: JsonAssetLookup::empty() } } diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index 79c41d4..9d71925 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -6,12 +6,13 @@ use std::{ use lerpable::{IsLerpingMethod, Lerpable}; // use murrelet_gui::CanMakeGUI; use palette::{rgb::Rgb, FromColor, Hsva, IntoColor, LinSrgb, LinSrgba, Srgb, Srgba, WithAlpha}; +use serde::{Deserialize, Serialize}; use crate::rgb_to_hex; // hrm, color is confusing, so make a newtype around LinSrgba for all our color stuff // i need to double check i'm handling linear/not rgb right -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Default, Serialize, Deserialize)] pub struct MurreletColor([f32; 4]); // hsva impl fmt::Debug for MurreletColor { @@ -153,6 +154,12 @@ impl MurreletColor { let [r, g, b, _a] = self.into_rgba_components(); rgb_to_hex(r, g, b) } + + pub fn with_saturation(&self, sat: f32) -> MurreletColor { + let mut c = self.clone(); + c.0[1] = sat; + c + } } // impl CanMakeGUI for MurreletColor { diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 866f19d..85e2e5b 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -3,10 +3,11 @@ use glam::{vec2, Vec2}; use itertools::Itertools; use rand::{rngs::StdRng, Rng, SeedableRng}; +use serde::{Deserialize, Serialize}; use crate::lerp; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct IdxInRange { i: u64, total: u64, // the count @@ -108,7 +109,7 @@ impl IdxInRange { self.i } - fn is_last(&self) -> bool { + pub fn is_last(&self) -> bool { self.i == self.total - 1 } @@ -120,6 +121,9 @@ impl IdxInRange { pub fn s(&self, range: Vec2) -> f32 { lerp(range.x, range.y, self.pct()) } + pub fn scale(&self, start: f32, end: f32) -> f32 { + lerp(start, end, self.pct()) + } } #[derive(Debug, Clone, Copy)] @@ -210,7 +214,6 @@ impl IdxInRange2d { let cell_idx = vec2(self.i.i as f32, self.j.i as f32); let centering_offset = -0.5 * self.totals_vec2(); - cell_idx + vec2(0.5, 0.5) + centering_offset } diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 44792a4..7603182 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -483,6 +483,36 @@ pub struct LivecodeSrc { vs: Vec>, } +#[derive(Default, Debug, Clone)] +pub struct CustomVars(Option>); + +impl CustomVars { + pub fn new(hash_map: HashMap) -> Self { + Self(Some(hash_map)) + } + + pub fn to_exec_funcs(&self) -> Vec<(String, LivecodeValue)> { + if let Some(hm) = &self.0 { + let mut v = vec![]; + for (key, value) in hm.iter() { + v.push((key.clone(), LivecodeValue::float(*value))) + } + v + } else { + vec![] + } + } + + pub fn update(&mut self, new: &Self) { + // basically update add, you can never delete >:D + if let Some(o) = &new.0 { + self.0 + .get_or_insert_with(Default::default) + .extend(o.iter().map(|(k, v)| (k.clone(), *v))); + } + } +} + // what is sent from apps (like nannou) #[derive(Default)] pub struct MurreletAppInput { @@ -491,6 +521,7 @@ pub struct MurreletAppInput { pub mouse_position: Vec2, pub mouse_left_is_down: bool, pub elapsed_frames: u64, + pub custom_vars: CustomVars, } impl MurreletAppInput { @@ -507,6 +538,7 @@ impl MurreletAppInput { mouse_position, mouse_left_is_down, elapsed_frames, + custom_vars: CustomVars::default(), } } @@ -515,6 +547,7 @@ impl MurreletAppInput { mouse_position: Vec2, mouse_left_is_down: bool, elapsed_frames: u64, + custom_vars: HashMap, ) -> Self { Self { keys: None, @@ -522,6 +555,7 @@ impl MurreletAppInput { mouse_position, mouse_left_is_down, elapsed_frames, + custom_vars: CustomVars::new(custom_vars), } } @@ -922,7 +956,6 @@ pub fn lerpify_vec_vec3( lerped.into_iter().map(|v| v.into()).collect_vec() } - #[derive(Clone, Copy, Debug)] pub struct Dim2d { x: usize, // e.g. rows diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index 813c399..9b91914 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -43,3 +43,5 @@ murrelet_gui = { version = "0.1.2", features = ["glam", "murrelet"] } kurbo = "0.11" svg = "0.10.0" lyon = "0.17" +delaunator = "1.0.2" +geo = "0.29.0" \ No newline at end of file diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index 3d62393..3fe86c7 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -127,3 +127,6 @@ impl CubicBezier { } } } + + + diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 8cfeffd..60666bf 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -1,10 +1,177 @@ use glam::*; use itertools::Itertools; use lerpable::Lerpable; +use lyon::{geom::CubicBezierSegment, path::Path}; use murrelet_common::*; use murrelet_livecode_derive::*; +use serde::{Deserialize, Serialize}; +use svg::node::element::path::Data; +// use crate::serialize::serialize_vec2; + +use crate::{ + cubic::CubicBezier, + livecodetypes::anglepi::*, + newtypes::*, + svg::glam_to_lyon, + tesselate::{ + cubic_bezier_path_to_lyon, flatten_cubic_bezier_path, parse_svg_data_as_vec2, ToVecVec2, + }, +}; + +#[derive(Debug, Clone, Default, Livecode, Lerpable, Serialize, Deserialize)] +pub enum CubicOptionVec2 { + #[default] + None, + Some(Vec2Newtype), +} +impl CubicOptionVec2 { + pub fn or_last(&self, anchor: Vec2, last_ctrl2: Vec2) -> Vec2 { + match self { + CubicOptionVec2::None => anchor * 2.0 - last_ctrl2, + CubicOptionVec2::Some(vec2_newtype) => vec2_newtype.vec2(), + } + } + + pub fn none() -> Self { + Self::None + } + + pub fn some(v: Vec2) -> Self { + Self::Some(Vec2Newtype::new(v)) + } +} + +#[derive(Debug, Clone, Default, Livecode, Lerpable, Serialize, Deserialize)] +pub struct CubicBezierTo { + pub ctrl1: CubicOptionVec2, + #[lerpable(func = "lerpify_vec2")] + // #[serde(serialize_with = "serialize_vec2")] + pub ctrl2: Vec2, + // #[serde(serialize_with = "serialize_vec2")] + #[lerpable(func = "lerpify_vec2")] + pub to: Vec2, +} + +impl CubicBezierTo { + pub fn new(ctrl1o: Option, ctrl2: Vec2, to: Vec2) -> Self { + let ctrl1 = match ctrl1o { + Some(c) => CubicOptionVec2::some(c), + None => CubicOptionVec2::none(), + }; + Self { ctrl1, ctrl2, to } + } +} + +#[derive(Debug, Clone, Default, Livecode, Lerpable, Serialize, Deserialize)] +pub struct CubicBezierPath { + #[lerpable(func = "lerpify_vec2")] + pub from: Vec2, + #[lerpable(func = "lerpify_vec2")] + pub ctrl1: Vec2, + pub curves: Vec, + pub closed: bool, +} +impl CubicBezierPath { + pub fn new(from: Vec2, ctrl1: Vec2, curves: Vec, closed: bool) -> Self { + Self { + from, + ctrl1, + curves, + closed, + } + } + + fn to_vec2_count(&self, count: usize) -> Vec { + let len = self.to_cd().length(); + let line_space = len / count as f32; + + let svg = self.to_data(); + let path = parse_svg_data_as_vec2(&svg, line_space); + + // if let Some(a) = path.last() { + // if a.distance(self.to.yx()) > 1.0e-3 { + // path.push(self.to.yx()) + // } + // } + + path.into_iter().map(|x| vec2(x.y, x.x)).collect_vec() + } + + pub fn to_cd(&self) -> CurveDrawer { + let mut cs = vec![]; + for c in self.to_cubic() { + cs.push(CurveSegment::cubic(c)); + } + CurveDrawer::new(cs, self.closed) + } + + pub fn to_cubic(&self) -> Vec { + let mut svg = vec![]; + + let mut from = self.from; -use crate::{cubic::CubicBezier, livecodetypes::anglepi::*, tesselate::ToVecVec2}; + let mut last_ctrl1 = self.ctrl1; + + for s in &self.curves { + let ctrl1 = s.ctrl1.or_last(from, last_ctrl1); + svg.push(CubicBezier::new(from, ctrl1, s.ctrl2, s.to)); + last_ctrl1 = s.ctrl2; + from = s.to; + } + + if self.closed { + let ctrl1 = CubicOptionVec2::none().or_last(from, last_ctrl1); + let ctrl2 = CubicOptionVec2::none().or_last(self.from, self.ctrl1); + svg.push(CubicBezier::new(from, ctrl1, ctrl2, self.from)); + } + + svg + } + + pub fn to_data(&self) -> Data { + let mut svg = svg::node::element::path::Data::new(); + + let x = self.from.x; + let y = self.from.y; + let start: svg::node::element::path::Parameters = vec![x, y].into(); + svg = svg.move_to(start); + + let mut last = self.from; + let mut last_ctrl1 = self.ctrl1; + + for s in &self.curves { + let ctrl1 = s.ctrl1.or_last(last, last_ctrl1); + + let cubic: svg::node::element::path::Parameters = + vec![ctrl1.x, ctrl1.y, s.ctrl2.x, s.ctrl2.y, s.to.x, s.to.y].into(); + svg = svg.cubic_curve_to(cubic); + + last_ctrl1 = s.ctrl2; + last = s.to; + } + svg + } + + pub fn to_lyon(&self) -> Option { + cubic_bezier_path_to_lyon(&self.to_cubic(), self.closed) + } + + pub fn to_vec2(&self) -> Option> { + flatten_cubic_bezier_path(&self.to_cubic(), self.closed) + } + + pub fn last_point(&self) -> Vec2 { + if let Some(last) = self.curves.last() { + last.to + } else { + self.from + } + } + + pub fn first_point(&self) -> Vec2 { + self.from + } +} #[derive(Debug, Clone, Livecode, Lerpable)] pub struct CurveDrawer { @@ -92,6 +259,33 @@ impl CurveDrawer { let last_command = self.segments().last()?; Some(last_command.last_point()) } + + pub fn length(&self) -> f32 { + self.segments + .iter() + .map(|segment| match segment { + CurveSegment::CubicBezier(c) => { + let lyon_cubic = CubicBezierSegment { + from: glam_to_lyon(c.from), + ctrl1: glam_to_lyon(c.ctrl1), + ctrl2: glam_to_lyon(c.ctrl2), + to: glam_to_lyon(c.to), + }; + lyon_cubic.approximate_length(0.1) + } + CurveSegment::Points(p) => p + .points() + .windows(2) + .map(|pts| pts[0].distance(pts[1])) + .sum(), + CurveSegment::Arc(a) => { + let angle_diff = (a.end_pi.angle_pi() - a.start_pi.angle_pi()).rem_euclid(2.0); + let sweep_angle_rads = angle_diff * std::f32::consts::PI; + a.radius * sweep_angle_rads + } + }) + .sum() + } } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -254,6 +448,10 @@ impl CurveSegment { to, }) } + + fn cubic(c: CubicBezier) -> CurveSegment { + Self::CubicBezier(CurveCubicBezier::from_cubic(c)) + } } #[derive(Debug, Clone, Livecode, Lerpable)] diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index 8aac732..01e445a 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -345,7 +345,7 @@ pub trait Sdraw: Sized { } fn transform(&self) -> Mat4; - // fn set_transform(&self, m: Mat4) -> Self; + fn set_transform(&self, m: M) -> Self; fn add_transform_after(&self, t: Mat4) -> Self { @@ -353,8 +353,8 @@ pub trait Sdraw: Sized { self.set_transform(m) } - fn add_transform_before(&self, t: Mat4) -> Self { - let m = self.transform() * t; + fn add_transform_before(&self, t: M) -> Self { + let m = self.transform() * t.change_to_mat4(); self.set_transform(m) } @@ -505,9 +505,9 @@ impl Sdraw for CoreSDrawCtx { ctx } - fn add_transform_before(&self, t: Mat4) -> Self { + fn add_transform_before(&self, t: M) -> Self { let mut ctx = self.clone(); - ctx.transform *= t; + ctx.transform *= t.change_to_mat4(); ctx } diff --git a/murrelet_draw/src/lib.rs b/murrelet_draw/src/lib.rs index 62a1d7d..2393b9c 100644 --- a/murrelet_draw/src/lib.rs +++ b/murrelet_draw/src/lib.rs @@ -9,3 +9,5 @@ pub mod svg; pub mod transform2d; pub mod cubic; pub mod tesselate; +pub mod scaffold; +pub mod serialize; diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs index b4c3d6a..b5ad7af 100644 --- a/murrelet_draw/src/livecodetypes.rs +++ b/murrelet_draw/src/livecodetypes.rs @@ -9,10 +9,11 @@ pub mod anglepi { use murrelet_gui::CanMakeGUI; use murrelet_livecode::{lazy::ControlLazyNodeF32, livecode::ControlF32}; use murrelet_livecode_derive::Livecode; + use serde::{Deserialize, Serialize}; use crate::transform2d::Transform2d; - #[derive(Clone, Copy, Debug, Livecode, Lerpable, Default, PartialEq)] + #[derive(Clone, Copy, Debug, Livecode, Lerpable, Default, PartialEq, Serialize, Deserialize)] pub struct LivecodeAnglePi(f32); impl std::ops::Add for LivecodeAnglePi { diff --git a/murrelet_draw/src/newtypes.rs b/murrelet_draw/src/newtypes.rs index 7411b08..31c920c 100644 --- a/murrelet_draw/src/newtypes.rs +++ b/murrelet_draw/src/newtypes.rs @@ -1,10 +1,12 @@ // these could probably be somewhere else, but they are used by this crate // and just give more livecode defaults to work with instead of always // needing to create a new type. +// use crate::serialize::serialize_vec2; use glam::{Vec2, Vec3}; use lerpable::Lerpable; use murrelet_common::*; use murrelet_livecode_derive::Livecode; +use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, Debug, Livecode, Lerpable, Default)] pub struct F32Newtype { @@ -17,7 +19,7 @@ impl F32Newtype { } } -#[derive(Copy, Clone, Debug, Livecode, Lerpable, Default)] +#[derive(Copy, Clone, Debug, Livecode, Lerpable, Default, Serialize, Deserialize)] pub struct Vec2Newtype { #[lerpable(func = "lerpify_vec2")] v: Vec2, diff --git a/murrelet_draw/src/scaffold.rs b/murrelet_draw/src/scaffold.rs new file mode 100644 index 0000000..52875bf --- /dev/null +++ b/murrelet_draw/src/scaffold.rs @@ -0,0 +1,55 @@ +use glam::{vec2, Vec2}; +use itertools::Itertools; + +pub fn line_to_multipolygon(curves: &[Vec2]) -> geo::MultiPolygon { + geo::MultiPolygon::new(vec![line_to_polygon(curves)]) +} + +pub fn line_to_polygon(curves: &[Vec2]) -> geo::Polygon { + geo::Polygon::new(vec2_to_line_string(curves), vec![]) +} + +fn vec2_to_line_string(vs: &[Vec2]) -> geo::LineString { + let coords = vs.iter().map(|x| x.to_coord()).collect_vec(); + geo::LineString::new(coords) +} + +pub fn multipolygon_to_vec2(p: &geo::MultiPolygon) -> Vec> { + p.iter().map(|pp| polygon_to_vec2(pp)).collect_vec() +} + +pub fn polygon_to_vec2(p: &geo::Polygon) -> Vec { + let mut coords = p + .exterior() + .coords() + .into_iter() + .map(|coord| coord.to_vec2()) + .collect_vec(); + + if coords.first() == coords.last() { + coords.pop(); + } + + coords +} + +trait ToCoord { + fn to_coord(&self) -> ::geo::Coord; +} + +impl ToCoord for Vec2 { + fn to_coord(&self) -> ::geo::Coord { + ::geo::coord! {x: self.x as f64, y: self.y as f64} + } +} + +trait ToVec2 { + fn to_vec2(&self) -> Vec2; +} + +impl ToVec2 for ::geo::Coord { + fn to_vec2(&self) -> Vec2 { + let (x, y) = self.x_y(); + vec2(x as f32, y as f32) + } +} diff --git a/murrelet_draw/src/sequencers.rs b/murrelet_draw/src/sequencers.rs index 4271841..9438e2e 100644 --- a/murrelet_draw/src/sequencers.rs +++ b/murrelet_draw/src/sequencers.rs @@ -3,9 +3,9 @@ use itertools::Itertools; use lerpable::Lerpable; use murrelet_common::*; use murrelet_livecode::unitcells::{UnitCellContext, UnitCellCreator, UnitCellExprWorldContext}; -use murrelet_livecode_derive::*; - +// use murrelet_livecode_derive::*; use crate::transform2d::{Transform2d, Transform2dStep}; +use murrelet_livecode_derive::Livecode; const REFERENCE_SIZE: f32 = 100.0; @@ -117,3 +117,58 @@ fn make_grid( }) .collect_vec() } + +#[derive(Clone, Debug, Livecode, Lerpable, Default)] +pub struct SimpleCountSequence(pub usize); +impl SimpleCountSequence { + pub fn val(&self) -> usize { + self.0 + } +} + +impl UnitCellCreator for SimpleCountSequence { + fn to_unit_cell_ctxs(&self) -> Vec { + let v = (0..self.0) + .map(|x| { + let idx = IdxInRange::new(x, self.0); + let ctx = UnitCellExprWorldContext::from_idx1d(idx); + UnitCellContext::new(ctx, SimpleTransform2d::ident()) + }) + .collect_vec(); + v + } +} + +#[derive(Clone, Debug, Default, Livecode, Lerpable)] +pub struct SimpleVec2Sequence(#[lerpable(func = "lerpify_vec2")] Vec2); +impl SimpleVec2Sequence { + pub fn max_x_usize(&self) -> usize { + self.0.x as usize + } + + pub fn max_y_usize(&self) -> usize { + self.0.y as usize + } + + pub fn to_vec2(&self) -> Vec2 { + self.0 + } +} + +impl UnitCellCreator for SimpleVec2Sequence { + fn to_unit_cell_ctxs(&self) -> Vec { + let x_usize = self.0.x as usize; + let y_usize = self.0.y as usize; + (0..x_usize) + .flat_map(|x| { + let x_idx = IdxInRange::new(x, x_usize); + (0..y_usize).map(move |y| { + let y_idx = IdxInRange::new(y, y_usize); + let idx = IdxInRange2d::new_from_idx(x_idx, y_idx); + let ctx = UnitCellExprWorldContext::from_idx2d(idx, 1.0); + UnitCellContext::new(ctx, SimpleTransform2d::ident()) + }) + }) + .collect_vec() + } +} diff --git a/murrelet_draw/src/serialize.rs b/murrelet_draw/src/serialize.rs new file mode 100644 index 0000000..8c280ff --- /dev/null +++ b/murrelet_draw/src/serialize.rs @@ -0,0 +1,32 @@ +// use glam::Vec2; +use murrelet_common::MurreletColor; +use serde::{ser::SerializeSeq, Serializer}; + +use anyhow::Result; + +pub fn serialize_color(x: &MurreletColor, serializer: S) -> Result +where + S: Serializer, +{ + let hsva = x.into_hsva_components(); + + let mut seq = serializer.serialize_seq(Some(hsva.len()))?; + for number in &hsva { + seq.serialize_element(number)?; + } + seq.end() +} + +// just use glam's serde feature flag! +// pub fn serialize_vec2(x: &Vec2, serializer: S) -> Result +// where +// S: Serializer, +// { +// let xy = [x.x, x.y]; + +// let mut seq = serializer.serialize_seq(Some(xy.len()))?; +// for number in &xy { +// seq.serialize_element(number)?; +// } +// seq.end() +// } \ No newline at end of file diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 32832a9..ac6a11b 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -2,7 +2,7 @@ use crate::{ curve_drawer::CurveDrawer, draw::*, - svg::{SvgPathDef, TransformedSvgShape}, + svg::{SvgCircle, SvgPathDef, SvgShape, TransformedSvgShape}, tesselate::parse_svg_path_as_vec2, transform2d::*, }; @@ -562,6 +562,10 @@ impl MurreletPath { Self::Polyline(path.as_polyline()) } + pub fn svg_circle(loc: Vec2, rad: f32) -> Self { + Self::Svg(TransformedSvgShape::from_shape(SvgShape::circle(loc, rad))) + } + pub fn curve(cd: CurveDrawer) -> Self { Self::Curve(MurreletCurve::new(cd)) } diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index 08b3f1f..ac7aaa6 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -7,7 +7,11 @@ use murrelet_gui::MurreletGUI; use murrelet_livecode_derive::Livecode; use crate::curve_drawer::{CurveArc, CurveDrawer}; - +macro_rules! log { + ( $( $t:tt )* ) => { + web_sys::console::log_1(&format!( $( $t )* ).into()) + } +} #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] pub struct SvgRect { #[livecode(serde_default = "0")] @@ -268,7 +272,6 @@ impl SvgPathDef { } } -#[allow(dead_code)] pub fn glam_to_lyon(vec: Vec2) -> Point2D { Point::new(vec.x, vec.y) } @@ -317,6 +320,14 @@ impl SvgShape { SvgShape::Path(svg_path_def) => svg_path_def.clone(), } } + + pub(crate) fn circle(loc: Vec2, rad: f32) -> SvgShape { + SvgShape::Circle(SvgCircle { + x: loc.x, + y: loc.y, + r: rad, + }) + } } #[derive(Clone, Debug)] diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index adc9fe1..30464ba 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, ops}; use crate::{ cubic::CubicBezier, - // curve_drawer::{CurveDrawer, CurveSegment}, + curve_drawer::{CubicBezierPath, CurveDrawer, CurveSegment}, svg::SvgPathDef, }; use glam::{vec2, Vec2, Vec2Swizzles}; @@ -20,10 +20,18 @@ use murrelet_common::{triangulate::VertexSimple, Polyline}; pub trait ToVecVec2 { fn to_vec2(&self) -> Vec; + + fn to_vec2_line_space(&self, line_space: f32) -> Vec { + todo!() + } + + fn to_vec2_count(&self, count: usize) -> Vec { + todo!() + } } impl ToVecVec2 for CubicBezier { - fn to_vec2(&self) -> Vec { + fn to_vec2_line_space(&self, line_space: f32) -> Vec { let mut svg = svg::node::element::path::Data::new(); let x = self.from.x; @@ -42,7 +50,7 @@ impl ToVecVec2 for CubicBezier { .into(); svg = svg.cubic_curve_to(cubic); - let mut path = parse_svg_data_as_vec2(&svg, 1.0); + let mut path = parse_svg_data_as_vec2(&svg, line_space); if let Some(a) = path.last() { if a.distance(self.to.yx()) > 1.0e-3 { @@ -50,6 +58,24 @@ impl ToVecVec2 for CubicBezier { } } + path.into_iter().map(|x| vec2(x.y, x.x)).collect_vec() + } + fn to_vec2(&self) -> Vec { + self.to_vec2_line_space(1.0) + } +} + +impl ToVecVec2 for CubicBezierPath { + fn to_vec2(&self) -> Vec { + let svg = self.to_data(); + let path = parse_svg_data_as_vec2(&svg, 1.0); + + // if let Some(a) = path.last() { + // if a.distance(self.to.yx()) > 1.0e-3 { + // path.push(self.to.yx()) + // } + // } + path.into_iter().map(|x| vec2(x.y, x.x)).collect_vec() } } @@ -95,6 +121,52 @@ pub fn many_pt2_to_vec2(ps: &Vec) -> Vec { ps.iter().map(|p| p.as_vec2()).collect_vec() } +pub fn cubic_bezier_length(c: &CubicBezier) -> f32 { + let line = lyon::geom::CubicBezierSegment { + from: vec2_to_pt(c.from), + ctrl1: vec2_to_pt(c.ctrl1), + ctrl2: vec2_to_pt(c.ctrl2), + to: vec2_to_pt(c.to), + }; + line.approximate_length(0.1) +} + +pub fn flatten_cubic_bezier_path(path: &[CubicBezier], closed: bool) -> Option> { + if path.is_empty() { + return None; + } + let mut kurbo_path = BezPath::new(); + + kurbo_path.move_to(vec2_to_kurbo(path[0].from)); + for c in path { + kurbo_path.curve_to( + vec2_to_kurbo(c.ctrl1), + vec2_to_kurbo(c.ctrl2), + vec2_to_kurbo(c.to), + ) + } + + if closed { + kurbo_path.close_path(); + } + + let tolerance = 0.01; + let mut points = Vec::new(); + kurbo::flatten(kurbo_path, tolerance, |el| match el { + kurbo::PathEl::MoveTo(p) | kurbo::PathEl::LineTo(p) => { + points.push(vec2(p.x as f32, p.y as f32)); + } + kurbo::PathEl::ClosePath => { + if let Some(first) = points.first() { + points.push(*first); + } + } + _ => {} + }); + + Some(points) +} + pub fn cubic_bezier_path_to_lyon(path: &[CubicBezier], closed: bool) -> Option { // let mut builder = Path::builder(); @@ -261,13 +333,13 @@ pub fn tesselate_lyon(path: &Path) -> (Vec, Vec<[f32; 3]>) { (geometry.indices, geometry.vertices) } -pub fn parse_svg_data_as_vec2(data: &svg::node::element::path::Data, tolerance: f32) -> Vec { - parse_data(data, tolerance) +pub fn parse_svg_data_as_vec2(data: &svg::node::element::path::Data, line_space: f32) -> Vec { + parse_data(data, line_space) } // svg loader -fn parse_data(data: &svg::node::element::path::Data, tolerance: f32) -> Vec { - let mut segment_state = SegmentState::new_with_line_space(tolerance); +fn parse_data(data: &svg::node::element::path::Data, line_space: f32) -> Vec { + let mut segment_state = SegmentState::new_with_line_space(line_space); let mut from = Pt::new(0.0, 0.0); @@ -696,7 +768,7 @@ impl SegmentState { // CurveDrawer::new(curve_segments, close) // } -pub fn load_all_data(path: T, tolerance: f32) -> HashMap>> +pub fn load_all_data(path: T, line_space: f32) -> HashMap>> where T: AsRef, { @@ -710,7 +782,7 @@ where println!("processing {:?}", k); ( k.to_string(), - v.iter().map(|vv| parse_data(vv, tolerance)).collect_vec(), + v.iter().map(|vv| parse_data(vv, line_space)).collect_vec(), ) }) .collect(); @@ -747,7 +819,7 @@ where // SvgPathDef is a simplified svg thingy.. this just converts back to the full // svg and then parses like usual -pub fn parse_svg_path_as_vec2(data: &SvgPathDef, tolerance: f32) -> Vec { +pub fn parse_svg_path_as_vec2(data: &SvgPathDef, line_space: f32) -> Vec { let mut cmds = svg::node::element::path::Data::new(); let (start_x, start_y) = data.svg_move_to(); cmds = cmds.move_to(vec![start_x, start_y]); @@ -766,11 +838,11 @@ pub fn parse_svg_path_as_vec2(data: &SvgPathDef, tolerance: f32) -> Vec { crate::svg::SvgCmd::ArcTo(svg_arc) => { let (a, b, c, d, e, f, g) = svg_arc.params(); cmds = cmds.elliptical_arc_to(vec![a, b, c, d, e, f, g]); - }, + } } } - parse_svg_data_as_vec2(&cmds, tolerance) + parse_svg_data_as_vec2(&cmds, line_space) } // fn point_to_param(pt: &Point2D) -> Vec { @@ -800,3 +872,50 @@ impl LayersFromSvg { LayersFromSvg { layers } } } + +pub fn tesselate_delauney(v: Vec) -> (Vec, Vec) { + let points: Vec<_> = v + .iter() + .map(|vertex| delaunator::Point { + x: vertex.position[0] as f64, + y: vertex.position[1] as f64, + }) + .collect(); + let triangulation = delaunator::triangulate(&points); + + // chatgpt + fn point_in_poly(x: f64, y: f64, poly: &[(f64, f64)]) -> bool { + let mut inside = false; + let n = poly.len(); + for i in 0..n { + let (xi, yi) = poly[i]; + let (xj, yj) = poly[(i + 1) % n]; + let intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); + if intersect { + inside = !inside; + } + } + inside + } + + let mut filtered_indices = Vec::new(); + for tri in triangulation.triangles.chunks(3) { + let a = &points[tri[0]]; + let b = &points[tri[1]]; + let c = &points[tri[2]]; + let cx = (a.x + b.x + c.x) / 3.0; + let cy = (a.y + b.y + c.y) / 3.0; + if point_in_poly( + cx, + cy, + &v.iter() + .map(|p| (p.position[0] as f64, p.position[1] as f64)) + .collect::>(), + ) { + filtered_indices.extend_from_slice(&[tri[0] as u32, tri[1] as u32, tri[2] as u32]); + } + } + + let vertices = v.clone(); + (filtered_indices, vertices) +} diff --git a/murrelet_gen/Cargo.toml b/murrelet_gen/Cargo.toml index e8bd5da..dc43fa2 100644 --- a/murrelet_gen/Cargo.toml +++ b/murrelet_gen/Cargo.toml @@ -13,7 +13,7 @@ thiserror = "2.0.11" serde = { version = "1.0.104", features = ["derive"] } serde_json = "1.0.48" rand = "0.8" -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } murrelet_common = "0.1.2" diff --git a/murrelet_gen/src/lib.rs b/murrelet_gen/src/lib.rs index aa3cf86..eba3839 100644 --- a/murrelet_gen/src/lib.rs +++ b/murrelet_gen/src/lib.rs @@ -12,6 +12,10 @@ pub trait CanSampleFromDist: Sized { // given rn of length ^, it'll generate! fn sample_dist(rn: &[f32], start_idx: usize) -> Self; + fn from_dist(rn: &[f32]) -> Self { + Self::sample_dist(rn, 0) + } + // usually you'll call this one fn gen_from_seed(seed: u64) -> Self { let mut rng = StdRng::seed_from_u64(seed); diff --git a/murrelet_gen_derive/src/gen_methods.rs b/murrelet_gen_derive/src/gen_methods.rs index d7ef4e7..59ecde2 100644 --- a/murrelet_gen_derive/src/gen_methods.rs +++ b/murrelet_gen_derive/src/gen_methods.rs @@ -26,6 +26,13 @@ pub enum GenMethod { F32Fixed { val: syn::Expr, }, + Vec2UniformGridStart { + // chooses random points + start_x: syn::Expr, + start_y: syn::Expr, + width: f32, + height: f32, + }, Vec2UniformGrid { // chooses random points x: syn::Expr, @@ -44,6 +51,9 @@ pub enum GenMethod { min: usize, max: usize, }, + VecLengthFixed { + val: syn::Expr, + }, ColorNormal, // samples h, s, and v values ColorTransparency, // same as ColorNormal, plus alpha StringChoice { @@ -68,248 +78,279 @@ impl GenMethod { match self { GenMethod::Recurse => { - let for_rn_count = quote! { #ty::rn_count() }; - let for_rn_names = quote! { #ty::rn_names() }; - let for_make_gen = quote! {{ - let r = #ty::sample_dist(rn, rn_start_idx); - rn_start_idx += #for_rn_count; - r - }}; - let for_to_dist = quote! { #name.to_dist() }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let for_rn_count = quote! { #ty::rn_count() }; + let for_rn_names = quote! { #ty::rn_names() }; + let for_make_gen = quote! {{ + let r = #ty::sample_dist(rn, rn_start_idx); + rn_start_idx += #for_rn_count; + r + }}; + let for_to_dist = quote! { #name.to_dist() }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::BoolBinomial { pct } => { - let for_rn_count = quote! { 1 }; - let for_rn_names = quote! { vec!["pct".to_string()] }; - let for_make_gen = quote! { { - let result = rn[rn_start_idx] > #pct; - rn_start_idx += #for_rn_count; - result - } }; - let for_to_dist = quote! { - if #name { - vec![1.0] - } else { - vec![0.0] + let for_rn_count = quote! { 1 }; + let for_rn_names = quote! { vec!["pct".to_string()] }; + let for_make_gen = quote! { { + let result = rn[rn_start_idx] > #pct; + rn_start_idx += #for_rn_count; + result + } }; + let for_to_dist = quote! { + if #name { + vec![1.0] + } else { + vec![0.0] + } + }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } - }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } GenMethod::F32Uniform { start, end } => { - let for_rn_count = quote! { 1 }; - let for_rn_names = quote! { vec!["uniform".to_string()] }; - let for_make_gen = quote! { { - let result = rn[rn_start_idx] * (#end - #start) + #start; - rn_start_idx += #for_rn_count; - result #maybe_as - } }; - let for_to_dist = quote! { vec![(#name as f32 - #start) / (#end - #start)] }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let for_rn_count = quote! { 1 }; + let for_rn_names = quote! { vec!["uniform".to_string()] }; + let for_make_gen = quote! { { + let result = rn[rn_start_idx] * (#end - #start) + #start; + rn_start_idx += #for_rn_count; + result #maybe_as + } }; + let for_to_dist = quote! { vec![(#name as f32 - #start) / (#end - #start)] }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::F32UniformPosNeg { start, end } => { - let for_rn_count = quote! { 2 }; - let for_rn_names = quote! { vec!["uniform".to_string(), "sign".to_string()] }; - let for_make_gen = quote! { { - let sgn = if(rn[rn_start_idx] > 0.5) { 1.0 } else { -1.0 }; - let result = rn[rn_start_idx + 1] * (#end - #start) + #start; - rn_start_idx += #for_rn_count; - (sgn * result) #maybe_as - } }; - - let for_to_dist = quote! { vec![ - if #name > 0.0 { 1.0 } else { 0.0 }, - (#name.abs() - #start) / (#end - #start) - ] }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec!["uniform".to_string(), "sign".to_string()] }; + let for_make_gen = quote! { { + let sgn = if(rn[rn_start_idx] > 0.5) { 1.0 } else { -1.0 }; + let result = rn[rn_start_idx + 1] * (#end - #start) + #start; + rn_start_idx += #for_rn_count; + (sgn * result) #maybe_as + } }; + + let for_to_dist = quote! { vec![ + if #name > 0.0 { 1.0 } else { 0.0 }, + (#name.abs() - #start) / (#end - #start) + ] }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::F32Fixed { val } => ( - quote! { 0 }, - quote! {vec![]}, - quote! { #val #maybe_as }, - quote! {vec![]}, - ), + quote! { 0 }, + quote! {vec![]}, + quote! { #val #maybe_as }, + quote! {vec![]}, + ), GenMethod::F32Normal { mu, sigma } => { - let for_rn_count = quote! { 2 }; - let for_rn_names = - quote! { vec![ "BoxMuller1".to_string(), "BoxMuller2".to_string()] }; - let for_make_gen = quote! { { - // avoid nans, make sure u1 is positive and non-zero - let u1 = rn[rn_start_idx].clamp(std::f32::MIN_POSITIVE, 1.0); - let u2 = rn[rn_start_idx + 1].clamp(0.0, 1.0); - rn_start_idx += 2; - - let r = (-2.0 * u1.ln()).sqrt(); - let theta = 2.0 * std::f32::consts::PI * u2; - - #mu + #sigma * r * theta.cos() #maybe_as - } }; - let for_to_dist = quote! { todo!() }; - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let for_rn_count = quote! { 2 }; + let for_rn_names = + quote! { vec![ "BoxMuller1".to_string(), "BoxMuller2".to_string()] }; + let for_make_gen = quote! { { + // avoid nans, make sure u1 is positive and non-zero + let u1 = rn[rn_start_idx].clamp(std::f32::MIN_POSITIVE, 1.0); + let u2 = rn[rn_start_idx + 1].clamp(0.0, 1.0); + rn_start_idx += 2; + + let r = (-2.0 * u1.ln()).sqrt(); + let theta = 2.0 * std::f32::consts::PI * u2; + + #mu + #sigma * r * theta.cos() #maybe_as + } }; + let for_to_dist = quote! { todo!() }; + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } + GenMethod::Vec2UniformGridStart { + start_x, + start_y, + width, + height, + } => { + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec![ "x".to_string(), "y".to_string()] }; + let for_make_gen = quote! {{ + let width = rn[rn_start_idx] * #width; + let height = rn[rn_start_idx + 1] * #height; + + rn_start_idx += #for_rn_count; + + glam::vec2(#start_x, #start_y) + glam::vec2(width, height) + }}; + + let for_to_dist = quote! { { + // let c = (#name + 0.5 * glam::vec2(#width, #height)); + // vec![c.x / #width, c.y / #height] + + let c = #name + - glam::vec2(#start_x, #start_y) + + 0.5 * glam::vec2(#width, #height); + vec![c.x / #width, c.y / #height] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::Vec2UniformGrid { - x, - y, - width, - height, - } => { - let for_rn_count = quote! { 2 }; - let for_rn_names = quote! { vec![ "x".to_string(), "y".to_string()] }; - let for_make_gen = quote! {{ - let width = rn[rn_start_idx] * #width; - let height = rn[rn_start_idx + 1] * #height; - - rn_start_idx += #for_rn_count; - - glam::vec2(#x, #y) - 0.5 * glam::vec2(#width, #height) + glam::vec2(width, height) - }}; - - let for_to_dist = quote! { { - // let c = (#name + 0.5 * glam::vec2(#width, #height)); - // vec![c.x / #width, c.y / #height] - - let c = #name - - glam::vec2(#x, #y) - + 0.5 * glam::vec2(#width, #height); - vec![c.x / #width, c.y / #height] - }}; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + x, + y, + width, + height, + } => { + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec![ "x".to_string(), "y".to_string()] }; + let for_make_gen = quote! {{ + let width = rn[rn_start_idx] * #width; + let height = rn[rn_start_idx + 1] * #height; + + rn_start_idx += #for_rn_count; + + glam::vec2(#x, #y) - 0.5 * glam::vec2(#width, #height) + glam::vec2(width, height) + }}; + + let for_to_dist = quote! { { + // let c = (#name + 0.5 * glam::vec2(#width, #height)); + // vec![c.x / #width, c.y / #height] + + let c = #name + - glam::vec2(#x, #y) + + 0.5 * glam::vec2(#width, #height); + vec![c.x / #width, c.y / #height] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::Vec2Circle { x, y, radius } => { - let for_rn_count = quote! { 2 }; - let for_rn_names = quote! { vec![ "theta".to_string(), "rad".to_string()] }; - - let for_make_gen = quote! {{ - let angle = rn[rn_start_idx] * 2.0 * std::f32::consts::PI; - let dist = rn[rn_start_idx + 1]; // sqrt it to even out the sampling - rn_start_idx += #for_rn_count; - glam::vec2(#x, #y) + glam::vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() - }}; - - let for_to_dist = quote! { { - let c = #name - glam::vec2(#x, #y); - let dist = (c.length() / #radius).powi(2); - let mut angle = c.to_angle(); - if angle <= 0.0 { - angle += 2.0 * std::f32::consts::PI + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec![ "theta".to_string(), "rad".to_string()] }; + + let for_make_gen = quote! {{ + let angle = rn[rn_start_idx] * 2.0 * std::f32::consts::PI; + let dist = rn[rn_start_idx + 1]; // sqrt it to even out the sampling + rn_start_idx += #for_rn_count; + glam::vec2(#x, #y) + glam::vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() + }}; + + let for_to_dist = quote! { { + let c = #name - glam::vec2(#x, #y); + let dist = (c.length() / #radius).powi(2); + let mut angle = c.to_angle(); + if angle <= 0.0 { + angle += 2.0 * std::f32::consts::PI + } + angle /= (2.0 * std::f32::consts::PI); + vec![angle, dist] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } - angle /= (2.0 * std::f32::consts::PI); - vec![angle, dist] - }}; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } GenMethod::ColorNormal => { - let for_rn_count = quote! { 3 }; - - let for_rn_names = - quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string()] }; - - let for_make_gen = quote! {{ - let h = rn[rn_start_idx]; - let s = rn[rn_start_idx + 1]; - let v = rn[rn_start_idx + 2]; - rn_start_idx += #for_rn_count; - murrelet_common::MurreletColor::hsva(h, s, v, 1.0) - }}; - - let for_to_dist = quote! {{ - let [h, s, v, _] = #name.into_hsva_components(); - vec![ - h % 1.0, - s % 1.0, - v % 1.0, - ] - }}; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let for_rn_count = quote! { 3 }; + + let for_rn_names = + quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string()] }; + + let for_make_gen = quote! {{ + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, 1.0) + }}; + + let for_to_dist = quote! {{ + let [h, s, v, _] = #name.into_hsva_components(); + vec![ + h % 1.0, + s % 1.0, + v % 1.0, + ] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::ColorTransparency => { - let for_rn_count = quote! { 4 }; - - let for_rn_names = quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string(), "alpha".to_string()] }; - - let for_make_gen = quote! { - { - let h = rn[rn_start_idx]; - let s = rn[rn_start_idx + 1]; - let v = rn[rn_start_idx + 2]; - let a = rn[rn_start_idx + 3]; - rn_start_idx += #for_rn_count; - murrelet_common::MurreletColor::hsva(h, s, v, a) + let for_rn_count = quote! { 4 }; + + let for_rn_names = quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string(), "alpha".to_string()] }; + + let for_make_gen = quote! { + { + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + let a = rn[rn_start_idx + 3]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, a) + } + }; + + let for_to_dist = quote! { { + let [h, s, v, a] = #name.into_hsva_components(); + vec![ + h % 1.0, + s % 1.0, + v % 1.0, + a % 1.0, + ] + } }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) } - }; - - let for_to_dist = quote! { { - let [h, s, v, a] = #name.into_hsva_components(); - vec![ - h % 1.0, - s % 1.0, - v % 1.0, - a % 1.0, - ] - } }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } GenMethod::VecLength { .. } => { - // this is handled in the vec parser - unreachable!("this location of veclength isn't supported yet!") - } + // this is handled in the vec parser + unreachable!("this location of veclength isn't supported yet!") + } + GenMethod::VecLengthFixed { val } => unreachable!(), + GenMethod::StringChoice { choices } => { - let one_hot = choices.len(); - let for_rn_count = quote! { #one_hot }; - - // let for_rn_names = quote! { vec![ "hue", "sat", "val", "alpha"] }; - let rn_names = choices.iter().map(|(key, _)| quote! { #key.to_string() }); - let for_rn_names = quote! { vec![#(#rn_names,)*] }; - - let weighted_rns = choices - .iter() - .enumerate() - .map(|(i, (key, weight))| { - quote! { (#key.clone(), #weight * rn[rn_start_idx + #i]) } - }) - .collect::>(); - - let for_make_gen = quote! { { - let result = vec![#(#weighted_rns,)*].into_iter().max_by(|a, b| a.1.partial_cmp(&b.1).unwrap()).expect("empty string choices??"); - rn_start_idx += #for_rn_count; - result.0.to_string() - } }; - - let for_to_dist_choices = choices - .iter() - .map(|(key, _)| { - quote! { if #key.clone() == #name { result.push(1.0) } else { result.push(0.0) } } - }) - .collect::>(); - - let for_to_dist = quote! { { - let mut result = vec![]; - #(#for_to_dist_choices;)* - result - } }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let one_hot = choices.len(); + let for_rn_count = quote! { #one_hot }; + + // let for_rn_names = quote! { vec![ "hue", "sat", "val", "alpha"] }; + let rn_names = choices.iter().map(|(key, _)| quote! { #key.to_string() }); + let for_rn_names = quote! { vec![#(#rn_names,)*] }; + + let weighted_rns = choices + .iter() + .enumerate() + .map(|(i, (key, weight))| { + quote! { (#key.clone(), #weight * rn[rn_start_idx + #i]) } + }) + .collect::>(); + + let for_make_gen = quote! { { + let result = vec![#(#weighted_rns,)*].into_iter().max_by(|a, b| a.1.partial_cmp(&b.1).unwrap()).expect("empty string choices??"); + rn_start_idx += #for_rn_count; + result.0.to_string() + } }; + + let for_to_dist_choices = choices + .iter() + .map(|(key, _)| { + quote! { if #key.clone() == #name { result.push(1.0) } else { result.push(0.0) } } + }) + .collect::>(); + + let for_to_dist = quote! { { + let mut result = vec![]; + #(#for_to_dist_choices;)* + result + } }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::Default => { - let for_rn_count = quote! { 0 }; + let for_rn_count = quote! { 0 }; - let for_rn_names = quote! { vec![] }; + let for_rn_names = quote! { vec![] }; - let for_make_gen = quote! { { - Default::default() - } }; + let for_make_gen = quote! { { + Default::default() + } }; - let for_to_dist = quote! { vec![] }; + let for_to_dist = quote! { vec![] }; - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::F32Lazy => todo!(), } } diff --git a/murrelet_gpu/Cargo.toml b/murrelet_gpu/Cargo.toml index 56a13f9..8ba21c5 100644 --- a/murrelet_gpu/Cargo.toml +++ b/murrelet_gpu/Cargo.toml @@ -37,7 +37,7 @@ murrelet_perform = { version = "0.1.2", default-features = false } lerpable = { version = "0.0.2" } -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } palette = "0.7.6" serde = { version = "1.0.104", features = ["derive"] } diff --git a/murrelet_gui/examples/tests_schema.rs b/murrelet_gui/examples/tests_schema.rs new file mode 100644 index 0000000..254f48a --- /dev/null +++ b/murrelet_gui/examples/tests_schema.rs @@ -0,0 +1,107 @@ +// use std::collections::HashMap; + +// use murrelet_gui::{CanChangeToGUI, CanMakeGUI, MurreletEnumValGUI, MurreletGUISchema, ValueGUI}; +// use murrelet_schema::*; +// use murrelet_schema_derive::MurreletSchema; + +// #[derive(MurreletSchema)] +// pub struct BasicTypes { +// a_number: f32, +// b_number: usize, +// c_number: u64, +// d_number: i32, +// bool: bool, +// something: Vec, +// s: String, +// referenced_string: String, +// } + +// fn custom_func() -> MurreletSchema { +// MurreletSchema::Val(murrelet_schema::MurreletPrimitive::Num) +// } + +// #[derive(MurreletSchema)] +// pub struct OverridesAndRecursive { +// a_number: f32, +// something: Vec, +// #[murrelet_schema(func = "custom_func")] +// label: String, +// #[murrelet_schema(kind = "skip")] +// b: HashMap, +// } + +// #[derive(MurreletSchema)] +// enum EnumTest { +// A, +// B(OverridesAndRecursive), +// } + +// #[derive(MurreletSchema)] +// struct SimpleNewtype(f32); + +// // fn lerp_partial(&self, pct: T) -> Self { +// // SimpleNewtype(pct.lerp_pct() as f32) +// // } +// // } + +// // #[derive(Debug, Clone, MurreletUX)] + +// fn main() { +// // let b = BasicTypes{ +// // a_number: 1.0, +// // b_number: -10.0, +// // }; +// let test_val = BasicTypes::make_schema().change_to_gui(); + +// let basic_types_schema = MurreletGUISchema::Struct( +// "BasicTypes".to_string(), +// vec![ +// ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), +// ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), +// ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), +// ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), +// ("bool".to_owned(), MurreletGUISchema::Val(ValueGUI::Bool)), +// ( +// "something".to_owned(), +// MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), +// ), +// ("s".to_owned(), MurreletGUISchema::Val(ValueGUI::String)), +// ( +// "referenced_string".to_owned(), +// MurreletGUISchema::Val(ValueGUI::String), +// ), +// ], +// ); + +// assert_eq!(test_val, basic_types_schema); + +// let test_val = OverridesAndRecursive::make_schema().change_to_gui(); + +// let overrides_and_recursive_schema = MurreletGUISchema::Struct( +// "OverridesAndRecursive".to_string(), +// vec![ +// ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), +// ( +// "something".to_owned(), +// MurreletGUISchema::list(basic_types_schema), +// ), +// ("label".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), // make sure it calls the override +// ("b".to_owned(), MurreletGUISchema::Skip), +// ], +// ); +// assert_eq!(test_val, overrides_and_recursive_schema); + +// let test_val = EnumTest::make_schema().change_to_gui(); + +// assert_eq!( +// test_val, +// MurreletGUISchema::Enum( +// "EnumTest".to_string(), +// vec![ +// (MurreletEnumValGUI::Unit("A".to_owned())), +// (MurreletEnumValGUI::Unnamed("B".to_owned(), overrides_and_recursive_schema)), +// ], +// false +// ) +// ); +// } diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs index 394a75a..fdb05f6 100644 --- a/murrelet_gui/src/lib.rs +++ b/murrelet_gui/src/lib.rs @@ -173,7 +173,10 @@ impl CanChangeToGUI for MurreletSchema { ), MurreletSchema::Enum(name, items, b) => MurreletGUISchema::Enum( name.clone(), - items.iter().map(|x| change_enum_to_gui(x)).collect_vec(), + items + .iter() + .map(|(_type, x)| change_enum_to_gui(x)) + .collect_vec(), *b, ), MurreletSchema::List(murrelet_schema) => { diff --git a/murrelet_livecode/Cargo.toml b/murrelet_livecode/Cargo.toml index 0ba2e02..759d70a 100644 --- a/murrelet_livecode/Cargo.toml +++ b/murrelet_livecode/Cargo.toml @@ -20,7 +20,7 @@ itertools = "0.10.5" regex = "1.7.3" uuid = "1.8.0" rand = "0.8" -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } anyhow = "1.0.86" noise = "0.9.0" evalexpr = { version = "11.1.0", features = ["serde_support"] } diff --git a/murrelet_livecode/src/app_src.rs b/murrelet_livecode/src/app_src.rs index c46ac05..0de82d5 100644 --- a/murrelet_livecode/src/app_src.rs +++ b/murrelet_livecode/src/app_src.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use glam::{vec2, Vec2}; -use murrelet_common::{IsLivecodeSrc, LivecodeSrcUpdateInput, LivecodeValue}; +use murrelet_common::{CustomVars, IsLivecodeSrc, LivecodeSrcUpdateInput, LivecodeValue}; // hacky, and maybe should include more keys or maybe it has too many, but this is quick to type (kDt) #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -81,6 +81,7 @@ pub struct AppInputValues { mouse_loc: Vec2, // doesn't need a click // can refactor. for now, this is a quick way to exclude, say, keyboard things from livecode web include_keyboard: bool, + custom_vars: CustomVars, } impl AppInputValues { // there's a nicer way to write this for keys... @@ -122,6 +123,7 @@ impl AppInputValues { ("w".to_owned(), LivecodeValue::Float(w as f64)), ("h".to_owned(), LivecodeValue::Float(h as f64)), ]); + r.extend(self.custom_vars.to_exec_funcs()); r } } @@ -143,6 +145,7 @@ impl IsLivecodeSrc for AppInputValues { let keys_cycle = self.keys_cycle; let keys_fired = self.keys_fire; + // i don't remember why i wrote it this way... self.result(has_click, cx, cy, mx, my, keys_cycle, keys_fired, w, h) } @@ -164,6 +167,7 @@ impl IsLivecodeSrc for AppInputValues { } self.mouse_loc = app.mouse_position; + // only update clicks if they are clicking! self.click_fire = false; self.click_changed = false; if app.mouse_left_is_down { @@ -173,6 +177,8 @@ impl IsLivecodeSrc for AppInputValues { self.click_changed = true; } + self.custom_vars.update(&src_input.app().custom_vars); + self.window_dims = app.window_dims; } } @@ -263,6 +269,7 @@ impl AppInputValues { mouse_loc: Vec2::ZERO, click_loc: Vec2::ZERO, include_keyboard, + custom_vars: CustomVars::default(), } } diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index 3a18365..a116293 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -506,15 +506,11 @@ impl IsLivecodeSrc for LiveCodeTimeInstantInfo { ("t".to_owned(), LivecodeValue::Float(time as f64)), ( "tease".to_owned(), - LivecodeValue::Float(ease( - time.into(), - (1.0 / self.timing_config.beats_per_bar).into(), - 0.0, - )), + LivecodeValue::Float(ease(time.into(), (1.0 / 4.0).into(), 0.0)), ), ( "stease".to_owned(), - LivecodeValue::Float(ease(time.into(), 0.01, 0.0)), + LivecodeValue::Float(ease(time.into(), 0.0125, 0.0)), ), ("ti".to_owned(), LivecodeValue::Int(time as i64)), ("f".to_owned(), LivecodeValue::Float(frame as f64)), diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 68dca55..3c81271 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -290,7 +290,7 @@ impl ControlVecElementRepeat { // } #[derive(Debug, Clone)] -#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +// #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] // pub enum ControlVecElement pub enum ControlVecElement where @@ -326,6 +326,37 @@ where } } +// chatgpt +#[cfg(feature = "schemars")] +impl schemars::JsonSchema for ControlVecElement +where + Source: schemars::JsonSchema + Clone + Debug, +{ + fn schema_name() -> String { + format!("ControlVecElement_{}", Source::schema_name()) + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + use schemars::schema::{Schema, SchemaObject, SubschemaValidation}; + // Variant 1: plain Source (your Single case without a wrapper key) + let single_schema = Source::json_schema(gen); + // Variant 2: the repeat object + let repeat_schema = >::json_schema(gen); + + Schema::Object(SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + one_of: Some(vec![single_schema, repeat_schema]), + ..Default::default() + })), + metadata: Some(Box::new(schemars::schema::Metadata { + description: Some("Either a single element (inline) OR a repeat object { repeat, prefix?, what }".to_string()), + ..Default::default() + })), + ..Default::default() + }) + } +} + // chatgpt // impl<'de, Sequencer, ControlSequencer, Source> Deserialize<'de> // for ControlVecElement diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml index 3560714..51046d6 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml +++ b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml @@ -25,7 +25,7 @@ murrelet_livecode = { version = "0.1.2", default-features = false } # just for examples... [dev-dependencies] murrelet_common = "0.1.2" -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } palette = "0.7.6" lerpable = "0.0.2" schemars = "0.8.21" diff --git a/murrelet_perform/Cargo.toml b/murrelet_perform/Cargo.toml index 80bec7a..de784c7 100644 --- a/murrelet_perform/Cargo.toml +++ b/murrelet_perform/Cargo.toml @@ -37,7 +37,7 @@ evalexpr = "11.1.0" rand = "0.8" itertools = "0.10.5" regex = "1.7.3" -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } palette = "0.7.6" anyhow = "1.0.86" diff --git a/murrelet_perform/src/asset_loader.rs b/murrelet_perform/src/asset_loader.rs index 15366bb..ed3f0b7 100644 --- a/murrelet_perform/src/asset_loader.rs +++ b/murrelet_perform/src/asset_loader.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, path::Path}; use itertools::Itertools; use lerpable::Lerpable; use murrelet_common::{ - Assets, RasterAsset, RasterAssetLookup, VectorAsset, VectorLayersAssetLookup, + Assets, JsonAssetLookup, RasterAsset, RasterAssetLookup, VectorAsset, VectorLayersAssetLookup }; use murrelet_gui::CanMakeGUI; use murrelet_livecode_derive::Livecode; @@ -18,6 +18,20 @@ pub trait RasterAssetLoader { fn load(&self, filename: &Path) -> RasterAsset; } +#[derive(Livecode, Lerpable, Clone, Debug)] +pub struct JsonStringFile { + #[livecode(kind = "none")] + name: String, + #[livecode(kind = "none")] + content: String, + // probably will want to add something to normalize the nums coming in... +} +impl JsonStringFile { + pub fn path(&self) -> &Path { + Path::new(&self.name) + } +} + #[derive(Livecode, Lerpable, Clone, Debug)] pub struct RasterFile { #[livecode(kind = "none")] @@ -50,6 +64,7 @@ pub fn _empty_filenames() -> ControlAssetFilenames { ControlAssetFilenames { vector_files: vec![], raster_files: vec![], + json_files: vec![], } } @@ -57,6 +72,7 @@ pub fn _empty_filenames_lazy() -> ControlLazyAssetFilenames { ControlLazyAssetFilenames { vector_files: vec![], raster_files: vec![], + json_files: vec![], } } @@ -86,6 +102,7 @@ pub struct AssetFilenames { // hmm, the parsers are all in a different part of the code vector_files: Vec, raster_files: Vec, + json_files: Vec, // just load as a string } impl AssetFilenames { @@ -93,6 +110,7 @@ impl AssetFilenames { Self { vector_files: Vec::new(), raster_files: Vec::new(), + json_files: Vec::new(), } } @@ -142,7 +160,12 @@ impl AssetFilenames { } } - Assets::new(polylines, raster) + let mut json = JsonAssetLookup::empty(); + for s in &self.json_files { + json.insert(s.name.clone(), s.content.clone()); + } + + Assets::new(polylines, raster, json) } } diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 70f4327..4d78ae2 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -6,7 +6,7 @@ use murrelet_common::{LivecodeSrc, LivecodeSrcUpdateInput, MurreletAppInput}; use murrelet_common::{MurreletColor, TransformVec2}; use murrelet_gui::MurreletGUI; use murrelet_livecode::expr::{MixedEvalDefs, MixedEvalDefsRef}; -use murrelet_livecode::lazy::ControlLazyNodeF32; +use murrelet_livecode::lazy::{ControlLazyNodeF32, LazyNodeF32}; use murrelet_livecode::state::{LivecodeTimingConfig, LivecodeWorldState}; use murrelet_livecode::types::{AdditionalContextNode, ControlVecElement, LivecodeResult}; use std::collections::{HashMap, HashSet}; @@ -262,6 +262,28 @@ fn _default_svg_save_lazy() -> ControlLazyNodeF32 { ControlLazyNodeF32::Bool(false) } +impl Default for ControlAppConfigTiming { + fn default() -> Self { + Self { + bpm: _default_bpm(), + beats_per_bar: _default_beats_per_bar(), + fps: _default_fps(), + realtime: ControlBool::Raw(true), + } + } +} + +impl Default for ControlLazyAppConfigTiming { + fn default() -> Self { + Self { + bpm: _default_bpm_lazy(), + beats_per_bar: _default_beats_per_bar_lazy(), + fps: _default_fps_lazy(), + realtime: ControlLazyNodeF32::Bool(true), + } + } +} + // this stuff adjusts how time works, so needs to be split off pretty early #[allow(dead_code)] #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] @@ -432,6 +454,88 @@ fn _default_should_reset_lazy() -> ControlLazyNodeF32 { ControlLazyNodeF32::Bool(false) } +fn _default_redraw() -> ControlF32 { + ControlF32::Int(1) +} + +fn _default_redraw_lazy() -> LazyNodeF32 { + LazyNodeF32::simple_number(1.0) +} + +fn _default_reload() -> ControlBool { + ControlBool::Raw(true) +} + +fn _default_reload_lazy() -> LazyNodeF32 { + LazyNodeF32::simple_number(1.0) +} + +fn _default_reload_rate() -> ControlF32 { + ControlF32::Int(1) +} + +fn _default_reload_rate_lazy() -> LazyNodeF32 { + LazyNodeF32::simple_number(1.0) +} + +fn _default_time() -> ControlAppConfigTiming { + ControlAppConfigTiming::default() +} + +fn _default_ctx() -> AdditionalContextNode { + AdditionalContextNode::new_dummy() +} + +fn _default_ctx_lazy() -> AdditionalContextNode { + AdditionalContextNode::new_dummy() +} + +fn _default_svg() -> ControlSvgConfig { + ControlSvgConfig::default() +} + +impl Default for ControlAppConfig { + fn default() -> Self { + Self { + should_reset: _default_should_reset(), + debug: ControlBool::Raw(false), + capture: ControlBool::Raw(false), + seed: _default_seed(), + width: _default_width(), + bg_alpha: _default_bg_alpha(), + clear_bg: _default_clear_bg(), + bg_color: _default_bg_color(), + capture_frame: _default_capture_frame(), + redraw: _default_redraw(), + reload: _default_reload(), + reload_rate: _default_reload_rate(), + time: _default_time(), + ctx: _default_ctx(), + svg: _default_svg(), + gpu: _default_gpu(), + reload_on_bar: _default_reload_on_bar(), + assets: _default_assets(), + lerp_rate: _default_lerp_rate(), + } + } +} + +fn _default_lerp_rate() -> ControlF32 { + ControlF32::Int(0) +} + +fn _default_assets() -> ControlAssetFilenames { + _empty_filenames() +} + +fn _default_reload_on_bar() -> ControlBool { + ControlBool::Raw(false) +} + +fn _default_gpu() -> ControlGpuConfig { + ControlGpuConfig::default() +} + #[allow(dead_code)] #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct AppConfig { @@ -459,8 +563,10 @@ pub struct AppConfig { pub reload: bool, // should reload and draw, good for slow drawing things #[livecode(serde_default = "0")] pub reload_rate: u64, // controls should_redraw, how many frames between redraw. if < 1, always defer to reload + #[livecode(serde_default = "default")] pub time: AppConfigTiming, #[livecode(kind = "none")] + #[livecode(serde_default = "_default_ctx")] pub ctx: AdditionalContextNode, #[livecode(serde_default = "default")] pub svg: SvgConfig, @@ -719,11 +825,13 @@ where self.lerp_pct += lerp_change; if self.lerp_pct >= 1.0 { + // todo, just move it out... if let Some(new_target) = &self.queued_configcontrol { - self.prev_controlconfig = self.controlconfig.clone(); - self.controlconfig = new_target.clone(); - self.lerp_pct = 0.0; - self.queued_configcontrol = None; + // self.prev_controlconfig = self.controlconfig.clone(); + // self.controlconfig = new_target.clone(); + // self.lerp_pct = 0.0; + // self.queued_configcontrol = None; + self.update_config_directly(new_target.clone())?; } } @@ -788,16 +896,25 @@ where pub fn update_config_to(&mut self, text: &str) -> Result<(), String> { match ControlConfType::cb_reload_and_update_info(&mut self.util, text) { Ok(d) => { - self.prev_controlconfig = self.controlconfig.clone(); - self.controlconfig = d; - self.queued_configcontrol = None; - self.lerp_pct = 0.0; - Ok(()) + // self.prev_controlconfig = self.controlconfig.clone(); + // self.controlconfig = d; + // self.queued_configcontrol = None; + // self.lerp_pct = 0.0; + // Ok(()) + self.update_config_directly(d).map_err(|x| x.to_string()) } Err(e) => Err(e), } } + pub fn update_config_directly(&mut self, control_conf: ControlConfType) -> LivecodeResult<()> { + self.prev_controlconfig = self.controlconfig.clone(); + self.controlconfig = control_conf; + self.queued_configcontrol = None; + self.lerp_pct = 0.0; + Ok(()) + } + /// if the bg_alpha is above 0.5 or clear_bg is true pub fn should_reset_bg(&self) -> bool { self.world().actual_frame_u64() <= 1 || self.app_config().should_clear_bg() diff --git a/murrelet_schema/src/lib.rs b/murrelet_schema/src/lib.rs index d567b3f..e586236 100644 --- a/murrelet_schema/src/lib.rs +++ b/murrelet_schema/src/lib.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; + use anyhow::{anyhow, Result}; use serde::Serialize; @@ -17,15 +19,16 @@ pub enum MurreletPrimitive { #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub enum MurreletEnumVal { - Unnamed(String, MurreletSchema), + Unnamed(String, Box), Unit(String), } #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub enum MurreletSchema { NewType(String, Box), - Struct(String, Vec<(String, MurreletSchema)>), - Enum(String, Vec, bool), + Struct(String, BTreeMap), + // hmm, we'll need to figure out how to provide this + Enum(String, BTreeMap, bool), List(Box), Val(MurreletPrimitive), Skip, @@ -39,7 +42,7 @@ impl MurreletSchema { Self::List(Box::new(m)) } - pub fn as_enum(&self) -> Option<&Vec> { + pub fn as_enum(&self) -> Option<&BTreeMap> { if let Self::Enum(_, v, _) = self { Some(v) } else { @@ -55,7 +58,7 @@ impl MurreletSchema { } } - pub fn unwrap_to_struct_fields(self) -> Vec<(String, MurreletSchema)> { + pub fn unwrap_to_struct_fields(self) -> BTreeMap { match self { MurreletSchema::Struct(_, items) => items, _ => unreachable!("tried to flatten a struct that wasn't a struct"), @@ -70,12 +73,16 @@ impl MurreletSchema { for (key, new_type) in s.iter() { let location = key.split(".").collect::>(); - let kind = match new_type.as_str() { - "Vec2" => Ok(MurreletPrimitive::Vec2), + let kind = match new_type.to_lowercase().as_str() { + "vec2" => Ok(MurreletPrimitive::Vec2), + "f32" => Ok(MurreletPrimitive::Num), + "number" => Ok(MurreletPrimitive::Num), + "num" => Ok(MurreletPrimitive::Num), + "string" => Ok(MurreletPrimitive::String), _ => Result::Err(anyhow!(format!("unsupported schema hint, {}", new_type))), }?; - n.update_with_one_hint(&location, &kind)?; + n.update_with_one_hint(key, &location, &kind)?; } Ok(n) @@ -83,14 +90,22 @@ impl MurreletSchema { pub fn update_with_one_hint( &mut self, + original_location: &str, // for debugging location: &[&str], value: &MurreletPrimitive, ) -> Result<()> { // basically we go through s and then traverse until we find the spot we need to update + if location.is_empty() { + // we're there! + // return Result::Err(anyhow!(format!("couldn't find {} in {:?}!", original_location, self))); + *self = MurreletSchema::Val(value.clone()); + return Ok(()); + } + match self { MurreletSchema::NewType(_, murrelet_schema) => { - murrelet_schema.update_with_one_hint(location, value) + murrelet_schema.update_with_one_hint(original_location, location, value) } MurreletSchema::Struct(_, items) => { if let Some((first_name, rest)) = location.split_first() { @@ -98,7 +113,7 @@ impl MurreletSchema { for (name, schema) in items { if name == first_name { found_match = true; - schema.update_with_one_hint(rest, value)?; + schema.update_with_one_hint(original_location, rest, value)?; break; } } @@ -110,13 +125,16 @@ impl MurreletSchema { } else { Result::Err(anyhow!("missing")) } - }, - MurreletSchema::Enum(_, murrelet_enum_vals, _) => todo!(), - MurreletSchema::List(murrelet_schema) => todo!(), - MurreletSchema::Val(murrelet_primitive) => todo!(), + } + MurreletSchema::Enum(_, _, _) => todo!(), + // need to do this one... + MurreletSchema::List(_) => todo!(), + // i think these should be handled in the struct level! + MurreletSchema::Val(_) => todo!(), MurreletSchema::Skip => Result::Err(anyhow!("hm, trying to edit a skip")), } } + } // this should be on the Control version diff --git a/murrelet_src_audio/Cargo.toml b/murrelet_src_audio/Cargo.toml index ae6a5ff..c64c577 100644 --- a/murrelet_src_audio/Cargo.toml +++ b/murrelet_src_audio/Cargo.toml @@ -11,6 +11,6 @@ license = "AGPL-3.0-or-later" murrelet_common = "0.1.2" cpal = "0.14" rustfft = "6.1.0" -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } itertools = "0.10.5" anyhow = "1.0.86" diff --git a/murrelet_svg/Cargo.toml b/murrelet_svg/Cargo.toml index a67be59..9cc0d22 100644 --- a/murrelet_svg/Cargo.toml +++ b/murrelet_svg/Cargo.toml @@ -15,6 +15,6 @@ schemars = ["murrelet_perform/schemars", "murrelet_draw/schemars"] murrelet_common = "0.1.2" murrelet_perform = { version = "0.1.2", default-features = false } murrelet_draw = { version = "0.1.2", default-features = false } -glam = "0.28.0" +glam = { version = "0.28.0", features = ["serde"] } itertools = "0.10.5" svg = "0.10.0" diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 6f9e5d7..d56c8a1 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -304,7 +304,12 @@ impl GetSvgAttributes for MurreletStyle { v.add_float("stroke-width", self.stroke_weight); v.add("stroke-linejoin", "round"); v.add("stroke-linecap", "round"); - v.add("stroke", &self.stroke_color.as_color().hex()); + + let sc = self.stroke_color.as_color(); + v.add("stroke", &sc.hex()); + if sc.alpha() < 1.0 { + v.add("stroke-opacity", &format!("{}", sc.alpha())); + } } } murrelet_draw::draw::MurreletDrawPlan::Outline => { @@ -314,7 +319,12 @@ impl GetSvgAttributes for MurreletStyle { v.add_float("stroke-width", self.stroke_weight); v.add("stroke-linejoin", "round"); v.add("stroke-linecap", "round"); - v.add("stroke", &self.color.as_color().hex()); + + let sc = self.color.as_color(); + v.add("stroke", &sc.hex()); + if sc.alpha() < 1.0 { + v.add("stroke-opacity", &format!("{}", sc.alpha())); + } } } murrelet_draw::draw::MurreletDrawPlan::Line => { @@ -324,7 +334,12 @@ impl GetSvgAttributes for MurreletStyle { v.add_float("stroke-width", self.stroke_weight); v.add("stroke-linejoin", "round"); v.add("stroke-linecap", "round"); - v.add("stroke", &self.color.as_color().hex()); + + let sc = self.color.as_color(); + v.add("stroke", &sc.hex()); + if sc.alpha() < 1.0 { + v.add("stroke-opacity", &format!("{}", sc.alpha())); + } } } } From efce50c2e14feee7c8c08e43246fb66911e35a2b Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 31 Aug 2025 17:57:01 -0400 Subject: [PATCH 064/149] wip --- murrelet_common/src/lib.rs | 10 ++ murrelet_draw/src/curve_drawer.rs | 9 +- murrelet_draw/src/scaffold.rs | 175 ++++++++++++++++++++++++++++++ murrelet_draw/src/style.rs | 17 +++ murrelet_draw/src/transform2d.rs | 7 ++ murrelet_schema/src/lib.rs | 6 +- 6 files changed, 221 insertions(+), 3 deletions(-) diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 7603182..ad50cf1 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -703,6 +703,16 @@ impl FixedPointF32 { pub fn abs(&self) -> Self { Self { x: self.x.abs() } } + + pub fn decode(s: &str) -> Self { + Self { + x: s.parse::().unwrap_or(0), + } + } + + pub fn encode(&self) -> String { + self.x.to_string() + } } impl std::ops::Sub for FixedPointF32 { diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 60666bf..95d834a 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -112,8 +112,13 @@ impl CubicBezierPath { let mut last_ctrl1 = self.ctrl1; + let mut first_ctrl1_used: Option = None; + for s in &self.curves { let ctrl1 = s.ctrl1.or_last(from, last_ctrl1); + if first_ctrl1_used.is_none() { + first_ctrl1_used = Some(ctrl1); + } svg.push(CubicBezier::new(from, ctrl1, s.ctrl2, s.to)); last_ctrl1 = s.ctrl2; from = s.to; @@ -121,7 +126,9 @@ impl CubicBezierPath { if self.closed { let ctrl1 = CubicOptionVec2::none().or_last(from, last_ctrl1); - let ctrl2 = CubicOptionVec2::none().or_last(self.from, self.ctrl1); + // let ctrl2 = CubicOptionVec2::none().or_last(self.from, self.ctrl1); + let ctrl2_source = first_ctrl1_used.unwrap_or(self.ctrl1); + let ctrl2 = CubicOptionVec2::none().or_last(self.from, ctrl2_source); svg.push(CubicBezier::new(from, ctrl1, ctrl2, self.from)); } diff --git a/murrelet_draw/src/scaffold.rs b/murrelet_draw/src/scaffold.rs index 52875bf..56eb455 100644 --- a/murrelet_draw/src/scaffold.rs +++ b/murrelet_draw/src/scaffold.rs @@ -1,3 +1,4 @@ +use geo::{BooleanOps, BoundingRect, Contains}; use glam::{vec2, Vec2}; use itertools::Itertools; @@ -53,3 +54,177 @@ impl ToVec2 for ::geo::Coord { vec2(x as f32, y as f32) } } + +#[derive(Debug, Clone)] +pub struct MaskCacheImpl { + bounding: geo::Rect, + polygon: geo::Polygon, +} +impl MaskCacheImpl { + fn center(&self) -> Vec2 { + 0.5 * (self.bounding.min().to_vec2() + self.bounding.max().to_vec2()) + } + + fn contains(&self, v: &Vec2) -> bool { + self.polygon.contains(&v.to_coord()) + } + + // left needs to be inside + fn last_point_containing(&self, left: &Vec2, right: &Vec2) -> Vec2 { + let mut left = *left; + let mut right = *right; + while left.distance(right) > 0.2 { + let midpoint = 0.5 * (left + right); + if self.contains(&midpoint) { + left = midpoint; + } else { + right = midpoint; + } + } + // finished! + 0.5 * (left + right) + } +} + +#[derive(Debug, Clone)] +pub enum MaskCache { + Impl(MaskCacheImpl), + AlwaysTrue, +} + +impl MaskCache { + fn center(&self) -> Vec2 { + match self { + MaskCache::Impl(s) => s.center(), + MaskCache::AlwaysTrue => Vec2::ZERO, + } + } + + pub fn last_point_containing(&self, left: &Vec2, right: &Vec2) -> Vec2 { + match self { + MaskCache::Impl(s) => s.last_point_containing(left, right), + MaskCache::AlwaysTrue => *right, + } + } + + pub fn new_vec2(curves: &[Vec2]) -> Self { + let polygon = geo::Polygon::new(vec2_to_line_string(curves), vec![]); + + MaskCache::Impl(MaskCacheImpl { + bounding: polygon.bounding_rect().unwrap(), + polygon, + }) + } + + // pub fn new_cd(cd: CurveDrawer) -> Self { + // Self::new(&vec![cd]) + // } + + // pub fn new(curves: &[CurveDrawer]) -> Self { + // // first curve is external + // let (first_curve, rest) = curves.split_first().unwrap(); + // let first = curve_segment_maker_to_line_string(first_curve); + + // let mut remaining = vec![]; + // // add all our points to a hashmap + // for curve_maker in rest { + // remaining.push(curve_segment_maker_to_line_string(curve_maker)); + // } + + // let polygon = ::geo::Polygon::new(first, remaining); + + // MaskCache::Impl(MaskCacheImpl { + // bounding: polygon.bounding_rect().unwrap(), + // polygon, + // }) + // } + + pub fn contains(&self, v: &Vec2) -> bool { + match self { + MaskCache::Impl(x) => x.contains(v), + MaskCache::AlwaysTrue => true, + } + } + + pub fn noop() -> MaskCache { + MaskCache::AlwaysTrue + } + + pub fn crop(&self, shape: &[Vec2]) -> Vec> { + match self { + MaskCache::Impl(x) => { + let other = line_to_polygon(shape); + let cropped = x.polygon.intersection(&other); + multipolygon_to_vec2(&cropped) + } + MaskCache::AlwaysTrue => vec![shape.to_vec()], + } + } + + // remove this object from all of the shapes + fn crop_inverse(&self, shape: &[Vec2]) -> Vec> { + match self { + MaskCache::Impl(x) => { + let other = line_to_polygon(shape); + let cropped = other.difference(&x.polygon); + multipolygon_to_vec2(&cropped) + } + MaskCache::AlwaysTrue => vec![shape.to_vec()], + } + } + + pub fn to_vec2(&self) -> Vec { + match self { + MaskCache::Impl(mask_cache_impl) => polygon_to_vec2(&mask_cache_impl.polygon), + MaskCache::AlwaysTrue => unreachable!(), + } + } + + pub fn crop_line(&self, v: &[Vec2]) -> Vec> { + let mut all_vals = vec![]; + let mut s = vec![]; + let mut last_val = None; + for c in v.into_iter() { + if self.contains(&c) { + last_val = Some(c); + s.push(*c) + } else if let Some(x) = last_val { + let last_point_containing = self.last_point_containing(&x, &c); + s.push(last_point_containing); + + all_vals.push(s); + last_val = None; + s = vec![]; + } + } + if s.len() > 0 { + all_vals.push(s); + } + + all_vals + } + + // pub fn crop_many(&self, v: &[DrawnShape]) -> Vec { + // let mut cropped = vec![]; + // for a in v.into_iter() { + // let mut new_cds = vec![]; + // for cd in a.faces() { + // new_cds.extend(self.crop(cd.vertices())); + // } + // cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); + // } + // return cropped; + // } + + // pub fn crop_inverse_many(&self, v: &[DrawnShape]) -> Vec { + // let mut cropped = vec![]; + // for a in v.into_iter() { + // let mut new_cds = vec![]; + // for cd in a.faces() { + // new_cds.extend(self.crop_inverse(cd.vertices())); + // } + // cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); + // } + // return cropped; + // } +} diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index ac6a11b..ac425c2 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -562,6 +562,7 @@ impl MurreletPath { Self::Polyline(path.as_polyline()) } + pub fn svg_circle(loc: Vec2, rad: f32) -> Self { Self::Svg(TransformedSvgShape::from_shape(SvgShape::circle(loc, rad))) } @@ -643,6 +644,10 @@ impl MurreletPathAnnotation { pub fn vals(&self) -> &Vec<(String, String)> { &self.0 } + + fn new_many(annotations: Vec<(String, String)>) -> MurreletPathAnnotation { + Self(annotations) + } } #[derive(Debug, Clone)] @@ -672,6 +677,18 @@ impl StyledPath { } } + pub fn new_from_path_with_multiple_annotations( + path: MurreletPath, + style: MurreletStyle, + annotations: Vec<(String, String)>, + ) -> Self { + Self { + path, + style, + annotations: MurreletPathAnnotation::new_many(annotations), + } + } + pub fn new(path: F, style: MurreletStyle) -> Self { Self { path: MurreletPath::Polyline(path.as_polyline()), diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index bf4517c..3bd347a 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -71,6 +71,13 @@ impl Transform2d { ))]) } + pub fn rotate_angle(angle_pi: A) -> Transform2d { + Transform2d::new(vec![Transform2dStep::Rotate(Rotate2::new( + Vec2::ZERO, + angle_pi, + ))]) + } + pub fn scale(scale_x: f32, scale_y: f32) -> Transform2d { Transform2d::new(vec![Transform2dStep::Scale(V2::new(vec2( scale_x, scale_y, diff --git a/murrelet_schema/src/lib.rs b/murrelet_schema/src/lib.rs index e586236..97b1306 100644 --- a/murrelet_schema/src/lib.rs +++ b/murrelet_schema/src/lib.rs @@ -78,6 +78,7 @@ impl MurreletSchema { "f32" => Ok(MurreletPrimitive::Num), "number" => Ok(MurreletPrimitive::Num), "num" => Ok(MurreletPrimitive::Num), + "color" => Ok(MurreletPrimitive::Color), "string" => Ok(MurreletPrimitive::String), _ => Result::Err(anyhow!(format!("unsupported schema hint, {}", new_type))), }?; @@ -128,13 +129,14 @@ impl MurreletSchema { } MurreletSchema::Enum(_, _, _) => todo!(), // need to do this one... - MurreletSchema::List(_) => todo!(), + MurreletSchema::List(murrelet_schema) => { + murrelet_schema.update_with_one_hint(original_location, location, value) + } // i think these should be handled in the struct level! MurreletSchema::Val(_) => todo!(), MurreletSchema::Skip => Result::Err(anyhow!("hm, trying to edit a skip")), } } - } // this should be on the Control version From a8b0fbbff3f83166247ba9c34f511c47ecfdf67d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 15 Sep 2025 17:00:45 -0400 Subject: [PATCH 065/149] unit cell idx --- murrelet_draw/src/sequencers.rs | 8 +-- murrelet_livecode/src/expr.rs | 6 +- murrelet_livecode/src/types.rs | 4 +- murrelet_livecode/src/unitcells.rs | 101 ++++++++++++++++------------- murrelet_perform/src/cli.rs | 1 + 5 files changed, 69 insertions(+), 51 deletions(-) diff --git a/murrelet_draw/src/sequencers.rs b/murrelet_draw/src/sequencers.rs index 9438e2e..ad151ae 100644 --- a/murrelet_draw/src/sequencers.rs +++ b/murrelet_draw/src/sequencers.rs @@ -2,7 +2,7 @@ use glam::*; use itertools::Itertools; use lerpable::Lerpable; use murrelet_common::*; -use murrelet_livecode::unitcells::{UnitCellContext, UnitCellCreator, UnitCellExprWorldContext}; +use murrelet_livecode::unitcells::{UnitCellContext, UnitCellCreator, UnitCellIdx}; // use murrelet_livecode_derive::*; use crate::transform2d::{Transform2d, Transform2dStep}; use murrelet_livecode_derive::Livecode; @@ -84,7 +84,7 @@ fn make_grid( (0..y_usize).map(move |y| { let y_idx = IdxInRange::new(y, y_usize); let idx = IdxInRange2d::new_from_idx(x_idx, y_idx); - let ctx = UnitCellExprWorldContext::from_idx2d(idx, 1.0); + let ctx = UnitCellIdx::from_idx2d(idx, 1.0); let mut center = if offset_alternating { let mut center = idx.to_alternating_i().center_of_cell(); @@ -131,7 +131,7 @@ impl UnitCellCreator for SimpleCountSequence { let v = (0..self.0) .map(|x| { let idx = IdxInRange::new(x, self.0); - let ctx = UnitCellExprWorldContext::from_idx1d(idx); + let ctx = UnitCellIdx::from_idx1d(idx); UnitCellContext::new(ctx, SimpleTransform2d::ident()) }) .collect_vec(); @@ -165,7 +165,7 @@ impl UnitCellCreator for SimpleVec2Sequence { (0..y_usize).map(move |y| { let y_idx = IdxInRange::new(y, y_usize); let idx = IdxInRange2d::new_from_idx(x_idx, y_idx); - let ctx = UnitCellExprWorldContext::from_idx2d(idx, 1.0); + let ctx = UnitCellIdx::from_idx2d(idx, 1.0); UnitCellContext::new(ctx, SimpleTransform2d::ident()) }) }) diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index 560a88a..9e84fc8 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -293,6 +293,10 @@ impl ExprWorldContextValues { Self(v) } + pub fn empty() -> Self { + Self::new(vec![]) + } + pub fn to_mixed_defs(&self) -> MixedEvalDefs { MixedEvalDefs::new_from_expr(self.clone()) } @@ -341,7 +345,7 @@ impl ExprWorldContextValues { Self::new(new_vals) } - fn combine(&mut self, vals: ExprWorldContextValues) -> Self { + pub fn combine(&mut self, vals: ExprWorldContextValues) -> Self { // have the new ones added later, so they'll overwrite if there are duplicates... Self::new([self.0.clone(), vals.0].concat()) } diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 3c81271..6625f86 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -12,7 +12,7 @@ use crate::{ expr::IntoExprWorldContext, livecode::{GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeVariable}, state::LivecodeWorldState, - unitcells::UnitCellExprWorldContext, + unitcells::UnitCellIdx, }; #[derive(Debug, Error)] @@ -220,7 +220,7 @@ impl ControlVecElementRepeat { for idx in self.repeat.iter() { let expr = - UnitCellExprWorldContext::from_idx2d(idx, 1.0).as_expr_world_context_values(); + UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); let new_w = w.clone_with_vals(expr, &prefix); for src in &self.what { diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index 409f2a3..6fc0537 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -8,7 +8,7 @@ use rand::{Rng, SeedableRng}; use std::fmt::Debug; use std::{any::Any, collections::HashMap, fmt}; -use crate::expr::{ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefsRef}; +use crate::expr::{ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefs, MixedEvalDefsRef}; use crate::livecode::LivecodeFromWorld; use crate::state::LivecodeWorldState; use crate::types::AdditionalContextNode; @@ -174,7 +174,7 @@ impl UnitCell { } pub fn idx(&self) -> IdxInRange2d { - self.detail.ctx.to_idx2d() + self.detail.idx.to_idx2d() } pub fn is_alternate(&self) -> bool { @@ -186,7 +186,7 @@ impl Default for UnitCell { Self { node: Default::default(), detail: UnitCellContext::new( - UnitCellExprWorldContext::from_idx1d(IdxInRange::new(0, 1)), + UnitCellIdx::from_idx1d(IdxInRange::new(0, 1)), SimpleTransform2d::ident(), ), } @@ -220,7 +220,7 @@ impl UnitCells { pub fn x_y_z_max(&self) -> (u64, u64, u64) { // each should have the same, so grab the first if let Some(first) = self.items.first() { - first.detail.ctx.max() + first.detail.idx.max() } else { (0, 0, 0) } @@ -235,9 +235,9 @@ impl UnitCells { for item in &self.items { hm.insert( ( - item.detail.ctx.x_i, - item.detail.ctx.y_i, - item.detail.ctx.z_i, + item.detail.idx.x_i, + item.detail.idx.y_i, + item.detail.idx.z_i, ), item.clone(), ); @@ -482,25 +482,38 @@ impl Clone for Box { // eh, should make this easier... #[derive(Debug, Clone)] pub struct UnitCellContext { - ctx: UnitCellExprWorldContext, + idx: UnitCellIdx, + ctx: Option, pub detail: UnitCellDetails, pub tile_info: Option>, } impl UnitCellContext { - pub fn new(ctx: UnitCellExprWorldContext, transform: SimpleTransform2d) -> UnitCellContext { + pub fn new(idx: UnitCellIdx, transform: SimpleTransform2d) -> UnitCellContext { UnitCellContext { - ctx, + idx, + ctx: None, detail: UnitCellDetails::new(transform), tile_info: None, } } - pub fn new_with_base( - ctx: UnitCellExprWorldContext, - detail: UnitCellDetails, + pub fn new_expr( + idx: UnitCellIdx, + ctx: ExprWorldContextValues, + transform: SimpleTransform2d, ) -> UnitCellContext { UnitCellContext { - ctx, + idx, + ctx: Some(ctx), + detail: UnitCellDetails::new(transform), + tile_info: None, + } + } + + pub fn new_with_base(ctx: UnitCellIdx, detail: UnitCellDetails) -> UnitCellContext { + UnitCellContext { + idx: ctx, + ctx: None, detail, tile_info: None, } @@ -511,24 +524,26 @@ impl UnitCellContext { } pub fn new_with_info( - ctx: UnitCellExprWorldContext, + ctx: UnitCellIdx, detail: UnitCellDetails, tile_info: Box, ) -> UnitCellContext { UnitCellContext { - ctx, + idx: ctx, + ctx: None, detail, tile_info: Some(tile_info), } } pub fn new_with_option_info( - ctx: UnitCellExprWorldContext, + ctx: UnitCellIdx, detail: UnitCellDetails, tile_info: Option>, ) -> UnitCellContext { UnitCellContext { - ctx, + idx: ctx, + ctx: None, detail, tile_info, } @@ -545,7 +560,8 @@ impl UnitCellContext { // applies the other transform _after_ current pub fn combine(&self, other: &UnitCellContext) -> UnitCellContext { UnitCellContext { - ctx: self.ctx, + idx: self.idx, + ctx: self.ctx.clone(), detail: other.detail.as_wallpaper().unwrap().combine(&self.detail), tile_info: None, } @@ -553,14 +569,15 @@ impl UnitCellContext { pub fn combine_keep_other_ctx(&self, other: &UnitCellContext) -> UnitCellContext { UnitCellContext { - ctx: other.ctx, + idx: self.idx, + ctx: other.ctx.clone(), detail: other.detail.as_wallpaper().unwrap().combine(&self.detail), tile_info: None, } } - pub fn ctx(&self) -> UnitCellExprWorldContext { - self.ctx + pub fn ctx(&self) -> UnitCellIdx { + self.idx } pub fn transform(&self) -> SimpleTransform2d { @@ -568,7 +585,7 @@ impl UnitCellContext { } pub fn idx(&self) -> IdxInRange2d { - self.ctx.to_idx2d() + self.idx.to_idx2d() } pub fn rect_bound(&self) -> Vec { @@ -623,7 +640,7 @@ impl UnitCellContext { impl IntoExprWorldContext for UnitCellContext { fn as_expr_world_context_values(&self) -> ExprWorldContextValues { - let mut ctx_vals = self.ctx.as_expr_world_context_values(); + let mut ctx_vals = self.idx.as_expr_world_context_values(); let loc = self.detail.transform_with_skew(&vec![ vec2(-50.0, -50.0), @@ -637,6 +654,10 @@ impl IntoExprWorldContext for UnitCellContext { ctx_vals.set_val("u_width", LivecodeValue::float(width)); ctx_vals.set_val("u_height", LivecodeValue::float(height)); + if let Some(expr) = &self.ctx { + ctx_vals.combine(expr.clone()); + } + ctx_vals } } @@ -656,7 +677,7 @@ impl Lerpable for UnitCellContext { // world state for unit cell #[derive(Copy, Clone, Debug)] -pub struct UnitCellExprWorldContext { +pub struct UnitCellIdx { x: f32, y: f32, z: f32, @@ -669,10 +690,10 @@ pub struct UnitCellExprWorldContext { seed: f32, h_ratio: f32, // width is always 100, what is h } -impl UnitCellExprWorldContext { +impl UnitCellIdx { // this just needs to be interesting.... not correct pub fn experimental_lerp(&self, other: &Self, pct: f32) -> Self { - UnitCellExprWorldContext { + UnitCellIdx { x: self.x.lerpify(&other.x, &pct), y: self.y.lerpify(&other.y, &pct), z: self.z.lerpify(&other.z, &pct), @@ -687,12 +708,8 @@ impl UnitCellExprWorldContext { } } - pub fn from_idx2d_and_actual_xy( - xy: Vec2, - idx: IdxInRange2d, - h_ratio: f32, - ) -> UnitCellExprWorldContext { - UnitCellExprWorldContext { + pub fn from_idx2d_and_actual_xy(xy: Vec2, idx: IdxInRange2d, h_ratio: f32) -> UnitCellIdx { + UnitCellIdx { x: xy.x, y: xy.y, z: 0.0, @@ -707,8 +724,8 @@ impl UnitCellExprWorldContext { } } - pub fn from_idx1d(idx: IdxInRange) -> UnitCellExprWorldContext { - UnitCellExprWorldContext { + pub fn from_idx1d(idx: IdxInRange) -> UnitCellIdx { + UnitCellIdx { x: idx.pct(), y: 0.0, z: 0.0, @@ -723,8 +740,8 @@ impl UnitCellExprWorldContext { } } - pub fn from_idx2d(idx: IdxInRange2d, h_ratio: f32) -> UnitCellExprWorldContext { - UnitCellExprWorldContext { + pub fn from_idx2d(idx: IdxInRange2d, h_ratio: f32) -> UnitCellIdx { + UnitCellIdx { x: idx.i.pct(), y: idx.j.pct(), z: 0.0, @@ -739,16 +756,12 @@ impl UnitCellExprWorldContext { } } - pub fn from_idx3d( - x_idx: IdxInRange, - y_idx: IdxInRange, - z_idx: IdxInRange, - ) -> UnitCellExprWorldContext { + pub fn from_idx3d(x_idx: IdxInRange, y_idx: IdxInRange, z_idx: IdxInRange) -> UnitCellIdx { let seed = z_idx.i() * (y_idx.total_usize() * x_idx.total_usize()) as u64 + y_idx.i() * (x_idx.total_usize() as u64) + x_idx.i(); - UnitCellExprWorldContext { + UnitCellIdx { x: x_idx.pct(), y: y_idx.pct(), z: z_idx.pct(), @@ -783,7 +796,7 @@ impl UnitCellExprWorldContext { } } -impl IntoExprWorldContext for UnitCellExprWorldContext { +impl IntoExprWorldContext for UnitCellIdx { fn as_expr_world_context_values(&self) -> ExprWorldContextValues { // make a few rns let mut rng = StdRng::seed_from_u64((self.seed + 19247.0) as u64); diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index 25db304..c8ed9db 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -43,6 +43,7 @@ impl Default for TextureDimensions { // height: 2000, // width: 1080, width: 1080, + // width: 1900, height: 1080, // width: 2000, // height: 2000, From 553ea49092c7446841ccf937623599ea48db8cc4 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 18 Sep 2025 14:55:44 -0400 Subject: [PATCH 066/149] wip --- murrelet_common/src/geometry.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index c92b154..2ac4b5a 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -214,6 +214,10 @@ impl Angle { Angle::new(t.transform_vec2(self.to_norm_dir()).to_angle()) } + pub fn rotate_vec2(&self, v: Vec2) -> Vec2 { + Mat3::from_angle(self.angle()).transform_vec2(v) + } + pub fn is_vertical(&self) -> bool { (self.angle_pi() - 0.5 % 1.0) < 1e-2 } @@ -225,6 +229,12 @@ impl Angle { // todo: mirror across angle } +impl TransformVec2 for Angle { + fn transform_vec2(&self, v: Vec2) -> Vec2 { + self.rotate_vec2(v) + } +} + impl std::fmt::Debug for Angle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { AnglePi::fmt(&(*self).into(), f) @@ -463,6 +473,10 @@ impl SpotOnCurve { c.angle = new.into(); c } + + pub fn line_to_spot(&self, length: f32) -> Vec2 { + self.loc() + self.angle().to_norm_dir() * length + } } #[derive(Clone, Copy, Debug)] From 8cece25bbc38f5e0a6312de8bf418996b538c74c Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 20 Sep 2025 15:47:34 -0400 Subject: [PATCH 067/149] wip --- murrelet_perform/src/perform.rs | 3 ++- murrelet_src_osc/src/osc.rs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 4d78ae2..3ea8855 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -939,7 +939,8 @@ where self.livecode_src.update(&update_input); - if app.elapsed_frames() % 20 == 0 { + // todo, set this as a variable? + if app.elapsed_frames() % 1 == 0 { let variables = self .controlconfig .variable_identifiers() diff --git a/murrelet_src_osc/src/osc.rs b/murrelet_src_osc/src/osc.rs index e967104..5c1a77f 100644 --- a/murrelet_src_osc/src/osc.rs +++ b/murrelet_src_osc/src/osc.rs @@ -53,7 +53,10 @@ impl IsLivecodeSrc for OscMng { let packet_data = rosc::encoder::encode(&OscPacket::Message(msg)); if let Ok(pd) = packet_data { if let Some(a) = &self.cxn.target_addr { - print_expect(self.cxn.send_socket.send_to(&pd, a), "failed to send osc"); + print_expect( + self.cxn.send_socket.send_to(&pd, a), + "failed to send osc", + ); } } } From 15f2f4b088dab8ffc2e3331d1b249ff5c76705e7 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 22 Sep 2025 00:57:42 -0400 Subject: [PATCH 068/149] show --- murrelet_livecode/src/expr.rs | 35 +++++++++++++++++++++++++----- murrelet_livecode/src/unitcells.rs | 22 +++++++++++++++---- murrelet_perform/src/cli.rs | 2 +- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index 9e84fc8..f1c7b2a 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -1,5 +1,5 @@ #![allow(dead_code)] -use std::{f64::consts::PI, fmt::Debug, sync::Arc}; +use std::{collections::HashMap, f64::consts::PI, fmt::Debug, sync::Arc}; use evalexpr::*; use glam::{vec2, Vec2}; @@ -9,6 +9,7 @@ use noise::{NoiseFn, Perlin}; use rand::{rngs::StdRng, Rng, SeedableRng}; use crate::types::{AdditionalContextNode, LivecodeError, LivecodeResult}; +use regex::Regex; pub fn init_evalexpr_func_ctx() -> LivecodeResult { context_map!{ @@ -287,10 +288,10 @@ fn lc_val_to_expr(v: &LivecodeValue) -> Value { // simple mapping of values #[derive(Debug, Clone)] -pub struct ExprWorldContextValues(Vec<(String, LivecodeValue)>); +pub struct ExprWorldContextValues(HashMap); impl ExprWorldContextValues { pub fn new(v: Vec<(String, LivecodeValue)>) -> Self { - Self(v) + Self(v.into_iter().collect()) } pub fn empty() -> Self { @@ -313,7 +314,20 @@ impl ExprWorldContextValues { } pub fn set_val(&mut self, name: &str, val: LivecodeValue) { - self.0.push((name.to_owned(), val)) + if self.0.contains_key(name) { + let re = Regex::new(r"^(.*?)(\d+)$").unwrap(); + let new_name = if let Some(caps) = re.captures(name) { + let base = caps.get(1).map_or("", |m| m.as_str()); + let num = caps.get(2).map_or("0", |m| m.as_str()); + let num: u32 = num.parse().unwrap_or(0); + format!("{}{}", base, num + 1) + } else { + format!("{}_0", name) + }; + self.0.insert(new_name, val); + } else { + self.0.insert(name.to_owned(), val); + } } pub fn new_from_idx(idx: IdxInRange) -> Self { @@ -321,6 +335,7 @@ impl ExprWorldContextValues { ("i".to_string(), LivecodeValue::Int(idx.i() as i64)), ("if".to_string(), LivecodeValue::Float(idx.i() as f64)), ("pct".to_string(), LivecodeValue::Float(idx.pct() as f64)), + ("x".to_string(), LivecodeValue::Float(idx.pct() as f64)), // just in case i use the wrong one ("total".to_string(), LivecodeValue::Int(idx.total() as i64)), ( "totalf".to_string(), @@ -347,7 +362,15 @@ impl ExprWorldContextValues { pub fn combine(&mut self, vals: ExprWorldContextValues) -> Self { // have the new ones added later, so they'll overwrite if there are duplicates... - Self::new([self.0.clone(), vals.0].concat()) + + let mut new = self.clone(); + + for (name, val) in vals.0 { + new.set_val(&name, val); + } + + // Self::new([self.0.clone(), vals.0].concat()) + new } } @@ -360,7 +383,7 @@ impl IntoExprWorldContext for Vec<(String, f32)> { let v = self .iter() .map(|(s, x)| (s.to_owned(), LivecodeValue::Float(*x as f64))) - .collect_vec(); + .collect(); ExprWorldContextValues(v) } } diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index 6fc0537..6072c73 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -8,7 +8,7 @@ use rand::{Rng, SeedableRng}; use std::fmt::Debug; use std::{any::Any, collections::HashMap, fmt}; -use crate::expr::{ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefs, MixedEvalDefsRef}; +use crate::expr::{ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefsRef}; use crate::livecode::LivecodeFromWorld; use crate::state::LivecodeWorldState; use crate::types::AdditionalContextNode; @@ -559,18 +559,32 @@ impl UnitCellContext { // just updates details... // applies the other transform _after_ current pub fn combine(&self, other: &UnitCellContext) -> UnitCellContext { + let ctx = match (&self.ctx, &other.ctx) { + (None, None) => self.ctx.clone(), + (None, Some(that)) => Some(that.clone()), + (Some(this), None) => Some(this.clone()), + (Some(this), Some(that)) => Some(this.clone().combine(that.clone())), + }; + UnitCellContext { idx: self.idx, - ctx: self.ctx.clone(), + ctx, detail: other.detail.as_wallpaper().unwrap().combine(&self.detail), tile_info: None, } } pub fn combine_keep_other_ctx(&self, other: &UnitCellContext) -> UnitCellContext { + let ctx = match (&self.ctx, &other.ctx) { + (None, None) => self.ctx.clone(), + (None, Some(that)) => Some(that.clone()), + (Some(this), None) => Some(this.clone()), + (Some(this), Some(that)) => Some(that.clone().combine(this.clone())), + }; + UnitCellContext { idx: self.idx, - ctx: other.ctx.clone(), + ctx: ctx, detail: other.detail.as_wallpaper().unwrap().combine(&self.detail), tile_info: None, } @@ -655,7 +669,7 @@ impl IntoExprWorldContext for UnitCellContext { ctx_vals.set_val("u_height", LivecodeValue::float(height)); if let Some(expr) = &self.ctx { - ctx_vals.combine(expr.clone()); + ctx_vals = ctx_vals.combine(expr.clone()); } ctx_vals diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index c8ed9db..585ce72 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -42,8 +42,8 @@ impl Default for TextureDimensions { // width: 3000, // height: 2000, // width: 1080, + // width: 1080, width: 1080, - // width: 1900, height: 1080, // width: 2000, // height: 2000, From 170e442d90b68d7d623b5178c8d451ba68ff50b8 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 26 Sep 2025 23:21:11 -0400 Subject: [PATCH 069/149] fix a bug --- murrelet_common/src/idx.rs | 14 ++++++++++++++ murrelet_livecode/src/types.rs | 31 +++++++++++-------------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 85e2e5b..6a33bef 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -124,6 +124,10 @@ impl IdxInRange { pub fn scale(&self, start: f32, end: f32) -> f32 { lerp(start, end, self.pct()) } + + pub fn to_2d(&self) -> IdxInRange2d { + IdxInRange2d::new_from_single_idx(*self) + } } #[derive(Debug, Clone, Copy)] @@ -143,6 +147,16 @@ impl IdxInRange2d { } } + pub fn enumerate_counts(ii: usize, jj: usize) -> Vec { + let mut v = vec![]; + for i in 0..ii { + for j in 0..jj { + v.push(IdxInRange2d::new_rect(i, j, ii, jj)); + } + } + v + } + pub fn to_alternating_i(&self) -> IdxInRange2d { IdxInRange2d { i: IdxInRange::new(self.i.i() / 2, self.i.total / 2), diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 6625f86..9e89078 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -3,7 +3,7 @@ use std::{collections::HashSet, fmt::Debug}; use evalexpr::{build_operator_tree, EvalexprError, HashMapContext, Node}; use itertools::Itertools; use lerpable::{step, Lerpable}; -use murrelet_common::IdxInRange2d; +use murrelet_common::{IdxInRange, IdxInRange2d}; use murrelet_gui::CanMakeGUI; use serde::{Deserialize, Deserializer}; use thiserror::Error; @@ -125,22 +125,11 @@ impl ControlVecElementRepeatMethod { } fn iter(&self) -> Vec { match self { - ControlVecElementRepeatMethod::Single(s) => { - let mut v = vec![]; - for i in 0..*s { - v.push(IdxInRange2d::new(i, 1, *s)); - } - v - } - ControlVecElementRepeatMethod::Rect(s) => { - let mut v = vec![]; - for i in 0..s[0] { - for j in 0..s[1] { - v.push(IdxInRange2d::new_rect(i, j, s[0], s[1])); - } - } - v - } + ControlVecElementRepeatMethod::Single(s) => IdxInRange::enumerate_count(*s) + .iter() + .map(|x| x.to_2d()) + .collect_vec(), + ControlVecElementRepeatMethod::Rect(s) => IdxInRange2d::enumerate_counts(s[0], s[1]), } } } @@ -219,8 +208,7 @@ impl ControlVecElementRepeat { }; for idx in self.repeat.iter() { - let expr = - UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); + let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); let new_w = w.clone_with_vals(expr, &prefix); for src in &self.what { @@ -349,7 +337,10 @@ where ..Default::default() })), metadata: Some(Box::new(schemars::schema::Metadata { - description: Some("Either a single element (inline) OR a repeat object { repeat, prefix?, what }".to_string()), + description: Some( + "Either a single element (inline) OR a repeat object { repeat, prefix?, what }" + .to_string(), + ), ..Default::default() })), ..Default::default() From e3d8ff3fb30980faa760ba4ac9ff7cd7d013f192 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 1 Oct 2025 22:50:05 -0400 Subject: [PATCH 070/149] wi --- murrelet_gui/examples/tests_schema.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/murrelet_gui/examples/tests_schema.rs b/murrelet_gui/examples/tests_schema.rs index 254f48a..acb4070 100644 --- a/murrelet_gui/examples/tests_schema.rs +++ b/murrelet_gui/examples/tests_schema.rs @@ -105,3 +105,5 @@ // ) // ); // } + +fn main() {} From b60276100ad9c9b45998000db03906d257f2d90a Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 2 Oct 2025 18:08:23 -0400 Subject: [PATCH 071/149] refactor editable shaders --- murrelet_gpu/src/editable_shaders.rs | 116 +++++++++++++++++++++++++++ murrelet_gpu/src/lib.rs | 1 + 2 files changed, 117 insertions(+) create mode 100644 murrelet_gpu/src/editable_shaders.rs diff --git a/murrelet_gpu/src/editable_shaders.rs b/murrelet_gpu/src/editable_shaders.rs new file mode 100644 index 0000000..f8048ba --- /dev/null +++ b/murrelet_gpu/src/editable_shaders.rs @@ -0,0 +1,116 @@ +use std::collections::HashMap; + +use crate::gpu_macros::ShaderStr; +use lerpable::Lerpable; +use murrelet_livecode_derive::Livecode; +use wgpu_for_latest::naga; +#[cfg(feature = "nannou")] +use wgpu_for_nannou as wgpu; + +#[cfg(not(feature = "nannou"))] +use wgpu_for_latest as wgpu; + +use crate::{ + build_shader, build_shader_2tex, + device_state::GraphicsWindowConf, + graphics_ref::{GraphicsCreator, GraphicsRef}, +}; + +#[derive(Debug, Clone, Livecode, Lerpable)] +pub struct ShaderStrings { + #[livecode(kind = "none")] + #[lerpable(method = "skip")] + shaders: HashMap, +} +impl ShaderStrings { + fn shader(shader: &str) -> String { + build_shader! { + ( + raw shader; + ) + } + } + + fn shader2tex(shader: &str) -> String { + build_shader_2tex! { + ( + raw shader; + ) + } + } + + pub fn get_graphics_ref(&self, c: &GraphicsWindowConf, name: &str) -> Option { + if let Some(str) = self.shaders.get(name) { + Some( + GraphicsCreator::default() + .with_mag_filter(wgpu::FilterMode::Nearest) + .to_graphics_ref(c, name, &Self::shader(&str)), + ) + } else { + None + } + } + + pub fn get_graphics_ref_2tex(&self, c: &GraphicsWindowConf, name: &str) -> Option { + if let Some(str) = self.shaders.get(name) { + Some( + GraphicsCreator::default() + .with_mag_filter(wgpu::FilterMode::Nearest) + .with_second_texture() + .to_graphics_ref(c, name, &Self::shader2tex(&str)), + ) + } else { + None + } + } + + pub fn has_changed(&self, other: &ControlShaderStrings) -> bool { + self.shaders != other.shaders + } + + pub fn naga_if_needed(&self, prev_shaders: &ControlShaderStrings) -> bool { + if self.has_changed(&prev_shaders) { + let mut all_success = true; + + for (name, shader_str) in self.shaders.iter() { + let t = ShaderStrings::shader2tex(&shader_str); + if let Err(err) = naga::front::wgsl::parse_str(&t) { + println!( + "error with shader {:?}, {:?}, not updating until it works!", + name, err + ); + all_success = false; + } + } + + all_success + } else { + false + } + } +} + +impl ControlShaderStrings { + fn to_normal(&self) -> ShaderStrings { + ShaderStrings { + shaders: self.shaders.clone(), + } + } + + pub fn should_update( + &self, + prev: &ControlShaderStrings, + force_reload: bool, + ) -> Option { + let shaders = self.to_normal(); + + let shader_changed_and_compiles = shaders.naga_if_needed(&prev); + + if force_reload || shader_changed_and_compiles { + // just in case there's lerp, be sure to use the one we tested + Some(shaders) + } else { + None + } + } +} diff --git a/murrelet_gpu/src/lib.rs b/murrelet_gpu/src/lib.rs index cbe23f9..178791a 100644 --- a/murrelet_gpu/src/lib.rs +++ b/murrelet_gpu/src/lib.rs @@ -1,4 +1,5 @@ pub mod device_state; +pub mod editable_shaders; pub mod gpu_livecode; pub mod gpu_macros; pub mod graphics_ref; From 8aba1021eb97a3a31414ee977726f949d3623d0c Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 4 Oct 2025 19:06:55 -0400 Subject: [PATCH 072/149] wi --- murrelet_gpu/src/shader_str.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/murrelet_gpu/src/shader_str.rs b/murrelet_gpu/src/shader_str.rs index 691d953..58593fd 100644 --- a/murrelet_gpu/src/shader_str.rs +++ b/murrelet_gpu/src/shader_str.rs @@ -130,6 +130,20 @@ fn rand2(n: vec2) -> f32 { return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); } + // more things + fn hash21(p: vec2) -> f32 { + // Dave-Hoskins style + let p3 = fract(vec3(p.x, p.y, p.x) * 0.1031); + let p3x = p3.x + dot(p3, p3.yzx + 33.33); + return fract((p3x + p3.y) * p3.z); +} + +fn hash22(p: vec2) -> vec2 { + var p3 = fract(vec3(p.x, p.y, p.x) * 0.1031); + p3 = p3 + dot(p3, p3.yzx + 33.33); + return fract((p3.xx + p3.yz) * p3.zy); +} + // i don't know where this went fn smoothStep(edge0: vec2, edge1: vec2, x: vec2) -> vec2 { let t: vec2 = clamp((x - edge0) / (edge1 - edge0), vec2(0.0, 0.0), vec2(1.0, 1.0)); @@ -195,6 +209,10 @@ fn is_almost_zero(v: f32) -> f32 { return 1.0 - is_almost_nonzero(v); } + fn soft_disk(d: f32, r: f32, w: f32) -> f32 { + return 1.0 - smoothstep(r - w, r, d); +} + fn fbm(i: vec2) -> f32 { var p = i; From 21c0627a63d4989fc12ecf417689ce97b35173e9 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 6 Oct 2025 11:51:04 -0400 Subject: [PATCH 073/149] wip --- murrelet_draw/src/tesselate.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 30464ba..8a297bf 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -5,6 +5,7 @@ use crate::{ curve_drawer::{CubicBezierPath, CurveDrawer, CurveSegment}, svg::SvgPathDef, }; +use delaunator::Triangulation; use glam::{vec2, Vec2, Vec2Swizzles}; use itertools::Itertools; use kurbo::BezPath; @@ -873,7 +874,7 @@ impl LayersFromSvg { } } -pub fn tesselate_delauney(v: Vec) -> (Vec, Vec) { +pub fn tesselate_delauney(v: Vec) -> (Vec, Vec, Triangulation) { let points: Vec<_> = v .iter() .map(|vertex| delaunator::Point { @@ -917,5 +918,5 @@ pub fn tesselate_delauney(v: Vec) -> (Vec, Vec) } let vertices = v.clone(); - (filtered_indices, vertices) + (filtered_indices, vertices, triangulation) } From e894b946c5482fd4bfc05fda2816dea66a253866 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 9 Oct 2025 21:20:13 -0400 Subject: [PATCH 074/149] wip --- murrelet_common/src/color.rs | 7 +++++++ murrelet_perform/src/cli.rs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index 9d71925..abd5c6f 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, Mul}, }; +use glam::Vec3; use lerpable::{IsLerpingMethod, Lerpable}; // use murrelet_gui::CanMakeGUI; use palette::{rgb::Rgb, FromColor, Hsva, IntoColor, LinSrgb, LinSrgba, Srgb, Srgba, WithAlpha}; @@ -58,6 +59,12 @@ impl MurreletColor { Self::from_srgba(c) } + pub fn rgb_vec3(rgb: Vec3) -> Self { + let (r, g, b) = rgb.into(); + let c = Srgba::new(r, g, b, 1.0); + Self::from_srgba(c) + } + pub fn gray(g: f32) -> Self { Self::srgb(g, g, g) } diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index 585ce72..e932baf 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -63,7 +63,7 @@ pub struct BaseConfigArgs { #[arg(short, long, default_value_t = Default::default())] pub resolution: TextureDimensions, // window resolution - #[arg(long, default_value_t = 4, value_parser = clap::value_parser!(u32).range(1..=8))] + #[arg(long, default_value_t = 2, value_parser = clap::value_parser!(u32).range(1..=8))] pub texture_multiplier: u32, // controls number of pixels the shaders work on #[arg(long)] From 4a75d423075f37bb8feb21e56de337b1f27d77bd Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 12 Oct 2025 16:59:06 -0400 Subject: [PATCH 075/149] start refactoring gpu so i can add compute shaders --- murrelet_gpu/src/compute.rs | 48 ++++++++++++++ murrelet_gpu/src/gpu_macros.rs | 4 +- murrelet_gpu/src/graphics_ref.rs | 104 +----------------------------- murrelet_gpu/src/lib.rs | 3 + murrelet_gpu/src/uniforms.rs | 105 +++++++++++++++++++++++++++++++ murrelet_gpu/src/window.rs | 59 +++++++++++++++++ 6 files changed, 219 insertions(+), 104 deletions(-) create mode 100644 murrelet_gpu/src/compute.rs create mode 100644 murrelet_gpu/src/uniforms.rs create mode 100644 murrelet_gpu/src/window.rs diff --git a/murrelet_gpu/src/compute.rs b/murrelet_gpu/src/compute.rs new file mode 100644 index 0000000..1c82850 --- /dev/null +++ b/murrelet_gpu/src/compute.rs @@ -0,0 +1,48 @@ +use std::{cell::RefCell, rc::Rc}; + +#[cfg(feature = "nannou")] +use wgpu_for_nannou as wgpu; + +#[cfg(not(feature = "nannou"))] +use wgpu_for_latest as wgpu; + +use crate::{uniforms::BasicUniform, window::GraphicsWindowConf}; + +// like Graphics, what's needed to create a compute pipeline +pub struct ComputeGraphics { + name: String, + // conf: GraphicsCreator, + // bind_group: wgpu::BindGroup, + // vertex_buffers: VertexBuffers, + // render_pipeline: wgpu::RenderPipeline, + pub uniforms: BasicUniform, + pub uniforms_buffer: wgpu::Buffer, // used internally + // pub input_texture_view: wgpu::TextureView, + // pub input_texture_view_other: Option, + // sampler: wgpu::Sampler, + // bind_group_layout: wgpu::BindGroupLayout, + // // i guess need this to create nannou texture + // pub texture_and_desc: TextureAndDesc, + // pub other_texture_and_desc: Option, + // textures_for_3d: Option, +} +impl ComputeGraphics { + pub fn update_uniforms_other( + &mut self, + c: &GraphicsWindowConf, + more_info: [f32; 4], + more_info_other: [f32; 4], + ) { + let queue = &c.device.queue(); + self.uniforms.more_info = more_info; + self.uniforms.more_info_other = more_info_other; + + // println!("{:?}", self.uniform.more_info); + queue.write_buffer(&self.uniforms_buffer, 0, self.uniforms.as_bytes()); + } +} + +#[derive(Clone)] +pub struct ComputeShaderRef { + pub graphics: Rc>, +} diff --git a/murrelet_gpu/src/gpu_macros.rs b/murrelet_gpu/src/gpu_macros.rs index 72d3fa2..1f0f551 100644 --- a/murrelet_gpu/src/gpu_macros.rs +++ b/murrelet_gpu/src/gpu_macros.rs @@ -9,7 +9,7 @@ use crate::{ device_state::{DeviceStateForRender, GraphicsAssets, GraphicsWindowConf}, gpu_livecode::ControlGraphicsRef, graphics_ref::{ - BasicUniform, Graphics, GraphicsCreator, GraphicsRef, GraphicsRefWithControlFn, + Graphics, GraphicsCreator, GraphicsRef, GraphicsRefWithControlFn, DEFAULT_LOADED_TEXTURE_FORMAT, }, shader_str::*, @@ -938,7 +938,7 @@ impl VideoTexture { ) }; - let _uniforms = BasicUniform::from_dims(c.dims); + // let _uniforms = BasicUniform::from_dims(c.dims); let conf = GraphicsCreator::default() .with_first_texture_format(DEFAULT_TEXTURE_FORMAT) diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index 50dabbb..75c5aae 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -20,6 +20,7 @@ use wgpu::TextureDescriptor; use crate::device_state::*; use crate::gpu_livecode::{ControlGraphics, ControlGraphicsRef}; use crate::shader_str::{VERTEX_SHADER, VERTEX_SHADER_3D}; +use crate::uniforms::{BasicUniform, UniformsPair}; #[cfg(not(feature = "nannou"))] pub const DEFAULT_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm; @@ -490,104 +491,6 @@ impl GraphicsCreator { } } -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct BasicUniform { - dims: [f32; 4], - more_info: [f32; 4], - more_info_other: [f32; 4], -} - -unsafe impl Zeroable for BasicUniform {} -unsafe impl Pod for BasicUniform {} - -impl BasicUniform { - fn empty_4() -> [f32; 4] { - [0.0, 0.0, 0.0, 0.0] - } - - pub fn from_empty() -> BasicUniform { - BasicUniform { - dims: BasicUniform::empty_4(), - more_info: BasicUniform::empty_4(), - more_info_other: BasicUniform::empty_4(), - } - } - - fn _dims_to_more_info(w: f32, h: f32) -> [f32; 4] { - [w, h, 1.0 / w, 1.0 / h] - } - - pub fn from_dims([w, h]: [u32; 2]) -> BasicUniform { - let w_f32 = w as f32; - let h_f32 = h as f32; - let dims = BasicUniform::_dims_to_more_info(w_f32, h_f32); - BasicUniform { - dims, - more_info: BasicUniform::empty_4(), - more_info_other: BasicUniform::empty_4(), - } - } - - pub fn from_dims_and_more([w, h]: [u32; 2], more_info: [f32; 4]) -> BasicUniform { - let w_f32 = w as f32; - let h_f32 = h as f32; - let dims = BasicUniform::_dims_to_more_info(w_f32, h_f32); - BasicUniform { - dims, - more_info, - more_info_other: BasicUniform::empty_4(), - } - } - - pub fn update_more_info(&mut self, more_info: [f32; 4]) { - self.more_info = more_info - } - - pub fn update_more_info_other(&mut self, more_info: [f32; 4]) { - self.more_info_other = more_info - } - - fn as_bytes(&self) -> &[u8] { - bytemuck::bytes_of(self) - } - - fn uniforms_size(&self) -> u64 { - std::mem::size_of::() as wgpu::BufferAddress - } - - fn to_buffer(&self, device: &wgpu::Device) -> wgpu::Buffer { - device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: self.uniforms_size(), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }) - } - - // fn copy_to_buffer( - // &self, - // dest: &wgpu::Buffer, - // device: &wgpu::Device, - // encoder: &mut wgpu::CommandEncoder, - // ) { - // println!("copy to buffer"); - // encoder.copy_buffer_to_buffer(&self.to_buffer(device), 0, dest, 0, self.uniforms_size()); - // } -} - -pub struct UniformsPair { - more_info: [f32; 4], - more_info_other: [f32; 4], -} -impl UniformsPair { - pub fn new(more_info: [f32; 4], more_info_other: [f32; 4]) -> UniformsPair { - UniformsPair { - more_info, - more_info_other, - } - } -} #[derive(Clone)] pub struct GraphicsRef { @@ -1125,10 +1028,7 @@ impl Graphics { let color_state = vec![Some(wgpu::ColorTargetState { format: dst_format, - // blend: None, - // blend: Some(wgpu::BlendState::ALPHA_BLENDING), - // blend: Some(wgpu::BlendState::REPLACE), //None, - blend: Some(conf.blend_state()), // todo get this to work! + blend: Some(conf.blend_state()), write_mask: wgpu::ColorWrites::ALL, })]; diff --git a/murrelet_gpu/src/lib.rs b/murrelet_gpu/src/lib.rs index 178791a..63373d4 100644 --- a/murrelet_gpu/src/lib.rs +++ b/murrelet_gpu/src/lib.rs @@ -4,3 +4,6 @@ pub mod gpu_livecode; pub mod gpu_macros; pub mod graphics_ref; pub mod shader_str; +pub mod compute; +pub mod uniforms; +pub mod window; diff --git a/murrelet_gpu/src/uniforms.rs b/murrelet_gpu/src/uniforms.rs new file mode 100644 index 0000000..1a9e70b --- /dev/null +++ b/murrelet_gpu/src/uniforms.rs @@ -0,0 +1,105 @@ +use bytemuck::{Pod, Zeroable}; +#[cfg(feature = "nannou")] +use wgpu_for_nannou as wgpu; + +#[cfg(not(feature = "nannou"))] +use wgpu_for_latest as wgpu; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct BasicUniform { + dims: [f32; 4], + pub more_info: [f32; 4], + pub more_info_other: [f32; 4], +} + +unsafe impl Zeroable for BasicUniform {} +unsafe impl Pod for BasicUniform {} + +impl BasicUniform { + fn empty_4() -> [f32; 4] { + [0.0, 0.0, 0.0, 0.0] + } + + pub fn from_empty() -> BasicUniform { + BasicUniform { + dims: BasicUniform::empty_4(), + more_info: BasicUniform::empty_4(), + more_info_other: BasicUniform::empty_4(), + } + } + + fn _dims_to_more_info(w: f32, h: f32) -> [f32; 4] { + [w, h, 1.0 / w, 1.0 / h] + } + + pub fn from_dims([w, h]: [u32; 2]) -> BasicUniform { + let w_f32 = w as f32; + let h_f32 = h as f32; + let dims = BasicUniform::_dims_to_more_info(w_f32, h_f32); + BasicUniform { + dims, + more_info: BasicUniform::empty_4(), + more_info_other: BasicUniform::empty_4(), + } + } + + pub fn from_dims_and_more([w, h]: [u32; 2], more_info: [f32; 4]) -> BasicUniform { + let w_f32 = w as f32; + let h_f32 = h as f32; + let dims = BasicUniform::_dims_to_more_info(w_f32, h_f32); + BasicUniform { + dims, + more_info, + more_info_other: BasicUniform::empty_4(), + } + } + + pub fn update_more_info(&mut self, more_info: [f32; 4]) { + self.more_info = more_info + } + + pub fn update_more_info_other(&mut self, more_info: [f32; 4]) { + self.more_info_other = more_info + } + + pub fn as_bytes(&self) -> &[u8] { + bytemuck::bytes_of(self) + } + + fn uniforms_size(&self) -> u64 { + std::mem::size_of::() as wgpu::BufferAddress + } + + pub fn to_buffer(&self, device: &wgpu::Device) -> wgpu::Buffer { + device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: self.uniforms_size(), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }) + } + + // fn copy_to_buffer( + // &self, + // dest: &wgpu::Buffer, + // device: &wgpu::Device, + // encoder: &mut wgpu::CommandEncoder, + // ) { + // println!("copy to buffer"); + // encoder.copy_buffer_to_buffer(&self.to_buffer(device), 0, dest, 0, self.uniforms_size()); + // } +} + +pub struct UniformsPair { + pub more_info: [f32; 4], + pub more_info_other: [f32; 4], +} +impl UniformsPair { + pub fn new(more_info: [f32; 4], more_info_other: [f32; 4]) -> UniformsPair { + UniformsPair { + more_info, + more_info_other, + } + } +} diff --git a/murrelet_gpu/src/window.rs b/murrelet_gpu/src/window.rs new file mode 100644 index 0000000..c6ea37e --- /dev/null +++ b/murrelet_gpu/src/window.rs @@ -0,0 +1,59 @@ +// stores info about the window.. + +use crate::device_state::{DeviceState, GraphicsAssets}; +#[cfg(feature = "nannou")] +use wgpu_for_nannou as wgpu; + +#[cfg(not(feature = "nannou"))] +use wgpu_for_latest as wgpu; + +#[derive(Clone, Debug)] +pub struct GraphicsWindowConf<'a> { + pub device: &'a DeviceState<'a>, + pub dims: [u32; 2], + pub assets_path: GraphicsAssets, +} +impl<'a> GraphicsWindowConf<'a> { + pub fn new( + device: &'a DeviceState, + dims: [u32; 2], + assets_path: GraphicsAssets, + ) -> GraphicsWindowConf<'a> { + GraphicsWindowConf { + device, + dims, + assets_path, + } + } + + pub fn multi(&self, multiplier: f32) -> GraphicsWindowConf { + let [x, y] = self.dims; + GraphicsWindowConf { + device: self.device, + dims: [ + (x as f32 * multiplier) as u32, + (y as f32 * multiplier) as u32, + ], + assets_path: GraphicsAssets::Nothing, + } + } + + pub fn dims(&self) -> [u32; 2] { + self.dims + } + + pub fn device(&self) -> &wgpu::Device { + &self.device.device() + } + + pub fn with_dims(&self, dims: [u32; 2]) -> Self { + Self { + dims, + ..self.clone() + } + } + + pub fn queue(&self) -> &wgpu::Queue { + self.device.queue() + } +} \ No newline at end of file From 8a1f2f990e82392d7f044bb9cb109963be9f629d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 12 Oct 2025 20:22:49 -0400 Subject: [PATCH 076/149] almost there with compute shader --- murrelet_gpu/src/compute.rs | 358 ++++++++++++++++++++++++++- murrelet_gpu/src/device_state.rs | 51 ---- murrelet_gpu/src/editable_shaders.rs | 3 +- murrelet_gpu/src/gpu_livecode.rs | 4 +- murrelet_gpu/src/gpu_macros.rs | 69 +++++- murrelet_gpu/src/graphics_ref.rs | 9 +- murrelet_gpu/src/shader_str.rs | 70 ++++++ 7 files changed, 494 insertions(+), 70 deletions(-) diff --git a/murrelet_gpu/src/compute.rs b/murrelet_gpu/src/compute.rs index 1c82850..28ba709 100644 --- a/murrelet_gpu/src/compute.rs +++ b/murrelet_gpu/src/compute.rs @@ -1,22 +1,77 @@ -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, rc::Rc, sync::Arc}; +use bytemuck::Pod; #[cfg(feature = "nannou")] use wgpu_for_nannou as wgpu; #[cfg(not(feature = "nannou"))] use wgpu_for_latest as wgpu; -use crate::{uniforms::BasicUniform, window::GraphicsWindowConf}; +use wgpu::util::DeviceExt; + +use crate::{ + device_state::{DeviceState, DeviceStateForRender}, + graphics_ref::{shader_from_path, GraphicsRef, TextureAndDesc, DEFAULT_TEXTURE_FORMAT}, + uniforms::BasicUniform, + window::GraphicsWindowConf, +}; + +struct ComputeBindings { + input: wgpu::Buffer, + cell_offsets: wgpu::Buffer, + cell_indices: wgpu::Buffer, + uniforms: wgpu::Buffer, +} + +pub struct CSR { + offsets: Vec, // size is N + 1, contains the start/end of each group! fence post + indices: Vec, +} +impl CSR { + fn empty() -> CSR { + Self { + offsets: vec![0, 0], // need to put something... + indices: vec![0], + } + } +} + +pub struct CSRData { + cells: Vec>, +} +impl CSRData { + pub fn new(cells: Vec>) -> Self { + Self { cells } + } + + fn for_buffers(self) -> CSR { + let mut offsets = Vec::with_capacity(self.cells.len() + 1); + let mut indices = Vec::new(); + offsets.push(0); + for v in self.cells { + indices.extend_from_slice(&v); + offsets.push(indices.len() as u32); + } + CSR { offsets, indices } + } +} // like Graphics, what's needed to create a compute pipeline -pub struct ComputeGraphics { +pub struct ComputeGraphicsToTexture { name: String, // conf: GraphicsCreator, // bind_group: wgpu::BindGroup, // vertex_buffers: VertexBuffers, // render_pipeline: wgpu::RenderPipeline, pub uniforms: BasicUniform, - pub uniforms_buffer: wgpu::Buffer, // used internally + pub buffers: ComputeBindings, + texture: TextureAndDesc, + + bind_group_layout: wgpu::BindGroupLayout, + pipeline: wgpu::ComputePipeline, + data: Vec, + dims: [u32; 2], + // used internally // pub input_texture_view: wgpu::TextureView, // pub input_texture_view_other: Option, // sampler: wgpu::Sampler, @@ -26,7 +81,244 @@ pub struct ComputeGraphics { // pub other_texture_and_desc: Option, // textures_for_3d: Option, } -impl ComputeGraphics { +impl ComputeGraphicsToTexture { + pub fn init<'a>( + name: String, + c: &GraphicsWindowConf<'a>, + compute_shader: &str, + ) -> ComputeGraphicsToTextureRef { + ComputeGraphicsToTextureRef::new(Rc::new(RefCell::new(Self::new( + name, + c, + compute_shader, + BasicUniform::from_empty(), + CSR::empty(), + vec![], + )))) + } + + pub fn new<'a>( + name: String, + c: &GraphicsWindowConf<'a>, + shader_data: &str, + initial_uniform: BasicUniform, + csr: CSR, // helps limit what data you need to check per cell + data: Vec, + ) -> Self { + let device: &wgpu::Device = c.device.device(); + + let input_data_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&data), + usage: wgpu::BufferUsages::STORAGE, + }); + + let cell_indices_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&csr.indices), + usage: wgpu::BufferUsages::STORAGE, + }); + + let cell_offsets_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&csr.offsets), + usage: wgpu::BufferUsages::STORAGE, + }); + + let uniforms_buffer = initial_uniform.to_buffer(device); + + // now create the layout! + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("compute bind group layout"), + entries: &[ + // 0: input + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // 2: CSR offsets + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // 3: CSR indices + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // 4: uniforms + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // and the output texture + wgpu::BindGroupLayoutEntry { + binding: 4, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::StorageTexture { + access: wgpu::StorageTextureAccess::WriteOnly, + format: wgpu::TextureFormat::Rgba8Unorm, // match your texture/WGSL + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }, + // // add back for cpu output! + // wgpu::BindGroupLayoutEntry { + // binding: 1, + // visibility: wgpu::ShaderStages::COMPUTE, + // ty: wgpu::BindingType::Buffer { + // ty: wgpu::BufferBindingType::Storage { read_only: false }, + // has_dynamic_offset: false, + // min_binding_size: None, + // }, + // count: None, + // }, + ], + }); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[], + }); + + // let out_texture = output_texture + // .graphics() + // .graphics + // .borrow() + // .texture_and_desc + // .texture + // .create_view(&Default::default()); + + let desc = wgpu::TextureDescriptor { + size: wgpu::Extent3d { + width: c.dims[0], + height: c.dims[1], + depth_or_array_layers: 1, + }, + format: DEFAULT_TEXTURE_FORMAT, + usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + label: None, + view_formats: &[], + }; + + let texture = TextureAndDesc { + texture: Arc::new(device.create_texture(&desc)), + desc, + }; + + let buffers = ComputeBindings { + uniforms: uniforms_buffer, + input: input_data_buffer, + cell_offsets: cell_offsets_buffer, + cell_indices: cell_indices_buffer, + }; + + // now bind the buffers! + // let bind_group = Self::bind_group( + // device, + // &bind_group_layout, + // &buffers, + // &texture.default_view(), + // ); + + let shader = shader_from_path(device, shader_data); + + let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + module: &shader, + entry_point: "main", + #[cfg(not(feature = "nannou"))] + compilation_options: wgpu::PipelineCompilationOptions::default(), + #[cfg(not(feature = "nannou"))] + cache: None, + }); + + let dims = c.dims; + + Self { + name, + data, + pipeline, + buffers, + uniforms: initial_uniform, + bind_group_layout, + texture, + dims, + } + } + + // from https://github.com/gfx-rs/wgpu/blob/1cbebdcffe64c05e8ed14db7331333425f6feb65/examples/standalone/01_hello_compute/src/main.rs + + pub fn render(&self, d: &DeviceState, output_texture_view: &wgpu::TextureView) { + // first compute + let device = d.device(); + let queue = d.queue(); + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("compute encoder"), + }); + + { + // update our output texture + let bind_group = Self::bind_group( + device, + &self.bind_group_layout, + &self.buffers, + output_texture_view, + ); + + let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { + label: Some("compute pass"), + #[cfg(not(feature = "nannou"))] + timestamp_writes: None, + }); + pass.set_pipeline(&self.pipeline); + pass.set_bind_group(0, &bind_group, &[]); + + // dispatch over image size (stored in uniforms) + let [w, h] = self.dims; // ideally could get this from the texture... + const WGX: u32 = 8; + const WGY: u32 = 8; + let gx = (w + WGX - 1) / WGX; + let gy = (h + WGY - 1) / WGY; + + pass.dispatch_workgroups(gx, gy, 1); + } // drop(pass) + + queue.submit(Some(encoder.finish())); + + // now texture has teh output + } + pub fn update_uniforms_other( &mut self, c: &GraphicsWindowConf, @@ -38,11 +330,61 @@ impl ComputeGraphics { self.uniforms.more_info_other = more_info_other; // println!("{:?}", self.uniform.more_info); - queue.write_buffer(&self.uniforms_buffer, 0, self.uniforms.as_bytes()); + queue.write_buffer(&self.buffers.uniforms, 0, self.uniforms.as_bytes()); + } + + fn bind_group( + device: &wgpu::Device, + bind_group_layout: &wgpu::BindGroupLayout, + buffers: &ComputeBindings, + texture: &wgpu::TextureView, + ) -> wgpu::BindGroup { + device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: buffers.input.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: buffers.cell_offsets.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: buffers.cell_indices.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: buffers.uniforms.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 4, + resource: wgpu::BindingResource::TextureView(texture), + }, + ], + }) } } #[derive(Clone)] -pub struct ComputeShaderRef { - pub graphics: Rc>, +pub struct ComputeGraphicsToTextureRef { + pub graphics: Rc>>, +} +impl ComputeGraphicsToTextureRef { + fn new(graphics: Rc>>) -> Self { + Self { graphics } + } + + pub fn name(&self) -> String { + self.graphics.borrow().name.clone() + } + + pub fn render(&self, device_state_for_render: &DeviceStateForRender, other: &GraphicsRef) { + let view = &other.graphics.borrow_mut().input_texture_view; + self.graphics + .borrow() + .render(device_state_for_render.device_state(), view) + } } diff --git a/murrelet_gpu/src/device_state.rs b/murrelet_gpu/src/device_state.rs index 600b812..a4c2761 100644 --- a/murrelet_gpu/src/device_state.rs +++ b/murrelet_gpu/src/device_state.rs @@ -273,57 +273,6 @@ impl GraphicsAssets { } } -#[derive(Clone, Debug)] -pub struct GraphicsWindowConf<'a> { - pub device: &'a DeviceState<'a>, - pub dims: [u32; 2], - pub assets_path: GraphicsAssets, -} -impl<'a> GraphicsWindowConf<'a> { - pub fn new( - device: &'a DeviceState, - dims: [u32; 2], - assets_path: GraphicsAssets, - ) -> GraphicsWindowConf<'a> { - GraphicsWindowConf { - device, - dims, - assets_path, - } - } - - pub fn multi(&self, multiplier: f32) -> GraphicsWindowConf { - let [x, y] = self.dims; - GraphicsWindowConf { - device: self.device, - dims: [ - (x as f32 * multiplier) as u32, - (y as f32 * multiplier) as u32, - ], - assets_path: GraphicsAssets::Nothing, - } - } - - pub fn dims(&self) -> [u32; 2] { - self.dims - } - - pub fn device(&self) -> &wgpu::Device { - &self.device.device() - } - - pub fn with_dims(&self, dims: [u32; 2]) -> Self { - Self { - dims, - ..self.clone() - } - } - - pub fn queue(&self) -> &wgpu::Queue { - self.device.queue() - } -} - // new type just to pull in things available at render time pub struct DeviceStateForRender<'a> { device_state: DeviceState<'a>, diff --git a/murrelet_gpu/src/editable_shaders.rs b/murrelet_gpu/src/editable_shaders.rs index f8048ba..3b50877 100644 --- a/murrelet_gpu/src/editable_shaders.rs +++ b/murrelet_gpu/src/editable_shaders.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::gpu_macros::ShaderStr; +use crate::{gpu_macros::ShaderStr, window::GraphicsWindowConf}; use lerpable::Lerpable; use murrelet_livecode_derive::Livecode; use wgpu_for_latest::naga; @@ -12,7 +12,6 @@ use wgpu_for_latest as wgpu; use crate::{ build_shader, build_shader_2tex, - device_state::GraphicsWindowConf, graphics_ref::{GraphicsCreator, GraphicsRef}, }; diff --git a/murrelet_gpu/src/gpu_livecode.rs b/murrelet_gpu/src/gpu_livecode.rs index 0077313..0b05d7a 100644 --- a/murrelet_gpu/src/gpu_livecode.rs +++ b/murrelet_gpu/src/gpu_livecode.rs @@ -5,7 +5,7 @@ use murrelet_common::*; use murrelet_draw::newtypes::*; use murrelet_livecode_derive::Livecode; -use crate::{device_state::GraphicsWindowConf, graphics_ref::GraphicsRef}; +use crate::{graphics_ref::GraphicsRef, window::GraphicsWindowConf}; pub trait ControlGraphics { fn update_graphics(&self, c: &GraphicsWindowConf, g: &GraphicsRef) { @@ -144,9 +144,9 @@ impl ControlGraphics for GPURGBAGradient { pub mod prebuilt_shaders { use crate::{ - device_state::GraphicsWindowConf, gpu_macros::ShaderStr, graphics_ref::{GraphicsCreator, GraphicsRef}, + window::GraphicsWindowConf, *, }; diff --git a/murrelet_gpu/src/gpu_macros.rs b/murrelet_gpu/src/gpu_macros.rs index 1f0f551..71ad279 100644 --- a/murrelet_gpu/src/gpu_macros.rs +++ b/murrelet_gpu/src/gpu_macros.rs @@ -6,13 +6,10 @@ use murrelet_common::MurreletTime; use serde::Serialize; use crate::{ - device_state::{DeviceStateForRender, GraphicsAssets, GraphicsWindowConf}, - gpu_livecode::ControlGraphicsRef, - graphics_ref::{ + compute::{ComputeGraphicsToTexture, ComputeGraphicsToTextureRef}, device_state::{DeviceStateForRender, GraphicsAssets}, gpu_livecode::ControlGraphicsRef, graphics_ref::{ Graphics, GraphicsCreator, GraphicsRef, GraphicsRefWithControlFn, DEFAULT_LOADED_TEXTURE_FORMAT, - }, - shader_str::*, + }, shader_str::*, window::GraphicsWindowConf }; #[cfg(feature = "nannou")] @@ -110,19 +107,23 @@ pub enum ShaderStr { Binding1Tex, Binding2Tex, Binding3d, + Compute, Includes, Prefix, Suffix, + ComputeFormatStr, } impl ShaderStr { pub fn to_str(&self) -> &str { match self { ShaderStr::Binding1Tex => BINDING_1TEX, ShaderStr::Binding2Tex => BINDING_2TEX, + ShaderStr::Compute => COMPUTE_TEX, ShaderStr::Includes => INCLUDES, ShaderStr::Prefix => PREFIX, ShaderStr::Suffix => SUFFIX, ShaderStr::Binding3d => BINDING_3D, + ShaderStr::ComputeFormatStr => COMPUTE_FORMAT_STR, } } } @@ -156,6 +157,31 @@ macro_rules! build_shader_3d { }; } +// input_structure can be +// struct Input { +// a: vec2; +// b: vec2; +// } + +//COMPUTE_FORMAT_STR.replace("#PREFIX_CODEHERE", prefix).replace("#FORLOOP_CODEHERE#", forloop).replace("#SUFFIX_CODEHERE#", suffix); + +#[macro_export] +macro_rules! build_compute_shader { + // capture the initial one + ($($input_structure:tt, $prefix:tt, $loop:tt, $suffix:tt)*) => {{ + format!( + "{}\n{}\n{}\n{}", + input_structure, + ShaderStr::Compute.to_str(), + ShaderStr::Includes.to_str(), + ShaderStr::ComputeFormatStr + .replace("#PREFIX_CODEHERE", prefix) + .replace("#FORLOOP_CODEHERE#", forloop) + .replace("#SUFFIX_CODEHERE#", suffix) + ) + }}; +} + #[derive(Serialize)] pub struct RenderDebugPrint { pub src: String, @@ -423,6 +449,39 @@ impl RenderTrait for TextureViewRender { } } +pub struct ComputeTextureRender { + pub source: ComputeGraphicsToTextureRef, + pub dest: GraphicsRef, +} + +impl ComputeTextureRender { + pub fn new_box(source: ComputeGraphicsToTextureRef, dest: GraphicsRef) -> Box { + Box::new(Self { source, dest }) + } +} + +impl RenderTrait for ComputeTextureRender { + // whenver it's called, it'll increment! check if it's overdue before rendering! + fn render(&self, device_state_for_render: &DeviceStateForRender) { + let source_texture = &self.source; + let dest = &self.dest; + source_texture.render(device_state_for_render, dest); + } + + fn debug_print(&self) -> Vec { + let source = &self.source; + let dest = &self.dest; + vec![RenderDebugPrint { + src: source.name(), + dest: dest.name(), + }] + } + + fn dest(&self) -> Option { + Some(self.dest.clone()) + } +} + pub struct DisplayRender { pub source: GraphicsRef, } diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index 75c5aae..0ce78d1 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -21,6 +21,7 @@ use crate::device_state::*; use crate::gpu_livecode::{ControlGraphics, ControlGraphicsRef}; use crate::shader_str::{VERTEX_SHADER, VERTEX_SHADER_3D}; use crate::uniforms::{BasicUniform, UniformsPair}; +use crate::window::GraphicsWindowConf; #[cfg(not(feature = "nannou"))] pub const DEFAULT_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm; @@ -32,7 +33,7 @@ pub const DEFAULT_LOADED_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureForm #[cfg(feature = "nannou")] pub const DEFAULT_LOADED_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm; -fn shader_from_path(device: &wgpu::Device, data: &str) -> wgpu::ShaderModule { +pub fn shader_from_path(device: &wgpu::Device, data: &str) -> wgpu::ShaderModule { device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("Shader"), source: wgpu::ShaderSource::Wgsl(data.into()), @@ -491,7 +492,6 @@ impl GraphicsCreator { } } - #[derive(Clone)] pub struct GraphicsRef { pub graphics: Rc>, @@ -735,6 +735,11 @@ pub struct TextureAndDesc { pub texture: Arc, pub desc: wgpu::TextureDescriptor<'static>, } +impl TextureAndDesc { + pub(crate) fn default_view(&self) -> wgpu::TextureView { + self.texture.create_view(&Default::default()) + } +} pub struct TextureFor3d { shadow_pipeline: wgpu::RenderPipeline, diff --git a/murrelet_gpu/src/shader_str.rs b/murrelet_gpu/src/shader_str.rs index 58593fd..76a1a54 100644 --- a/murrelet_gpu/src/shader_str.rs +++ b/murrelet_gpu/src/shader_str.rs @@ -3,6 +3,43 @@ pub const SUFFIX: &str = r#" } "#; +// struct Input { +// a: vec2; +// b: vec2; +// }; + + +pub const COMPUTE_TEX: &str = r#" +struct Uniforms { + dims: vec4, + more_info: vec4, + more_info_other: vec4, +}; + +// --- bindings --- +@group(0) @binding(0) var input_data : array; +@group(0) @binding(1) var cell_offsets : array; +@group(0) @binding(2) var cell_indices : array; +@group(0) @binding(3) var uniforms : BasicUniform; + +@group(0) @binding(4) var out_img : texture_storage_2d; + +fn img_wh() -> vec2 { + return vec2(u32(uniforms.dims.x), u32(uniforms.dims.y)); +} + +fn to_uv(gid_xy: vec2) -> vec2 { + let wh = vec2(uniforms.dims.xy); + return (vec2(gid_xy) + vec2(0.5)) / wh; +} + +fn cell_id(uv: vec2, Nx: u32, Ny: u32) -> u32 { + let xy = clamp(vec2(floor(uv * vec2(f32(Nx), f32(Ny)))), + vec2(0u), vec2(Nx - 1u, Ny - 1u)); + return xy.y * Nx + xy.x; +} +"#; + pub const BINDING_2TEX: &str = r#" struct FragmentOutput { @location(0) f_color: vec4, @@ -415,3 +452,36 @@ pub const PREFIX: &str = r#" @fragment fn main(@location(0) tex_coords: vec2, @location(1) shad_info: vec4, @location(2) normal: vec3, @location(3) light_space_pos: vec4, @location(4) world_pos: vec3) -> FragmentOutput { "#; + + +pub const COMPUTE_FORMAT_STR: &str = r#" +@compute @workgroup_size(8, 8) +fn main(@builtin(global_invocation_id) gid: vec3) { + let w = u32(uniforms.dims.x); + let h = u32(uniforms.dims.y); + let uv = to_uv(gid.xy); + + let Nx = max(u32(uniforms.more_info.x), 1u); + let Ny = max(u32(uniforms.more_info.y), 1u); + let cid = cell_id(uv, Nx, Ny); + + let start_idx = cell_offsets[cid]; + let end_idx = cell_offsets[cid + 1u]; + + #PREFIX_CODEHERE# + + for (var i = start_idx; i < end_idx; i = i + 1u) { + let sid = cell_indices[i]; + let data = input_data[sid]; + // dmin = min(dmin, seg_dist(uv, seg.a, seg.b)); + #FORLOOP_CODEHERE# + + } + + #SUFFIX_CODEHERE# + + textureStore(out_img, vec2(gid.xy), result); +} +"#; + + From f70fb885f7d168a03907931b508db290744efe7f Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 12 Oct 2025 22:34:26 -0400 Subject: [PATCH 077/149] wip --- murrelet_draw/src/tesselate.rs | 6 +- murrelet_gpu/src/compute.rs | 173 ++++++++++++++++-- murrelet_gpu/src/gpu_macros.rs | 47 +++-- murrelet_gpu/src/graphics_ref.rs | 3 +- murrelet_gpu/src/shader_str.rs | 3 +- .../src/derive_graphics_trait.rs | 8 +- 6 files changed, 200 insertions(+), 40 deletions(-) diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 8a297bf..ce28ce5 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, ops}; use crate::{ cubic::CubicBezier, - curve_drawer::{CubicBezierPath, CurveDrawer, CurveSegment}, + curve_drawer::CubicBezierPath, svg::SvgPathDef, }; use delaunator::Triangulation; @@ -22,11 +22,11 @@ use murrelet_common::{triangulate::VertexSimple, Polyline}; pub trait ToVecVec2 { fn to_vec2(&self) -> Vec; - fn to_vec2_line_space(&self, line_space: f32) -> Vec { + fn to_vec2_line_space(&self, _line_space: f32) -> Vec { todo!() } - fn to_vec2_count(&self, count: usize) -> Vec { + fn to_vec2_count(&self, _count: usize) -> Vec { todo!() } } diff --git a/murrelet_gpu/src/compute.rs b/murrelet_gpu/src/compute.rs index 28ba709..3e958a5 100644 --- a/murrelet_gpu/src/compute.rs +++ b/murrelet_gpu/src/compute.rs @@ -1,6 +1,8 @@ -use std::{cell::RefCell, rc::Rc, sync::Arc}; +use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc}; use bytemuck::Pod; +use glam::Vec2; +use itertools::Itertools; #[cfg(feature = "nannou")] use wgpu_for_nannou as wgpu; @@ -22,6 +24,43 @@ struct ComputeBindings { cell_indices: wgpu::Buffer, uniforms: wgpu::Buffer, } +impl ComputeBindings { + fn update_csr_and_data(&mut self, c: &GraphicsWindowConf, csr: CSR, data: Vec) { + let device = c.device(); + + let mut offsets = csr.offsets; + if offsets.is_empty() { + offsets = vec![0; 2] + }; + let mut indices = csr.indices; + if indices.is_empty() { + indices = vec![0; 2] + }; + + // let queue = c.queue(); + // queue.write_buffer(&self.cell_offsets, 0, bytemuck::cast_slice(&offsets)); + // queue.write_buffer(&self.cell_indices, 0, bytemuck::cast_slice(&indices)); + // queue.write_buffer(&self.input, 0, bytemuck::cast_slice(&data)); + + self.input = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&data), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); + + self.cell_indices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&indices), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); + + self.cell_offsets = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&offsets), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); + } +} pub struct CSR { offsets: Vec, // size is N + 1, contains the start/end of each group! fence post @@ -54,10 +93,69 @@ impl CSRData { } CSR { offsets, indices } } + + fn from_data(nx: u32, ny: u32, data: &[T]) -> Self { + let cell_count = (nx * ny) as usize; + let mut orig_cells = vec![vec![]; cell_count]; + + // helpers + // let clamp01 = |v: f32| v.max(0.0).min(1.0); + let cell_id = |x_i: u32, y_i: u32| -> usize { (y_i * nx + x_i) as usize }; + let ix_clamp = |x: i32| -> u32 { x.max(0).min(nx as i32 - 1) as u32 }; + let iy_clamp = |y: i32| -> u32 { y.max(0).min(ny as i32 - 1) as u32 }; + + for (idx, d) in data.iter().enumerate() { + let bounds = d.to_aabb(); + + // Convert to cell-range (inclusive) + let ix0 = ix_clamp((bounds.min.x * nx as f32).floor() as i32); + let ix1 = ix_clamp((bounds.max.x * nx as f32).floor() as i32); + let iy0 = iy_clamp((bounds.min.y * ny as f32).floor() as i32); + let iy1 = iy_clamp((bounds.max.y * ny as f32).floor() as i32); + + for iy in iy0..=iy1 { + for ix in ix0..=ix1 { + orig_cells[cell_id(ix, iy)].push(idx as u32); + } + } + } + + // now go through the cells and append the neighboring cells! + let mut cells = vec![vec![]; cell_count]; + + for x_i in 0..nx { + for y_i in 0..ny { + let mut cell_val = HashSet::new(); + + for offset_x in -1..=1 { + for offset_y in -1..=1 { + let xii = ix_clamp(x_i as i32 + offset_x); + let yii = iy_clamp(y_i as i32 + offset_y); + + for c in &orig_cells[cell_id(xii, yii)] { + cell_val.insert(*c); + } + } + } + cells[cell_id(x_i, y_i)] = cell_val.into_iter().collect_vec(); + } + } + + CSRData { cells } + } +} + +pub struct AABB { + pub min: Vec2, + pub max: Vec2, +} + +pub trait ToAABB { + fn to_aabb(&self) -> AABB; } // like Graphics, what's needed to create a compute pipeline -pub struct ComputeGraphicsToTexture { +pub struct ComputeGraphicsToTexture { name: String, // conf: GraphicsCreator, // bind_group: wgpu::BindGroup, @@ -69,8 +167,9 @@ pub struct ComputeGraphicsToTexture { bind_group_layout: wgpu::BindGroupLayout, pipeline: wgpu::ComputePipeline, - data: Vec, + // data: Vec, dims: [u32; 2], + // csr: CSR, // used internally // pub input_texture_view: wgpu::TextureView, // pub input_texture_view_other: Option, @@ -81,23 +180,44 @@ pub struct ComputeGraphicsToTexture { // pub other_texture_and_desc: Option, // textures_for_3d: Option, } -impl ComputeGraphicsToTexture { - pub fn init<'a>( +impl ComputeGraphicsToTexture { + fn sync_data( + &mut self, + c: &GraphicsWindowConf, + nx: u32, + ny: u32, + data: &[T], + ) { + // the data should already be scaled 0.0 to 1.0 + + // make sure we use the same vars on both sides + self.update_uniforms_other(c, [nx as f32, ny as f32, 0.0, 0.0], [0.0; 4]); + + // build csr + let csr = CSRData::from_data(nx, ny, data).for_buffers(); + + let data = data.to_vec(); + + self.buffers.update_csr_and_data(c, csr, data); + } + + pub fn init<'a, T: Pod + ToAABB + Clone>( name: String, c: &GraphicsWindowConf<'a>, compute_shader: &str, - ) -> ComputeGraphicsToTextureRef { + data: Vec, + ) -> ComputeGraphicsToTextureRef { ComputeGraphicsToTextureRef::new(Rc::new(RefCell::new(Self::new( name, c, compute_shader, - BasicUniform::from_empty(), + BasicUniform::from_dims(c.dims), CSR::empty(), - vec![], + data, )))) } - pub fn new<'a>( + pub fn new<'a, T: Pod + ToAABB + Clone>( name: String, c: &GraphicsWindowConf<'a>, shader_data: &str, @@ -110,19 +230,19 @@ impl ComputeGraphicsToTexture { let input_data_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(&data), - usage: wgpu::BufferUsages::STORAGE, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, }); let cell_indices_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(&csr.indices), - usage: wgpu::BufferUsages::STORAGE, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, }); let cell_offsets_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(&csr.offsets), - usage: wgpu::BufferUsages::STORAGE, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, }); let uniforms_buffer = initial_uniform.to_buffer(device); @@ -142,7 +262,7 @@ impl ComputeGraphicsToTexture { }, count: None, }, - // 2: CSR offsets + // 21: CSR offsets wgpu::BindGroupLayoutEntry { binding: 1, visibility: wgpu::ShaderStages::COMPUTE, @@ -181,7 +301,7 @@ impl ComputeGraphicsToTexture { visibility: wgpu::ShaderStages::COMPUTE, ty: wgpu::BindingType::StorageTexture { access: wgpu::StorageTextureAccess::WriteOnly, - format: wgpu::TextureFormat::Rgba8Unorm, // match your texture/WGSL + format: wgpu::TextureFormat::Rgba16Float, view_dimension: wgpu::TextureViewDimension::D2, }, count: None, @@ -266,13 +386,14 @@ impl ComputeGraphicsToTexture { Self { name, - data, + // data, pipeline, buffers, uniforms: initial_uniform, bind_group_layout, texture, dims, + // csr: CSR::empty(), } } @@ -369,11 +490,11 @@ impl ComputeGraphicsToTexture { } #[derive(Clone)] -pub struct ComputeGraphicsToTextureRef { - pub graphics: Rc>>, +pub struct ComputeGraphicsToTextureRef { + pub graphics: Rc>, } -impl ComputeGraphicsToTextureRef { - fn new(graphics: Rc>>) -> Self { +impl ComputeGraphicsToTextureRef { + fn new(graphics: Rc>) -> Self { Self { graphics } } @@ -387,4 +508,18 @@ impl ComputeGraphicsToTextureRef { .borrow() .render(device_state_for_render.device_state(), view) } + + pub fn sync_data( + &self, + c: &GraphicsWindowConf, + nx: u32, + ny: u32, + segments: &[T], + ) { + if !segments.is_empty() { + self.graphics.borrow_mut().sync_data(c, nx, ny, segments) + } else { + println!("segments is empty, not doing anything"); + } + } } diff --git a/murrelet_gpu/src/gpu_macros.rs b/murrelet_gpu/src/gpu_macros.rs index 71ad279..56a9771 100644 --- a/murrelet_gpu/src/gpu_macros.rs +++ b/murrelet_gpu/src/gpu_macros.rs @@ -6,10 +6,15 @@ use murrelet_common::MurreletTime; use serde::Serialize; use crate::{ - compute::{ComputeGraphicsToTexture, ComputeGraphicsToTextureRef}, device_state::{DeviceStateForRender, GraphicsAssets}, gpu_livecode::ControlGraphicsRef, graphics_ref::{ + compute::{ComputeGraphicsToTexture, ComputeGraphicsToTextureRef}, + device_state::{DeviceStateForRender, GraphicsAssets}, + gpu_livecode::ControlGraphicsRef, + graphics_ref::{ Graphics, GraphicsCreator, GraphicsRef, GraphicsRefWithControlFn, DEFAULT_LOADED_TEXTURE_FORMAT, - }, shader_str::*, window::GraphicsWindowConf + }, + shader_str::*, + window::GraphicsWindowConf, }; #[cfg(feature = "nannou")] @@ -168,16 +173,17 @@ macro_rules! build_shader_3d { #[macro_export] macro_rules! build_compute_shader { // capture the initial one - ($($input_structure:tt, $prefix:tt, $loop:tt, $suffix:tt)*) => {{ + ($input_structure:tt, $prefix:tt, $forloop:tt, $suffix:tt) => {{ format!( "{}\n{}\n{}\n{}", - input_structure, + $input_structure, ShaderStr::Compute.to_str(), ShaderStr::Includes.to_str(), ShaderStr::ComputeFormatStr - .replace("#PREFIX_CODEHERE", prefix) - .replace("#FORLOOP_CODEHERE#", forloop) - .replace("#SUFFIX_CODEHERE#", suffix) + .to_str() + .replace("#PREFIX_CODEHERE#", $prefix) + .replace("#FORLOOP_CODEHERE#", $forloop) + .replace("#SUFFIX_CODEHERE#", $suffix) ) }}; } @@ -449,18 +455,18 @@ impl RenderTrait for TextureViewRender { } } -pub struct ComputeTextureRender { - pub source: ComputeGraphicsToTextureRef, +pub struct ComputeTextureRender { + pub source: ComputeGraphicsToTextureRef, pub dest: GraphicsRef, } -impl ComputeTextureRender { - pub fn new_box(source: ComputeGraphicsToTextureRef, dest: GraphicsRef) -> Box { +impl ComputeTextureRender { + pub fn new_box(source: ComputeGraphicsToTextureRef, dest: GraphicsRef) -> Box { Box::new(Self { source, dest }) } } -impl RenderTrait for ComputeTextureRender { +impl RenderTrait for ComputeTextureRender { // whenver it's called, it'll increment! check if it's overdue before rendering! fn render(&self, device_state_for_render: &DeviceStateForRender) { let source_texture = &self.source; @@ -819,6 +825,23 @@ macro_rules! build_shader_pipeline { } }; + // compute shaders: =a -> T + (@parse $pipeline:ident (=$source:ident -> $dest:ident;$($tail:tt)*)) => { + { + println!("add compute"); + $pipeline.add_step( + ComputeTextureRender::new_box( + $source.clone(), + $dest.graphics() + ) + ); + // pipeline_add_label!($pipeline, $source); + pipeline_add_label!($pipeline, $dest); + + build_shader_pipeline!(@parse $pipeline ($($tail)*)); + } + }; + // one source to output: a -> t (@parse $pipeline:ident ($source:ident -> $dest:ident;$($tail:tt)*)) => { { diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index 0ce78d1..2939cd7 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -825,7 +825,8 @@ impl Graphics { usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST - | wgpu::TextureUsages::COPY_SRC, + | wgpu::TextureUsages::COPY_SRC + | wgpu::TextureUsages::STORAGE_BINDING, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, diff --git a/murrelet_gpu/src/shader_str.rs b/murrelet_gpu/src/shader_str.rs index 76a1a54..f91a169 100644 --- a/murrelet_gpu/src/shader_str.rs +++ b/murrelet_gpu/src/shader_str.rs @@ -10,7 +10,7 @@ pub const SUFFIX: &str = r#" pub const COMPUTE_TEX: &str = r#" -struct Uniforms { +struct BasicUniform { dims: vec4, more_info: vec4, more_info_other: vec4, @@ -474,6 +474,7 @@ fn main(@builtin(global_invocation_id) gid: vec3) { let sid = cell_indices[i]; let data = input_data[sid]; // dmin = min(dmin, seg_dist(uv, seg.a, seg.b)); + #FORLOOP_CODEHERE# } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs index acf1ef4..a81cdcb 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs @@ -85,7 +85,7 @@ fn parse_graphics( .expect("that's not a function!"); quote! { - if #should_run_fn(render_in) { + if #should_run_fn(&livecoder, render_in) { v.push(&self.#ident as &dyn GraphicsRenderer); } } @@ -114,7 +114,7 @@ fn parse_graphics( .expect("that's not a function!"); quote! { - if !#should_run_fn(render_in) { + if !#should_run_fn(&livecoder, render_in) { v.push(&self.#ident as &dyn GraphicsRenderer); } } @@ -159,13 +159,13 @@ fn parse_graphics( #(#drawers;)* v } - fn gpu_pipelines(&self, render_in: &GraphicsRenderIn) -> Vec<&dyn GraphicsRenderer> { + fn gpu_pipelines<'a, 'b, 'c>(&'a self, livecoder: &'b #ctrlcls, render_in: &'c GraphicsRenderIn) -> Vec<&'a (dyn GraphicsRenderer + 'a)> { let mut v: Vec<&dyn GraphicsRenderer> = vec![]; #(#pipelines;)* v } - fn control_graphics<'a>(&'a self, livecoder: &'a #ctrlcls) -> Vec { + fn control_graphics<'a, 'b>(&'a self, livecoder: &'b #ctrlcls) -> Vec { let mut v: Vec = vec![]; #(#ctrl;)* v From e83a6a68cee6ad8459effc4d8aed980f473bf990 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 13 Oct 2025 21:26:12 -0400 Subject: [PATCH 078/149] wip --- murrelet_gpu/src/graphics_ref.rs | 2 +- murrelet_perform/src/cli.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index 2939cd7..9f3922d 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -826,7 +826,7 @@ impl Graphics { | wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC - | wgpu::TextureUsages::STORAGE_BINDING, + | wgpu::TextureUsages::STORAGE_BINDING, // needed for compute mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index e932baf..801c978 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -43,8 +43,8 @@ impl Default for TextureDimensions { // height: 2000, // width: 1080, // width: 1080, - width: 1080, - height: 1080, + width: 2000, + height: 2000, // width: 2000, // height: 2000, // width: 750, From 63ae847c313d69f02bc2c8524a76587a4e4370a9 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 18 Oct 2025 20:12:37 -0400 Subject: [PATCH 079/149] start trying to make svg faster --- murrelet_common/src/color.rs | 7 ++- murrelet_draw/src/style.rs | 3 +- murrelet_draw/src/svg.rs | 6 +- murrelet_gpu/src/compute.rs | 8 ++- murrelet_livecode/src/livecode.rs | 4 ++ murrelet_livecode/src/state.rs | 57 +++++++++++++------ .../src/derive_graphics_trait.rs | 7 +++ murrelet_perform/src/cli.rs | 10 ++-- murrelet_svg/Cargo.toml | 1 + murrelet_svg/src/svg.rs | 14 ++++- 10 files changed, 85 insertions(+), 32 deletions(-) diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index abd5c6f..cfddcd5 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -3,7 +3,7 @@ use std::{ ops::{Add, Mul}, }; -use glam::Vec3; +use glam::{vec3, Vec3}; use lerpable::{IsLerpingMethod, Lerpable}; // use murrelet_gui::CanMakeGUI; use palette::{rgb::Rgb, FromColor, Hsva, IntoColor, LinSrgb, LinSrgba, Srgb, Srgba, WithAlpha}; @@ -65,6 +65,11 @@ impl MurreletColor { Self::from_srgba(c) } + pub fn to_rgb_vec3(&self) -> Vec3 { + let [r, g, b, _] = self.into_rgba_components(); + vec3(r, g, b) + } + pub fn gray(g: f32) -> Self { Self::srgb(g, g, g) } diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index ac425c2..cea9367 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -2,7 +2,7 @@ use crate::{ curve_drawer::CurveDrawer, draw::*, - svg::{SvgCircle, SvgPathDef, SvgShape, TransformedSvgShape}, + svg::{SvgPathDef, SvgShape, TransformedSvgShape}, tesselate::parse_svg_path_as_vec2, transform2d::*, }; @@ -562,7 +562,6 @@ impl MurreletPath { Self::Polyline(path.as_polyline()) } - pub fn svg_circle(loc: Vec2, rad: f32) -> Self { Self::Svg(TransformedSvgShape::from_shape(SvgShape::circle(loc, rad))) } diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index ac7aaa6..3a020ed 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -7,11 +7,7 @@ use murrelet_gui::MurreletGUI; use murrelet_livecode_derive::Livecode; use crate::curve_drawer::{CurveArc, CurveDrawer}; -macro_rules! log { - ( $( $t:tt )* ) => { - web_sys::console::log_1(&format!( $( $t )* ).into()) - } -} + #[derive(Clone, Debug, Livecode, MurreletGUI, Lerpable)] pub struct SvgRect { #[livecode(serde_default = "0")] diff --git a/murrelet_gpu/src/compute.rs b/murrelet_gpu/src/compute.rs index 3e958a5..578dd47 100644 --- a/murrelet_gpu/src/compute.rs +++ b/murrelet_gpu/src/compute.rs @@ -378,8 +378,6 @@ impl ComputeGraphicsToTexture { entry_point: "main", #[cfg(not(feature = "nannou"))] compilation_options: wgpu::PipelineCompilationOptions::default(), - #[cfg(not(feature = "nannou"))] - cache: None, }); let dims = c.dims; @@ -522,4 +520,10 @@ impl ComputeGraphicsToTextureRef { println!("segments is empty, not doing anything"); } } + + // pub fn control_graphics(&self, conf: &GraphicsConf) -> Vec { + // let ctrl_graphics = (self.control_graphic_fn)(conf); + + // ControlGraphicsRef::new(self.label, ctrl_graphics, Some(self.graphics.clone())) + // } } diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index 7558a0b..964fbc7 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -29,6 +29,10 @@ pub fn empty_vec() -> Vec { pub trait LivecodeFromWorld { fn o(&self, w: &LivecodeWorldState) -> LivecodeResult; + + fn o_dummy(&self) -> LivecodeResult { + self.o(&LivecodeWorldState::new_dummy()) + } } impl LivecodeFromWorld for ControlF32 { diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index a116293..1a9d3f6 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -27,24 +27,53 @@ enum LivecodeWorldStateStage { // } #[derive(Clone, Debug)] -pub enum CachedHM { +pub enum CacheFlag { NotCached, - Cached(Arc), + Cached, +} + +#[derive(Clone, Debug)] +pub struct CachedHM { + flag: CacheFlag, + data: Arc, // we keep this around so we don't need to drop } impl CachedHM { fn new_rw() -> Arc> { - Arc::new(RwLock::new(CachedHM::NotCached)) + let hm = HashMapContext::new(); + Arc::new(RwLock::new(CachedHM { + data: Arc::new(hm), + flag: CacheFlag::NotCached, + })) } - fn update(&mut self, hm: HashMapContext) { - *self = CachedHM::Cached(Arc::new(hm)); - } + // fn update(&mut self, hm: HashMapContext) { + // // *self = CachedHM::Cached(Arc::new(hm)); + // self.data = Arc::new(hm); + // self.flag = CacheFlag::Cached; + // } fn clear(&mut self) { - *self = CachedHM::NotCached; + self.flag = CacheFlag::NotCached; + } + + fn cached(&self) -> CacheResult { + match self.flag { + CacheFlag::NotCached => CacheResult::NotCached, + CacheFlag::Cached => CacheResult::Cached(self.data.clone()), + } + } + + fn update_arc(&mut self, hm: Arc) { + self.data = hm; + self.flag = CacheFlag::Cached; } } +enum CacheResult { + Cached(Arc), + NotCached, +} + #[derive(Clone, Debug)] pub struct LivecodeWorldState { cached: Arc>, @@ -125,22 +154,18 @@ impl LivecodeWorldState { } pub(crate) fn ctx(&self) -> LivecodeResult> { - let mut cache = self.cached.write().unwrap(); - - if let CachedHM::Cached(c) = cache.clone() { + if let CacheResult::Cached(c) = self.cached.read().unwrap().cached() { return Ok(c); } + let mut cache = self.cached.write().unwrap(); let mut ctx = self.state.ctx().clone(); for mixed in &self.refs { mixed.update_ctx(&mut ctx)?; } - cache.update(ctx); + let arc = Arc::new(ctx); + cache.update_arc(arc.clone()); - if let CachedHM::Cached(c) = cache.clone() { - return Ok(c); - } else { - unreachable!("we just set it?") - } + Ok(arc) } pub fn actual_frame_u64(&self) -> u64 { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs index a81cdcb..eade638 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs @@ -30,6 +30,7 @@ enum GraphicKind { Drawer, Pipeline, Graphics, + ComputeTexture, // similar to graphics, but is a compute shader that outputs a texture Ref, DrawSrc, } @@ -39,6 +40,7 @@ impl GraphicKind { "drawer" => Self::Drawer, "pipeline" => Self::Pipeline, "graphics" => Self::Graphics, + "computetexture" => Self::ComputeTexture, "ref" => Self::Ref, "draw_src" => Self::DrawSrc, _ => panic!("unexpected kind"), @@ -147,6 +149,11 @@ fn parse_graphics( }) } } + GraphicKind::ComputeTexture => { + ctrl.push( + quote! {v.extend(self.#ident.control_graphics(&livecoder).into_iter())}, + ); + } } } } diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index 801c978..d81e125 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -41,14 +41,14 @@ impl Default for TextureDimensions { Self { // width: 3000, // height: 2000, - // width: 1080, - // width: 1080, - width: 2000, - height: 2000, + width: 1080, + height: 1080, + // width: 2000, + // height: 2000, // width: 2000, // height: 2000, // width: 750, - // height: 750 + // height: 750, } } } diff --git a/murrelet_svg/Cargo.toml b/murrelet_svg/Cargo.toml index 9cc0d22..d82fce9 100644 --- a/murrelet_svg/Cargo.toml +++ b/murrelet_svg/Cargo.toml @@ -18,3 +18,4 @@ murrelet_draw = { version = "0.1.2", default-features = false } glam = { version = "0.28.0", features = ["serde"] } itertools = "0.10.5" svg = "0.10.0" +regex = "1.7.3" diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index d56c8a1..a4eed4d 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -17,6 +17,7 @@ use murrelet_draw::{ svg::{SvgPathDef, SvgShape, TransformedSvgShape}, }; use murrelet_perform::perform::SvgDrawConfig; +use regex::{Captures, Regex}; use svg::{ node::element::{path::Data, Group}, Document, Node, @@ -103,6 +104,7 @@ pub trait ToSvgData { fn make_path(&self, style: &MurreletStyle) -> Option { if let Some(d) = self.to_svg_data() { let mut d = d; + if style.closed { d = d.close(); } @@ -859,7 +861,17 @@ impl SvgPathCache { pub fn make_html(&self) -> Vec { let (paths, defs) = self.config.make_html(self); - vec![defs.to_string(), paths.to_string()] + // hmm, figure out a better way to do this, but quantize it + + let re = Regex::new(r"(-?\d*\.\d{3,})").unwrap(); + let path_str = re + .replace_all(&paths.to_string(), |caps: &Captures| { + let val: f32 = caps[0].parse().unwrap_or(0.0); + format!("{:.2}", val) + }) + .into_owned(); + + vec![defs.to_string(), path_str] } } From d0b5d0d26bff5b049b285b1df930363713f99a72 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 19 Oct 2025 01:10:00 -0400 Subject: [PATCH 080/149] try to make lazy more efficient, found out about the evalexpr trait --- murrelet_livecode/src/expr.rs | 35 ++++++++++++++- murrelet_livecode/src/lazy.rs | 40 +++++++++-------- murrelet_livecode/src/state.rs | 78 ++++++++++++++++++++++++++++++++- murrelet_perform/src/perform.rs | 4 ++ 4 files changed, 136 insertions(+), 21 deletions(-) diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index f1c7b2a..818bc5e 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -278,7 +278,7 @@ pub fn init_evalexpr_func_ctx() -> LivecodeResult { }.map_err(|err| {LivecodeError::EvalExpr("error in init_evalexpr_func_ctx!".to_string(), err)}) } -fn lc_val_to_expr(v: &LivecodeValue) -> Value { +pub fn lc_val_to_expr(v: &LivecodeValue) -> Value { match v { LivecodeValue::Float(f) => Value::Float(*f), LivecodeValue::Bool(f) => Value::Boolean(*f), @@ -372,8 +372,37 @@ impl ExprWorldContextValues { // Self::new([self.0.clone(), vals.0].concat()) new } + + pub(crate) fn get_variable(&self, identifier: &str) -> Option<&LivecodeValue> { + self.0.get(identifier) + } + + pub(crate) fn to_vals(&self) -> Vec<(String, Value)> { + self.0 + .iter() + .map(|(k, v)| (k.clone(), lc_val_to_expr(v))) + .collect_vec() + } } +// impl Context for ExprWorldContextValues { +// fn get_value(&self, identifier: &str) -> Option<&Value> { +// todo!() +// } + +// fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult { +// todo!() +// } + +// fn are_builtin_functions_disabled(&self) -> bool { +// true +// } + +// fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> { + +// } +// } + pub trait IntoExprWorldContext { fn as_expr_world_context_values(&self) -> ExprWorldContextValues; } @@ -530,4 +559,8 @@ impl MixedEvalDefs { pub fn from_idx(idx: IdxInRange) -> Self { Self::new_from_expr(ExprWorldContextValues::new_from_idx(idx)) } + + pub(crate) fn expr_vals(&self) -> &ExprWorldContextValues { + &self.vals + } } diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 8051515..43e19ea 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -3,13 +3,13 @@ use std::sync::Arc; use evalexpr::{HashMapContext, IterateVariablesContext, Node}; use itertools::Itertools; use lerpable::{step, Lerpable}; -use murrelet_common::{IdxInRange, MurreletColor}; +use murrelet_common::{IdxInRange, LivecodeValue, MurreletColor}; use serde::Deserialize; use crate::{ expr::{ExprWorldContextValues, MixedEvalDefs, MixedEvalDefsRef}, livecode::{GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeFunction, LivecodeVariable}, - state::LivecodeWorldState, + state::{LivecodeWorldState, WorldWithLocalVariables}, types::{LivecodeError, LivecodeResult}, }; @@ -75,38 +75,42 @@ impl GetLivecodeIdentifiers for ControlLazyNodeF32 { #[derive(Debug, Clone)] pub struct LazyNodeF32Inner { n: Node, // what will be evaluated! - world: LivecodeWorldState, // this is a reference :D + world: WorldWithLocalVariables, //LivecodeWorldState, // this is a reference :D // more_defs: MixedEvalDefs, } impl LazyNodeF32Inner { pub fn new(n: Node, world: LivecodeWorldState) -> Self { Self { n, - world, + world: world.to_local(), // more_defs: MixedEvalDefs::new(), } } // options to add more details... pub fn add_more_defs(&self, more_defs: &MixedEvalDefs) -> Self { - let mut c = self.clone(); - c.world - .update_with_defs(MixedEvalDefsRef::new(more_defs.clone())); - c + // unreachable!(); + let c = self.clone(); + c.add_expr_values(more_defs.expr_vals()) + + // println!("dropping contexts..."); + // c.world + // .update_with_defs(MixedEvalDefsRef::new(more_defs.clone())); + // c } - pub fn add_expr_values(&self, more_vals: ExprWorldContextValues) -> Self { + pub fn add_expr_values(&self, more_vals: &ExprWorldContextValues) -> Self { // let mut c = self.clone(); // c.more_defs.set_vals(more_vals); // c let mut c = self.clone(); - c.world - .update_with_defs(MixedEvalDefsRef::new_from_expr(more_vals)); + c.world.update_with_simple_defs(more_vals); c } // internal function to build the ctx - fn build_ctx(&self) -> LivecodeResult> { + fn build_ctx(&self) -> &WorldWithLocalVariables { + // LivecodeResult> { // self.world.clone_with_mixed_defs(&self.more_defs) // self.world.ctx()?; @@ -115,7 +119,8 @@ impl LazyNodeF32Inner { // let a = w.ctx().as_ref() - self.world.ctx() + // self.world.ctx() + &self.world // let copied_world = self.world.ctx().as_ref().clone(); // let mut ctx = copied_world.clone(); @@ -125,9 +130,8 @@ impl LazyNodeF32Inner { // what you'll use pub fn eval(&self) -> LivecodeResult { - let a = self.build_ctx()?; - let ctx = a.as_ref(); - + let ctx = self.build_ctx(); + // let ctx = a.as_ref(); self.n .eval_float_with_context(ctx) @@ -206,7 +210,7 @@ impl LazyNodeF32 { LazyNodeF32::Node(v) => { let vals = ExprWorldContextValues::new_from_idx(idx).with_prefix(&format!("{}_", prefix)); - v.add_expr_values(vals).eval() + v.add_expr_values(&vals).eval() } LazyNodeF32::NoCtxNode(v) => v.result(), } @@ -226,7 +230,7 @@ impl LazyNodeF32 { pub fn variable_names(&self) -> LivecodeResult> { match self { LazyNodeF32::Uninitialized => Err(LivecodeError::Raw("not initialized".to_owned())), - LazyNodeF32::Node(c) => Ok(c.build_ctx()?.iter_variable_names().collect_vec()), + LazyNodeF32::Node(c) => Ok(c.build_ctx().variable_names()), LazyNodeF32::NoCtxNode(_) => Err(LivecodeError::Raw("no ctx".to_owned())), } } diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index 1a9d3f6..49fce10 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -3,11 +3,14 @@ use std::{ sync::{Arc, RwLock}, }; -use evalexpr::{HashMapContext, IterateVariablesContext}; +use evalexpr::{Context, EvalexprResult, HashMapContext, IterateVariablesContext, Value}; use murrelet_common::*; use crate::{ - expr::{ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefs, MixedEvalDefsRef}, + expr::{ + lc_val_to_expr, ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefs, + MixedEvalDefsRef, + }, types::{AdditionalContextNode, LivecodeResult}, unitcells::UnitCellContext, }; @@ -196,6 +199,77 @@ impl LivecodeWorldState { pub fn asset_layers_in_key(&self, key: &str) -> &[String] { self.state.asset_layers_in_key(key) } + + pub(crate) fn update_with_simple_defs( + &self, + more_vals: ExprWorldContextValues, + ) -> WorldWithLocalVariables { + let locals = more_vals.to_vals(); + + WorldWithLocalVariables { + base: self.ctx().unwrap(), + locals, + builtins_disabled: false, + } + } + + pub fn to_local(&self) -> WorldWithLocalVariables { + WorldWithLocalVariables { + base: self.ctx().unwrap(), + locals: vec![], + builtins_disabled: false, + } + } +} + +#[derive(Debug, Clone)] +pub struct WorldWithLocalVariables { + base: Arc, // your cached global ctx (world.ctx()?.as_ref()) + locals: Vec<(String, Value)>, // small slice like &[("loc_pct", 0.42.into())] + builtins_disabled: bool, +} +impl WorldWithLocalVariables { + pub fn update_with_simple_defs(&mut self, more_vals: &ExprWorldContextValues) { + let mut locals = more_vals.to_vals(); + locals.extend(self.locals.iter().cloned()); + self.locals = locals; + + // WorldWithLocalVariables { + // base: self.base.clone(), + // locals, + // builtins_disabled: true, + // } + } + + pub(crate) fn variable_names(&self) -> Vec { + let mut names: Vec = self.locals.iter().map(|(k, _)| k.clone()).collect(); + names.extend(self.base.iter_variable_names()); + names + } +} + +impl Context for WorldWithLocalVariables { + fn get_value(&self, identifier: &str) -> Option<&Value> { + // locals win + if let Some((_, v)) = self.locals.iter().find(|(k, v)| k == identifier) { + return Some(v); + } + // otherwise fallback to global + self.base.get_value(identifier) + } + + fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult { + self.base.call_function(identifier, argument) + } + + fn are_builtin_functions_disabled(&self) -> bool { + self.builtins_disabled + } + + fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> { + self.builtins_disabled = disabled; + Ok(()) + } } #[derive(Debug, Clone)] diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 3ea8855..5393e0f 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -1163,4 +1163,8 @@ where vec![] } } + + pub fn run_id(&self) -> u64 { + self.run_id + } } From 158744dc8fa12eaa78ec5101870e1a6ef127f4bb Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 19 Oct 2025 14:35:50 -0400 Subject: [PATCH 081/149] arc the node to avoid the large clone costs that's showing up in the flamegraph --- murrelet_livecode/src/lazy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 43e19ea..63a1900 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -74,14 +74,14 @@ impl GetLivecodeIdentifiers for ControlLazyNodeF32 { // todo, figure out how to only build this context once per unitcell/etc #[derive(Debug, Clone)] pub struct LazyNodeF32Inner { - n: Node, // what will be evaluated! + n: Arc, // what will be evaluated! world: WorldWithLocalVariables, //LivecodeWorldState, // this is a reference :D - // more_defs: MixedEvalDefs, + // more_defs: MixedEvalDefs, } impl LazyNodeF32Inner { pub fn new(n: Node, world: LivecodeWorldState) -> Self { Self { - n, + n: Arc::new(n), world: world.to_local(), // more_defs: MixedEvalDefs::new(), } From a3f5e0f8fb6fe3d7fbe881fe453250cb0eab7424 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 19 Oct 2025 15:50:13 -0400 Subject: [PATCH 082/149] wip --- murrelet_livecode/src/state.rs | 17 ++++++++++++++--- murrelet_svg/src/svg.rs | 16 +++------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index 49fce10..6114c01 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -8,8 +8,8 @@ use murrelet_common::*; use crate::{ expr::{ - lc_val_to_expr, ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefs, - MixedEvalDefsRef, + init_evalexpr_func_ctx, lc_val_to_expr, ExprWorldContextValues, IntoExprWorldContext, + MixedEvalDefs, MixedEvalDefsRef, }, types::{AdditionalContextNode, LivecodeResult}, unitcells::UnitCellContext, @@ -153,7 +153,7 @@ impl LivecodeWorldState { } pub(crate) fn new_dummy() -> Self { - Self::new_legacy(LivecodeWorldStateInner::new_dummy()).unwrap() + Self::new_legacy(LivecodeWorldStateInner::new_dummy_with_funcs()).unwrap() } pub(crate) fn ctx(&self) -> LivecodeResult> { @@ -428,6 +428,17 @@ impl LivecodeWorldStateInner { self.assets.layer_for_key(key) } + pub fn new_dummy_with_funcs() -> Self { + Self::new( + &init_evalexpr_func_ctx().unwrap(), + &LivecodeSrc::new(vec![]), + LiveCodeTimeInstantInfo::new_dummy(), // time + AdditionalContextNode::new_dummy(), // node + Arc::new(Assets::empty()), // assets + ) + .unwrap() + } + pub fn new_dummy() -> Self { let empty_ctx = HashMapContext::new(); Self::new( diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index a4eed4d..1b89f9e 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -756,7 +756,7 @@ impl SvgPathCacheRef { self.0.borrow_mut().add_styled_text(layer, text) } - pub fn make_html(&self) -> Vec { + pub fn make_html(&self) -> (String, String) { self.0.borrow().make_html() } } @@ -858,20 +858,10 @@ impl SvgPathCache { // can add these to a document. I don't give the full svg so I can leave things // like defs alone and just update the paths and patternTransforms. - pub fn make_html(&self) -> Vec { + pub fn make_html(&self) -> (String, String) { let (paths, defs) = self.config.make_html(self); - // hmm, figure out a better way to do this, but quantize it - - let re = Regex::new(r"(-?\d*\.\d{3,})").unwrap(); - let path_str = re - .replace_all(&paths.to_string(), |caps: &Captures| { - let val: f32 = caps[0].parse().unwrap_or(0.0); - format!("{:.2}", val) - }) - .into_owned(); - - vec![defs.to_string(), path_str] + (defs.to_string(), paths.to_string()) } } From eeda72625fb279eea13201b52516236b5bf682af Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 22 Oct 2025 13:04:46 -0400 Subject: [PATCH 083/149] add microseconds --- murrelet_common/src/lib.rs | 48 +++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index ad50cf1..8b29aa1 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -45,7 +45,7 @@ pub struct MurreletTime(u128); // millis impl MurreletTime { pub fn now() -> Self { - MurreletTime(epoch_time_ms()) + MurreletTime(epoch_time_us()) } pub fn epoch() -> Self { @@ -64,21 +64,25 @@ impl MurreletTime { MurreletTime::in_x_ms(1000) } - pub fn as_millis_u128(&self) -> u128 { - self.0 - } - pub fn as_secs(&self) -> u64 { - (self.0 / 1000) as u64 + (self.as_millis_u128() / 1000) as u64 } // f32 for historical reasons, can change at some point pub fn as_secs_f32(&self) -> f32 { - (self.0 as f32) / 1000.0 + (self.as_millis()) / 1000.0 + } + + pub fn as_millis_u128(&self) -> u128 { + self.0 / 1000 } pub fn as_millis(&self) -> f32 { - self.0 as f32 + self.0 as f32 / 1000.0 + } + + pub fn as_micro(&self) -> u128 { + self.0 } } @@ -90,7 +94,6 @@ impl std::ops::Sub for MurreletTime { } } -// in seconds pub fn epoch_time_ms() -> u128 { #[cfg(target_arch = "wasm32")] { @@ -118,6 +121,33 @@ pub fn epoch_time_ms() -> u128 { } } +pub fn epoch_time_us() -> u128 { + #[cfg(target_arch = "wasm32")] + { + //use wasm_bindgen::JsCast; + + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(js_namespace = Date)] + fn now() -> f64; + } + + (now() * 1000.0) as u128 + } + + #[cfg(not(target_arch = "wasm32"))] + { + use std::time::SystemTime; + use std::time::UNIX_EPOCH; + let s = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("wat") + .as_micros(); + // (s * 1_000_000.0) as u128 + s + } +} + // // RUN ID // From 4df17de818a0424c46874c6dd66ce6a40b4b238f Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 2 Nov 2025 15:22:19 -0500 Subject: [PATCH 084/149] let's remove murrelet examples for now and figure out a better way to do that...' --- examples/foolish_guillemot/Cargo.toml | 59 ----- examples/foolish_guillemot/src/draw.rs | 156 ------------- examples/foolish_guillemot/src/lib.rs | 212 ------------------ examples/murrelet_example/Cargo.toml | 27 --- .../murrelet_example/src/configs/basic.yaml | 43 ---- examples/murrelet_example/src/main.rs | 131 ----------- 6 files changed, 628 deletions(-) delete mode 100644 examples/foolish_guillemot/Cargo.toml delete mode 100644 examples/foolish_guillemot/src/draw.rs delete mode 100644 examples/foolish_guillemot/src/lib.rs delete mode 100644 examples/murrelet_example/Cargo.toml delete mode 100644 examples/murrelet_example/src/configs/basic.yaml delete mode 100644 examples/murrelet_example/src/main.rs diff --git a/examples/foolish_guillemot/Cargo.toml b/examples/foolish_guillemot/Cargo.toml deleted file mode 100644 index ff5a52f..0000000 --- a/examples/foolish_guillemot/Cargo.toml +++ /dev/null @@ -1,59 +0,0 @@ -[package] -name = "foolish_guillemot" -version = "0.1.2" -edition = "2021" -authors = ["Jessica Stringham"] - - -[profile.release] -lto = true -opt-level = 'z' - -[lib] -crate-type = ["cdylib"] - -[dependencies] -wasm-bindgen = "0.2" -wasm-bindgen-futures = "0.4.42" -console_error_panic_hook = "0.1" - -glam = { version = "0.28.0", features = ["serde"] } -itertools = "0.10.5" - -serde = { version = "1.0.104", features = ["derive"] } -serde_yaml = "0.9.17" - -murrelet = "0.1.2" -murrelet_common = "0.1.2" -murrelet_livecode = "0.1.2" -murrelet_livecode_macros = "0.1.2" -murrelet_livecode_derive = "0.1.2" -murrelet_perform = { version = "0.1.2", features = [ - "for_the_web", -] } -murrelet_draw = "0.1.2" -murrelet_svg = "0.1.2" -murrelet_gpu = { version = "0.1.2", features = [ - "no_nannou", -] } - -lerpable = { version = "0.0.2" } - -wgpu = { version = "0.20.1", features = ["webgpu", "webgl"] } -anyhow = "1.0.86" - -[dependencies.getrandom] -version = "0.2" -features = ["js"] - -# todo: sort out dev build -[dependencies.web-sys] -version = "0.3" -features = [ - "console", - "Window", - "Document", - "Element", - "HtmlCanvasElement", - "CanvasRenderingContext2d", -] diff --git a/examples/foolish_guillemot/src/draw.rs b/examples/foolish_guillemot/src/draw.rs deleted file mode 100644 index cf80a71..0000000 --- a/examples/foolish_guillemot/src/draw.rs +++ /dev/null @@ -1,156 +0,0 @@ -use glam::*; -use murrelet_common::*; -use murrelet_draw::{ - draw::{CoreSDrawCtx, CoreSDrawCtxUnitCell, MurreletStyle, Sdraw}, - style::{MurreletPath, StyledPath}, -}; -use murrelet_livecode::unitcells::UnitCellContext; -use murrelet_perform::perform::SvgDrawConfig; -use murrelet_svg::svg::{SvgPathCache, SvgPathCacheRef}; - -// from the wasm-rust tutorial, this let's you log messages to the js console -// extern crate web_sys; -// macro_rules! log { -// ( $( $t:tt )* ) => { -// web_sys::console::log_1(&format!( $( $t )* ).into()); -// } -// } - -#[derive(Clone)] -pub struct WebSDrawCtxUnitCell { - ctx: CoreSDrawCtxUnitCell, - sdraw: WebSDrawCtx, -} - -impl WebSDrawCtxUnitCell { - pub fn draw_curve_path(&self, cd: MurreletPath) { - // todo, this is a little clumsy - let mut path = cd; - path = path.transform_with(&self.ctx.unit_cell_transform()); - path = path.transform_with_mat4_after(self.sdraw.transform()); - - self.sdraw.svg_draw.add_styled_path( - "", - StyledPath::new_from_path(path, self.svg_style().clone()), - ); - } - - pub fn clear_context(&self) -> WebSDrawCtx { - self.sdraw.clone() - } - - pub fn with_unit_cell_skew(&self, skew: bool) -> Self { - let mut c = self.clone(); - c.ctx = self.ctx.with_unit_cell_skew(skew); - c - } -} - -impl Sdraw for WebSDrawCtxUnitCell { - fn with_svg_style(&self, svg_style: murrelet_draw::draw::MurreletStyle) -> Self { - let mut sdraw = self.clone(); - sdraw.ctx = self.ctx.with_svg_style(svg_style); - sdraw - } - - fn svg_style(&self) -> murrelet_draw::draw::MurreletStyle { - self.ctx.svg_style() - } - - fn transform(&self) -> Mat4 { - self.ctx.transform() - } - - fn set_transform(&self, m: Mat4) -> Self { - let mut sdraw = self.clone(); - sdraw.ctx = self.ctx.set_transform(m); - sdraw - } - - fn transform_points(&self, face: &F) -> Polyline { - self.ctx.transform_points(face) - } - - fn line_space_multi(&self) -> f32 { - self.ctx.line_space_multi() - } -} - -#[derive(Clone)] -pub struct WebSDrawCtx { - ctx: CoreSDrawCtx, - pub svg_draw: SvgPathCacheRef, -} - -impl WebSDrawCtx { - fn _draw_curve_path(&self, cd_raw: MurreletPath) { - // todo, this is clunky - let cd = cd_raw; - - let path = StyledPath::new_from_path(cd, self.svg_style().clone()); - - self.svg_draw.add_styled_path("", path) - } - - pub fn make_html(&self) -> Vec { - self.svg_draw.make_html() - } - - pub fn add_guides(&self) { - self.svg_draw.add_guides(); - } - - pub fn save_doc(&self) { - self.svg_draw.save_doc(); - } - - pub fn with_detail(&self, detail: &UnitCellContext) -> WebSDrawCtxUnitCell { - let ctx = self.ctx.with_detail(detail); - WebSDrawCtxUnitCell { - ctx, - sdraw: self.clone(), - } - } - - pub fn new(svg_draw_config: &SvgDrawConfig) -> WebSDrawCtx { - let svg_draw = SvgPathCache::svg_draw(svg_draw_config); - - let ctx = CoreSDrawCtx::new( - MurreletStyle::new_white(false, false), - svg_draw_config.frame() as f32, - Mat4::IDENTITY, - ); - - WebSDrawCtx { svg_draw, ctx } - } -} - -impl Sdraw for WebSDrawCtx { - fn with_svg_style(&self, svg_style: MurreletStyle) -> Self { - let mut c = self.clone(); - c.ctx = c.ctx.with_svg_style(svg_style); - c - } - - fn svg_style(&self) -> MurreletStyle { - self.ctx.svg_style() - } - - fn transform(&self) -> Mat4 { - self.ctx.transform() - } - - fn set_transform(&self, m: Mat4) -> Self { - let mut c = self.clone(); - c.ctx = c.ctx.set_transform(m); - c - } - - fn transform_points(&self, face: &F) -> Polyline { - self.ctx.transform_points(face) - } - - fn line_space_multi(&self) -> f32 { - self.ctx.line_space_multi() - } -} diff --git a/examples/foolish_guillemot/src/lib.rs b/examples/foolish_guillemot/src/lib.rs deleted file mode 100644 index 40659b1..0000000 --- a/examples/foolish_guillemot/src/lib.rs +++ /dev/null @@ -1,212 +0,0 @@ -// pub mod draw; - -// use draw::{WebSDrawCtx, WebSDrawCtxUnitCell}; -// use glam::*; - -// use lerpable::Lerpable; -// use murrelet::prelude::*; -// use murrelet_common::{lerpify_vec2, mat4_from_mat3_transform}; -// use murrelet_draw::{ -// compass::*, -// draw::*, -// sequencers::*, -// style::{styleconf::*, MurreletPath}, -// }; -// use murrelet_livecode::types::{AdditionalContextNode, LivecodeError}; -// use wasm_bindgen::prelude::*; - -// // from the wasm-rust tutorial, this let's you log messages to the js console -// // extern crate web_sys; - -// // A macro to provide `println!(..)`-style syntax for `console.log` logging. -// // macro_rules! log { -// // ( $( $t:tt )* ) => { -// // web_sys::console::log_1(&format!( $( $t )* ).into()) -// // } -// // } - -// #[derive(Debug, Clone, Default, Livecode, Lerpable)] -// struct StyledShape { -// shape: MurreletCompass, -// style: StyleConf, -// #[livecode(serde_default = "false")] -// skew: bool, -// } -// impl StyledShape { -// fn draw(&self, draw_ctx: &WebSDrawCtxUnitCell) { -// // first do the simple transform - -// draw_ctx -// .with_unit_cell_skew(self.skew) -// .with_style(self.style.clone()) -// .draw_curve_path(MurreletPath::curve(self.shape.to_curve_maker())); -// } -// } - -// #[derive(Debug, Clone, Default, Livecode, Lerpable)] -// struct SimpleTile(Vec); -// impl SimpleTile { -// fn draw(&self, draw_ctx: &WebSDrawCtxUnitCell) { -// for v in &self.0 { -// v.draw(draw_ctx); -// } -// } -// } - -// #[derive(Debug, Clone, Livecode, Lerpable)] -// struct DrawingConfig { -// #[livecode(serde_default = "false")] -// debug: bool, -// sequencer: Sequencer, -// ctx: AdditionalContextNode, -// #[livecode(src = "sequencer", ctx = "ctx")] -// node: UnitCells, -// #[lerpable(func = "lerpify_vec2")] -// offset: Vec2, -// scale: f32, -// } -// impl DrawingConfig { -// fn draw(&self, draw_ctx: &WebSDrawCtx) { -// // todo, this is a little clunky.. and isn't so clunky in my other code hrm -// let transform = mat4_from_mat3_transform( -// Mat3::from_scale(Vec2::ONE * self.scale) * Mat3::from_translation(self.offset), -// ); - -// for t in self.node.iter() { -// t.node -// .draw(&draw_ctx.set_transform(transform).with_detail(&t.detail)) -// } -// } -// } - -// // set up livecoder -// #[derive(Debug, Clone, Livecode, Lerpable, TopLevelLiveCode)] -// struct LiveCodeConf { -// app: AppConfig, -// drawing: DrawingConfig, -// } - -// #[wasm_bindgen] -// pub async fn new_model(conf: String) -> WasmMurreletModelResult { -// MurreletModel::new(conf).await -// } - -// #[wasm_bindgen] -// pub struct MurreletModel { -// livecode: LiveCode, -// } -// #[wasm_bindgen] -// impl MurreletModel { -// #[wasm_bindgen(constructor)] -// pub async fn new(conf: String) -> WasmMurreletModelResult { -// // turn this on if you need to debug -// // std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - -// let livecode_src = LivecodeSrc::new(vec![Box::new(AppInputValues::new(false))]); - -// match LiveCode::new_web(conf, livecode_src, &vec![]) { -// Ok(livecode) => { -// let r = MurreletModel { livecode }; -// WasmMurreletModelResult::ok(r) -// } -// Err(e) => WasmMurreletModelResult::err(e), -// } -// } - -// #[wasm_bindgen] -// pub fn update_config(&mut self, conf: String) -> String { -// match self.livecode.update_config_to(&conf) { -// Ok(_) => "Success!".to_owned(), -// Err(e) => e, -// } -// } - -// #[wasm_bindgen] -// pub fn update_frame( -// &mut self, -// frame: u64, -// dim_x: f32, -// dim_y: f32, -// mouse_x: f32, -// mouse_y: f32, -// click: bool, -// ) { -// let app_input = -// MurreletAppInput::new_no_key(vec2(dim_x, dim_y), vec2(mouse_x, mouse_y), click, frame); -// // todo, show an error about this? -// self.livecode.update(&app_input, false).ok(); -// } - -// // useful if you have shaders, this will list what canvases to draw to -// #[wasm_bindgen] -// pub fn canvas_ids(&self) -> Vec { -// Vec::new() -// } - -// // useful if you have shaders, this should generate the DOM for image textures in the svg -// #[wasm_bindgen] -// pub fn make_img_defs(&self) -> String { -// String::new() -// } - -// #[wasm_bindgen] -// pub fn draw(&self) -> Vec { -// let svg_draw_config = self.livecode.svg_save_path(); -// let draw_ctx = WebSDrawCtx::new(&svg_draw_config); - -// self.livecode.config().drawing.draw(&draw_ctx); - -// draw_ctx.make_html() -// } - -// #[wasm_bindgen] -// pub fn fps(&self) -> f32 { -// self.livecode.app_config().time.fps -// } - -// #[wasm_bindgen] -// pub fn bg_color(&self) -> String { -// // it's fine that this one drops transparency -// self.livecode.app_config().bg_color.to_svg_rgb() -// } -// } - -// // just creating a Result that we can send to javascript -// #[wasm_bindgen] -// pub struct WasmMurreletModelResult { -// m: Option, -// err: String, -// } - -// #[wasm_bindgen] -// impl WasmMurreletModelResult { -// fn ok(m: MurreletModel) -> WasmMurreletModelResult { -// WasmMurreletModelResult { -// m: Some(m), -// err: String::new(), -// } -// } - -// fn err(err: LivecodeError) -> WasmMurreletModelResult { -// WasmMurreletModelResult { -// m: None, -// err: err.to_string(), -// } -// } - -// #[wasm_bindgen] -// pub fn is_err(&self) -> bool { -// self.m.is_none() -// } - -// #[wasm_bindgen] -// pub fn err_msg(self) -> String { -// self.err -// } - -// #[wasm_bindgen] -// pub fn to_model(self) -> MurreletModel { -// // panics if you don't check is error first -// self.m.unwrap() -// } -// } diff --git a/examples/murrelet_example/Cargo.toml b/examples/murrelet_example/Cargo.toml deleted file mode 100644 index dd5c524..0000000 --- a/examples/murrelet_example/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "murrelet_example" -version = "0.1.2" -edition = "2021" -authors = ["Jessica Stringham "] -repository = "https://github.com/jessstringham/murrelet.git" - -[dependencies] -glam = { version = "0.28.0", features = ["serde"] } -itertools = "0.10.5" -serde = { version = "1.0.104", features = ["derive"] } -serde_yaml = "0.9.17" - -murrelet = "0.1.1" -murrelet_livecode = "0.1.1" -murrelet_livecode_macros = "0.1.1" -murrelet_common = "0.1.1" -murrelet_livecode_derive = "0.1.1" -murrelet_perform = "0.1.1" -murrelet_draw = "0.1.1" -murrelet_svg = "0.1.1" - -murrelet_src_audio = "0.1.1" -murrelet_src_midi = "0.1.1" - -lerpable = "0.0.2" -schemars = "0.8.21" diff --git a/examples/murrelet_example/src/configs/basic.yaml b/examples/murrelet_example/src/configs/basic.yaml deleted file mode 100644 index e9c54f6..0000000 --- a/examples/murrelet_example/src/configs/basic.yaml +++ /dev/null @@ -1,43 +0,0 @@ -app: - debug: false - capture: false - width: 600.0 - clear_bg: kCt - bg_alpha: 0.2 # low enough so it won't clear for gpu - time: - realtime: true - fps: 40.0 - bpm: 120.0 - midi: true - audio: true - redraw: 1.0 - svg: - size: 120.0 - save: kAt - ctx: | - true; - -drawing: - sequencer: - type: Square # Rect, Hex - rows: 5 - cols: 5 - size: 10 - ctx: | - something = 10.0; - node: - val: x + something * a - curve: - closed: true - start: - type: Basic # Reference - loc: [0.0, 0.0] - angle_pi: 0.0 - dirs: - - type: Line - length: 50.0 - - type: Angle - angle_pi: 0.5 - - type: Arc - arc_length: 0.5 - radius: 50.0 \ No newline at end of file diff --git a/examples/murrelet_example/src/main.rs b/examples/murrelet_example/src/main.rs deleted file mode 100644 index cac5f34..0000000 --- a/examples/murrelet_example/src/main.rs +++ /dev/null @@ -1,131 +0,0 @@ -// use lerpable::Lerpable; -// use murrelet::prelude::*; -// use murrelet_draw::{compass::*, sequencers::*, style::MurreletPath}; -// use murrelet_livecode::types::*; -// use murrelet_src_audio::audio_src::AudioMng; -// //use murrelet_src_blte::blte::BlteMng; -// use murrelet_src_midi::midi::MidiMng; -// use murrelet_svg::svg::ToSvgData; - -// #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// struct SimpleTile { -// val: f32, -// curve: MurreletCompass, -// } -// impl SimpleTile { -// fn draw(&self, _ctx: &UnitCellContext) { -// // very simple program that outputs the value in test - -// let m = MurreletPath::curve(self.curve.to_curve_maker()).to_svg(); - -// println!("val {:?}", self.val); -// println!("m {:?}", m); -// //println!("ctx {:?}", ctx); -// } -// } - -// #[derive(Debug, Clone, Livecode, Lerpable)] -// struct DrawingConfig { -// #[livecode(serde_default = "false")] -// debug: bool, -// sequencer: Sequencer, -// ctx: AdditionalContextNode, -// #[livecode(src = "sequencer", ctx = "ctx")] -// node: UnitCells, -// } -// impl DrawingConfig { -// fn output(&self) { -// for t in self.node.iter() { -// t.node.draw(&t.detail) -// } -// } -// } - -// // set up livecoder -// #[derive(Debug, Clone, Livecode, Lerpable, TopLevelLiveCode)] -// struct LiveCodeConf { -// // global things -// app: AppConfig, -// drawing: DrawingConfig, -// } - -// struct Model { -// livecode: LiveCode, -// curr_frame: u64, -// } -// impl Model { -// fn new() -> Model { -// let capture_path_prefix = std::env::current_dir() -// .expect("failed to locate `project_path`") -// .join("_recordings"); - -// // here is where you connect the livecode srcs (time is always included) -// let livecode_src = LivecodeSrc::new(vec![ -// Box::new(AppInputValues::new(true)), -// Box::new(AudioMng::new()), -// Box::new(MidiMng::new()), -// //Box::new(BlteMng::new()), -// ]); - -// let livecode = LiveCode::new(capture_path_prefix, livecode_src, &[]); -// // let drawing_state = { -// // Drawing::new(&livecode.config().drawing) -// // }; - -// Model { -// livecode, -// curr_frame: 0, -// } -// } - -// fn update(&mut self) { -// self.curr_frame += 1; - -// let app_input = MurreletAppInput::default_with_frames(self.curr_frame); - -// self.livecode.update(&app_input, true).ok(); - -// // this is also where you could update state -// // instead of having the model hold the config directly, you can have -// // it hold state -// // if self.livecode.app_config().reload { -// // self.drawing_state = Drawing::new(&self.livecode.config().drawing); -// // } -// } - -// fn should_update(&self) -> bool { -// let w = self.livecode.world(); -// w.actual_frame() as u64 % self.livecode.app_config().redraw != 0 -// } - -// fn draw(&self) { -// let _svg_draw_config = self.livecode.svg_save_path(); - -// if !self.livecode.app_config().svg.save && self.should_update() { -// return; -// } - -// // let draw_ctx = SDrawCtx::new( -// // draw, -// // &svg_draw_config, -// // self.livecode.should_save_svg() -// // ); - -// // self.livecode.draw(&draw_ctx); // clear bg, etc - -// // draw your stuff -// // self.draw(&draw_ctx); -// self.livecode.config().drawing.output() -// } -// } - -fn main() { - // normally you call this on start up - // let mut model = Model::new(); - - // for _ in 0..10 { - // model.update(); - // model.draw(); - // std::thread::sleep(std::time::Duration::from_secs(1)); - // } -} From 02ab3d6e5eefbe08f65a9260df057d16cdf3a7b9 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 2 Nov 2025 15:22:26 -0500 Subject: [PATCH 085/149] wip --- murrelet_draw/src/tesselate.rs | 10 +- murrelet_draw/src/transform2d.rs | 12 ++ murrelet_perform/src/cli.rs | 2 +- murrelet_schema/examples/tests.rs | 254 +++++++++++++++--------------- 4 files changed, 143 insertions(+), 135 deletions(-) diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index ce28ce5..e540512 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -1,10 +1,6 @@ use std::{collections::HashMap, ops}; -use crate::{ - cubic::CubicBezier, - curve_drawer::CubicBezierPath, - svg::SvgPathDef, -}; +use crate::{cubic::CubicBezier, curve_drawer::CubicBezierPath, svg::SvgPathDef}; use delaunator::Triangulation; use glam::{vec2, Vec2, Vec2Swizzles}; use itertools::Itertools; @@ -780,7 +776,7 @@ where let r: HashMap>> = map .iter() .map(|(k, v)| { - println!("processing {:?}", k); + // println!("processing {:?}", k); ( k.to_string(), v.iter().map(|vv| parse_data(vv, line_space)).collect_vec(), @@ -803,7 +799,7 @@ where for event in svg::open(path, &mut content).unwrap() { if let svg::parser::Event::Tag(_, _, attributes) = event { if let Some(id) = attributes.get("id") { - println!("loading {:?}", id); + // println!("loading {:?}", id); recent_id = id.to_string(); } diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index 3bd347a..62fc79f 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -256,6 +256,18 @@ impl Transform2d { pub fn steps(&self) -> &Vec { &self.0 } + + pub fn transform_vec_vec(&self, vs: &[Vec]) -> Vec> { + let mut vv = vec![]; + for line in vs { + let mut vvv = vec![]; + for v in line { + vvv.push(self.transform_vec2(*v)); + } + vv.push(vvv); + } + vv + } } impl Default for ControlTransform2d { diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index d81e125..bddc0e3 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -63,7 +63,7 @@ pub struct BaseConfigArgs { #[arg(short, long, default_value_t = Default::default())] pub resolution: TextureDimensions, // window resolution - #[arg(long, default_value_t = 2, value_parser = clap::value_parser!(u32).range(1..=8))] + #[arg(long, default_value_t = 4, value_parser = clap::value_parser!(u32).range(1..=8))] pub texture_multiplier: u32, // controls number of pixels the shaders work on #[arg(long)] diff --git a/murrelet_schema/examples/tests.rs b/murrelet_schema/examples/tests.rs index 6ceac73..cf50077 100644 --- a/murrelet_schema/examples/tests.rs +++ b/murrelet_schema/examples/tests.rs @@ -1,127 +1,127 @@ -use std::collections::HashMap; - -use murrelet_schema::*; -use murrelet_schema_derive::MurreletSchema; - -#[derive(MurreletSchema)] -pub struct BasicTypes { - a_number: f32, - b_number: usize, - c_number: u64, - d_number: i32, - bool: bool, - something: Vec, - s: String, -} - -fn custom_func() -> MurreletSchema { - MurreletSchema::Val(MurreletPrimitive::Num) -} - -#[derive(MurreletSchema)] -pub struct OverridesAndRecursive { - a_number: f32, - something: Vec, - #[murrelet_schema(func = "custom_func")] - label: String, - #[murrelet_schema(kind = "skip")] - b: HashMap, -} - -#[derive(MurreletSchema)] -enum EnumTest { - A, - B(OverridesAndRecursive), -} - -#[derive(MurreletSchema)] -struct SimpleNewtype(f32); - -// // fn lerp_partial(&self, pct: T) -> Self { -// // SimpleNewtype(pct.lerp_pct() as f32) -// // } -// // } - -// // #[derive(Debug, Clone, MurreletUX)] - -fn main() { - // let b = BasicTypes{ - // a_number: 1.0, - // b_number: -10.0, - // }; - let test_val = BasicTypes::make_schema(); - - let basic_types_schema = MurreletSchema::Struct( - "BasicTypes".to_string(), - vec![ - ( - "a_number".to_owned(), - MurreletSchema::Val(MurreletPrimitive::Num), - ), - ( - "b_number".to_owned(), - MurreletSchema::Val(MurreletPrimitive::Num), - ), - ( - "c_number".to_owned(), - MurreletSchema::Val(MurreletPrimitive::Num), - ), - ( - "d_number".to_owned(), - MurreletSchema::Val(MurreletPrimitive::Num), - ), - ( - "bool".to_owned(), - MurreletSchema::Val(MurreletPrimitive::Bool), - ), - ( - "something".to_owned(), - MurreletSchema::list(MurreletSchema::Val(MurreletPrimitive::Num)), - ), - ( - "s".to_owned(), - MurreletSchema::Val(MurreletPrimitive::String), - ), - ], - ); - - assert_eq!(test_val, basic_types_schema); - - let test_val = OverridesAndRecursive::make_schema(); - - let overrides_and_recursive_schema = MurreletSchema::Struct( - "OverridesAndRecursive".to_string(), - vec![ - ( - "a_number".to_owned(), - MurreletSchema::Val(MurreletPrimitive::Num), - ), - ( - "something".to_owned(), - MurreletSchema::list(basic_types_schema), - ), - ( - "label".to_owned(), - MurreletSchema::Val(MurreletPrimitive::Num), - ), // make sure it calls the override - ("b".to_owned(), MurreletSchema::Skip), - ], - ); - assert_eq!(test_val, overrides_and_recursive_schema); - - let test_val = EnumTest::make_schema(); - - assert_eq!( - test_val, - MurreletSchema::Enum( - "EnumTest".to_string(), - vec![ - (MurreletEnumVal::Unit("A".to_owned())), - (MurreletEnumVal::Unnamed("B".to_owned(), overrides_and_recursive_schema)), - ], - false - ) - ); -} - -// fn main() {} +// use std::collections::HashMap; + +// use murrelet_schema::*; +// use murrelet_schema_derive::MurreletSchema; + +// #[derive(MurreletSchema)] +// pub struct BasicTypes { +// a_number: f32, +// b_number: usize, +// c_number: u64, +// d_number: i32, +// bool: bool, +// something: Vec, +// s: String, +// } + +// fn custom_func() -> MurreletSchema { +// MurreletSchema::Val(MurreletPrimitive::Num) +// } + +// #[derive(MurreletSchema)] +// pub struct OverridesAndRecursive { +// a_number: f32, +// something: Vec, +// #[murrelet_schema(func = "custom_func")] +// label: String, +// #[murrelet_schema(kind = "skip")] +// b: HashMap, +// } + +// #[derive(MurreletSchema)] +// enum EnumTest { +// A, +// B(OverridesAndRecursive), +// } + +// #[derive(MurreletSchema)] +// struct SimpleNewtype(f32); + +// // // fn lerp_partial(&self, pct: T) -> Self { +// // // SimpleNewtype(pct.lerp_pct() as f32) +// // // } +// // // } + +// // // #[derive(Debug, Clone, MurreletUX)] + +// fn main() { +// // let b = BasicTypes{ +// // a_number: 1.0, +// // b_number: -10.0, +// // }; +// let test_val = BasicTypes::make_schema(); + +// let basic_types_schema = MurreletSchema::Struct( +// "BasicTypes".to_string(), +// vec![ +// ( +// "a_number".to_owned(), +// MurreletSchema::Val(MurreletPrimitive::Num), +// ), +// ( +// "b_number".to_owned(), +// MurreletSchema::Val(MurreletPrimitive::Num), +// ), +// ( +// "c_number".to_owned(), +// MurreletSchema::Val(MurreletPrimitive::Num), +// ), +// ( +// "d_number".to_owned(), +// MurreletSchema::Val(MurreletPrimitive::Num), +// ), +// ( +// "bool".to_owned(), +// MurreletSchema::Val(MurreletPrimitive::Bool), +// ), +// ( +// "something".to_owned(), +// MurreletSchema::list(MurreletSchema::Val(MurreletPrimitive::Num)), +// ), +// ( +// "s".to_owned(), +// MurreletSchema::Val(MurreletPrimitive::String), +// ), +// ], +// ); + +// assert_eq!(test_val, basic_types_schema); + +// let test_val = OverridesAndRecursive::make_schema(); + +// let overrides_and_recursive_schema = MurreletSchema::Struct( +// "OverridesAndRecursive".to_string(), +// vec![ +// ( +// "a_number".to_owned(), +// MurreletSchema::Val(MurreletPrimitive::Num), +// ), +// ( +// "something".to_owned(), +// MurreletSchema::list(basic_types_schema), +// ), +// ( +// "label".to_owned(), +// MurreletSchema::Val(MurreletPrimitive::Num), +// ), // make sure it calls the override +// ("b".to_owned(), MurreletSchema::Skip), +// ], +// ); +// assert_eq!(test_val, overrides_and_recursive_schema); + +// let test_val = EnumTest::make_schema(); + +// assert_eq!( +// test_val, +// MurreletSchema::Enum( +// "EnumTest".to_string(), +// vec![ +// (MurreletEnumVal::Unit("A".to_owned())), +// (MurreletEnumVal::Unnamed("B".to_owned(), overrides_and_recursive_schema)), +// ], +// false +// ) +// ); +// } + +fn main() {} From e150e9d43c1a57cffafeedfff12cfa50ece0192e Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 5 Nov 2025 22:56:59 -0500 Subject: [PATCH 086/149] wip --- Cargo.toml | 2 -- murrelet_common/src/idx.rs | 10 ++++--- murrelet_livecode/src/unitcells.rs | 42 ++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0a7b5b8..2cc3464 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,6 @@ members = [ "murrelet_src_midi", "murrelet_src_osc", "murrelet_svg", - "examples/murrelet_example", - "examples/foolish_guillemot", "tinylivecode", "murrelet_schema", "murrelet_schema_derive", diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 6a33bef..343dd71 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -235,11 +235,13 @@ impl IdxInRange2d { vec2(self.i.half_step_pct(), self.j.half_step_pct()) } - pub fn lerp_idx(&self, x: f32, y: f32) -> [(usize, usize); 4] { - // helps tell which indexes to use for lerping - let x_idx = x as usize; - let y_idx = y as usize; + // pub fn lerp_idx(&self, x: f32, y: f32) -> [(usize, usize); 4] { + // self.lerp_idx_u(x as usize, y as usize) + // } + pub fn lerp_idx(&self) -> [(usize, usize); 4] { + let x_idx = self.i.i() as usize; + let y_idx = self.j.i() as usize; let x_is_too_far = x_idx + 1 >= self.i.total as usize; let y_is_too_far = y_idx + 1 >= self.j.total as usize; diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index 6072c73..e2dfe5e 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -391,6 +391,48 @@ impl UnitCellLookup { pub fn force_get_dim(&self, dim: &Dim2d) -> &UnitCell { self.force_get_ij(dim.i(), dim.j()) } + + // this probably only works for rectangles... + pub fn loc_to_tile_idx_and_offset( + &self, + v: Vec2, + ) -> Option { + // blah + for uc in self.data.values() { + if uc.bounds().contains(v) { + let local_offset = v - uc.center(); + let scaled_offset = local_offset / uc.bounds().wh(); + return Some(IdxAndOffset { + ij: uc.idx(), + offset_i: scaled_offset.x, + offset_j: scaled_offset.y, + }); + } + } + None + } + + // pub fn get_vec2(&self, v: Vec2) {} +} + +pub struct IdxAndOffset { + ij: IdxInRange2d, + // how far in i and j they are + offset_i: f32, + offset_j: f32, +} +impl IdxAndOffset { + pub fn lerp_idxes(&self) -> [(usize, usize); 4] { + self.ij.lerp_idx() + } + + pub fn offset_i(&self) -> f32 { + self.offset_i + } + + pub fn offset_j(&self) -> f32 { + self.offset_j + } } #[derive(PartialEq, Eq, Copy, Clone)] From cc9224fed7f2009397f22a4dbbd58d99455bcdaa Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 7 Nov 2025 19:34:20 -0500 Subject: [PATCH 087/149] add stricter check --- .../src/derive_livecode.rs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 8cb207a..f46a59e 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -181,14 +181,17 @@ impl GenFinal for FieldTokensLivecode { let for_variable_idents = variants.iter().map(|x| x.for_variable_idents.clone()); let for_function_idents = variants.iter().map(|x| x.for_function_idents.clone()); - let maybe_cfg_attr = if cfg!(feature = "schemars") { - quote! {, schemars::JsonSchema} + let (maybe_cfg_attr, additional) = if cfg!(feature = "schemars") { + ( + quote! {, schemars::JsonSchema}, + quote! {#[schemars(deny_unknown_fields)]}, + ) } else { - quote! {} + (quote! {}, quote! {}) }; - quote! { #[derive(Debug, Clone, serde::Deserialize #maybe_cfg_attr)] + #additional #vis struct #new_ident { #(#for_struct,)* } @@ -247,14 +250,17 @@ impl GenFinal for FieldTokensLivecode { let enum_tag = idents.tags; - let maybe_cfg_attr = if cfg!(feature = "schemars") { - quote! {, schemars::JsonSchema} + let (maybe_cfg_attr, additional) = if cfg!(feature = "schemars") { + ( + quote! {, schemars::JsonSchema}, + quote! {#[schemars(deny_unknown_fields)]}, + ) } else { - quote! {} + (quote! {}, quote! {}) }; - quote! { #[derive(Debug, Clone, serde::Deserialize #maybe_cfg_attr)] + #additional #[allow(non_camel_case_types)] #enum_tag #vis enum #new_ident { @@ -306,14 +312,18 @@ impl GenFinal for FieldTokensLivecode { let for_variable_idents = variants.iter().map(|x| x.for_variable_idents.clone()); let for_function_idents = variants.iter().map(|x| x.for_function_idents.clone()); - let maybe_cfg_attr = if cfg!(feature = "schemars") { - quote! {, schemars::JsonSchema} + let (maybe_cfg_attr, additional) = if cfg!(feature = "schemars") { + ( + quote! {, schemars::JsonSchema}, + quote! {#[schemars(deny_unknown_fields)]}, + ) } else { - quote! {} + (quote! {}, quote! {}) }; quote! { #[derive(Debug, Clone, serde::Deserialize #maybe_cfg_attr)] + #additional #vis struct #new_ident(#(#for_struct,)*); impl murrelet_livecode::livecode::LivecodeFromWorld<#name> for #new_ident { From 53d13c41838e3d01566d6323bc4d77cf0b706e98 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 8 Nov 2025 22:43:25 -0500 Subject: [PATCH 088/149] wip --- murrelet_common/src/idx.rs | 15 +++++++++++++++ murrelet_draw/src/curve_drawer.rs | 30 ++++++++++++++++++++++++++++++ murrelet_draw/src/scaffold.rs | 5 ++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 343dd71..765b67f 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -7,6 +7,13 @@ use serde::{Deserialize, Serialize}; use crate::lerp; +#[derive(Debug, Clone, Copy)] +pub enum IdxMatch { + First, + Last, + Idx(u64), +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct IdxInRange { i: u64, @@ -38,6 +45,14 @@ impl IdxInRange { (0..total).map(|i| IdxInRange::new(i, total)).collect_vec() } + pub fn matches(&self, m: &IdxMatch) -> bool { + match m { + IdxMatch::First => self.i() == 0, + IdxMatch::Last => self.is_last(), + IdxMatch::Idx(i) => self.i() == *i, + } + } + pub fn prev_i(&self) -> Option { if self.i == 0 { None diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 95d834a..4eedb09 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -691,3 +691,33 @@ impl CurvePoints { CurvePoints::new(self.points.iter().cloned().rev().collect_vec()) } } + +pub trait ToCurveDrawer { + fn to_segments(&self) -> Vec; + fn to_cd_closed(&self) -> CurveDrawer { + CurveDrawer::new(self.to_segments(), true) + } + fn to_cd_open(&self) -> CurveDrawer { + CurveDrawer::new(self.to_segments(), false) + } +} + +impl ToCurveDrawer for Vec { + fn to_segments(&self) -> Vec { + self.clone() + } +} + +impl ToCurveDrawer for Vec { + fn to_segments(&self) -> Vec { + vec![CurveSegment::new_simple_points(self.clone())] + } +} + +impl ToCurveDrawer for Vec { + fn to_segments(&self) -> Vec { + vec![CurveSegment::new_simple_points( + self.iter().map(|x| x.loc()).collect_vec(), + )] + } +} diff --git a/murrelet_draw/src/scaffold.rs b/murrelet_draw/src/scaffold.rs index 56eb455..6a10541 100644 --- a/murrelet_draw/src/scaffold.rs +++ b/murrelet_draw/src/scaffold.rs @@ -1,6 +1,9 @@ -use geo::{BooleanOps, BoundingRect, Contains}; +use geo::{BooleanOps, BoundingRect, Contains, MultiPolygon}; use glam::{vec2, Vec2}; use itertools::Itertools; +use murrelet_common::Polyline; + +use crate::curve_drawer::CurveDrawer; pub fn line_to_multipolygon(curves: &[Vec2]) -> geo::MultiPolygon { geo::MultiPolygon::new(vec![line_to_polygon(curves)]) From f5d613027465d1373c085bc87afaade87ce09853 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 9 Nov 2025 18:39:06 -0500 Subject: [PATCH 089/149] wip --- murrelet_draw/src/curve_drawer.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 4eedb09..b3c47d8 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -6,7 +6,6 @@ use murrelet_common::*; use murrelet_livecode_derive::*; use serde::{Deserialize, Serialize}; use svg::node::element::path::Data; -// use crate::serialize::serialize_vec2; use crate::{ cubic::CubicBezier, @@ -44,11 +43,10 @@ impl CubicOptionVec2 { #[derive(Debug, Clone, Default, Livecode, Lerpable, Serialize, Deserialize)] pub struct CubicBezierTo { pub ctrl1: CubicOptionVec2, - #[lerpable(func = "lerpify_vec2")] + // #[serde(serialize_with = "serialize_vec2")] pub ctrl2: Vec2, // #[serde(serialize_with = "serialize_vec2")] - #[lerpable(func = "lerpify_vec2")] pub to: Vec2, } @@ -64,9 +62,8 @@ impl CubicBezierTo { #[derive(Debug, Clone, Default, Livecode, Lerpable, Serialize, Deserialize)] pub struct CubicBezierPath { - #[lerpable(func = "lerpify_vec2")] pub from: Vec2, - #[lerpable(func = "lerpify_vec2")] + pub ctrl1: Vec2, pub curves: Vec, pub closed: bool, @@ -81,7 +78,7 @@ impl CubicBezierPath { } } - fn to_vec2_count(&self, count: usize) -> Vec { + pub fn to_vec2_count(&self, count: usize) -> Vec { let len = self.to_cd().length(); let line_space = len / count as f32; @@ -464,7 +461,6 @@ impl CurveSegment { #[derive(Debug, Clone, Livecode, Lerpable)] pub struct CurveArc { #[livecode(serde_default = "zeros")] - #[lerpable(func = "lerpify_vec2")] pub loc: Vec2, // center of circle pub radius: f32, pub start_pi: LivecodeAnglePi, @@ -607,13 +603,12 @@ impl CurveArc { #[derive(Debug, Clone, Livecode, Lerpable)] pub struct CurveCubicBezier { - #[lerpable(func = "lerpify_vec2")] from: Vec2, - #[lerpable(func = "lerpify_vec2")] + ctrl1: Vec2, - #[lerpable(func = "lerpify_vec2")] + ctrl2: Vec2, - #[lerpable(func = "lerpify_vec2")] + to: Vec2, } impl CurveCubicBezier { From e99ce4cecedb6e50fe6db07b939075bc4b4e0a01 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 15 Nov 2025 17:39:11 -0500 Subject: [PATCH 090/149] wip --- murrelet_common/src/geometry.rs | 12 ++++++++++++ murrelet_common/src/idx.rs | 15 +++++++++++++++ murrelet_common/src/intersection.rs | 1 - murrelet_draw/src/cubic.rs | 15 +++++++++++---- murrelet_draw/src/curve_drawer.rs | 6 ++++++ murrelet_draw/src/scaffold.rs | 5 +---- murrelet_livecode/src/lazy.rs | 10 ++++++++-- 7 files changed, 53 insertions(+), 11 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 2ac4b5a..682d2a3 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -477,6 +477,10 @@ impl SpotOnCurve { pub fn line_to_spot(&self, length: f32) -> Vec2 { self.loc() + self.angle().to_norm_dir() * length } + + pub fn flip(&self) -> SpotOnCurve { + Self::new(self.loc, self.angle.perp_to_left().perp_to_left()) + } } #[derive(Clone, Copy, Debug)] @@ -623,6 +627,10 @@ impl PointToPoint { pub fn pct(&self, loc: f32) -> Vec2 { self.start + loc * (self.end - self.start) } + + pub fn start_spot(&self) -> SpotOnCurve { + SpotOnCurve::new(self.start, self.angle()) + } } #[derive(Copy, Clone, Debug)] @@ -647,6 +655,10 @@ impl LineFromVecAndLen { pub fn to_vec(&self) -> Vec { vec![self.start, self.to_last_point()] } + + pub fn to_p2p(&self) -> PointToPoint { + PointToPoint::new(self.start, self.to_last_point()) + } } // more curve things diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 765b67f..74f2fac 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -143,6 +143,21 @@ impl IdxInRange { pub fn to_2d(&self) -> IdxInRange2d { IdxInRange2d::new_from_single_idx(*self) } + + pub fn skip>(&self, count: T) -> Option + where + >::Error: core::fmt::Debug, + { + let count_u64 = count.try_into().expect("can't convert to u64"); + if self.i + count_u64 >= self.total { + None + } else { + Some(IdxInRange { + i: self.i + count_u64, + total: self.total, + }) + } + } } #[derive(Debug, Clone, Copy)] diff --git a/murrelet_common/src/intersection.rs b/murrelet_common/src/intersection.rs index b93f65b..e517b64 100644 --- a/murrelet_common/src/intersection.rs +++ b/murrelet_common/src/intersection.rs @@ -28,7 +28,6 @@ pub fn find_intersection_inf(line0: (Vec2, Vec2), line1: (Vec2, Vec2)) -> Option let intersection = vec2(px, py); - // okay we have an intersection! now just make sure it's in each segment Some(intersection) } } diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index 3fe86c7..cfd35de 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -1,7 +1,7 @@ use glam::Vec2; use murrelet_common::{Angle, IsAngle, SpotOnCurve}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct CubicBezier { pub from: Vec2, pub ctrl1: Vec2, @@ -126,7 +126,14 @@ impl CubicBezier { to: self.from, } } -} - - + pub fn tangent_at_pct_safe(&self, pct: f32) -> SpotOnCurve { + if pct < 0.01 { + self.start_to_tangent().0 + } else if pct > 0.99 { + self.end_to_tangent().0 + } else { + self.tangent_at_pct(pct) + } + } +} diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index b3c47d8..8ee6ac2 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -697,6 +697,12 @@ pub trait ToCurveDrawer { } } +impl ToCurveDrawer for CurveSegment { + fn to_segments(&self) -> Vec { + vec![self.clone()] + } +} + impl ToCurveDrawer for Vec { fn to_segments(&self) -> Vec { self.clone() diff --git a/murrelet_draw/src/scaffold.rs b/murrelet_draw/src/scaffold.rs index 6a10541..56eb455 100644 --- a/murrelet_draw/src/scaffold.rs +++ b/murrelet_draw/src/scaffold.rs @@ -1,9 +1,6 @@ -use geo::{BooleanOps, BoundingRect, Contains, MultiPolygon}; +use geo::{BooleanOps, BoundingRect, Contains}; use glam::{vec2, Vec2}; use itertools::Itertools; -use murrelet_common::Polyline; - -use crate::curve_drawer::CurveDrawer; pub fn line_to_multipolygon(curves: &[Vec2]) -> geo::MultiPolygon { geo::MultiPolygon::new(vec![line_to_polygon(curves)]) diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 63a1900..28322fe 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -250,11 +250,17 @@ where fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult; - fn eval_idx(&self, idx: IdxInRange, prefix: &str) -> LivecodeResult { - let vals = ExprWorldContextValues::new_from_idx(idx).with_prefix(&format!("{}_", prefix)); + // without a _, like unitcell.. + fn eval_idx_(&self, idx: IdxInRange, prefix: &str) -> LivecodeResult { + let vals = ExprWorldContextValues::new_from_idx(idx).with_prefix(&format!("{}", prefix)); self.eval_lazy(&MixedEvalDefs::new_from_expr(vals)) } + + // backwards compatible + fn eval_idx(&self, idx: IdxInRange, prefix: &str) -> LivecodeResult { + self.eval_idx_(idx, &format!("{}_", prefix)) + } } impl IsLazy for LazyNodeF32 { From 899eda490761cf0d243c2f4f9ee9d36ac8d21d32 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 16 Nov 2025 14:35:34 -0500 Subject: [PATCH 091/149] wip --- murrelet_draw/src/tesselate.rs | 107 +++++++++++++++++++++++++++++-- murrelet_draw/src/transform2d.rs | 12 ++++ murrelet_svg/src/svg.rs | 1 - 3 files changed, 115 insertions(+), 5 deletions(-) diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index e540512..e08d370 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -13,7 +13,9 @@ use lyon::{ path::{FillRule, Path}, tessellation::{BuffersBuilder, FillOptions, FillTessellator, FillVertex}, }; -use murrelet_common::{triangulate::VertexSimple, Polyline}; +use murrelet_common::{ + curr_next_no_loop_iter, triangulate::VertexSimple, PointToPoint, Polyline, SpotOnCurve, +}; pub trait ToVecVec2 { fn to_vec2(&self) -> Vec; @@ -128,7 +130,101 @@ pub fn cubic_bezier_length(c: &CubicBezier) -> f32 { line.approximate_length(0.1) } -pub fn flatten_cubic_bezier_path(path: &[CubicBezier], closed: bool) -> Option> { +pub fn segment_vec(from: Vec2, to: Vec2, line_space: f32, offset: f32) -> (Vec, f32) { + if to.distance(from) < 1e-7 { + return (vec![], offset); + } + + let mut dist_since_last = offset; // how far into this one we should start + dist_since_last += (to - from).length(); // add how far we will travel + + let backwards_dir = (to - from).normalize(); + + let mut lines = vec![]; + + while dist_since_last >= line_space { + // okay find how much we overshot it, and move backwards + // it should be within to and from.. or else we would've stopped before? + let overshot_amount = dist_since_last - line_space; + // figure out how to go backwards + // and now move backwards + let new_to = to - backwards_dir * overshot_amount; + lines.push(new_to); + dist_since_last = overshot_amount; + } + + (lines, dist_since_last) +} + +pub fn evenly_split_cubic_bezier(c: &CubicBezier, count: usize) -> Vec { + // always include the start (and end) + let v = flatten_cubic_bezier_path_with_tolerance(&vec![c.clone()], false, 0.1); + if count <= 1 { + let angle = PointToPoint::new(c.from, c.to).angle(); + return vec![c.from, c.to] + .into_iter() + .map(|x| SpotOnCurve::new(x, angle)) + .collect_vec(); + } + + if let Some(a) = v { + let mut length = 0.0; + for (curr, next) in curr_next_no_loop_iter(&a) { + length += curr.distance(*next); + } + + let spacing_for_dot = length / count as f32; + + let mut last_angle = None; + // let mut last_point = None; + + let mut v = vec![]; + let mut dist = 0.0; + for (curr, next) in curr_next_no_loop_iter(&a) { + let (vs, a) = segment_vec(*curr, *next, spacing_for_dot, dist); + let angle = PointToPoint::new(*curr, *next).angle(); + + // if it's the very first item + if last_angle.is_none() { + v.push(SpotOnCurve::new(*curr, angle)) + } + + let new = vs + .into_iter() + .map(|x| SpotOnCurve::new(x, angle)) + .collect_vec(); + v.extend(new); + dist = a; + + last_angle = Some(angle); + // if let Some(last) = new.last() { + // last_point = Some(last.loc); + // } + } + + // if let Some(last) = last_point { + // // skip this spike or reuse previous direction + // continue; + // } else { + // // v.push(SpotOnCurve::new(c.from, last_angle.unwrap())); + + // } + + v + } else { + let angle = PointToPoint::new(c.from, c.to).angle(); + return vec![c.from, c.to] + .into_iter() + .map(|x| SpotOnCurve::new(x, angle)) + .collect_vec(); + } +} + +pub fn flatten_cubic_bezier_path_with_tolerance( + path: &[CubicBezier], + closed: bool, + tolerance: f32, +) -> Option> { if path.is_empty() { return None; } @@ -147,9 +243,8 @@ pub fn flatten_cubic_bezier_path(path: &[CubicBezier], closed: bool) -> Option { points.push(vec2(p.x as f32, p.y as f32)); } @@ -164,6 +259,10 @@ pub fn flatten_cubic_bezier_path(path: &[CubicBezier], closed: bool) -> Option Option> { + flatten_cubic_bezier_path_with_tolerance(path, closed, 0.01) +} + pub fn cubic_bezier_path_to_lyon(path: &[CubicBezier], closed: bool) -> Option { // let mut builder = Path::builder(); diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index 62fc79f..546b9de 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -268,6 +268,18 @@ impl Transform2d { } vv } + + pub fn with_one_action(&self, action: Transform2dStep) -> Transform2d { + let mut c = self.clone(); + c.append_one_action(action); + c + } + + pub fn with_transform(&self, loc: Transform2d) -> Transform2d { + let mut c = self.clone(); + c.append_transform(&loc); + c + } } impl Default for ControlTransform2d { diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 1b89f9e..61947f7 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -17,7 +17,6 @@ use murrelet_draw::{ svg::{SvgPathDef, SvgShape, TransformedSvgShape}, }; use murrelet_perform::perform::SvgDrawConfig; -use regex::{Captures, Regex}; use svg::{ node::element::{path::Data, Group}, Document, Node, From 86c327a5bd8d206496c9c1710f60d3f7dd219cf8 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 16 Nov 2025 16:07:59 -0500 Subject: [PATCH 092/149] wip --- murrelet_gpu/src/editable_shaders.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/murrelet_gpu/src/editable_shaders.rs b/murrelet_gpu/src/editable_shaders.rs index 3b50877..66f42a4 100644 --- a/murrelet_gpu/src/editable_shaders.rs +++ b/murrelet_gpu/src/editable_shaders.rs @@ -38,6 +38,22 @@ impl ShaderStrings { } } + pub fn get_shader_str(&self, c: &GraphicsWindowConf, name: &str) -> Option { + if let Some(str) = self.shaders.get(name) { + Some(Self::shader(&str)) + } else { + None + } + } + + pub fn get_shader_str_2tex(&self, c: &GraphicsWindowConf, name: &str) -> Option { + if let Some(str) = self.shaders.get(name) { + Some(Self::shader2tex(&str)) + } else { + None + } + } + pub fn get_graphics_ref(&self, c: &GraphicsWindowConf, name: &str) -> Option { if let Some(str) = self.shaders.get(name) { Some( From c8930ecad3568d77069e3274535f0792688c62e9 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 16 Nov 2025 23:54:20 -0500 Subject: [PATCH 093/149] wip --- murrelet_common/src/triangulate.rs | 190 ++++++-- murrelet_draw/src/tesselate.rs | 20 +- murrelet_gpu/src/compute.rs | 8 +- murrelet_gpu/src/editable_shaders.rs | 23 +- murrelet_gpu/src/gpu_livecode.rs | 75 ++- murrelet_gpu/src/gpu_macros.rs | 459 ++++++++++++------ murrelet_gpu/src/graphics_ref.rs | 407 ++++++++++------ .../src/derive_graphics_trait.rs | 20 +- 8 files changed, 820 insertions(+), 382 deletions(-) diff --git a/murrelet_common/src/triangulate.rs b/murrelet_common/src/triangulate.rs index a58bfd5..091f532 100644 --- a/murrelet_common/src/triangulate.rs +++ b/murrelet_common/src/triangulate.rs @@ -1,36 +1,19 @@ +// use glam::{vec2, Vec2, Vec3}; +// use lerpable::Lerpable; + +use bytemuck::{Pod, Zeroable}; use glam::{vec2, Vec2, Vec3}; use lerpable::Lerpable; -impl Lerpable for VertexSimple { - fn lerpify(&self, other: &Self, pct: &T) -> Self { - VertexSimple { - position: [ - self.position[0].lerpify(&other.position[0], pct), - self.position[1].lerpify(&other.position[1], pct), - self.position[2].lerpify(&other.position[2], pct), - ], - normal: [ - self.normal[0].lerpify(&other.normal[0], pct), - self.normal[1].lerpify(&other.normal[1], pct), - self.normal[2].lerpify(&other.normal[2], pct), - ], - face_pos: [ - self.face_pos[0].lerpify(&other.face_pos[0], pct), - self.face_pos[1].lerpify(&other.face_pos[1], pct), - ], - } - } -} - -#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)] #[repr(C)] -pub struct VertexSimple { +#[derive(Clone, Copy, Debug)] +pub struct DefaultVertex { pub position: [f32; 3], pub normal: [f32; 3], pub face_pos: [f32; 2], } -impl VertexSimple { +impl DefaultVertex { pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { Self { position, @@ -46,6 +29,29 @@ impl VertexSimple { glam::vec3(self.position[0], self.position[1], self.position[2]) } + // pub fn from_simple(vs: &VertexSimple) -> Self { + // Self { + // position: vs.position, + // normal: vs.normal, + // face_pos: vs.face_pos, + // } + // } + + // pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { + // Self { + // position, + // normal, + // face_pos, + // } + // } + // pub fn pos(&self) -> [f32; 3] { + // self.position + // } + + // pub fn pos_vec3(&self) -> Vec3 { + // glam::vec3(self.position[0], self.position[1], self.position[2]) + // } + pub fn pos2d(&self) -> Vec2 { vec2(self.position[0], self.position[1]) } @@ -61,13 +67,97 @@ impl VertexSimple { } } +impl Lerpable for DefaultVertex { + fn lerpify(&self, other: &Self, pct: &T) -> Self { + DefaultVertex { + position: [ + self.position[0].lerpify(&other.position[0], pct), + self.position[1].lerpify(&other.position[1], pct), + self.position[2].lerpify(&other.position[2], pct), + ], + normal: [ + self.normal[0].lerpify(&other.normal[0], pct), + self.normal[1].lerpify(&other.normal[1], pct), + self.normal[2].lerpify(&other.normal[2], pct), + ], + face_pos: [ + self.face_pos[0].lerpify(&other.face_pos[0], pct), + self.face_pos[1].lerpify(&other.face_pos[1], pct), + ], + } + } +} + +unsafe impl Zeroable for DefaultVertex {} +unsafe impl Pod for DefaultVertex {} + +// impl Lerpable for VertexSimple { +// fn lerpify(&self, other: &Self, pct: &T) -> Self { +// VertexSimple { +// position: [ +// self.position[0].lerpify(&other.position[0], pct), +// self.position[1].lerpify(&other.position[1], pct), +// self.position[2].lerpify(&other.position[2], pct), +// ], +// normal: [ +// self.normal[0].lerpify(&other.normal[0], pct), +// self.normal[1].lerpify(&other.normal[1], pct), +// self.normal[2].lerpify(&other.normal[2], pct), +// ], +// face_pos: [ +// self.face_pos[0].lerpify(&other.face_pos[0], pct), +// self.face_pos[1].lerpify(&other.face_pos[1], pct), +// ], +// } +// } +// } + +// #[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)] +// #[repr(C)] +// pub struct VertexSimple { +// pub position: [f32; 3], +// pub normal: [f32; 3], +// pub face_pos: [f32; 2], +// } + +// impl VertexSimple { +// pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { +// Self { +// position, +// normal, +// face_pos, +// } +// } +// pub fn pos(&self) -> [f32; 3] { +// self.position +// } + +// pub fn pos_vec3(&self) -> Vec3 { +// glam::vec3(self.position[0], self.position[1], self.position[2]) +// } + +// pub fn pos2d(&self) -> Vec2 { +// vec2(self.position[0], self.position[1]) +// } + +// pub fn attrs(&self) -> Vec { +// vec![ +// self.normal[0], +// self.normal[1], +// self.normal[2], +// self.face_pos[0], +// self.face_pos[1], +// ] +// } +// } + #[derive(Debug, Clone)] -pub struct Triangulate { - pub vertices: Vec, +pub struct Triangulate { + pub vertices: Vec, pub order: Vec, } -impl Triangulate { +impl Triangulate { pub fn new() -> Self { Triangulate { vertices: vec![], @@ -75,23 +165,23 @@ impl Triangulate { } } - pub fn add_many_vertices_and_offset(&mut self, vertices: Vec, indices: Vec) { + pub fn add_many_vertices_and_offset(&mut self, vertices: Vec, indices: Vec) { let vertex_offset = self.vertices.len() as u32; self.vertices.extend(vertices); self.order .extend(indices.iter().map(|i| *i + vertex_offset)); } - pub fn vertices(&self) -> &[VertexSimple] { + pub fn vertices(&self) -> &[Vertex] { &self.vertices } - pub fn add_vertex(&mut self, v: [f32; 3], n: [f32; 3], face_pos: [f32; 2]) -> u32 { - let vv = VertexSimple::new(v, n, face_pos); - self.add_vertex_simple(vv) - } + // pub fn add_vertex(&mut self, v: [f32; 3], n: [f32; 3], face_pos: [f32; 2]) -> u32 { + // let vv = VertexSimple::new(v, n, face_pos); + // self.add_vertex_simple(vv) + // } - pub fn add_vertex_simple(&mut self, vv: VertexSimple) -> u32 { + pub fn add_vertex_simple(&mut self, vv: Vertex) -> u32 { self.vertices.push(vv); (self.vertices.len() - 1) as u32 } @@ -101,22 +191,22 @@ impl Triangulate { } // alternatively can add vertices and then add teh vec - pub fn add_rect(&mut self, v: &[Vec3; 4], flip: bool) { - let edge1 = v[0] - v[1]; - let edge2 = v[3] - v[1]; - let normal = edge1.cross(edge2).normalize().to_array(); - - let v0 = self.add_vertex(v[0].to_array(), normal, [1.0, 0.0]); - let v1 = self.add_vertex(v[1].to_array(), normal, [0.0, 0.0]); - let v2 = self.add_vertex(v[2].to_array(), normal, [1.0, 1.0]); - let v3 = self.add_vertex(v[3].to_array(), normal, [0.0, 1.0]); - - if !flip { - self.order.extend([v0, v2, v1, v1, v2, v3]) - } else { - self.order.extend([v0, v1, v2, v1, v3, v2]) - } - } + // pub fn add_rect(&mut self, v: &[Vec3; 4], flip: bool) { + // let edge1 = v[0] - v[1]; + // let edge2 = v[3] - v[1]; + // let normal = edge1.cross(edge2).normalize().to_array(); + + // let v0 = self.add_vertex(v[0].to_array(), normal, [1.0, 0.0]); + // let v1 = self.add_vertex(v[1].to_array(), normal, [0.0, 0.0]); + // let v2 = self.add_vertex(v[2].to_array(), normal, [1.0, 1.0]); + // let v3 = self.add_vertex(v[3].to_array(), normal, [0.0, 1.0]); + + // if !flip { + // self.order.extend([v0, v2, v1, v1, v2, v3]) + // } else { + // self.order.extend([v0, v1, v2, v1, v3, v2]) + // } + // } pub fn set_order(&mut self, u: Vec) { self.order = u; diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index e08d370..9e0a001 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -14,7 +14,7 @@ use lyon::{ tessellation::{BuffersBuilder, FillOptions, FillTessellator, FillVertex}, }; use murrelet_common::{ - curr_next_no_loop_iter, triangulate::VertexSimple, PointToPoint, Polyline, SpotOnCurve, + curr_next_no_loop_iter, triangulate::DefaultVertex, PointToPoint, Polyline, SpotOnCurve, }; pub trait ToVecVec2 { @@ -305,9 +305,9 @@ pub fn cubic_bezier_path_to_lyon(path: &[CubicBezier], closed: bool) -> Option (Vec, Vec) { + outline: &[DefaultVertex], + steiner: &[DefaultVertex], +) -> (Vec, Vec) { let mut path_builder = Path::builder_with_attributes(5); // convert path to lyon @@ -342,7 +342,7 @@ pub fn tesselate_lyon_vertex_with_steiner( .with_fill_rule(FillRule::EvenOdd) .with_intersections(true); - let mut geometry: lyon::lyon_tessellation::VertexBuffers = + let mut geometry: lyon::lyon_tessellation::VertexBuffers = lyon::lyon_tessellation::VertexBuffers::new(); let mut tess = FillTessellator::new(); tess.tessellate_path( @@ -352,7 +352,7 @@ pub fn tesselate_lyon_vertex_with_steiner( let pos = v.position(); let attrs = v.interpolated_attributes(); - VertexSimple { + DefaultVertex { position: [pos.x, pos.y, 0.0], normal: [attrs[0], attrs[1], attrs[2]], face_pos: [attrs[3], attrs[4]], @@ -364,7 +364,7 @@ pub fn tesselate_lyon_vertex_with_steiner( (geometry.indices, geometry.vertices) } -pub fn tesselate_lyon_vertex_simple(outline: &[VertexSimple]) -> (Vec, Vec) { +pub fn tesselate_lyon_vertex_simple(outline: &[DefaultVertex]) -> (Vec, Vec) { let mut path_builder = Path::builder_with_attributes(5); // convert path to lyon @@ -385,7 +385,7 @@ pub fn tesselate_lyon_vertex_simple(outline: &[VertexSimple]) -> (Vec, Vec< .with_fill_rule(FillRule::EvenOdd) .with_intersections(true); - let mut geometry: lyon::lyon_tessellation::VertexBuffers = + let mut geometry: lyon::lyon_tessellation::VertexBuffers = lyon::lyon_tessellation::VertexBuffers::new(); let mut tess = FillTessellator::new(); tess.tessellate_path( @@ -395,7 +395,7 @@ pub fn tesselate_lyon_vertex_simple(outline: &[VertexSimple]) -> (Vec, Vec< let pos = v.position(); let attrs = v.interpolated_attributes(); - VertexSimple { + DefaultVertex { position: [pos.x, pos.y, 0.0], normal: [attrs[0], attrs[1], attrs[2]], face_pos: [attrs[3], attrs[4]], @@ -969,7 +969,7 @@ impl LayersFromSvg { } } -pub fn tesselate_delauney(v: Vec) -> (Vec, Vec, Triangulation) { +pub fn tesselate_delauney(v: Vec) -> (Vec, Vec, Triangulation) { let points: Vec<_> = v .iter() .map(|vertex| delaunator::Point { diff --git a/murrelet_gpu/src/compute.rs b/murrelet_gpu/src/compute.rs index 578dd47..0c8ff28 100644 --- a/murrelet_gpu/src/compute.rs +++ b/murrelet_gpu/src/compute.rs @@ -13,7 +13,7 @@ use wgpu::util::DeviceExt; use crate::{ device_state::{DeviceState, DeviceStateForRender}, - graphics_ref::{shader_from_path, GraphicsRef, TextureAndDesc, DEFAULT_TEXTURE_FORMAT}, + graphics_ref::{shader_from_path, GraphicsRefCustom, TextureAndDesc, DEFAULT_TEXTURE_FORMAT}, uniforms::BasicUniform, window::GraphicsWindowConf, }; @@ -500,7 +500,11 @@ impl ComputeGraphicsToTextureRef { self.graphics.borrow().name.clone() } - pub fn render(&self, device_state_for_render: &DeviceStateForRender, other: &GraphicsRef) { + pub fn render( + &self, + device_state_for_render: &DeviceStateForRender, + other: &GraphicsRefCustom, + ) { let view = &other.graphics.borrow_mut().input_texture_view; self.graphics .borrow() diff --git a/murrelet_gpu/src/editable_shaders.rs b/murrelet_gpu/src/editable_shaders.rs index 66f42a4..259f54f 100644 --- a/murrelet_gpu/src/editable_shaders.rs +++ b/murrelet_gpu/src/editable_shaders.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use crate::{gpu_macros::ShaderStr, window::GraphicsWindowConf}; use lerpable::Lerpable; +use murrelet_common::triangulate::DefaultVertex; use murrelet_livecode_derive::Livecode; use wgpu_for_latest::naga; #[cfg(feature = "nannou")] @@ -12,7 +13,7 @@ use wgpu_for_latest as wgpu; use crate::{ build_shader, build_shader_2tex, - graphics_ref::{GraphicsCreator, GraphicsRef}, + graphics_ref::{GraphicsCreator, GraphicsRefCustom}, }; #[derive(Debug, Clone, Livecode, Lerpable)] @@ -38,7 +39,7 @@ impl ShaderStrings { } } - pub fn get_shader_str(&self, c: &GraphicsWindowConf, name: &str) -> Option { + pub fn get_shader_str(&self, _c: &GraphicsWindowConf, name: &str) -> Option { if let Some(str) = self.shaders.get(name) { Some(Self::shader(&str)) } else { @@ -46,7 +47,7 @@ impl ShaderStrings { } } - pub fn get_shader_str_2tex(&self, c: &GraphicsWindowConf, name: &str) -> Option { + pub fn get_shader_str_2tex(&self, _c: &GraphicsWindowConf, name: &str) -> Option { if let Some(str) = self.shaders.get(name) { Some(Self::shader2tex(&str)) } else { @@ -54,10 +55,14 @@ impl ShaderStrings { } } - pub fn get_graphics_ref(&self, c: &GraphicsWindowConf, name: &str) -> Option { + pub fn get_graphics_ref( + &self, + c: &GraphicsWindowConf, + name: &str, + ) -> Option> { if let Some(str) = self.shaders.get(name) { Some( - GraphicsCreator::default() + GraphicsCreator::::default() .with_mag_filter(wgpu::FilterMode::Nearest) .to_graphics_ref(c, name, &Self::shader(&str)), ) @@ -66,10 +71,14 @@ impl ShaderStrings { } } - pub fn get_graphics_ref_2tex(&self, c: &GraphicsWindowConf, name: &str) -> Option { + pub fn get_graphics_ref_2tex( + &self, + c: &GraphicsWindowConf, + name: &str, + ) -> Option> { if let Some(str) = self.shaders.get(name) { Some( - GraphicsCreator::default() + GraphicsCreator::::default() .with_mag_filter(wgpu::FilterMode::Nearest) .with_second_texture() .to_graphics_ref(c, name, &Self::shader2tex(&str)), diff --git a/murrelet_gpu/src/gpu_livecode.rs b/murrelet_gpu/src/gpu_livecode.rs index 0b05d7a..cd2c6db 100644 --- a/murrelet_gpu/src/gpu_livecode.rs +++ b/murrelet_gpu/src/gpu_livecode.rs @@ -1,31 +1,61 @@ #![allow(dead_code)] +use bytemuck::NoUninit; use glam::*; use lerpable::Lerpable; -use murrelet_common::*; +use murrelet_common::{triangulate::DefaultVertex, *}; use murrelet_draw::newtypes::*; use murrelet_livecode_derive::Livecode; -use crate::{graphics_ref::GraphicsRef, window::GraphicsWindowConf}; +use crate::{ + graphics_ref::{GraphicsRefCustom, GraphicsRefWithControlFn}, + window::GraphicsWindowConf, +}; pub trait ControlGraphics { - fn update_graphics(&self, c: &GraphicsWindowConf, g: &GraphicsRef) { - g.update_uniforms_other_tuple(c, self.more_info_other_tuple()); + fn more_info_other_tuple(&self) -> ([f32; 4], [f32; 4]); +} + +pub trait AnyControlRef { + fn update(&self, c: &GraphicsWindowConf); +} + +impl AnyControlRef for ControlGraphicsRef +where + V: NoUninit + Clone + Copy + std::fmt::Debug + 'static, +{ + fn update(&self, c: &GraphicsWindowConf) { + // use the stored GraphicsRef inside ControlGraphicsRef + self.update_graphics(c); + } +} + +impl ControlProvider for GraphicsRefWithControlFn +where + V: NoUninit + Clone + Copy + std::fmt::Debug + 'static, +{ + fn make_controls(&self, conf: &GraphicsConf) -> Vec> { + self.control_graphics(conf) + .into_iter() + .map(|c| Box::new(c) as Box) + .collect() } +} - fn more_info_other_tuple(&self) -> ([f32; 4], [f32; 4]); +pub trait ControlProvider { + fn make_controls(&self, conf: &GraphicsConf) -> Vec>; } -pub struct ControlGraphicsRef { +pub struct ControlGraphicsRef { pub label: &'static str, pub control: Box, - graphics: GraphicsRef, + graphics: GraphicsRefCustom, } -impl ControlGraphicsRef { +impl ControlGraphicsRef { pub fn new( label: &'static str, control: Box, - graphics: Option, - ) -> Vec { + graphics: Option>, + ) -> Vec> { // using a vec here to make it easier to concat with other lists if let Some(gg) = graphics { vec![ControlGraphicsRef { @@ -39,8 +69,11 @@ impl ControlGraphicsRef { vec![] } } + pub fn update_graphics(&self, c: &GraphicsWindowConf) { - self.control.update_graphics(c, &self.graphics); + //}, g: &GraphicsRef) { + self.graphics + .update_uniforms_other_tuple(c, self.control.more_info_other_tuple()); } } @@ -82,7 +115,7 @@ impl GPUNoise { ) } - pub fn shader(c: &GraphicsWindowConf) -> GraphicsRef { + pub fn shader(c: &GraphicsWindowConf) -> GraphicsRefCustom { prebuilt_shaders::new_shader_noise(c) } } @@ -143,18 +176,28 @@ impl ControlGraphics for GPURGBAGradient { pub mod prebuilt_shaders { + use murrelet_common::triangulate::DefaultVertex; + use crate::{ gpu_macros::ShaderStr, - graphics_ref::{GraphicsCreator, GraphicsRef}, + graphics_ref::{GraphicsCreator, GraphicsRefCustom}, window::GraphicsWindowConf, *, }; - pub fn new_shader_basic(c: &GraphicsWindowConf, name: &str, shader: &str) -> GraphicsRef { + pub fn new_shader_basic( + c: &GraphicsWindowConf, + name: &str, + shader: &str, + ) -> GraphicsRefCustom { GraphicsCreator::default().to_graphics_ref(c, name, shader) } - pub fn new_shader_2tex(c: &GraphicsWindowConf, name: &str, shader: &str) -> GraphicsRef { + pub fn new_shader_2tex( + c: &GraphicsWindowConf, + name: &str, + shader: &str, + ) -> GraphicsRefCustom { let name = format!("{} {:?}", name, c.dims); GraphicsCreator::default() .with_second_texture() @@ -173,7 +216,7 @@ pub mod prebuilt_shaders { /// - 1.y: max value for noise /// ## Returns /// - GraphicsRef - pub fn new_shader_noise(c: &GraphicsWindowConf) -> GraphicsRef { + pub fn new_shader_noise(c: &GraphicsWindowConf) -> GraphicsRefCustom { let shader: String = build_shader_2tex! { ( raw r###" diff --git a/murrelet_gpu/src/gpu_macros.rs b/murrelet_gpu/src/gpu_macros.rs index 56a9771..e292556 100644 --- a/murrelet_gpu/src/gpu_macros.rs +++ b/murrelet_gpu/src/gpu_macros.rs @@ -1,17 +1,18 @@ #![allow(dead_code)] -use std::{cell::RefCell, collections::HashMap, fs, path::PathBuf, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, fs, path::PathBuf, rc::Rc, sync::Arc}; +use bytemuck::NoUninit; use glam::Vec2; -use murrelet_common::MurreletTime; +use murrelet_common::{triangulate::DefaultVertex, MurreletTime}; use serde::Serialize; use crate::{ - compute::{ComputeGraphicsToTexture, ComputeGraphicsToTextureRef}, + compute::ComputeGraphicsToTextureRef, device_state::{DeviceStateForRender, GraphicsAssets}, - gpu_livecode::ControlGraphicsRef, + gpu_livecode::{AnyControlRef, ControlGraphicsRef, ControlProvider}, graphics_ref::{ - Graphics, GraphicsCreator, GraphicsRef, GraphicsRefWithControlFn, - DEFAULT_LOADED_TEXTURE_FORMAT, + AnyGraphicsRef, Graphics, GraphicsCreator, GraphicsRefCustom, GraphicsRefWithControlFn, + TextureAndDesc, DEFAULT_LOADED_TEXTURE_FORMAT, }, shader_str::*, window::GraphicsWindowConf, @@ -204,7 +205,8 @@ pub trait RenderTrait { fn render(&self, device_state_for_render: &DeviceStateForRender); fn debug_print(&self) -> Vec; - fn dest(&self) -> Option; + fn dest_view(&self) -> Option; + fn dest_view_other(&self) -> Option; // hmm, i don't know how to do this with the boxes fn is_choice(&self) -> bool { @@ -214,23 +216,36 @@ pub trait RenderTrait { fn adjust_choice(&mut self, _choice_val: usize) {} } -pub struct SimpleRender { - pub source: GraphicsRef, - pub dest: GraphicsRef, +pub struct SimpleRender { + pub source: GraphicsRefCustom, + pub dest: GraphicsRefCustom, } -impl SimpleRender { - pub fn new_box(source: GraphicsRef, dest: GraphicsRef) -> Box { +impl< + VertexKindSource: Clone + Copy + NoUninit + std::fmt::Debug, + VertexKindDest: Clone + Copy + NoUninit + std::fmt::Debug, + > SimpleRender +{ + pub fn new_box( + source: GraphicsRefCustom, + dest: GraphicsRefCustom, + ) -> Box> { Box::new(SimpleRender { source, dest }) } - fn dest(&self) -> Option { - Some(self.dest.clone()) + fn dest(&self) -> Option { + Some(self.dest.texture_view()) } } -impl RenderTrait for SimpleRender { +impl RenderTrait + for SimpleRender +where + VertexKindSource: NoUninit + Clone + std::fmt::Debug, + VertexKindDest: NoUninit + Clone + std::fmt::Debug, +{ fn render(&self, device: &DeviceStateForRender) { - self.source.render(device.device_state(), &self.dest); + self.source + .render(device.device_state(), &self.dest.texture_view()); } fn debug_print(&self) -> Vec { @@ -240,22 +255,32 @@ impl RenderTrait for SimpleRender { }] } - fn dest(&self) -> Option { - Some(self.dest.clone()) + fn dest_view(&self) -> Option { + Some(self.dest.texture_view()) + } + + fn dest_view_other(&self) -> Option { + self.dest.texture_view_other() + } + + fn is_choice(&self) -> bool { + false } + + fn adjust_choice(&mut self, _choice_val: usize) {} } -pub struct TwoSourcesRender { - pub source_main: GraphicsRef, - pub source_other: GraphicsRef, - pub dest: GraphicsRef, +pub struct TwoSourcesRender { + pub source_main: GraphicsRefCustom, + pub source_other: GraphicsRefCustom, + pub dest: GraphicsRefCustom, } -impl TwoSourcesRender { +impl TwoSourcesRender { pub fn new_box( - source_main: GraphicsRef, - source_other: GraphicsRef, - dest: GraphicsRef, - ) -> Box { + source_main: GraphicsRefCustom, + source_other: GraphicsRefCustom, + dest: GraphicsRefCustom, + ) -> Box> { Box::new(TwoSourcesRender { source_main, source_other, @@ -263,16 +288,25 @@ impl TwoSourcesRender { }) } - fn dest(&self) -> Option { - Some(self.dest.clone()) + fn dest_view(&self) -> Option { + Some(self.dest.texture_view()) + } + + fn dest_view_other(&self) -> Option { + self.dest.texture_view_other() } } -impl RenderTrait for TwoSourcesRender { +impl RenderTrait + for TwoSourcesRender +{ + // type VertexKind = VertexKind; + fn render(&self, device: &DeviceStateForRender) { - self.source_main.render(device.device_state(), &self.dest); + self.source_main + .render(device.device_state(), &self.dest_view().unwrap()); self.source_other - .render_2tex(device.device_state(), &self.dest); + .render(device.device_state(), &self.dest_view_other().unwrap()); } fn debug_print(&self) -> Vec { @@ -288,23 +322,33 @@ impl RenderTrait for TwoSourcesRender { ] } - fn dest(&self) -> Option { - Some(self.dest.clone()) + fn dest_view(&self) -> Option { + Some(self.dest.texture_view()) } + + fn dest_view_other(&self) -> Option { + self.dest.texture_view_other() + } + + // fn dest(&self) -> Option> { + // Some(self.dest.clone()) + // } } // holds a gpu pipeline :O -pub struct PipelineRender { - pub source: GraphicsRef, +pub struct PipelineRender { + pub source: GraphicsRefCustom, pub pipeline: GPUPipelineRef, - pub dest: GraphicsRef, + pub dest: GraphicsRefCustom, } -impl PipelineRender { +impl + PipelineRender +{ pub fn new_box( - source: GraphicsRef, + source: GraphicsRefCustom, pipeline: GPUPipelineRef, - dest: GraphicsRef, + dest: GraphicsRefCustom, ) -> Box { Box::new(Self { source, @@ -313,7 +357,11 @@ impl PipelineRender { }) } } -impl RenderTrait for PipelineRender { +impl RenderTrait + for PipelineRender +{ + // type VertexKind = VertexKind; + fn render(&self, device_state_for_render: &DeviceStateForRender) { // write source to pipeline source self.source.render( @@ -327,19 +375,30 @@ impl RenderTrait for PipelineRender { self.pipeline.debug_print() } - fn dest(&self) -> Option { - Some(self.dest.clone()) + fn dest_view(&self) -> Option { + Some(self.dest.texture_view()) + } + + fn dest_view_other(&self) -> Option { + self.dest.texture_view_other() } + + // fn dest(&self) -> Option> { + // Some(self.dest.clone()) + // } } // given a list of inputs, choose which one to use -pub struct ChoiceRender { - pub sources: Vec, - pub dest: GraphicsRef, +pub struct ChoiceRender { + pub sources: Vec>, + pub dest: GraphicsRefCustom, choice: usize, } -impl ChoiceRender { - pub fn new_box(sources: Vec, dest: GraphicsRef) -> Box { +impl ChoiceRender { + pub fn new_box( + sources: Vec>, + dest: GraphicsRefCustom, + ) -> Box> { Box::new(ChoiceRender { sources, dest, @@ -348,11 +407,15 @@ impl ChoiceRender { } } -impl RenderTrait for ChoiceRender { +impl RenderTrait + for ChoiceRender +{ + // type VertexKind = VertexKind; + fn render(&self, device: &DeviceStateForRender) { let source = &self.sources[self.choice % self.sources.len()]; let dest = &self.dest; - source.render(device.device_state(), dest); + source.render(device.device_state(), &dest.texture_view()); } fn debug_print(&self) -> Vec { @@ -362,9 +425,9 @@ impl RenderTrait for ChoiceRender { todo!() } - fn dest(&self) -> Option { - Some(self.dest.clone()) - } + // fn dest(&self) -> Option> { + // Some(self.dest.clone()) + // } fn is_choice(&self) -> bool { true @@ -374,27 +437,47 @@ impl RenderTrait for ChoiceRender { fn adjust_choice(&mut self, choice_val: usize) { self.choice = choice_val % self.sources.len() } + + fn dest_view(&self) -> Option { + Some(self.dest.texture_view()) + } + + fn dest_view_other(&self) -> Option { + None + } } -pub struct PingPongRender { +pub struct PingPongRender { pub k: usize, - pub ping: GraphicsRef, // it'll end up here - pub pong: GraphicsRef, + pub ping: GraphicsRefCustom, // it'll end up here + pub pong: GraphicsRefCustom, } -impl PingPongRender { - pub fn new_box(k: usize, ping: GraphicsRef, pong: GraphicsRef) -> Box { +impl PingPongRender { + pub fn new_box( + k: usize, + ping: GraphicsRefCustom, + pong: GraphicsRefCustom, + ) -> Box> { Box::new(PingPongRender { k, ping, pong }) } } -impl RenderTrait for PingPongRender { +impl RenderTrait + for PingPongRender +{ + // type VertexKind = VertexKind; + fn render(&self, device: &DeviceStateForRender) { - let ping = &self.ping; - let pong = &self.pong; + let ping_texture = &self.ping.texture_view(); + let pong_texture = &self.pong.texture_view(); for _ in 0..self.k { - ping.render(device.device_state(), &pong); - pong.render(device.device_state(), &ping); + self.ping + .graphics() + .render(device.device_state(), &pong_texture); + self.pong + .graphics() + .render(device.device_state(), &ping_texture); } } @@ -421,52 +504,85 @@ impl RenderTrait for PingPongRender { ] } - fn dest(&self) -> Option { - Some(self.pong.clone()) + fn dest_view(&self) -> Option { + Some(self.pong.texture_view()) } -} -pub struct TextureViewRender { - pub source: GraphicsRef, - pub dest: wgpu::TextureView, -} - -impl TextureViewRender { - pub fn new_box(source: GraphicsRef, dest: wgpu::TextureView) -> Box { - Box::new(TextureViewRender { source, dest }) + fn dest_view_other(&self) -> Option { + None } + + // fn dest(&self) -> Option> { + // Some(self.pong.clone()) + // } } -impl RenderTrait for TextureViewRender { - fn render(&self, device: &DeviceStateForRender) { - let source = &self.source; - source.render_to_texture(device.device_state(), &self.dest); - } - fn debug_print(&self) -> Vec { - let source = &self.source; - vec![RenderDebugPrint { - src: source.name(), - dest: "texture view!".to_string(), - }] - } +// pub struct TextureViewRender { +// pub source: GraphicsRef, +// pub dest: TextureAndDesc, +// } - fn dest(&self) -> Option { - None - } -} +// impl TextureViewRender { +// pub fn new_box( +// source: GraphicsRef, +// dest: wgpu::TextureView, +// ) -> Box> { +// Box::new(TextureViewRender { +// source, +// dest: Arc::new(dest), +// }) +// } +// } + +// impl RenderTrait +// for TextureViewRender +// { +// // type VertexKind = VertexKind; + +// fn render(&self, device: &DeviceStateForRender) { +// let source = &self.source; +// source.render_to_texture(device.device_state(), &self.dest); +// } +// fn debug_print(&self) -> Vec { +// let source = &self.source; +// vec![RenderDebugPrint { +// src: source.name(), +// dest: "texture view!".to_string(), +// }] +// } + +// fn dest_view(&self) -> Option { +// self.dest +// } + +// fn dest_view_other(&self) -> Option { +// None +// } + +// // fn dest(&self) -> Option> { +// // None +// // } +// } -pub struct ComputeTextureRender { +pub struct ComputeTextureRender { pub source: ComputeGraphicsToTextureRef, - pub dest: GraphicsRef, + pub dest: GraphicsRefCustom, } -impl ComputeTextureRender { - pub fn new_box(source: ComputeGraphicsToTextureRef, dest: GraphicsRef) -> Box { +impl ComputeTextureRender { + pub fn new_box( + source: ComputeGraphicsToTextureRef, + dest: GraphicsRefCustom, + ) -> Box { Box::new(Self { source, dest }) } } -impl RenderTrait for ComputeTextureRender { +impl RenderTrait + for ComputeTextureRender +{ + // type VertexKind = VertexKind; + // whenver it's called, it'll increment! check if it's overdue before rendering! fn render(&self, device_state_for_render: &DeviceStateForRender) { let source_texture = &self.source; @@ -483,22 +599,35 @@ impl RenderTrait for ComputeTextureRender { }] } - fn dest(&self) -> Option { - Some(self.dest.clone()) + fn dest_view(&self) -> Option { + Some(self.dest.texture_view()) } + + fn dest_view_other(&self) -> Option { + // todo!() + None + } + + // fn dest(&self) -> Option> { + // Some(self.dest.clone()) + // } } -pub struct DisplayRender { - pub source: GraphicsRef, +pub struct DisplayRender { + pub source: GraphicsRefCustom, } -impl DisplayRender { - pub fn new_box(source: GraphicsRef) -> Box { +impl DisplayRender { + pub fn new_box(source: GraphicsRefCustom) -> Box> { Box::new(DisplayRender { source }) } } -impl RenderTrait for DisplayRender { +impl RenderTrait + for DisplayRender +{ + // type VertexKind = VertexKind; + fn render(&self, device: &DeviceStateForRender) { let source = &self.source; source.render_to_texture(device.device_state(), device.display_view()); @@ -511,16 +640,24 @@ impl RenderTrait for DisplayRender { }] } - fn dest(&self) -> Option { + fn dest_view(&self) -> Option { None } + + fn dest_view_other(&self) -> Option { + None + } + + // fn dest(&self) -> Option> { + // None + // } } pub struct GPUPipeline { pub dag: Vec>, choices: Vec, - names: HashMap, // todo, do i need this with ctrl? - ctrl: Vec>, + names: HashMap>, // todo, do i need this with ctrl? + ctrl: Vec>>, source: Option, } @@ -535,21 +672,35 @@ impl GPUPipeline { } } - pub fn add_control_graphics( - &mut self, - _label: &str, - control_graphics_fn: GraphicsRefWithControlFn, - ) { - self.ctrl.push(control_graphics_fn) + pub fn add_control_graphics

(&mut self, _label: &str, provider: P) + where + P: ControlProvider + 'static, + { + self.ctrl.push(Box::new(provider)); } - pub fn control_graphics(&self, t: &GraphicConf) -> Vec { - let mut v = vec![]; - for c in &self.ctrl { - v.extend(c.control_graphics(t).into_iter()); + pub fn control_graphics(&self, t: &GraphicConf) -> Vec> { + let mut out = Vec::new(); + for p in &self.ctrl { + out.extend(p.make_controls(t)); } - v - } + out + } + // pub fn add_control_graphics( + // &mut self, + // _label: &str, + // control_graphics_fn: GraphicsRefWithControlFn, + // ) { + // self.ctrl.push(control_graphics_fn) + // } + + // pub fn control_graphics(&self, t: &GraphicConf) -> Vec> { + // let mut v = vec![]; + // for c in &self.ctrl { + // v.extend(c.control_graphics(t).into_iter()); + // } + // v + // } pub fn set_source(&mut self, src: &str) { self.source = Some(src.to_string()); @@ -568,12 +719,15 @@ impl GPUPipeline { self.dag.push(d); } - pub fn add_label(&mut self, name: &str, g: GraphicsRef) { - self.names.insert(name.to_string(), g); + pub fn add_label(&mut self, name: &str, g: GraphicsRefCustom) + where + V: NoUninit + Clone + Copy + std::fmt::Debug + 'static, + { + self.names.insert(name.to_string(), Box::new(g)); } - pub fn get_graphic(&self, name: &str) -> Option { - self.names.get(name).cloned() + pub fn get_graphic(&self, name: &str) -> Option<&dyn AnyGraphicsRef> { + self.names.get(name).map(|g| g.as_ref()) } // no-op if it doesn't exist @@ -594,7 +748,7 @@ impl GPUPipeline { self.dag.iter().flat_map(|x| x.debug_print()).collect() } - fn source(&self) -> GraphicsRef { + fn source(&self) -> wgpu::TextureView { // hm this should happen on start let name = self .source @@ -602,6 +756,7 @@ impl GPUPipeline { .expect("should have set a source if you're gonna get it source"); self.get_graphic(&name) .expect(&format!("gave a source {} that doesn't exist", name)) + .texture_view() } } @@ -627,26 +782,33 @@ impl GPUPipelineRef { self.0.borrow().debug_print() } - pub fn source(&self) -> GraphicsRef { + pub fn source(&self) -> wgpu::TextureView { self.0.borrow().source() } - pub fn get_graphic(&self, name: &str) -> Option { - self.0.borrow().get_graphic(name) - } + // pub fn get_graphic(&self, name: &str) -> Option<&dyn AnyGraphicsRef> { + // // self.0.borrow().get_graphic(name) + // let pipeline = self.0.borrow(); + // pipeline.get_graphic(name).map(f) + // } - pub fn control_graphics(&self, conf: &GraphicsConf) -> Vec { + pub fn control_graphics(&self, conf: &GraphicsConf) -> Vec> { + //Vec> { + // self.0.borrow().control_graphics(conf) self.0.borrow().control_graphics(conf) } } pub struct SingleTextureRender { pub source: ImageTextureRef, - pub dest: GraphicsRef, + pub dest: GraphicsRefCustom, } impl SingleTextureRender { - pub fn new_box(source: ImageTextureRef, dest: GraphicsRef) -> Box { + pub fn new_box( + source: ImageTextureRef, + dest: GraphicsRefCustom, + ) -> Box { Box::new(SingleTextureRender { source, dest }) } } @@ -656,7 +818,7 @@ impl RenderTrait for SingleTextureRender { fn render(&self, device_state_for_render: &DeviceStateForRender) { let source_texture = &self.source; let dest = &self.dest; - source_texture.render(device_state_for_render, dest); + source_texture.render(device_state_for_render, &dest.texture_view()); } fn debug_print(&self) -> Vec { @@ -668,9 +830,17 @@ impl RenderTrait for SingleTextureRender { }] } - fn dest(&self) -> Option { - Some(self.dest.clone()) + fn dest_view(&self) -> Option { + Some(self.dest.texture_view()) } + + fn dest_view_other(&self) -> Option { + None + } + + // fn dest(&self) -> Option> { + // Some(self.dest.clone()) + // } } // makes it easier to ad control grpahics @@ -881,7 +1051,7 @@ macro_rules! build_shader_pipeline { #[derive(Clone)] pub struct ImageTextureRef(Rc>); impl ImageTextureRef { - pub fn render(&self, device_state_for_render: &DeviceStateForRender, dest: &GraphicsRef) { + pub fn render(&self, device_state_for_render: &DeviceStateForRender, dest: &wgpu::TextureView) { self.0.borrow().render(device_state_for_render, dest) } pub fn name(&self) -> String { @@ -897,7 +1067,7 @@ impl ImageTextureRef { pub struct VideoTextureRef(Rc>); pub struct VideoTexture { name: String, - pub graphics: GraphicsRef, + pub graphics: GraphicsRefCustom, pub binds: Vec, // path to pngs, probably keep it smapp pub fps: u64, last_time: Option, @@ -1028,7 +1198,7 @@ impl VideoTexture { .with_mag_filter(wgpu::FilterMode::Linear) .with_address_mode(wgpu::AddressMode::Repeat); - let graphics = GraphicsRef::new(name, c, &gradient_shader, &conf); + let graphics = GraphicsRefCustom::new(name, c, &gradient_shader, &conf); graphics.update_uniforms_other( c, [1.0, 0.0, 0.0, 0.0], @@ -1045,8 +1215,11 @@ impl VideoTexture { .iter() .map(|path| { // let texture = wgpu::Texture::from_path(c.window, path).unwrap(); // load the path - let texture_and_desc = - Graphics::texture(source_dims, c.device(), DEFAULT_LOADED_TEXTURE_FORMAT); + let texture_and_desc = Graphics::::texture( + source_dims, + c.device(), + DEFAULT_LOADED_TEXTURE_FORMAT, + ); GraphicsAssets::LocalFilesystem(path.to_path_buf()) .maybe_load_texture(c.device, &texture_and_desc.texture); let texture_view = @@ -1076,11 +1249,15 @@ impl VideoTexture { pub struct ImageTexture { name: String, - pub graphics: GraphicsRef, + pub graphics: GraphicsRefCustom, } impl ImageTexture { - pub fn render(&self, device_state_for_render: &DeviceStateForRender, other: &GraphicsRef) { + pub fn render( + &self, + device_state_for_render: &DeviceStateForRender, + other: &wgpu::TextureView, + ) { self.graphics .render(device_state_for_render.device_state(), other); } @@ -1165,7 +1342,7 @@ impl ImageTexture { .with_mag_filter(wgpu::FilterMode::Nearest) .with_address_mode(address_mode); - let graphics = GraphicsRef::new_with_src( + let graphics = GraphicsRefCustom::new_with_src( name, c, // gets dims from here &repeat_img, @@ -1208,7 +1385,7 @@ impl ImageTexture { .with_mag_filter(wgpu::FilterMode::Nearest) .with_address_mode(wgpu::AddressMode::ClampToEdge); - let graphics = GraphicsRef::new_with_src( + let graphics = GraphicsRefCustom::new_with_src( name, c, &repeat_img, diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index 9f3922d..b049018 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -1,10 +1,10 @@ #![allow(dead_code)] use std::{cell::RefCell, sync::Arc}; -use bytemuck::{Pod, Zeroable}; -use glam::{Mat4, Vec3}; -use itertools::Itertools; -use murrelet_common::triangulate::{Triangulate, VertexSimple}; +use bytemuck::NoUninit; +use glam::Mat4; + +use murrelet_common::triangulate::{DefaultVertex, Triangulate}; use std::rc::Rc; #[cfg(feature = "nannou")] @@ -42,60 +42,110 @@ pub fn shader_from_path(device: &wgpu::Device, data: &str) -> wgpu::ShaderModule // for each vertex, this is what we'll pass in -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub struct Vertex { - position: [f32; 3], - normal: [f32; 3], - face_pos: [f32; 2], -} +// #[repr(C)] +// #[derive(Clone, Copy, Debug)] +// pub struct DefaultVertex { +// position: [f32; 3], +// normal: [f32; 3], +// face_pos: [f32; 2], +// } -impl Vertex { - pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { - Self { - position, - normal, - face_pos, - } - } - pub fn pos(&self) -> [f32; 3] { - self.position - } +// impl DefaultVertex { +// pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { +// Self { +// position, +// normal, +// face_pos, +// } +// } +// pub fn pos(&self) -> [f32; 3] { +// self.position +// } - pub fn pos_vec3(&self) -> Vec3 { - glam::vec3(self.position[0], self.position[1], self.position[2]) - } +// pub fn pos_vec3(&self) -> Vec3 { +// glam::vec3(self.position[0], self.position[1], self.position[2]) +// } - pub fn from_simple(vs: &VertexSimple) -> Self { - Self { - position: vs.position, - normal: vs.normal, - face_pos: vs.face_pos, - } - } -} +// // pub fn from_simple(vs: &VertexSimple) -> Self { +// // Self { +// // position: vs.position, +// // normal: vs.normal, +// // face_pos: vs.face_pos, +// // } +// // } + +// // pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { +// // Self { +// // position, +// // normal, +// // face_pos, +// // } +// // } +// // pub fn pos(&self) -> [f32; 3] { +// // self.position +// // } + +// // pub fn pos_vec3(&self) -> Vec3 { +// // glam::vec3(self.position[0], self.position[1], self.position[2]) +// // } + +// pub fn pos2d(&self) -> Vec2 { +// vec2(self.position[0], self.position[1]) +// } + +// pub fn attrs(&self) -> Vec { +// vec![ +// self.normal[0], +// self.normal[1], +// self.normal[2], +// self.face_pos[0], +// self.face_pos[1], +// ] +// } +// } -unsafe impl Zeroable for Vertex {} -unsafe impl Pod for Vertex {} +// impl Lerpable for DefaultVertex { +// fn lerpify(&self, other: &Self, pct: &T) -> Self { +// VertexSimple { +// position: [ +// self.position[0].lerpify(&other.position[0], pct), +// self.position[1].lerpify(&other.position[1], pct), +// self.position[2].lerpify(&other.position[2], pct), +// ], +// normal: [ +// self.normal[0].lerpify(&other.normal[0], pct), +// self.normal[1].lerpify(&other.normal[1], pct), +// self.normal[2].lerpify(&other.normal[2], pct), +// ], +// face_pos: [ +// self.face_pos[0].lerpify(&other.face_pos[0], pct), +// self.face_pos[1].lerpify(&other.face_pos[1], pct), +// ], +// } +// } +// } + +// unsafe impl Zeroable for DefaultVertex {} +// unsafe impl Pod for DefaultVertex {} // in the default vertex shader, z is dropped -pub const VERTICES: [Vertex; 4] = [ - Vertex { +pub const VERTICES: [DefaultVertex; 4] = [ + DefaultVertex { position: [-1.0, 1.0, 0.0], normal: [0.0, 0.0, 0.0], face_pos: [1.0, 0.0], }, - Vertex { + DefaultVertex { position: [-1.0, -1.0, 0.0], normal: [0.0, 0.0, 0.0], face_pos: [0.0, 0.0], }, - Vertex { + DefaultVertex { position: [1.0, 1.0, 0.0], normal: [0.0, 0.0, 0.0], face_pos: [1.0, 1.0], }, - Vertex { + DefaultVertex { position: [1.0, -1.0, 0.0], normal: [0.0, 0.0, 0.0], face_pos: [1.0, 0.0], @@ -209,42 +259,18 @@ pub struct Scene { // this is the conf that you'll interface with #[derive(Debug, Clone)] -pub struct InputVertexConf { +pub struct InputVertexConf { is_3d: bool, // todo, maybe can simplify now that i have this, e.g. vs_mod vs_mod: &'static str, view: VertexUniforms, topology: wgpu::PrimitiveTopology, - vertices: Vec, - order: Vec, + tri: Triangulate, // vertices: Vec, + // order: Vec, } -impl InputVertexConf { +impl InputVertexConf { pub fn buffer_slice(&self) -> &[u32] { - self.order.as_slice() - } - - pub fn from_triangulate_2d(t: &Triangulate) -> Self { - let mut c = Self::default(); - c.vertices = t - .vertices - .iter() - .map(|x| Vertex::from_simple(x)) - .collect_vec(); - c.order = t.order.clone(); - c - } - - pub fn from_triangulate(t: &Triangulate) -> Self { - let mut c = Self::default(); - c.is_3d = true; - c.vs_mod = VERTEX_SHADER_3D; - c.vertices = t - .vertices - .iter() - .map(|x| Vertex::from_simple(x)) - .collect_vec(); - c.order = t.order.clone(); - c + self.tri.order.as_slice() } pub fn set_view(mut self, view: Mat4, light: Mat4) -> Self { @@ -269,31 +295,63 @@ fn main(@location(0) position: vec3) -> @builtin(position) vec4 { ) } - pub fn with_custom_vertices(mut self, tri: &Triangulate) -> Self { - self.vertices = tri - .vertices - .iter() - .map(|x| Vertex::from_simple(x)) - .collect_vec(); + pub fn with_custom_vertices(mut self, tri: &Triangulate) -> Self { + // self.vertices = tri + // .vertices + // .iter() + // .map(|x| DefaultVertex::from_simple(x)) + // .collect_vec(); + // self.order = tri.order.clone(); + self.tri = tri.clone(); self.topology = wgpu::PrimitiveTopology::TriangleList; - self.order = tri.order.clone(); self } pub fn indices(&self) -> u32 { - self.order.len() as u32 - } + self.tri.order.len() as u32 + } + + // pub fn default() -> Self { + // Self { + // vs_mod: VERTEX_SHADER, + // view: VertexUniforms::identity(), + // topology: wgpu::PrimitiveTopology::TriangleList, + // tri: Triangulate:: { + // vertices: VERTICES.to_vec(), + // order: vec![0, 1, 2, 1, 3, 2], + // }, + // is_3d: false, + // } + // } +} - pub fn default() -> Self { - Self { +impl InputVertexConf { + pub fn default() -> InputVertexConf { + InputVertexConf { vs_mod: VERTEX_SHADER, view: VertexUniforms::identity(), topology: wgpu::PrimitiveTopology::TriangleList, - vertices: VERTICES.to_vec(), - order: vec![0, 1, 2, 1, 3, 2], + tri: Triangulate:: { + vertices: VERTICES.to_vec(), + order: vec![0, 1, 2, 1, 3, 2], + }, is_3d: false, } } + + pub fn from_triangulate_2d(t: &Triangulate) -> InputVertexConf { + let mut c = Self::default(); + c.tri = t.clone(); + c + } + + pub fn from_triangulate(t: &Triangulate) -> InputVertexConf { + let mut c = Self::default(); + c.is_3d = true; + c.vs_mod = VERTEX_SHADER_3D; + c.tri = t.clone(); + c + } } #[derive(Debug, Clone)] @@ -377,16 +435,16 @@ struct TextureCreator { } #[derive(Debug, Clone)] -pub struct GraphicsCreator { +pub struct GraphicsCreator { first_texture: TextureCreator, second_texture: Option, details: ShaderOptions, color_blend: wgpu::BlendComponent, dst_texture: TextureCreator, - input_vertex: InputVertexConf, // defaults to the square + input_vertex: InputVertexConf, // defaults to the square blend_state: wgpu::BlendState, } -impl Default for GraphicsCreator { +impl Default for GraphicsCreator { fn default() -> Self { GraphicsCreator { first_texture: TextureCreator { @@ -406,7 +464,18 @@ impl Default for GraphicsCreator { } } } -impl GraphicsCreator { +impl GraphicsCreator { + pub fn with_custom_triangle(mut self, t: &Triangulate, is_3d: bool) -> Self { + if is_3d { + self.input_vertex = InputVertexConf::from_triangulate(t); + } else { + self.input_vertex = InputVertexConf::from_triangulate_2d(t); + } + self + } +} + +impl GraphicsCreator { pub fn with_first_texture_format(mut self, format: wgpu::TextureFormat) -> Self { self.first_texture = TextureCreator { format }; self @@ -419,15 +488,6 @@ impl GraphicsCreator { self } - pub fn with_custom_triangle(mut self, t: &Triangulate, is_3d: bool) -> Self { - if is_3d { - self.input_vertex = InputVertexConf::from_triangulate(t); - } else { - self.input_vertex = InputVertexConf::from_triangulate_2d(t); - } - self - } - pub fn with_second_texture_format(mut self, format: wgpu::TextureFormat) -> Self { self.second_texture = Some(TextureCreator { format }); self @@ -473,14 +533,14 @@ impl GraphicsCreator { c: &GraphicsWindowConf<'a>, name: &str, fs_shader: &str, - ) -> GraphicsRef { + ) -> GraphicsRefCustom { if self.color_blend != wgpu::BlendComponent::REPLACE && self.dst_texture.format == wgpu::TextureFormat::Rgba32Float { panic!("can't blend with float32 textures"); } - GraphicsRef::new(name, c, fs_shader, self) + GraphicsRefCustom::new(name, c, fs_shader, self) } fn is_3d(&self) -> bool { @@ -493,11 +553,13 @@ impl GraphicsCreator { } #[derive(Clone)] -pub struct GraphicsRef { - pub graphics: Rc>, +pub struct GraphicsRefCustom { + pub graphics: Rc>>, } -impl GraphicsRef { +pub type GraphicsRef = GraphicsRefCustom; + +impl GraphicsRefCustom { pub fn name(&self) -> String { self.graphics.borrow().name.clone() } @@ -513,7 +575,7 @@ impl GraphicsRef { name: &str, c: &GraphicsWindowConf<'a>, fs_shader: &str, - conf: &GraphicsCreator, + conf: &GraphicsCreator, assets: GraphicsAssets, ) -> Self { println!("name {:?}", name); @@ -533,7 +595,7 @@ impl GraphicsRef { name: &str, c: &GraphicsWindowConf<'a>, fs_shader: &str, - conf: &GraphicsCreator, + conf: &GraphicsCreator, ) -> Self { Self::new_with_src(name, c, fs_shader, conf, GraphicsAssets::Nothing) } @@ -548,19 +610,23 @@ impl GraphicsRef { graphics_rc.update_uniforms_other_tuple(c, more_info) } + #[deprecated(note = "Use render_to_view instead")] pub fn render_to_view(&self, device: &DeviceState, view: &wgpu::TextureView) { - self.graphics.borrow_mut().render(device, view) + // self.graphics.borrow_mut().render(device, view) + self.render(device, view) } - pub fn render(&self, device: &DeviceState, other: &GraphicsRef) { - let view = &other.graphics.borrow_mut().input_texture_view; + pub fn render(&self, device: &DeviceState, view: &wgpu::TextureView) { + // let view = &other.graphics.borrow_mut().input_texture_view; self.graphics.borrow_mut().render(device, view) } - pub fn render_2tex(&self, device_state: &DeviceState, other: &GraphicsRef) { - let binding = other.graphics.borrow_mut(); - let view = binding.input_texture_view_other.as_ref().unwrap(); - self.graphics.borrow_mut().render(device_state, view) + #[deprecated(note = "Use render_to_view instead")] + pub fn render_2tex(&self, device: &DeviceState, view: &wgpu::TextureView) { + // let binding = other.graphics.borrow_mut(); + // let view = binding.input_texture_view_other.as_ref().unwrap(); + // self.graphics.borrow_mut().render(device_state, view) + self.render(device, view) } pub fn update_uniforms(&self, c: &GraphicsWindowConf, more_info: [f32; 4]) { @@ -607,7 +673,7 @@ impl GraphicsRef { &self, label: &'static str, control_graphic_fn: Arc Box + 'static>, - ) -> GraphicsRefWithControlFn { + ) -> GraphicsRefWithControlFn { GraphicsRefWithControlFn { label, graphics: self.clone(), @@ -615,13 +681,13 @@ impl GraphicsRef { } } - pub fn graphics(&self) -> GraphicsRef { + pub fn graphics(&self) -> GraphicsRefCustom { self.clone() } pub fn control_graphics_fn( &self, - ) -> Option> { + ) -> Option> { None } @@ -630,28 +696,30 @@ impl GraphicsRef { Mat4::from_cols_array_2d(&col) } - pub fn update_tri(&mut self, c: &GraphicsWindowConf, tri: Triangulate) { + pub fn update_tri(&mut self, c: &GraphicsWindowConf, tri: Triangulate) { // capture previous buffer sizes let (old_vert_bytes_len, old_index_bytes_len) = { let g = self.graphics.borrow(); ( - bytemuck::cast_slice::(&g.conf.input_vertex.vertices).len(), - bytemuck::cast_slice::(&g.conf.input_vertex.order).len(), + bytemuck::cast_slice::(&g.conf.input_vertex.tri.vertices).len(), + bytemuck::cast_slice::(&g.conf.input_vertex.tri.order).len(), ) }; { let mut g = self.graphics.borrow_mut(); - g.conf.input_vertex.vertices = tri - .vertices - .iter() - .map(|x| Vertex::from_simple(x)) - .collect_vec(); - g.conf.input_vertex.order = tri.order.clone(); + g.conf.input_vertex.tri.vertices = tri.vertices.clone(); + // tri + // .vertices + // .iter() + // .map(|x| DefaultVertex::from_simple(x)) + // .collect_vec(); + g.conf.input_vertex.tri.order = tri.order.clone(); let queue = c.device.queue(); // vertex buffer: either recreate or overwrite - let new_vert_bytes = bytemuck::cast_slice::(&g.conf.input_vertex.vertices); + let new_vert_bytes = + bytemuck::cast_slice::(&g.conf.input_vertex.tri.vertices); if new_vert_bytes.len() > old_vert_bytes_len { // recreate vertex buffer with new size let vb = c @@ -669,7 +737,7 @@ impl GraphicsRef { // index buffer with 4-byte alignment: recreate if growing const ALIGN: usize = 4; - let raw_index = bytemuck::cast_slice::(&g.conf.input_vertex.order); + let raw_index = bytemuck::cast_slice::(&g.conf.input_vertex.tri.order); let (index_bytes, needs_recreate) = if raw_index.len() % ALIGN != 0 { // pad to alignment let pad = ALIGN - (raw_index.len() % ALIGN); @@ -698,27 +766,65 @@ impl GraphicsRef { } } } + + pub(crate) fn texture_view(&self) -> wgpu::TextureView { + self.graphics.borrow().texture_and_desc.default_view() + } + + pub(crate) fn texture_view_other(&self) -> Option { + self.graphics + .borrow() + .other_texture_and_desc + .as_ref() + .map(|x| x.default_view()) + } } #[derive(Clone)] -pub struct GraphicsRefWithControlFn { +pub struct GraphicsRefWithControlFn { pub label: &'static str, - pub graphics: GraphicsRef, + pub graphics: GraphicsRefCustom, pub control_graphic_fn: Arc Box>, } -impl GraphicsRefWithControlFn { - pub fn control_graphics(&self, conf: &GraphicsConf) -> Vec { +pub trait AnyGraphicsRef { + fn name(&self) -> String; + fn texture_view(&self) -> wgpu::TextureView; + fn render_to_texture(&self, dev: &DeviceState, dst: &wgpu::TextureView); +} +impl AnyGraphicsRef for GraphicsRefCustom +where + V: NoUninit + Clone + Copy + std::fmt::Debug + 'static, +{ + fn name(&self) -> String { + self.name() + } + + fn texture_view(&self) -> wgpu::TextureView { + self.texture_view() + } + + fn render_to_texture(&self, dev: &DeviceState, dst: &wgpu::TextureView) { + self.render_to_texture(dev, dst) + } +} + +impl + GraphicsRefWithControlFn +{ + pub fn control_graphics(&self, conf: &GraphicsConf) -> Vec> { let ctrl_graphics = (self.control_graphic_fn)(conf); ControlGraphicsRef::new(self.label, ctrl_graphics, Some(self.graphics.clone())) } - pub fn graphics(&self) -> GraphicsRef { + pub fn graphics(&self) -> GraphicsRefCustom { self.graphics.clone() } - pub fn control_graphics_fn(&self) -> Option> { + pub fn control_graphics_fn( + &self, + ) -> Option> { // Some(self.clone()) let c = GraphicsRefWithControlFn { label: self.label, @@ -751,9 +857,9 @@ pub struct TextureFor3d { } // represents things needed to create a single texture... it's a bit of a mess -pub struct Graphics { +pub struct Graphics { name: String, - conf: GraphicsCreator, + conf: GraphicsCreator, bind_group: wgpu::BindGroup, vertex_buffers: VertexBuffers, render_pipeline: wgpu::RenderPipeline, @@ -769,7 +875,7 @@ pub struct Graphics { textures_for_3d: Option, } -impl Graphics { +impl Graphics { pub fn update_uniforms(&mut self, c: &GraphicsWindowConf, more_info: [f32; 4]) { let queue = &c.device.queue(); self.uniforms.more_info = more_info; @@ -1010,17 +1116,17 @@ impl Graphics { } fn _render_pipeline( - conf: &GraphicsCreator, + conf: &GraphicsCreator, device: &wgpu::Device, bind_group_layout: &wgpu::BindGroupLayout, fs_mod: &wgpu::ShaderModule, dst_format: wgpu::TextureFormat, ) -> wgpu::RenderPipeline { let vertex_conf = &conf.input_vertex; - let pipeline_layout = Graphics::_pipeline_layout(device, bind_group_layout); + let pipeline_layout = Graphics::::_pipeline_layout(device, bind_group_layout); let vertex_buffer_layouts = wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, + array_stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3, 2 => Float32x2], }; @@ -1097,7 +1203,7 @@ impl Graphics { fs_shader_data: &str, initial_uniform: BasicUniform, texture_src_path: GraphicsAssets, - conf: GraphicsCreator, + conf: GraphicsCreator, ) -> Rc> { // todo, i used to have code here to check the conf's destination texture was okay @@ -1119,7 +1225,7 @@ impl Graphics { fs_shader_data: &str, initial_uniform: BasicUniform, texture_src_path: GraphicsAssets, - conf: GraphicsCreator, + conf: GraphicsCreator, ) -> Self { let conf_c = conf.clone(); let has_second_texture = conf.second_texture.is_some(); @@ -1137,7 +1243,8 @@ impl Graphics { // make a bind group layout let first_texture_format = texture_src_path.to_format(first_format); - let texture_and_desc = Graphics::texture(c.dims, device, first_texture_format); + let texture_and_desc = + Graphics::::texture(c.dims, device, first_texture_format); let input_texture = &texture_and_desc.texture; // maybe load the image source if we have one @@ -1146,7 +1253,8 @@ impl Graphics { let input_texture_view = input_texture.create_view(&Default::default()); let (input_texture_view_other, other_texture_and_desc) = if has_second_texture { - let other_texture = Graphics::texture(c.dims(), device, second_format.unwrap()); + let other_texture = + Graphics::::texture(c.dims(), device, second_format.unwrap()); ( Some(other_texture.texture.create_view(&Default::default())), Some(other_texture), @@ -1155,8 +1263,8 @@ impl Graphics { (None, None) }; - let sampler = Graphics::_sampler(device, details); - let bind_group_layout = Graphics::_bind_group_layout( + let sampler = Graphics::::_sampler(device, details); + let bind_group_layout = Graphics::::_bind_group_layout( device, has_second_texture, false, @@ -1245,7 +1353,7 @@ impl Graphics { // needs to be same let vertex_buffer_layouts = wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, + array_stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3, 2 => Float32x2], }; @@ -1295,7 +1403,7 @@ impl Graphics { None }; - let bind_group = Graphics::_bind_group( + let bind_group = Graphics::::_bind_group( device, &bind_group_layout, &input_texture_view, @@ -1337,7 +1445,7 @@ impl Graphics { ) -> wgpu::BindGroup { println!("making custom {:?} {:?}", texture_view, self.sampler); - Graphics::_bind_group( + Graphics::::_bind_group( device, &self.bind_group_layout, texture_view, @@ -1465,7 +1573,7 @@ impl Graphics { } pub fn quick_texture(dims: [u32; 2], device: &wgpu::Device) -> TextureAndDesc { - Graphics::texture(dims, device, DEFAULT_TEXTURE_FORMAT) + Graphics::::texture(dims, device, DEFAULT_TEXTURE_FORMAT) } pub struct VertexBuffers { @@ -1476,15 +1584,18 @@ pub struct VertexBuffers { impl VertexBuffers { // inits them all - fn from_conf(device: &wgpu::Device, conf: &InputVertexConf) -> Self { + fn from_conf( + device: &wgpu::Device, + conf: &InputVertexConf, + ) -> Self { let vertex = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, - contents: bytemuck::cast_slice(&conf.vertices[..]), + contents: bytemuck::cast_slice(&conf.tri.vertices[..]), usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, }); let order = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, - contents: bytemuck::cast_slice(&conf.order[..]), + contents: bytemuck::cast_slice(&conf.tri.order[..]), usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, }); let uniform = conf.view.to_buffer(device); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs index eade638..e8acb5d 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_graphics_trait.rs @@ -141,12 +141,16 @@ fn parse_graphics( let ctrl_ident = syn::Ident::new(ctrl_, name.span()); let ident_str = ident.to_string(); ctrl.push(quote! { - v.extend(ControlGraphicsRef::new( - #ident_str, - Box::new(livecoder.#ctrl_ident.clone()), - Some(self.#ident.clone()), - ).into_iter()) - }) + v.extend( + ControlGraphicsRef::new( + #ident_str, + Box::new(livecoder.#ctrl_ident.clone()), + Some(self.#ident.clone()), + ) + .into_iter() + .map(|c| Box::new(c) as Box) + ); + }); } } GraphicKind::ComputeTexture => { @@ -172,8 +176,8 @@ fn parse_graphics( v } - fn control_graphics<'a, 'b>(&'a self, livecoder: &'b #ctrlcls) -> Vec { - let mut v: Vec = vec![]; + fn control_graphics(&self, livecoder: &#ctrlcls) -> Vec> { + let mut v: Vec> = vec![]; #(#ctrl;)* v } From 8ce87141230df3b740968bd2b5c6c600619edd51 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 17 Nov 2025 22:50:05 -0500 Subject: [PATCH 094/149] wip --- murrelet_common/src/geometry.rs | 17 ++++ murrelet_common/src/triangulate.rs | 10 +- murrelet_draw/src/scaffold.rs | 4 +- murrelet_draw/src/tesselate.rs | 44 +++++++-- murrelet_gpu/src/editable_shaders.rs | 42 +++++++- murrelet_gpu/src/gpu_livecode.rs | 14 +-- murrelet_gpu/src/gpu_macros.rs | 143 ++++++++++++++++++--------- murrelet_gpu/src/graphics_ref.rs | 114 +++++++++++++++------ 8 files changed, 285 insertions(+), 103 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 682d2a3..682b742 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -7,6 +7,7 @@ use glam::{vec2, Mat3, Mat4, Vec2}; use crate::{ intersection::{find_intersection_inf, within_segment}, transform::TransformVec2, + triangulate::DefaultVertex, SimpleTransform2d, SimpleTransform2dStep, }; @@ -715,3 +716,19 @@ pub fn tangents_between_two_circles( Some((start, end)) } + +pub trait ToVec2 { + fn to_vec2(&self) -> Vec2; +} + +impl ToVec2 for Vec2 { + fn to_vec2(&self) -> Vec2 { + self.clone() + } +} + +impl ToVec2 for DefaultVertex { + fn to_vec2(&self) -> Vec2 { + self.pos2d() + } +} diff --git a/murrelet_common/src/triangulate.rs b/murrelet_common/src/triangulate.rs index 091f532..1bdce47 100644 --- a/murrelet_common/src/triangulate.rs +++ b/murrelet_common/src/triangulate.rs @@ -6,7 +6,7 @@ use glam::{vec2, Vec2, Vec3}; use lerpable::Lerpable; #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Zeroable, Pod)] pub struct DefaultVertex { pub position: [f32; 3], pub normal: [f32; 3], @@ -88,8 +88,8 @@ impl Lerpable for DefaultVertex { } } -unsafe impl Zeroable for DefaultVertex {} -unsafe impl Pod for DefaultVertex {} +// unsafe impl Zeroable for DefaultVertex {} +// unsafe impl Pod for DefaultVertex {} // impl Lerpable for VertexSimple { // fn lerpify(&self, other: &Self, pct: &T) -> Self { @@ -165,6 +165,10 @@ impl Triangulate { } } + pub fn new_from_vertices_indices(vertices: Vec, order: Vec) -> Self { + Triangulate { vertices, order } + } + pub fn add_many_vertices_and_offset(&mut self, vertices: Vec, indices: Vec) { let vertex_offset = self.vertices.len() as u32; self.vertices.extend(vertices); diff --git a/murrelet_draw/src/scaffold.rs b/murrelet_draw/src/scaffold.rs index 56eb455..53d38bd 100644 --- a/murrelet_draw/src/scaffold.rs +++ b/murrelet_draw/src/scaffold.rs @@ -44,11 +44,11 @@ impl ToCoord for Vec2 { } } -trait ToVec2 { +trait ToVec2Griddable { fn to_vec2(&self) -> Vec2; } -impl ToVec2 for ::geo::Coord { +impl ToVec2Griddable for ::geo::Coord { fn to_vec2(&self) -> Vec2 { let (x, y) = self.x_y(); vec2(x as f32, y as f32) diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 9e0a001..62b3c59 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -14,7 +14,7 @@ use lyon::{ tessellation::{BuffersBuilder, FillOptions, FillTessellator, FillVertex}, }; use murrelet_common::{ - curr_next_no_loop_iter, triangulate::DefaultVertex, PointToPoint, Polyline, SpotOnCurve, + curr_next_no_loop_iter, triangulate::DefaultVertex, PointToPoint, Polyline, SpotOnCurve, ToVec2, }; pub trait ToVecVec2 { @@ -969,12 +969,17 @@ impl LayersFromSvg { } } -pub fn tesselate_delauney(v: Vec) -> (Vec, Vec, Triangulation) { +pub fn tesselate_delauney( + v: Vec, +) -> (Vec, Vec, Triangulation) { let points: Vec<_> = v .iter() - .map(|vertex| delaunator::Point { - x: vertex.position[0] as f64, - y: vertex.position[1] as f64, + .map(|vertex| { + let loc = vertex.to_vec2(); + delaunator::Point { + x: loc.x as f64, + y: loc.y as f64, + } }) .collect(); let triangulation = delaunator::triangulate(&points); @@ -1005,7 +1010,10 @@ pub fn tesselate_delauney(v: Vec) -> (Vec, Vec>(), ) { filtered_indices.extend_from_slice(&[tri[0] as u32, tri[1] as u32, tri[2] as u32]); @@ -1015,3 +1023,27 @@ pub fn tesselate_delauney(v: Vec) -> (Vec, Vec( + v: Vec, +) -> (Vec, Vec, Triangulation) { + let points: Vec<_> = v + .iter() + .map(|vertex| { + let loc = vertex.to_vec2(); + delaunator::Point { + x: loc.x as f64, + y: loc.y as f64, + } + }) + .collect(); + let triangulation = delaunator::triangulate(&points); + + let vertices = v.clone(); + let indices = triangulation + .triangles + .iter() + .map(|x| *x as u32) + .collect_vec(); + (indices, vertices, triangulation) +} diff --git a/murrelet_gpu/src/editable_shaders.rs b/murrelet_gpu/src/editable_shaders.rs index 259f54f..6abd8a8 100644 --- a/murrelet_gpu/src/editable_shaders.rs +++ b/murrelet_gpu/src/editable_shaders.rs @@ -1,6 +1,9 @@ use std::collections::HashMap; -use crate::{gpu_macros::ShaderStr, window::GraphicsWindowConf}; +use crate::{ + build_shader_custom_vertex, gpu_macros::ShaderStr, graphics_ref::GraphicsVertex, + window::GraphicsWindowConf, +}; use lerpable::Lerpable; use murrelet_common::triangulate::DefaultVertex; use murrelet_livecode_derive::Livecode; @@ -23,6 +26,10 @@ pub struct ShaderStrings { shaders: HashMap, } impl ShaderStrings { + fn shader_str(shader: &str) -> String { + Self::shader_custom_prefix(shader, VertexKind::fragment_prefix()) + } + fn shader(shader: &str) -> String { build_shader! { ( @@ -31,6 +38,15 @@ impl ShaderStrings { } } + fn shader_custom_prefix(shader: &str, prefix: &str) -> String { + build_shader_custom_vertex! { + ( + prefix prefix; + raw shader; + ) + } + } + fn shader2tex(shader: &str) -> String { build_shader_2tex! { ( @@ -55,6 +71,19 @@ impl ShaderStrings { } } + pub fn get_shader_str_custom_prefix( + &self, + _c: &GraphicsWindowConf, + name: &str, + prefix: &str, + ) -> Option { + if let Some(str) = self.shaders.get(name) { + Some(Self::shader_custom_prefix(&str, prefix)) + } else { + None + } + } + pub fn get_graphics_ref( &self, c: &GraphicsWindowConf, @@ -92,12 +121,15 @@ impl ShaderStrings { self.shaders != other.shaders } - pub fn naga_if_needed(&self, prev_shaders: &ControlShaderStrings) -> bool { + pub fn naga_if_needed( + &self, + prev_shaders: &ControlShaderStrings, + ) -> bool { if self.has_changed(&prev_shaders) { let mut all_success = true; for (name, shader_str) in self.shaders.iter() { - let t = ShaderStrings::shader2tex(&shader_str); + let t = ShaderStrings::shader_str::(&shader_str); if let Err(err) = naga::front::wgsl::parse_str(&t) { println!( "error with shader {:?}, {:?}, not updating until it works!", @@ -121,14 +153,14 @@ impl ControlShaderStrings { } } - pub fn should_update( + pub fn should_update( &self, prev: &ControlShaderStrings, force_reload: bool, ) -> Option { let shaders = self.to_normal(); - let shader_changed_and_compiles = shaders.naga_if_needed(&prev); + let shader_changed_and_compiles = shaders.naga_if_needed::(&prev); if force_reload || shader_changed_and_compiles { // just in case there's lerp, be sure to use the one we tested diff --git a/murrelet_gpu/src/gpu_livecode.rs b/murrelet_gpu/src/gpu_livecode.rs index cd2c6db..4beb222 100644 --- a/murrelet_gpu/src/gpu_livecode.rs +++ b/murrelet_gpu/src/gpu_livecode.rs @@ -1,5 +1,4 @@ #![allow(dead_code)] -use bytemuck::NoUninit; use glam::*; use lerpable::Lerpable; use murrelet_common::{triangulate::DefaultVertex, *}; @@ -7,7 +6,7 @@ use murrelet_draw::newtypes::*; use murrelet_livecode_derive::Livecode; use crate::{ - graphics_ref::{GraphicsRefCustom, GraphicsRefWithControlFn}, + graphics_ref::{GraphicsRefCustom, GraphicsRefWithControlFn, GraphicsVertex}, window::GraphicsWindowConf, }; @@ -19,9 +18,9 @@ pub trait AnyControlRef { fn update(&self, c: &GraphicsWindowConf); } -impl AnyControlRef for ControlGraphicsRef +impl AnyControlRef for ControlGraphicsRef where - V: NoUninit + Clone + Copy + std::fmt::Debug + 'static, + VertexKind: GraphicsVertex + 'static, { fn update(&self, c: &GraphicsWindowConf) { // use the stored GraphicsRef inside ControlGraphicsRef @@ -29,9 +28,10 @@ where } } -impl ControlProvider for GraphicsRefWithControlFn +impl ControlProvider + for GraphicsRefWithControlFn where - V: NoUninit + Clone + Copy + std::fmt::Debug + 'static, + VertexKind: GraphicsVertex + 'static, { fn make_controls(&self, conf: &GraphicsConf) -> Vec> { self.control_graphics(conf) @@ -50,7 +50,7 @@ pub struct ControlGraphicsRef { pub control: Box, graphics: GraphicsRefCustom, } -impl ControlGraphicsRef { +impl ControlGraphicsRef { pub fn new( label: &'static str, control: Box, diff --git a/murrelet_gpu/src/gpu_macros.rs b/murrelet_gpu/src/gpu_macros.rs index e292556..c6928d4 100644 --- a/murrelet_gpu/src/gpu_macros.rs +++ b/murrelet_gpu/src/gpu_macros.rs @@ -1,7 +1,6 @@ #![allow(dead_code)] -use std::{cell::RefCell, collections::HashMap, fs, path::PathBuf, rc::Rc, sync::Arc}; +use std::{cell::RefCell, collections::HashMap, fs, path::PathBuf, rc::Rc}; -use bytemuck::NoUninit; use glam::Vec2; use murrelet_common::{triangulate::DefaultVertex, MurreletTime}; use serde::Serialize; @@ -9,10 +8,10 @@ use serde::Serialize; use crate::{ compute::ComputeGraphicsToTextureRef, device_state::{DeviceStateForRender, GraphicsAssets}, - gpu_livecode::{AnyControlRef, ControlGraphicsRef, ControlProvider}, + gpu_livecode::{AnyControlRef, ControlProvider}, graphics_ref::{ - AnyGraphicsRef, Graphics, GraphicsCreator, GraphicsRefCustom, GraphicsRefWithControlFn, - TextureAndDesc, DEFAULT_LOADED_TEXTURE_FORMAT, + AnyGraphicsRef, Graphics, GraphicsCreator, GraphicsRefCustom, GraphicsVertex, + DEFAULT_LOADED_TEXTURE_FORMAT, }, shader_str::*, window::GraphicsWindowConf, @@ -45,6 +44,60 @@ const DEFAULT_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16F /// use "ray_march" /// } /// +/// + +#[macro_export] +macro_rules! build_shader_custom_vertex { + (@parse ()) => {{""}}; // done! + + // for raw things in the prefix + (@parse (raw $raw:expr;$($tail:tt)*)) => { + { + let rest = build_shader!(@parse ($($tail)*)); + format!("{}\n{}", $raw, rest) + } + }; + + (@parsecode ()) => {{""}}; // done! + + (@parsecode (raw $raw:expr;$($tail:tt)*)) => { + { + let rest = build_shader!(@parsecode ($($tail)*)); + format!("{}\n{}", $raw, rest) + } + }; + + // wrap the main code itself in () + (@parse ((prefix $prefix:expr; $($tail:tt)*))) => { + { + //let prefix = ShaderStr::Prefix.to_str(); + let rest = build_shader!(@parsecode ($($tail)*)); + let suffix = ShaderStr::Suffix.to_str(); + format!("{}\n{}\n{}", $prefix, rest, suffix) + } + }; // includes + + // arm for funky parsing + (@parse $($raw:tt)*) => { + { + println!("???"); + "???" + // unreachable!(); + } + }; + + // capture the initial one + ($($raw:tt)*) => { + { + format!( + "{}\n{}\n{}", + ShaderStr::Binding1Tex.to_str(), + ShaderStr::Includes.to_str(), + build_shader_custom_vertex!(@parse ($($raw)*)), + ) + } + }; +} #[macro_export] macro_rules! build_shader { @@ -220,10 +273,8 @@ pub struct SimpleRender { pub source: GraphicsRefCustom, pub dest: GraphicsRefCustom, } -impl< - VertexKindSource: Clone + Copy + NoUninit + std::fmt::Debug, - VertexKindDest: Clone + Copy + NoUninit + std::fmt::Debug, - > SimpleRender +impl + SimpleRender { pub fn new_box( source: GraphicsRefCustom, @@ -240,8 +291,8 @@ impl< impl RenderTrait for SimpleRender where - VertexKindSource: NoUninit + Clone + std::fmt::Debug, - VertexKindDest: NoUninit + Clone + std::fmt::Debug, + VertexKindSource: GraphicsVertex, + VertexKindDest: GraphicsVertex, { fn render(&self, device: &DeviceStateForRender) { self.source @@ -270,12 +321,12 @@ where fn adjust_choice(&mut self, _choice_val: usize) {} } -pub struct TwoSourcesRender { +pub struct TwoSourcesRender { pub source_main: GraphicsRefCustom, pub source_other: GraphicsRefCustom, pub dest: GraphicsRefCustom, } -impl TwoSourcesRender { +impl TwoSourcesRender { pub fn new_box( source_main: GraphicsRefCustom, source_other: GraphicsRefCustom, @@ -297,9 +348,7 @@ impl TwoSourcesRender RenderTrait - for TwoSourcesRender -{ +impl RenderTrait for TwoSourcesRender { // type VertexKind = VertexKind; fn render(&self, device: &DeviceStateForRender) { @@ -336,19 +385,23 @@ impl RenderTrait } // holds a gpu pipeline :O -pub struct PipelineRender { - pub source: GraphicsRefCustom, +pub struct PipelineRender< + GraphicsConf, + VertexKindSource: GraphicsVertex, + VertexKindDest: GraphicsVertex, +> { + pub source: GraphicsRefCustom, pub pipeline: GPUPipelineRef, - pub dest: GraphicsRefCustom, + pub dest: GraphicsRefCustom, } -impl - PipelineRender +impl + PipelineRender { pub fn new_box( - source: GraphicsRefCustom, + source: GraphicsRefCustom, pipeline: GPUPipelineRef, - dest: GraphicsRefCustom, + dest: GraphicsRefCustom, ) -> Box { Box::new(Self { source, @@ -357,8 +410,8 @@ impl }) } } -impl RenderTrait - for PipelineRender +impl RenderTrait + for PipelineRender { // type VertexKind = VertexKind; @@ -389,12 +442,12 @@ impl Render } // given a list of inputs, choose which one to use -pub struct ChoiceRender { +pub struct ChoiceRender { pub sources: Vec>, pub dest: GraphicsRefCustom, choice: usize, } -impl ChoiceRender { +impl ChoiceRender { pub fn new_box( sources: Vec>, dest: GraphicsRefCustom, @@ -407,9 +460,7 @@ impl ChoiceRender RenderTrait - for ChoiceRender -{ +impl RenderTrait for ChoiceRender { // type VertexKind = VertexKind; fn render(&self, device: &DeviceStateForRender) { @@ -447,13 +498,13 @@ impl RenderTrait } } -pub struct PingPongRender { +pub struct PingPongRender { pub k: usize, pub ping: GraphicsRefCustom, // it'll end up here pub pong: GraphicsRefCustom, } -impl PingPongRender { +impl PingPongRender { pub fn new_box( k: usize, ping: GraphicsRefCustom, @@ -463,9 +514,7 @@ impl PingPongRender RenderTrait - for PingPongRender -{ +impl RenderTrait for PingPongRender { // type VertexKind = VertexKind; fn render(&self, device: &DeviceStateForRender) { @@ -517,12 +566,12 @@ impl RenderTrait // } } -// pub struct TextureViewRender { +// pub struct TextureViewRender { // pub source: GraphicsRef, // pub dest: TextureAndDesc, // } -// impl TextureViewRender { +// impl TextureViewRender { // pub fn new_box( // source: GraphicsRef, // dest: wgpu::TextureView, @@ -534,7 +583,7 @@ impl RenderTrait // } // } -// impl RenderTrait +// impl RenderTrait // for TextureViewRender // { // // type VertexKind = VertexKind; @@ -564,12 +613,12 @@ impl RenderTrait // // } // } -pub struct ComputeTextureRender { +pub struct ComputeTextureRender { pub source: ComputeGraphicsToTextureRef, pub dest: GraphicsRefCustom, } -impl ComputeTextureRender { +impl ComputeTextureRender { pub fn new_box( source: ComputeGraphicsToTextureRef, dest: GraphicsRefCustom, @@ -578,9 +627,7 @@ impl ComputeTextureRender } } -impl RenderTrait - for ComputeTextureRender -{ +impl RenderTrait for ComputeTextureRender { // type VertexKind = VertexKind; // whenver it's called, it'll increment! check if it's overdue before rendering! @@ -617,15 +664,13 @@ pub struct DisplayRender { pub source: GraphicsRefCustom, } -impl DisplayRender { +impl DisplayRender { pub fn new_box(source: GraphicsRefCustom) -> Box> { Box::new(DisplayRender { source }) } } -impl RenderTrait - for DisplayRender -{ +impl RenderTrait for DisplayRender { // type VertexKind = VertexKind; fn render(&self, device: &DeviceStateForRender) { @@ -719,9 +764,9 @@ impl GPUPipeline { self.dag.push(d); } - pub fn add_label(&mut self, name: &str, g: GraphicsRefCustom) + pub fn add_label(&mut self, name: &str, g: GraphicsRefCustom) where - V: NoUninit + Clone + Copy + std::fmt::Debug + 'static, + VertexKind: GraphicsVertex + 'static, { self.names.insert(name.to_string(), Box::new(g)); } diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index b049018..7e1a9f5 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -19,7 +19,7 @@ use wgpu::TextureDescriptor; use crate::device_state::*; use crate::gpu_livecode::{ControlGraphics, ControlGraphicsRef}; -use crate::shader_str::{VERTEX_SHADER, VERTEX_SHADER_3D}; +use crate::shader_str::{PREFIX, VERTEX_SHADER, VERTEX_SHADER_3D}; use crate::uniforms::{BasicUniform, UniformsPair}; use crate::window::GraphicsWindowConf; @@ -40,6 +40,29 @@ pub fn shader_from_path(device: &wgpu::Device, data: &str) -> wgpu::ShaderModule }) } +pub trait GraphicsVertex: NoUninit + Copy + Clone + std::fmt::Debug + 'static { + fn vertex_shader() -> &'static str; + fn vertex_shader_3d() -> &'static str { + unimplemented!() + } + + fn fragment_prefix() -> &'static str; +} + +impl GraphicsVertex for DefaultVertex { + fn vertex_shader() -> &'static str { + VERTEX_SHADER + } + + fn vertex_shader_3d() -> &'static str { + VERTEX_SHADER_3D + } + + fn fragment_prefix() -> &'static str { + PREFIX + } +} + // for each vertex, this is what we'll pass in // #[repr(C)] @@ -328,7 +351,7 @@ fn main(@location(0) position: vec3) -> @builtin(position) vec4 { impl InputVertexConf { pub fn default() -> InputVertexConf { InputVertexConf { - vs_mod: VERTEX_SHADER, + vs_mod: DefaultVertex::vertex_shader(), view: VertexUniforms::identity(), topology: wgpu::PrimitiveTopology::TriangleList, tri: Triangulate:: { @@ -338,19 +361,26 @@ impl InputVertexConf { is_3d: false, } } - - pub fn from_triangulate_2d(t: &Triangulate) -> InputVertexConf { - let mut c = Self::default(); - c.tri = t.clone(); - c +} +impl InputVertexConf { + pub fn from_triangulate_2d(tri: Triangulate) -> InputVertexConf { + InputVertexConf { + vs_mod: VertexKind::vertex_shader(), + view: VertexUniforms::identity(), + topology: wgpu::PrimitiveTopology::TriangleList, + tri, + is_3d: false, + } } - pub fn from_triangulate(t: &Triangulate) -> InputVertexConf { - let mut c = Self::default(); - c.is_3d = true; - c.vs_mod = VERTEX_SHADER_3D; - c.tri = t.clone(); - c + pub fn from_triangulate(tri: Triangulate) -> InputVertexConf { + InputVertexConf { + vs_mod: VertexKind::vertex_shader_3d(), + view: VertexUniforms::identity(), + topology: wgpu::PrimitiveTopology::TriangleList, + tri, + is_3d: true, + } } } @@ -467,15 +497,39 @@ impl Default for GraphicsCreator { impl GraphicsCreator { pub fn with_custom_triangle(mut self, t: &Triangulate, is_3d: bool) -> Self { if is_3d { - self.input_vertex = InputVertexConf::from_triangulate(t); + self.input_vertex = InputVertexConf::from_triangulate(t.clone()); } else { - self.input_vertex = InputVertexConf::from_triangulate_2d(t); + self.input_vertex = InputVertexConf::from_triangulate_2d(t.clone()); } self } } -impl GraphicsCreator { +impl GraphicsCreator { + pub fn default_with_custom_vertex(t: &Triangulate, is_3d: bool) -> Self { + let input_vertex = if is_3d { + InputVertexConf::from_triangulate(t.clone()) + } else { + InputVertexConf::from_triangulate_2d(t.clone()) + }; + GraphicsCreator { + first_texture: TextureCreator { + format: DEFAULT_TEXTURE_FORMAT, + }, + second_texture: None, + details: ShaderOptions::new_with_options( + wgpu::FilterMode::Linear, + wgpu::AddressMode::ClampToEdge, + ), + color_blend: wgpu::BlendComponent::REPLACE, + dst_texture: TextureCreator { + format: DEFAULT_TEXTURE_FORMAT, + }, + input_vertex, + blend_state: wgpu::BlendState::REPLACE, + } + } + pub fn with_first_texture_format(mut self, format: wgpu::TextureFormat) -> Self { self.first_texture = TextureCreator { format }; self @@ -559,7 +613,7 @@ pub struct GraphicsRefCustom { pub type GraphicsRef = GraphicsRefCustom; -impl GraphicsRefCustom { +impl GraphicsRefCustom { pub fn name(&self) -> String { self.graphics.borrow().name.clone() } @@ -788,30 +842,28 @@ pub struct GraphicsRefWithControlFn { } pub trait AnyGraphicsRef { - fn name(&self) -> String; + // fn name(&self) -> String; fn texture_view(&self) -> wgpu::TextureView; - fn render_to_texture(&self, dev: &DeviceState, dst: &wgpu::TextureView); + // fn render_to_texture(&self, dev: &DeviceState, dst: &wgpu::TextureView); } -impl AnyGraphicsRef for GraphicsRefCustom +impl AnyGraphicsRef for GraphicsRefCustom where - V: NoUninit + Clone + Copy + std::fmt::Debug + 'static, + VertexKind: GraphicsVertex + 'static, { - fn name(&self) -> String { - self.name() - } + // fn name(&self) -> String { + // self.name() + // } fn texture_view(&self) -> wgpu::TextureView { self.texture_view() } - fn render_to_texture(&self, dev: &DeviceState, dst: &wgpu::TextureView) { - self.render_to_texture(dev, dst) - } + // fn render_to_texture(&self, dev: &DeviceState, dst: &wgpu::TextureView) { + // self.render_to_texture(dev, dst) + // } } -impl - GraphicsRefWithControlFn -{ +impl GraphicsRefWithControlFn { pub fn control_graphics(&self, conf: &GraphicsConf) -> Vec> { let ctrl_graphics = (self.control_graphic_fn)(conf); @@ -875,7 +927,7 @@ pub struct Graphics { textures_for_3d: Option, } -impl Graphics { +impl Graphics { pub fn update_uniforms(&mut self, c: &GraphicsWindowConf, more_info: [f32; 4]) { let queue = &c.device.queue(); self.uniforms.more_info = more_info; From 387a84517774f8505e1fcc5c76bc635705299f36 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 18 Nov 2025 21:34:44 -0500 Subject: [PATCH 095/149] wip --- murrelet_common/src/geometry.rs | 5 +++++ murrelet_draw/src/compass.rs | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 682b742..9f5eef6 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -455,6 +455,11 @@ impl SpotOnCurve { .to_last_point() } + pub fn offset_amount(&self, offset: Vec2) -> Vec2 { + let a = self.to_line(offset.x).to_last_point(); + SpotOnCurve::new(a, self.angle).move_right_perp_dist(offset.y) + } + pub fn rotate(&self, rotate: AnglePi) -> Self { Self { loc: self.loc, diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index 1daf89c..c77f00a 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -2,8 +2,8 @@ use glam::Vec2; use lerpable::Lerpable; use murrelet_common::*; -use murrelet_gui::MurreletGUI; use murrelet_gui::make_gui_vec2; +use murrelet_gui::MurreletGUI; use murrelet_livecode_derive::Livecode; use crate::{ @@ -233,7 +233,7 @@ impl InteractiveCompassBuilder { self.pen_down = true; - // trying granular to see if we can mask + // know there's at least one point so we can unwrap CurveSegment::Points(CurvePoints::new(points)) } From 62eed288fef5108b6b9df3b3632489e29bab40ad Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 21 Nov 2025 00:25:26 -0500 Subject: [PATCH 096/149] make angle livecode --- murrelet_common/src/geometry.rs | 15 ++++++++++- murrelet_draw/src/cubic.rs | 5 ++++ murrelet_draw/src/curve_drawer.rs | 26 ++++++++++++------- murrelet_draw/src/livecodetypes.rs | 5 ++-- murrelet_livecode/src/lazy.rs | 8 +++--- murrelet_livecode/src/livecode.rs | 9 +++++++ murrelet_livecode/src/nestedit.rs | 16 +++++++++++- .../src/derive_lazy.rs | 16 ++++++++++++ .../src/derive_livecode.rs | 17 ++++++++++++ .../murrelet_livecode_derive/src/parser.rs | 8 ++++++ 10 files changed, 108 insertions(+), 17 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 9f5eef6..9167d62 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -3,6 +3,8 @@ use std::{f32::consts::PI, ops::Add}; use glam::{vec2, Mat3, Mat4, Vec2}; +use lerpable::Lerpable; +use serde::{Deserialize, Serialize}; use crate::{ intersection::{find_intersection_inf, within_segment}, @@ -15,7 +17,7 @@ pub fn a_pi(a: f32) -> AnglePi { AnglePi::new(a) } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, Lerpable, Serialize, Deserialize)] pub struct AnglePi(f32); impl AnglePi { pub const ZERO: Self = AnglePi(0.0); @@ -487,6 +489,13 @@ impl SpotOnCurve { pub fn flip(&self) -> SpotOnCurve { Self::new(self.loc, self.angle.perp_to_left().perp_to_left()) } + + pub fn with_loc(&self, loc: Vec2) -> SpotOnCurve { + Self { + loc, + angle: self.angle, + } + } } #[derive(Clone, Copy, Debug)] @@ -637,6 +646,10 @@ impl PointToPoint { pub fn start_spot(&self) -> SpotOnCurve { SpotOnCurve::new(self.start, self.angle()) } + + pub fn end_spot(&self) -> SpotOnCurve { + SpotOnCurve::new(self.end, self.angle()) + } } #[derive(Copy, Clone, Debug)] diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index cfd35de..2b7b3c2 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -72,6 +72,11 @@ impl CubicBezier { ) } + pub fn loc_at_pct(&self, t: f32) -> Vec2 { + let (a, _) = self.split(t); + a.to + } + pub fn start_to_tangent(&self) -> (SpotOnCurve, f32) { // let side_len = self.from.distance(self.to); diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 8ee6ac2..9ab7516 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -13,7 +13,8 @@ use crate::{ newtypes::*, svg::glam_to_lyon, tesselate::{ - cubic_bezier_path_to_lyon, flatten_cubic_bezier_path, parse_svg_data_as_vec2, ToVecVec2, + cubic_bezier_path_to_lyon, flatten_cubic_bezier_path, + flatten_cubic_bezier_path_with_tolerance, parse_svg_data_as_vec2, ToVecVec2, }, }; @@ -304,8 +305,8 @@ impl CurveSegment { Self::Arc(CurveArc { loc, radius, - start_pi: LivecodeAnglePi::new(start_pi), - end_pi: LivecodeAnglePi::new(end_pi), + start_pi: start_pi.as_angle_pi(), + end_pi: end_pi.as_angle_pi(), }) } @@ -463,16 +464,16 @@ pub struct CurveArc { #[livecode(serde_default = "zeros")] pub loc: Vec2, // center of circle pub radius: f32, - pub start_pi: LivecodeAnglePi, - pub end_pi: LivecodeAnglePi, + pub start_pi: AnglePi, + pub end_pi: AnglePi, } impl CurveArc { pub fn new(loc: Vec2, radius: f32, start_pi: A1, end_pi: A2) -> Self { Self { loc, radius, - start_pi: LivecodeAnglePi::new(start_pi), - end_pi: LivecodeAnglePi::new(end_pi), + start_pi: start_pi.as_angle_pi(), + end_pi: end_pi.as_angle_pi(), } } @@ -604,11 +605,8 @@ impl CurveArc { #[derive(Debug, Clone, Livecode, Lerpable)] pub struct CurveCubicBezier { from: Vec2, - ctrl1: Vec2, - ctrl2: Vec2, - to: Vec2, } impl CurveCubicBezier { @@ -657,6 +655,14 @@ impl CurveCubicBezier { pub fn to(&self) -> Vec2 { self.to } + + pub fn flatten(&self, tolerance: f32) -> Vec { + flatten_cubic_bezier_path_with_tolerance(&vec![self.to_cubic()], false, tolerance).unwrap() + } + + pub fn to_pts(&self, tolerance: f32) -> CurveSegment { + CurveSegment::new_simple_points(self.flatten(tolerance)) + } } #[derive(Debug, Clone, Livecode, Lerpable)] diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs index b5ad7af..c0fcd11 100644 --- a/murrelet_draw/src/livecodetypes.rs +++ b/murrelet_draw/src/livecodetypes.rs @@ -13,7 +13,9 @@ pub mod anglepi { use crate::transform2d::Transform2d; - #[derive(Clone, Copy, Debug, Livecode, Lerpable, Default, PartialEq, Serialize, Deserialize)] + #[derive( + Clone, Copy, Debug, Livecode, Lerpable, Default, PartialEq, Serialize, Deserialize, + )] pub struct LivecodeAnglePi(f32); impl std::ops::Add for LivecodeAnglePi { @@ -51,7 +53,6 @@ pub mod anglepi { pub fn add(&self, f: A) -> LivecodeAnglePi { Self(self.0 + f.angle_pi()) } - } impl From for Angle { diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 28322fe..9c21ed6 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -1,13 +1,13 @@ use std::sync::Arc; -use evalexpr::{HashMapContext, IterateVariablesContext, Node}; +use evalexpr::Node; use itertools::Itertools; use lerpable::{step, Lerpable}; -use murrelet_common::{IdxInRange, LivecodeValue, MurreletColor}; +use murrelet_common::{IdxInRange, MurreletColor}; use serde::Deserialize; use crate::{ - expr::{ExprWorldContextValues, MixedEvalDefs, MixedEvalDefsRef}, + expr::{ExprWorldContextValues, MixedEvalDefs}, livecode::{GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeFunction, LivecodeVariable}, state::{LivecodeWorldState, WorldWithLocalVariables}, types::{LivecodeError, LivecodeResult}, @@ -25,6 +25,8 @@ pub enum ControlLazyNodeF32 { } impl ControlLazyNodeF32 { + pub const ZERO: Self = ControlLazyNodeF32::Float(0.0); + pub fn new(n: Node) -> Self { Self::Expr(n) } diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index 964fbc7..8bd92db 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -10,6 +10,7 @@ use glam::Vec2; use glam::Vec3; use itertools::Itertools; use murrelet_common::clamp; +use murrelet_common::AnglePi; use murrelet_common::MurreletColor; use serde::Deserialize; @@ -99,6 +100,12 @@ impl LivecodeToControl for bool { } } +impl LivecodeToControl for AnglePi { + fn to_control(&self) -> ControlF32 { + ControlF32::Raw(self._angle_pi()) + } +} + impl LivecodeToControl<[ControlF32; 2]> for Vec2 { fn to_control(&self) -> [ControlF32; 2] { [self.x.to_control(), self.y.to_control()] @@ -469,6 +476,8 @@ pub enum ControlF32 { } impl ControlF32 { + pub const ZERO: ControlF32 = ControlF32::Float(0.0); + // for backwards compatibility #[allow(non_snake_case)] pub fn Raw(v: f32) -> ControlF32 { diff --git a/murrelet_livecode/src/nestedit.rs b/murrelet_livecode/src/nestedit.rs index a0b7a6a..92a362f 100644 --- a/murrelet_livecode/src/nestedit.rs +++ b/murrelet_livecode/src/nestedit.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use evalexpr::Node; use glam::{vec2, vec3, Vec2, Vec3}; -use murrelet_common::MurreletColor; +use murrelet_common::{AnglePi, MurreletColor}; use crate::lazy::LazyNodeF32; use crate::types::{AdditionalContextNode, LivecodeError, LivecodeResult}; @@ -135,6 +135,20 @@ impl NestEditable for i32 { } } +impl NestEditable for AnglePi { + fn nest_update(&self, mods: NestedMod) -> Self { + AnglePi::new( + mods.get_curr_as_f32() + .map(|x| x) + .unwrap_or(self._angle_pi()), + ) + } + + fn nest_get(&self, getter: &[&str]) -> LivecodeResult { + nest_default(getter, format!("{}", self._angle_pi())) + } +} + impl NestEditable for Vec2 { fn nest_update(&self, mods: NestedMod) -> Self { let x = mods.get_subfield_as_f32("x").unwrap_or(self.x); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index d160274..fe02adc 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -27,6 +27,9 @@ impl LazyFieldType { // already lazy... quote! { murrelet_livecode::lazy::LazyNodeF32 } } + ControlType::AnglePi => { + quote! { murrelet_livecode::lazy::LazyNodeF32 } + } // ControlType::LinSrgbaUnclamped => quote!{[murrelet_livecode::livecode::ControlF32; 4]}, _ => panic!("unitcell doesn't have this one yet"), } @@ -49,6 +52,10 @@ impl LazyFieldType { quote! { murrelet_livecode::lazy::eval_lazy_color(#ident, ctx) } } ControlType::Bool => quote! {#ident.eval_lazy(ctx)? > 0.0}, + ControlType::AnglePi => { + // for number-like things, we also enable clamping! (it's a bit experimental though, be careful) + quote! {murrelet_common::AnglePi::new(#ident.eval_lazy(ctx)?)} + } _ => { // for number-like things, we also enable clamping! (it's a bit experimental though, be careful) let f32_out = match (f32min, f32max) { @@ -79,6 +86,9 @@ impl LazyFieldType { } ControlType::Bool => quote! {#name: self.#name.eval_lazy(ctx)? > 0.0}, ControlType::LazyNodeF32 => quote! {#name: self.#name.add_more_defs(ctx)? }, + ControlType::AnglePi => { + quote! {#name: murrelet_common::AnglePi::new(self.#name.eval_lazy(ctx)?)} + } _ => { // for number-like things, we also enable clamping! (it's a bit experimental though, be careful) let f32_out = match (idents.data.f32min, idents.data.f32max) { @@ -108,6 +118,9 @@ impl LazyFieldType { quote! {#name: self.#name.map(|name| murrelet_common::MurreletColor::hsva(name[0].eval_lazy(ctx)? as f32, name[1].eval_lazy(ctx)? as f32, name[2].eval_lazy(ctx)? as f32, name[3].eval_lazy(ctx)? as f32))} } ControlType::Bool => quote! {#name: self.#name.map(|name| name.eval_lazy(ctx)? > 0.0)}, + ControlType::AnglePi => { + quote! {#name: self.#name.map(|name| murrelet_common::AnglePi::new(name.eval_lazy(ctx)?))} + } ControlType::LazyNodeF32 => { quote! {#name: { if let Some(name) = &self.#name { @@ -157,6 +170,9 @@ impl LazyFieldType { } // ControlType::LinSrgbaUnclamped => quote!{murrelet_livecode::livecode::ControlF32::hsva_unclamped(&self.0, w)}, ControlType::Bool => quote! {self.0.eval_lazy(ctx)? > 0.0}, + ControlType::AnglePi => { + quote! {murrelet_common::AnglePi::new(self.0.eval_lazy(ctx)?)} + } // _ => quote!{self.0.eval_lazy(ctx)? as #orig_ty} _ => { let f32_out = match (idents.data.f32min, idents.data.f32max) { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index f46a59e..6cedbb0 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -18,6 +18,7 @@ impl LivecodeFieldType { ControlType::F32_3 => quote! {[murrelet_livecode::livecode::ControlF32; 3]}, ControlType::Color => quote! {[murrelet_livecode::livecode::ControlF32; 4]}, ControlType::ColorUnclamped => quote! {[murrelet_livecode::livecode::ControlF32; 4]}, + ControlType::AnglePi => quote! {murrelet_livecode::livecode::ControlF32}, // ControlType::EvalExpr => quote! {murrelet_livecode::expr::ControlExprF32}, ControlType::LazyNodeF32 => quote! {murrelet_livecode::lazy::ControlLazyNodeF32}, } @@ -41,6 +42,9 @@ impl LivecodeFieldType { quote! {murrelet_livecode::livecode::ControlF32::hsva_unclamped(&self.#name, w)?} } ControlType::LazyNodeF32 => quote! {self.#name.o(w)?}, + ControlType::AnglePi => { + quote! {murrelet_common::AnglePi::new(self.#name.o(w)?)} + } _ => { let f32_out = match (f32min, f32max) { (None, None) => quote! {self.#name.o(w)?}, @@ -80,6 +84,16 @@ impl LivecodeFieldType { None } }, + ControlType::AnglePi => { + quote! { + if let Some(name) = &self.#name { + let a = name.o(w)?; + Some(murrelet_common::AnglePi::new(a)) + } else { + None + } + } + } _ => { let f32_out = match (f32min, f32max) { (None, None) => quote! { @@ -133,6 +147,9 @@ impl LivecodeFieldType { ControlType::ColorUnclamped => { quote! {murrelet_livecode::livecode::ControlF32::hsva_unclamped(&self.0, w)?} } + ControlType::AnglePi => { + quote! {murrelet_common::AnglePi::new(self.0.o(w)?)} + } _ => { let f32_out = match (idents.data.f32min, idents.data.f32max) { (None, None) => quote! {self.0.o(w)?}, diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index 1c2c2db..8cfb571 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -539,6 +539,7 @@ pub enum ControlType { Color, ColorUnclamped, LazyNodeF32, + AnglePi, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -609,6 +610,7 @@ impl HowToControlThis { OverrideOrInferred::Override, RecursiveControlType::Vec, ), + "a" => HowToControlThis::WithType(OverrideOrInferred::Override, ControlType::AnglePi), // "expr" => { // HowToControlThis::WithType(OverrideOrInferred::Override, ControlType::EvalExpr) // } @@ -655,6 +657,9 @@ impl HowToControlThis { "LazyNodeF32" => { HowToControlThis::WithType(OverrideOrInferred::Inferred, ControlType::LazyNodeF32) } + "AnglePi" => { + HowToControlThis::WithType(OverrideOrInferred::Inferred, ControlType::AnglePi) + } // _ => HowToControlThis::WithNone(OverrideOrInferred::Inferred) _ => { if value.starts_with("Lazy") { @@ -689,6 +694,9 @@ impl SerdeDefault { (ControlType::F32, SerdeDefault::Zeros, _) => { "murrelet_livecode::livecode::_auto_default_f32_0".to_string() } + (ControlType::AnglePi, SerdeDefault::Zeros, _) => { + "murrelet_livecode::livecode::_auto_default_f32_0".to_string() + } (ControlType::F32, SerdeDefault::Ones, _) => { "murrelet_livecode::livecode::_auto_default_f32_1".to_string() } From 79ef5a9c4929fdd2066d069b0d77c000dc41b6da Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 21 Nov 2025 16:01:49 -0500 Subject: [PATCH 097/149] wip --- murrelet_draw/src/curve_drawer.rs | 58 +++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 9ab7516..40e4f35 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -9,7 +9,6 @@ use svg::node::element::path::Data; use crate::{ cubic::CubicBezier, - livecodetypes::anglepi::*, newtypes::*, svg::glam_to_lyon, tesselate::{ @@ -64,7 +63,6 @@ impl CubicBezierTo { #[derive(Debug, Clone, Default, Livecode, Lerpable, Serialize, Deserialize)] pub struct CubicBezierPath { pub from: Vec2, - pub ctrl1: Vec2, pub curves: Vec, pub closed: bool, @@ -693,6 +691,34 @@ impl CurvePoints { } } +pub trait ToCurveSegment { + fn to_segment(&self) -> CurveSegment; +} + +impl ToCurveSegment for CubicBezier { + fn to_segment(&self) -> CurveSegment { + CurveSegment::cubic(*self) + } +} + +impl ToCurveSegment for Vec2 { + fn to_segment(&self) -> CurveSegment { + CurveSegment::new_simple_point(*self) + } +} + +impl ToCurveSegment for Vec { + fn to_segment(&self) -> CurveSegment { + CurveSegment::new_simple_points(self.clone()) + } +} + +impl ToCurveSegment for Polyline { + fn to_segment(&self) -> CurveSegment { + CurveSegment::new_simple_points(self.clone_to_vec()) + } +} + pub trait ToCurveDrawer { fn to_segments(&self) -> Vec; fn to_cd_closed(&self) -> CurveDrawer { @@ -709,18 +735,27 @@ impl ToCurveDrawer for CurveSegment { } } -impl ToCurveDrawer for Vec { +impl ToCurveDrawer for T +where + T: ToCurveSegment, +{ fn to_segments(&self) -> Vec { - self.clone() + self.to_segment().to_segments() } } -impl ToCurveDrawer for Vec { +impl ToCurveDrawer for Vec { fn to_segments(&self) -> Vec { - vec![CurveSegment::new_simple_points(self.clone())] + self.clone() } } +// impl ToCurveDrawer for Vec { +// fn to_segments(&self) -> Vec { +// vec![CurveSegment::new_simple_points(self.clone())] +// } +// } + impl ToCurveDrawer for Vec { fn to_segments(&self) -> Vec { vec![CurveSegment::new_simple_points( @@ -728,3 +763,14 @@ impl ToCurveDrawer for Vec { )] } } + +#[macro_export] +macro_rules! curve_segments { + ($($expr:expr),* $(,)?) => {{ + let mut v: Vec = Vec::new(); + $( + v.extend($expr.to_segments()); + )* + v + }}; +} From a4140b8dd6f04230d604737b4798a0ab12920da3 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 22 Nov 2025 21:16:57 -0500 Subject: [PATCH 098/149] wip --- murrelet_common/src/idx.rs | 15 ++++++++++++++- murrelet_livecode/src/expr.rs | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 74f2fac..7ab4eb4 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -160,7 +160,7 @@ impl IdxInRange { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct IdxInRange2d { pub i: IdxInRange, pub j: IdxInRange, @@ -313,7 +313,20 @@ impl IdxInRange2d { vec2(x, y) } + #[deprecated] pub fn width(&self) -> f32 { + self.i_total() + } + + pub fn i_total(&self) -> f32 { self.i.total as f32 } + + pub fn i(&self) -> u64 { + self.i.i() + } + + pub fn j(&self) -> u64 { + self.j.i() + } } diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index 818bc5e..93258db 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -563,4 +563,18 @@ impl MixedEvalDefs { pub(crate) fn expr_vals(&self) -> &ExprWorldContextValues { &self.vals } + + pub fn add_idx(&mut self, i: IdxInRange, prefix: &str) { + self.set_vals(ExprWorldContextValues::new_from_idx(i).with_prefix(prefix)); + } + + pub fn with_idx(mut self, i: IdxInRange, prefix: &str) -> MixedEvalDefs { + self.set_vals(ExprWorldContextValues::new_from_idx(i).with_prefix(prefix)); + self + } + + pub fn with_val(mut self, prefix: &str, val: LivecodeValue) -> MixedEvalDefs { + self.set_val(prefix, val); + self + } } From 5a2303e21554a387966af1950edbf691a4e08a6c Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 23 Nov 2025 12:00:32 -0500 Subject: [PATCH 099/149] remove livecodeanglepi --- murrelet_common/src/geometry.rs | 58 +++++++++++------- murrelet_draw/src/compass.rs | 28 ++++----- murrelet_draw/src/curve_drawer.rs | 6 ++ murrelet_draw/src/lib.rs | 9 ++- murrelet_draw/src/livecodetypes.rs | 96 ------------------------------ murrelet_gui/src/lib.rs | 5 ++ 6 files changed, 67 insertions(+), 135 deletions(-) delete mode 100644 murrelet_draw/src/livecodetypes.rs diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 9167d62..19e3a22 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -179,7 +179,7 @@ impl Angle { a._angle_pi() } - pub fn scale(&self, s: f32) -> Self { + pub fn _scale(&self, s: f32) -> Self { Angle(self.angle() * s) } @@ -244,38 +244,36 @@ impl std::fmt::Debug for Angle { } } -pub trait IsAngle { +pub trait IsAngle: Sized { fn angle_pi(&self) -> f32; fn angle(&self) -> f32; fn as_angle(&self) -> Angle; fn as_angle_pi(&self) -> AnglePi; fn to_norm_dir(&self) -> Vec2; fn to_mat3(&self) -> Mat3; - fn perp_to_left(&self) -> Angle; - fn perp_to_right(&self) -> Angle; -} - -impl IsAngle for A -where - Angle: From, - A: Copy, -{ - fn to_norm_dir(&self) -> Vec2 { - Angle::from(*self)._to_norm_dir() - } + fn perp_to_left(&self) -> Self; + fn perp_to_right(&self) -> Self; + fn scale(&self, s: f32) -> Self; - fn perp_to_left(&self) -> Angle { - Angle::from(*self)._perp_to_left() + fn flip(&self) -> Self { + self.perp_to_left().perp_to_left() } - fn perp_to_right(&self) -> Angle { - Angle::from(*self)._perp_to_right() + fn reflect_x(&self) -> Self { + self.scale(-1.0) } - fn to_mat3(&self) -> Mat3 { - Mat3::from_angle(Angle::from(*self).angle()) + fn reflect_y(&self) -> Self { + self.perp_to_left().scale(-1.0).perp_to_right() } +} +impl IsAngle for A +where + Angle: From, + A: From, + A: Copy, +{ fn angle_pi(&self) -> f32 { Angle::from(*self)._angle_pi() } @@ -291,6 +289,26 @@ where fn as_angle(&self) -> Angle { (*self).into() } + + fn to_norm_dir(&self) -> Vec2 { + Angle::from(*self)._to_norm_dir() + } + + fn perp_to_left(&self) -> Self { + Angle::from(*self)._perp_to_left().into() + } + + fn perp_to_right(&self) -> Self { + Angle::from(*self)._perp_to_right().into() + } + + fn to_mat3(&self) -> Mat3 { + Mat3::from_angle(Angle::from(*self).angle()) + } + + fn scale(&self, s: f32) -> Self { + self.as_angle()._scale(s).into() + } } // LENGTH diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index c77f00a..c237da5 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -2,28 +2,26 @@ use glam::Vec2; use lerpable::Lerpable; use murrelet_common::*; +use murrelet_gui::make_gui_angle; use murrelet_gui::make_gui_vec2; use murrelet_gui::MurreletGUI; use murrelet_livecode_derive::Livecode; -use crate::{ - curve_drawer::{CurveArc, CurveDrawer, CurvePoints, CurveSegment}, - livecodetypes::anglepi::*, -}; +use crate::curve_drawer::{CurveArc, CurveDrawer, CurvePoints, CurveSegment}; #[derive(Debug, Clone, Copy, Livecode, MurreletGUI, Lerpable)] pub struct CurveStart { - #[lerpable(func = "lerpify_vec2")] #[murrelet_gui(func = "make_gui_vec2")] loc: Vec2, - angle_pi: LivecodeAnglePi, + #[murrelet_gui(func = "make_gui_angle")] + angle_pi: AnglePi, } impl CurveStart { pub fn new(loc: Vec2, angle: A) -> Self { Self { loc, - angle_pi: LivecodeAnglePi::new(angle), + angle_pi: angle.as_angle_pi(), } } } @@ -34,7 +32,8 @@ fn empty_string() -> String { #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct CompassDir { - angle_pi: LivecodeAnglePi, + #[murrelet_gui(func = "make_gui_angle")] + angle_pi: AnglePi, #[livecode(serde_default = "false")] #[murrelet_gui(kind = "skip")] is_absolute: bool, @@ -46,7 +45,7 @@ pub struct CompassDir { impl CompassDir { pub fn new(angle: A, is_absolute: bool, label: String) -> Self { Self { - angle_pi: LivecodeAnglePi::new(angle), + angle_pi: angle.as_angle_pi(), is_absolute, label, } @@ -56,7 +55,8 @@ impl CompassDir { #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct CompassArc { radius: f32, - arc_length: LivecodeAnglePi, + #[murrelet_gui(func = "make_gui_angle")] + arc_length: AnglePi, #[livecode(serde_default = "false")] #[murrelet_gui(kind = "skip")] is_absolute: bool, @@ -106,7 +106,7 @@ impl CompassAction { pub fn angle(angle_pi: A, is_absolute: bool, label: String) -> CompassAction { CompassAction::Angle(CompassDir { - angle_pi: LivecodeAnglePi::new(angle_pi), + angle_pi: angle_pi.as_angle_pi(), is_absolute, label, }) @@ -120,7 +120,7 @@ impl CompassAction { ) -> CompassAction { CompassAction::Arc(CompassArc { radius, - arc_length: LivecodeAnglePi::new(arc_length_pi), + arc_length: arc_length_pi.as_angle_pi(), is_absolute, label, }) @@ -137,7 +137,7 @@ impl CompassAction { impl Default for CompassAction { fn default() -> Self { CompassAction::Angle(CompassDir { - angle_pi: LivecodeAnglePi::ZERO, + angle_pi: AnglePi::ZERO, is_absolute: false, label: String::new(), }) @@ -214,7 +214,7 @@ impl InteractiveCompassBuilder { fn to_basic(&self) -> CurveStart { CurveStart { loc: self.curr_loc, - angle_pi: LivecodeAnglePi::new(self.curr_angle), + angle_pi: self.curr_angle.as_angle_pi(), } } diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 40e4f35..18756e8 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -719,6 +719,12 @@ impl ToCurveSegment for Polyline { } } +impl ToCurveSegment for Circle { + fn to_segment(&self) -> CurveSegment { + CurveSegment::new_simple_circle(self.center, self.radius) + } +} + pub trait ToCurveDrawer { fn to_segments(&self) -> Vec; fn to_cd_closed(&self) -> CurveDrawer { diff --git a/murrelet_draw/src/lib.rs b/murrelet_draw/src/lib.rs index 2393b9c..14b883e 100644 --- a/murrelet_draw/src/lib.rs +++ b/murrelet_draw/src/lib.rs @@ -1,13 +1,12 @@ pub mod compass; +pub mod cubic; pub mod curve_drawer; pub mod draw; -pub mod livecodetypes; pub mod newtypes; +pub mod scaffold; pub mod sequencers; +pub mod serialize; pub mod style; pub mod svg; -pub mod transform2d; -pub mod cubic; pub mod tesselate; -pub mod scaffold; -pub mod serialize; +pub mod transform2d; diff --git a/murrelet_draw/src/livecodetypes.rs b/murrelet_draw/src/livecodetypes.rs deleted file mode 100644 index c0fcd11..0000000 --- a/murrelet_draw/src/livecodetypes.rs +++ /dev/null @@ -1,96 +0,0 @@ -// place to put newtypes for livecode - -pub mod anglepi { - use std::ops::Sub; - - use glam::Vec2; - use lerpable::Lerpable; - use murrelet_common::{Angle, AnglePi, IsAngle}; - use murrelet_gui::CanMakeGUI; - use murrelet_livecode::{lazy::ControlLazyNodeF32, livecode::ControlF32}; - use murrelet_livecode_derive::Livecode; - use serde::{Deserialize, Serialize}; - - use crate::transform2d::Transform2d; - - #[derive( - Clone, Copy, Debug, Livecode, Lerpable, Default, PartialEq, Serialize, Deserialize, - )] - pub struct LivecodeAnglePi(f32); - - impl std::ops::Add for LivecodeAnglePi { - type Output = Self; - - fn add(self, other: Self) -> Self::Output { - Self(self.0 + other.0) - } - } - impl LivecodeAnglePi { - pub const ZERO: Self = LivecodeAnglePi(0.0); - - fn _to_angle_pi(&self) -> AnglePi { - AnglePi::new(self.0) - } - - pub fn new(f: A) -> Self { - Self(f.angle_pi()) - } - - pub fn from_angle_pi(angle_pi: f32) -> Self { - Self(angle_pi) - } - - pub fn scale(&self, scale: f32) -> Self { - Self(self.0 * scale) - } - - pub fn transform_vec2(&self, v: glam::Vec2) -> Vec2 { - Transform2d::rotate(self.angle_pi()) - .to_mat3() - .transform_vector2(v) - } - - pub fn add(&self, f: A) -> LivecodeAnglePi { - Self(self.0 + f.angle_pi()) - } - } - - impl From for Angle { - fn from(value: LivecodeAnglePi) -> Self { - value._to_angle_pi().as_angle() - } - } - - impl From for AnglePi { - fn from(value: LivecodeAnglePi) -> Self { - value._to_angle_pi() - } - } - - impl CanMakeGUI for LivecodeAnglePi { - fn make_gui() -> murrelet_gui::MurreletGUISchema { - murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Angle) - } - } - - impl Sub for LivecodeAnglePi { - type Output = Self; - - fn sub(self, other: Self) -> Self::Output { - let new_angle = self.0 - other.0; - LivecodeAnglePi(new_angle) - } - } - - impl ControlLivecodeAnglePi { - pub fn from_angle(a: AnglePi) -> Self { - Self(ControlF32::Float(a.angle_pi())) - } - } - - impl ControlLazyLivecodeAnglePi { - pub fn from_angle(a: AnglePi) -> Self { - Self(ControlLazyNodeF32::Float(a.angle_pi())) - } - } -} diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs index fdb05f6..947915d 100644 --- a/murrelet_gui/src/lib.rs +++ b/murrelet_gui/src/lib.rs @@ -138,6 +138,11 @@ impl CanMakeGUI for MurreletColor { } } +#[cfg(feature = "murrelet")] +pub fn make_gui_angle() -> MurreletGUISchema { + MurreletGUISchema::Val(ValueGUI::Angle) +} + #[cfg(feature = "murrelet")] pub fn make_gui_vec2() -> MurreletGUISchema { MurreletGUISchema::Val(ValueGUI::Vec2) From 7e3f851be38a2cc3ad7be4b8238fcaf060fe8ee6 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 23 Nov 2025 19:18:30 -0500 Subject: [PATCH 100/149] wip --- murrelet_common/src/geometry.rs | 3 ++- murrelet_common/src/intersection.rs | 9 +++++++++ murrelet_draw/src/curve_drawer.rs | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 19e3a22..31381e5 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -475,9 +475,10 @@ impl SpotOnCurve { .to_last_point() } + // x moves along the direction, y moves left perp pub fn offset_amount(&self, offset: Vec2) -> Vec2 { let a = self.to_line(offset.x).to_last_point(); - SpotOnCurve::new(a, self.angle).move_right_perp_dist(offset.y) + SpotOnCurve::new(a, self.angle).move_left_perp_dist(offset.y) } pub fn rotate(&self, rotate: AnglePi) -> Self { diff --git a/murrelet_common/src/intersection.rs b/murrelet_common/src/intersection.rs index e517b64..634b4eb 100644 --- a/murrelet_common/src/intersection.rs +++ b/murrelet_common/src/intersection.rs @@ -1,5 +1,7 @@ use glam::{vec2, Vec2}; +use crate::SpotOnCurve; + // todo, can i replace this with geo? pub fn find_intersection_inf(line0: (Vec2, Vec2), line1: (Vec2, Vec2)) -> Option { let (line0_start, line0_end) = line0; @@ -32,6 +34,13 @@ pub fn find_intersection_inf(line0: (Vec2, Vec2), line1: (Vec2, Vec2)) -> Option } } +pub fn find_intersect_spots(spot0: SpotOnCurve, spot1: SpotOnCurve) -> Option { + find_intersection_inf( + (spot0.loc(), spot0.to_line(-100.0).to_last_point()), + (spot1.loc(), spot1.to_line(-100.0).to_last_point()), + ) +} + pub fn find_intersection_segments(line0: (Vec2, Vec2), line1: (Vec2, Vec2)) -> Option { find_intersection_inf(line0, line1).filter(|&intersection| { within_segment(line0, intersection, 0.0001) && within_segment(line1, intersection, 0.0001) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 18756e8..bc089cd 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -725,6 +725,12 @@ impl ToCurveSegment for Circle { } } +impl ToCurveSegment for CurveArc { + fn to_segment(&self) -> CurveSegment { + CurveSegment::Arc(self.clone()) + } +} + pub trait ToCurveDrawer { fn to_segments(&self) -> Vec; fn to_cd_closed(&self) -> CurveDrawer { @@ -756,6 +762,18 @@ impl ToCurveDrawer for Vec { } } +impl ToCurveDrawer for Option +where + T: ToCurveDrawer, +{ + fn to_segments(&self) -> Vec { + match self { + Some(t) => t.to_segments(), + None => vec![], + } + } +} + // impl ToCurveDrawer for Vec { // fn to_segments(&self) -> Vec { // vec![CurveSegment::new_simple_points(self.clone())] From 10c20ab3621d4f6de8ea238982999cbbb14235f4 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 23 Nov 2025 19:29:19 -0500 Subject: [PATCH 101/149] wip --- murrelet_draw/src/curve_drawer.rs | 49 +++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index bc089cd..784fcd9 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -774,12 +774,6 @@ where } } -// impl ToCurveDrawer for Vec { -// fn to_segments(&self) -> Vec { -// vec![CurveSegment::new_simple_points(self.clone())] -// } -// } - impl ToCurveDrawer for Vec { fn to_segments(&self) -> Vec { vec![CurveSegment::new_simple_points( @@ -798,3 +792,46 @@ macro_rules! curve_segments { v }}; } + +// useful for drawnshape... +pub trait ToCurveDrawers { + fn to_curve_drawers(&self) -> Vec; +} + +impl ToCurveDrawers for CurveDrawer { + fn to_curve_drawers(&self) -> Vec { + vec![self.clone()] + } +} + +impl ToCurveDrawers for Vec +where + T: ToCurveDrawers + Clone, +{ + fn to_curve_drawers(&self) -> Vec { + self.iter().map(|x| x.to_curve_drawers()).concat() + } +} + +impl ToCurveDrawers for Option +where + T: ToCurveDrawers, +{ + fn to_curve_drawers(&self) -> Vec { + match self { + Some(t) => t.to_curve_drawers(), + None => vec![], + } + } +} + +#[macro_export] +macro_rules! curve_drawers { + ($($expr:expr),* $(,)?) => {{ + let mut v: Vec = Vec::new(); + $( + v.extend($expr.to_curve_drawers()); + )* + v + }}; +} From 129df73a6865ed0ba6232a392a07927875762002 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 25 Nov 2025 13:44:55 -0500 Subject: [PATCH 102/149] wip --- murrelet_common/src/geometry.rs | 7 +++++++ murrelet_draw/src/curve_drawer.rs | 6 ++++++ murrelet_livecode/src/expr.rs | 11 +++++++++++ 3 files changed, 24 insertions(+) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 31381e5..890f859 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -515,6 +515,13 @@ impl SpotOnCurve { angle: self.angle, } } + + pub fn travel(&self, length: f32) -> SpotOnCurve { + Self { + loc: self.line_to_spot(length), + angle: self.angle, + } + } } #[derive(Clone, Copy, Debug)] diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 784fcd9..d3421df 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -731,6 +731,12 @@ impl ToCurveSegment for CurveArc { } } +impl ToCurveSegment for SpotOnCurve { + fn to_segment(&self) -> CurveSegment { + self.loc.to_segment() + } +} + pub trait ToCurveDrawer { fn to_segments(&self) -> Vec; fn to_cd_closed(&self) -> CurveDrawer { diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index 93258db..291bb5f 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -28,6 +28,17 @@ pub fn init_evalexpr_func_ctx() -> LivecodeResult { } Ok(Value::Empty) }), + "p" => Function::new(move |argument| { + if let Ok(a) = argument.as_float() { + println!("{:?} (float)", a); + Ok(Value::Float(a)) + } else { + let a = argument.as_int()?; + println!("{:?} (int)", a); + Ok(Value::Int(a)) + } + }), + "manymod" => Function::new(move |argument| { let a = argument.as_tuple()?; From 09d6f82c5dfb17d4e2f4322d4919e477a6a7227e Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Nov 2025 00:43:59 -0500 Subject: [PATCH 103/149] wip --- murrelet_common/src/geometry.rs | 32 +++++++++++++++++++++ murrelet_common/src/idx.rs | 4 +++ murrelet_draw/src/cubic.rs | 4 +++ murrelet_livecode/src/types.rs | 49 +++++++++++++++++++++------------ 4 files changed, 71 insertions(+), 18 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 890f859..49cae0a 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -21,6 +21,9 @@ pub fn a_pi(a: f32) -> AnglePi { pub struct AnglePi(f32); impl AnglePi { pub const ZERO: Self = AnglePi(0.0); + pub const HALF: Self = AnglePi(0.5); + pub const ONE: Self = AnglePi(1.0); + pub const NEG_HALF: Self = AnglePi(-0.5); pub fn new(v: f32) -> AnglePi { AnglePi(v) @@ -524,6 +527,35 @@ impl SpotOnCurve { } } +pub trait ToSpotWithAngle { + fn to_spot_a>(&self, a: A) -> SpotOnCurve; + + fn to_spot_forward(&self) -> SpotOnCurve { + self.to_spot_a(AnglePi::new(0.0)) + } + fn to_spot_backward(&self) -> SpotOnCurve { + self.to_spot_a(AnglePi::new(1.0)) + } + fn to_spot_upward(&self) -> SpotOnCurve { + self.to_spot_a(AnglePi::new(0.5)) + } + fn to_spot_downward(&self) -> SpotOnCurve { + self.to_spot_a(AnglePi::new(1.5)) + } +} + +impl ToSpotWithAngle for Vec2 { + fn to_spot_a>(&self, a: A) -> SpotOnCurve { + SpotOnCurve::new(*self, a.into()) + } +} + +impl ToSpotWithAngle for SpotOnCurve { + fn to_spot_a>(&self, a: A) -> SpotOnCurve { + SpotOnCurve::new(self.loc, a.into()) + } +} + #[derive(Clone, Copy, Debug)] pub struct CornerAngleToAngle { point: Vec2, diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 7ab4eb4..9c3047c 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -158,6 +158,10 @@ impl IdxInRange { }) } } + + pub fn is_first(&self) -> bool { + self.i == 0 + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index 2b7b3c2..0bfa85e 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -9,6 +9,10 @@ pub struct CubicBezier { pub to: Vec2, } impl CubicBezier { + pub fn from_spots_s(in_spot: SpotOnCurve, out_spot: SpotOnCurve, strength: Vec2) -> Self { + Self::from_spots(in_spot, strength.x, out_spot, strength.y) + } + pub fn from_spots( in_spot: SpotOnCurve, in_strength: f32, diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 9e89078..5c7210d 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -10,7 +10,7 @@ use thiserror::Error; use crate::{ expr::IntoExprWorldContext, - livecode::{GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeVariable}, + livecode::{ControlF32, GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeVariable}, state::LivecodeWorldState, unitcells::UnitCellIdx, }; @@ -113,24 +113,37 @@ impl Lerpable for AdditionalContextNode { #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[serde(untagged)] pub enum ControlVecElementRepeatMethod { - Single(usize), - Rect([usize; 2]), + Single(ControlF32), + Rect([ControlF32; 2]), } impl ControlVecElementRepeatMethod { - fn len(&self) -> usize { - match self { - ControlVecElementRepeatMethod::Single(s) => *s, - ControlVecElementRepeatMethod::Rect(r) => r[0] * r[1], - } + fn len(&self, w: &LivecodeWorldState) -> LivecodeResult { + let v = match self { + ControlVecElementRepeatMethod::Single(s) => { + let ss = s.o(w)?; + ss + } + ControlVecElementRepeatMethod::Rect(r) => { + let rr = r.o(w)?; + rr.x * rr.y + } + }; + Ok(v as usize) } - fn iter(&self) -> Vec { - match self { - ControlVecElementRepeatMethod::Single(s) => IdxInRange::enumerate_count(*s) - .iter() - .map(|x| x.to_2d()) - .collect_vec(), - ControlVecElementRepeatMethod::Rect(s) => IdxInRange2d::enumerate_counts(s[0], s[1]), - } + fn iter(&self, w: &LivecodeWorldState) -> LivecodeResult> { + let v = match self { + ControlVecElementRepeatMethod::Single(s) => { + IdxInRange::enumerate_count(s.o(w)? as usize) + .iter() + .map(|x| x.to_2d()) + .collect_vec() + } + ControlVecElementRepeatMethod::Rect(s) => { + let rr = s.o(w)?; + IdxInRange2d::enumerate_counts(rr.x as usize, rr.y as usize) + } + }; + Ok(v) } } @@ -199,7 +212,7 @@ impl ControlVecElementRepeat { where Source: LivecodeFromWorld, { - let mut result = Vec::with_capacity(self.repeat.len() * self.what.len()); + let mut result = Vec::with_capacity(self.repeat.len(w)? * self.what.len()); let prefix = if self.prefix.is_empty() { "i_".to_string() @@ -207,7 +220,7 @@ impl ControlVecElementRepeat { format!("{}_", self.prefix) }; - for idx in self.repeat.iter() { + for idx in self.repeat.iter(w)? { let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); let new_w = w.clone_with_vals(expr, &prefix); From 6d4bad7cc96e71010e51aa632ea4b7cc9fde3dab Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Nov 2025 14:55:49 -0500 Subject: [PATCH 104/149] wip --- Cargo.toml | 1 + murrelet_livecode/src/lazy.rs | 10 + murrelet_livecode/src/types.rs | 333 +++++++++++++++++- .../examples/tests.rs | 201 ++++++----- .../src/derive_lazy.rs | 44 ++- .../src/derive_livecode.rs | 96 +++-- 6 files changed, 540 insertions(+), 145 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2cc3464..19f918c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "murrelet_gpu", "murrelet_livecode", "murrelet_livecode_macros", + "murrelet_livecode_macros/murrelet_livecode_derive", "murrelet_perform", "murrelet_src_audio", "murrelet_src_midi", diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 9c21ed6..d3a19ec 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -272,6 +272,16 @@ impl IsLazy for LazyNodeF32 { } } +impl IsLazy for Vec +where + Source: IsLazy, +{ + type Target = Vec; + fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult> { + self.iter().map(|x| x.eval_lazy(expr)).collect() + } +} + impl crate::unitcells::UnitCellCreator for T where T: IsLazy, diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 5c7210d..bfbdfdd 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -9,8 +9,12 @@ use serde::{Deserialize, Deserializer}; use thiserror::Error; use crate::{ - expr::IntoExprWorldContext, - livecode::{ControlF32, GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeVariable}, + expr::{IntoExprWorldContext, MixedEvalDefs}, + lazy::{ControlLazyNodeF32, IsLazy, LazyNodeF32}, + livecode::{ + ControlF32, GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeToControl, LivecodeVariable, + }, + nestedit::NestEditable, state::LivecodeWorldState, unitcells::UnitCellIdx, }; @@ -147,6 +151,254 @@ impl ControlVecElementRepeatMethod { } } +#[derive(Debug, Clone, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[serde(untagged)] +pub enum DeserLazyControlVecElementRepeatMethod { + Single(ControlLazyNodeF32), + Rect([ControlLazyNodeF32; 2]), +} +impl DeserLazyControlVecElementRepeatMethod { + fn o(&self, w: &LivecodeWorldState) -> LivecodeResult { + match self { + DeserLazyControlVecElementRepeatMethod::Single(lazy) => { + let v = lazy.o(w)?; + Ok(LazyVecElementRepeatMethod::Single(v)) + } + DeserLazyControlVecElementRepeatMethod::Rect(lazy) => { + let x = lazy[0].o(w)?; + let y = lazy[1].o(w)?; + Ok(LazyVecElementRepeatMethod::Rect([x, y])) + } + } + } +} + +#[derive(Debug, Clone, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct DeserLazyControlVecElementRepeat { + repeat: DeserLazyControlVecElementRepeatMethod, + prefix: String, + what: Vec>, +} +impl DeserLazyControlVecElementRepeat { + fn o(&self, w: &LivecodeWorldState) -> LivecodeResult> + where + Source: LivecodeFromWorld, + Target: IsLazy + Debug + Clone, + { + let what = self + .what + .iter() + .map(|x| x.o(w)) + .collect::, _>>()?; + Ok(LazyVecElementRepeat { + repeat: self.repeat.o(w)?, + prefix: self.prefix.clone(), + what, + }) + } +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub enum DeserLazyControlVecElement +where + Source: Clone + Debug, +{ + Single(Source), + Repeat(DeserLazyControlVecElementRepeat), +} +impl DeserLazyControlVecElement { + pub fn o( + &self, + w: &LivecodeWorldState, + ) -> LivecodeResult> + where + Source: LivecodeFromWorld, + Target: IsLazy, + { + let a = match self { + DeserLazyControlVecElement::Single(a) => LazyControlVecElement::Single(a.o(w)?), + DeserLazyControlVecElement::Repeat(r) => LazyControlVecElement::Repeat(r.o(w)?), + }; + Ok(a) + } + + // just to match the interface used by the derive macros for Vec fields + pub fn eval_and_expand_vec( + &self, + w: &LivecodeWorldState, + ) -> LivecodeResult>> + where + Source: LivecodeFromWorld, + Target: IsLazy, + { + let a = self.o(w)?; + Ok(vec![a]) + } +} + +// chatgpt +impl<'de, Source> Deserialize<'de> for DeserLazyControlVecElement +where + Source: Deserialize<'de> + Clone + Debug, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = serde_yaml::Value::deserialize(deserializer)?; + + let mut errors = Vec::new(); + + // try the simple one + match Source::deserialize(value.clone()) { + Ok(single) => return Ok(DeserLazyControlVecElement::Single(single)), + Err(e) => errors.push(format!("{}", e)), + } + + match DeserLazyControlVecElementRepeat::deserialize(value.clone()) { + Ok(repeat) => return Ok(DeserLazyControlVecElement::Repeat(repeat)), + Err(e) => { + // it's gonna fail, so just check what + errors.push(format!("(repeat {})", e)) + } + } + + // match VecUnitCell::deserialize(value.clone()) { + // Ok(repeat) => return Ok(ControlVecElement::Repeat(repeat)), + // Err(e) => { + // // it's gonna fail, so just check what + // errors.push(format!("(repeat {})", e)) + // } + // } + + // Both variants failed, return an error with detailed messages + Err(serde::de::Error::custom(format!( + "ControlVecElement {}", + errors.join(" ") + ))) + } +} + +// just an intermediate type...? +#[derive(Debug, Clone)] +pub enum LazyVecElementRepeatMethod { + Single(LazyNodeF32), + Rect([LazyNodeF32; 2]), +} +impl LazyVecElementRepeatMethod { + fn len(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + let v = match self { + LazyVecElementRepeatMethod::Single(s) => { + let ss = s.eval_lazy(ctx)?; + ss + } + LazyVecElementRepeatMethod::Rect(r) => { + let rx = r[0].eval_lazy(ctx)?; + let ry = r[1].eval_lazy(ctx)?; + rx * ry + } + }; + Ok(v as usize) + } + fn iter(&self, ctx: &MixedEvalDefs) -> LivecodeResult> { + let v = match self { + LazyVecElementRepeatMethod::Single(s) => { + IdxInRange::enumerate_count(s.eval_lazy(ctx)? as usize) + .iter() + .map(|x| x.to_2d()) + .collect_vec() + } + LazyVecElementRepeatMethod::Rect(s) => { + let rx = s[0].eval_lazy(ctx)?; + let ry = s[1].eval_lazy(ctx)?; + IdxInRange2d::enumerate_counts(rx as usize, ry as usize) + } + }; + Ok(v) + } +} + +// just internal method, if we realize we're looking at a lazy, +#[derive(Debug, Clone)] +pub struct LazyVecElementRepeat { + repeat: LazyVecElementRepeatMethod, + prefix: String, + what: Vec>, +} +impl LazyVecElementRepeat { + pub fn eval_and_expand_vec(&self, ctx: &MixedEvalDefs) -> LivecodeResult> + where + Source: IsLazy, + { + let mut result = Vec::with_capacity(self.repeat.len(ctx)? * self.what.len()); + + let prefix = if self.prefix.is_empty() { + "i_".to_string() + } else { + format!("{}_", self.prefix) + }; + + for idx in self.repeat.iter(ctx)? { + let mut ctx = ctx.clone(); + let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); + ctx.set_vals(expr.with_prefix(&prefix)); + + for src in &self.what { + match src { + LazyControlVecElement::Single(c) => { + let o = c.eval_lazy(&ctx)?; + result.push(o); + } + LazyControlVecElement::Repeat(c) => { + let o = c.eval_and_expand_vec(&ctx)?; + result.extend(o.into_iter()); + } + } + } + } + Ok(result) + } +} + +impl LivecodeToControl> + for LazyControlVecElement +where + Source: Debug + Clone + LivecodeToControl + IsLazy, + ControlSource: Debug + Clone, +{ + fn to_control(&self) -> DeserLazyControlVecElement { + match self { + LazyControlVecElement::Single(src) => { + DeserLazyControlVecElement::Single(src.to_control()) + } + LazyControlVecElement::Repeat(rep) => { + let repeat = match &rep.repeat { + LazyVecElementRepeatMethod::Single(l) => { + DeserLazyControlVecElementRepeatMethod::Single(l.to_control()) + } + LazyVecElementRepeatMethod::Rect([x, y]) => { + DeserLazyControlVecElementRepeatMethod::Rect([ + x.to_control(), + y.to_control(), + ]) + } + }; + + let what = rep.what.iter().map(|e| e.to_control()).collect::>(); + + DeserLazyControlVecElement::Repeat(DeserLazyControlVecElementRepeat { + repeat, + prefix: rep.prefix.clone(), + what, + }) + } + } + } +} + #[derive(Debug, Clone, Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ControlVecElementRepeat { @@ -304,6 +556,15 @@ where // UnitCell(VecUnitCell), } +#[derive(Debug, Clone)] +pub enum LazyControlVecElement +where + Source: Clone + Debug + crate::lazy::IsLazy, +{ + Single(Source), + Repeat(LazyVecElementRepeat), +} + // impl ControlVecElement impl ControlVecElement where @@ -325,6 +586,28 @@ where // ControlVecElement::UnitCell(c) => c.eval_and_expand_vec(w), } } + + // pub fn map_source(&self, f: F) -> LivecodeResult> + // where + // F: Fn(&Source) -> LivecodeResult, + // Target: Clone + Debug, + // { + // match self { + // ControlVecElement::Single(c) => f(c).map(|t| ControlVecElement::Single(t)), + // ControlVecElement::Repeat(r) => Ok({ + // let mapped_what = r + // .what + // .iter() + // .map(|e| e.map_source(&f)) + // .collect::>>()?; + // ControlVecElement::Repeat(ControlVecElementRepeat { + // repeat: r.repeat.clone(), + // prefix: r.prefix.clone(), + // what: mapped_what, + // }) + // }), + // } + // } } // chatgpt @@ -361,6 +644,37 @@ where } } +impl GetLivecodeIdentifiers for DeserLazyControlVecElement +where + Source: Clone + Debug + GetLivecodeIdentifiers, +{ + fn variable_identifiers(&self) -> Vec { + match self { + DeserLazyControlVecElement::Single(c) => c.variable_identifiers(), + DeserLazyControlVecElement::Repeat(r) => r + .what + .iter() + .flat_map(|x| x.variable_identifiers()) + .collect::>() + .into_iter() + .collect_vec(), + } + } + + fn function_identifiers(&self) -> Vec { + match self { + DeserLazyControlVecElement::Single(c) => c.function_identifiers(), + DeserLazyControlVecElement::Repeat(r) => r + .what + .iter() + .flat_map(|x| x.function_identifiers()) + .collect::>() + .into_iter() + .collect_vec(), + } + } +} + // chatgpt // impl<'de, Sequencer, ControlSequencer, Source> Deserialize<'de> // for ControlVecElement @@ -408,3 +722,18 @@ where ))) } } + +impl crate::nestedit::NestEditable for LazyControlVecElement +where + LazyTarget: IsLazy, +{ + fn nest_update(&self, _mods: crate::nestedit::NestedMod) -> Self { + self.clone() + } + + fn nest_get(&self, _getter: &[&str]) -> LivecodeResult { + Err(LivecodeError::NestGetExtra( + "LazyControlVecElement".to_owned(), + )) + } +} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index cf56833..75ddf2a 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -13,8 +13,8 @@ pub struct BasicTypes { #[lerpable(func = "lerpify_vec2")] c_vec2: Vec2, something: Vec, - #[lerpable(func = "lerpify_vec_vec2")] - list_of_vec2: Vec, + // #[lerpable(func = "lerpify_vec_vec2")] + // list_of_vec2: Vec, // option_f32: Option, // option_vec2: Option, } @@ -45,104 +45,103 @@ pub struct BasicTypesWithDefaults { b: HashMap, } -#[derive(Debug, Clone, Livecode, Lerpable, Default)] -struct TestLazy { - #[lerpable(method = "skip")] - lazy: LazyBasicTypes, -} - -#[derive(Debug, Clone, Livecode, Lerpable, Default)] -enum EnumTest { - #[default] - A, - B(TestLazy), - C(#[lerpable(method = "skip")] LazyTestLazy), -} - -#[derive(Debug, Clone, Livecode, Lerpable, Default)] -struct TestNewType(Vec); - -#[derive(Debug, Clone, Livecode, Lerpable, Default)] -struct SequencerTest { - sequencer: SimpleSquareSequence, - ctx: AdditionalContextNode, - #[livecode(src = "sequencer", ctx = "ctx")] - node: UnitCells, - #[livecode(src = "sequencer", ctx = "ctx")] - #[lerpable(method = "skip")] - node_two: UnitCells, -} - -fn make_grid( - x: usize, - y: usize, - cell_size: Vec2, - offset_alternating: bool, -) -> Vec { - let x_usize = x; - let y_usize = y; - - (0..x_usize) - .flat_map(|x| { - let x_idx = IdxInRange::new(x, x_usize); - (0..y_usize).map(move |y| { - let y_idx = IdxInRange::new(y, y_usize); - let idx = IdxInRange2d::new_from_idx(x_idx, y_idx); - let ctx = UnitCellExprWorldContext::from_idx2d(idx, 1.0); - - let mut center = if offset_alternating { - let mut center = idx.to_alternating_i().center_of_cell(); - center += vec2(-0.5, 0.0); - if idx.i.i() % 2 == 1 { - center += vec2(0.5, 0.5); - } - - let offset_angle = AnglePi::new(1.0 / 6.0); - let diag_scale = offset_angle.to_norm_dir() * cell_size.x / (100.0 * 0.5); - - center *= diag_scale; - center - } else { - let mut center = idx.center_of_cell(); - center *= vec2(cell_size.x, cell_size.y) / 100.0; - center - }; - - center *= 100.0; - - let transform = SimpleTransform2d::new(vec![ - SimpleTransform2dStep::translate(center), - SimpleTransform2dStep::scale_both(cell_size.x / 100.0), - ]); - - UnitCellContext::new(ctx, transform) - }) - }) - .collect::>() -} - -#[derive(Clone, Debug, Default, Livecode, Lerpable)] -pub struct SimpleSquareSequence { - rows: usize, - cols: usize, - size: f32, -} -impl UnitCellCreator for SimpleSquareSequence { - fn to_unit_cell_ctxs(&self) -> Vec { - make_grid(self.cols, self.rows, vec2(self.size, self.size), false) - } -} - -// new type - -#[derive(Clone, Debug, Default, Livecode, Lerpable)] -pub struct NewTypeWithType(f32); - -#[derive(Clone, Debug, Default, Livecode, Lerpable)] -pub struct NewTypeWithVec(Vec); - - -#[derive(Clone, Debug, Default, LivecodeOnly)] -pub struct NewTypeWithStruct(BasicTypes); +// #[derive(Debug, Clone, Livecode, Lerpable, Default)] +// struct TestLazy { +// #[lerpable(method = "skip")] +// lazy: LazyBasicTypes, +// } + +// #[derive(Debug, Clone, Livecode, Lerpable, Default)] +// enum EnumTest { +// #[default] +// A, +// B(TestLazy), +// C(#[lerpable(method = "skip")] LazyTestLazy), +// } + +// #[derive(Debug, Clone, Livecode, Lerpable, Default)] +// struct TestNewType(Vec); + +// #[derive(Debug, Clone, Livecode, Lerpable, Default)] +// struct SequencerTest { +// sequencer: SimpleSquareSequence, +// ctx: AdditionalContextNode, +// #[livecode(src = "sequencer", ctx = "ctx")] +// node: UnitCells, +// #[livecode(src = "sequencer", ctx = "ctx")] +// #[lerpable(method = "skip")] +// node_two: UnitCells, +// } + +// fn make_grid( +// x: usize, +// y: usize, +// cell_size: Vec2, +// offset_alternating: bool, +// ) -> Vec { +// let x_usize = x; +// let y_usize = y; + +// (0..x_usize) +// .flat_map(|x| { +// let x_idx = IdxInRange::new(x, x_usize); +// (0..y_usize).map(move |y| { +// let y_idx = IdxInRange::new(y, y_usize); +// let idx = IdxInRange2d::new_from_idx(x_idx, y_idx); +// let ctx = UnitCellIdx::from_idx2d(idx, 1.0); + +// let mut center = if offset_alternating { +// let mut center = idx.to_alternating_i().center_of_cell(); +// center += vec2(-0.5, 0.0); +// if idx.i.i() % 2 == 1 { +// center += vec2(0.5, 0.5); +// } + +// let offset_angle = AnglePi::new(1.0 / 6.0); +// let diag_scale = offset_angle.to_norm_dir() * cell_size.x / (100.0 * 0.5); + +// center *= diag_scale; +// center +// } else { +// let mut center = idx.center_of_cell(); +// center *= vec2(cell_size.x, cell_size.y) / 100.0; +// center +// }; + +// center *= 100.0; + +// let transform = SimpleTransform2d::new(vec![ +// SimpleTransform2dStep::translate(center), +// SimpleTransform2dStep::scale_both(cell_size.x / 100.0), +// ]); + +// UnitCellContext::new(ctx, transform) +// }) +// }) +// .collect::>() +// } + +// #[derive(Clone, Debug, Default, Livecode, Lerpable)] +// pub struct SimpleSquareSequence { +// rows: usize, +// cols: usize, +// size: f32, +// } +// impl UnitCellCreator for SimpleSquareSequence { +// fn to_unit_cell_ctxs(&self) -> Vec { +// make_grid(self.cols, self.rows, vec2(self.size, self.size), false) +// } +// } + +// // new type + +// #[derive(Clone, Debug, Default, Livecode, Lerpable)] +// pub struct NewTypeWithType(f32); + +// #[derive(Clone, Debug, Default, Livecode, Lerpable)] +// pub struct NewTypeWithVec(Vec); + +// #[derive(Clone, Debug, Default, LivecodeOnly)] +// pub struct NewTypeWithStruct(BasicTypes); fn main() {} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index fe02adc..66fc076 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -479,7 +479,9 @@ impl GenFinal for FieldTokensLazy { let new_ty = match wrapper { VecDepth::NotAVec => unreachable!("huh, parsing a not-vec in the vec function"), // why is it in this function? - VecDepth::Vec => quote! {Vec<#internal_type>}, + VecDepth::Vec => { + quote! {Vec>} + } VecDepth::VecVec => todo!(), }; quote! {#back_to_quote #name: #new_ty} @@ -488,13 +490,43 @@ impl GenFinal for FieldTokensLazy { match how_to_control_internal { HowToControlThis::WithType(_, c) => { // local variable... - let x = syn::Ident::new("x", idents.name().span()); - let c = - LazyFieldType(*c).for_world_func(x, idents.data.f32min, idents.data.f32max); - quote! {#name: self.#name.iter().map(|x| #c).collect::, _>>()?} + let x_ident = syn::Ident::new("x", idents.name().span()); + let c_expr = LazyFieldType(*c).for_world_func( + x_ident.clone(), + idents.data.f32min, + idents.data.f32max, + ); + quote! { + #name: self.#name + .iter() + .map(|item| { + let expanded = item.eval_and_expand_vec(ctx)?; + expanded.into_iter() + .map(|#x_ident| #c_expr) + .collect::, _>>() + }) + .collect::, _>>()? + .into_iter() + .flatten() + .collect() + } } HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => { - quote! {#name: self.#name.iter().map(|x| x.eval_lazy(ctx)).collect::, _>>()?} + quote! { + #name: self.#name + .iter() + .map(|item| { + let expanded = item.eval_and_expand_vec(ctx)?; + expanded + .into_iter() + .map(|x| x.eval_lazy(ctx)) + .collect::, _>>() + }) + .collect::, _>>()? + .into_iter() + .flatten() + .collect() + } } HowToControlThis::WithNone(_) => { let target_type = parsed_type_info.internal_type(); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 6cedbb0..77cf8f8 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -623,7 +623,16 @@ impl GenFinal for FieldTokensLivecode { let how_to_control_internal = parsed_type_info.how_to_control_internal(); let wrapper = parsed_type_info.wrapper_type(); - let for_struct = { + // Special-case: when the Vec's internal type is itself a lazy + // struct (e.g. Vec> inside a + // Lazy* struct), we want to use DeserLazyControlVecElement as the + // control element type instead of ControlVecElement. + let inner_is_lazy_struct = parsed_type_info + .second_how_to + .map(|h| h.is_lazy()) + .unwrap_or(false); + + let for_struct: TokenStream2 = { let src_type = match how_to_control_internal { HowToControlThis::WithType(_, c) => LivecodeFieldType(*c).to_token(), HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => { @@ -643,13 +652,19 @@ impl GenFinal for FieldTokensLivecode { e => panic!("need vec something {:?}", e), }; + let vec_elem_type: TokenStream2 = if inner_is_lazy_struct { + quote! {murrelet_livecode::types::DeserLazyControlVecElement} + } else { + quote! {murrelet_livecode::types::ControlVecElement} + }; + let new_ty = match wrapper { VecDepth::NotAVec => unreachable!("not a vec in a vec?"), VecDepth::Vec => { - quote! { Vec> } + quote! { Vec<#vec_elem_type<#src_type>> } } VecDepth::VecVec => { - quote! { Vec>> } + quote! { Vec>> } } }; @@ -668,6 +683,12 @@ impl GenFinal for FieldTokensLivecode { .into_iter() .flatten() .collect()} + + // quote! { + // #name: self.#name.iter() + // .map(|elem| elem.o(|source| source.o(w))) + // .collect::, _>>()? + // } } VecDepth::VecVec => { quote! { @@ -691,18 +712,6 @@ impl GenFinal for FieldTokensLivecode { } else { quote! {#name: self.#name.clone()} } - - // match infer { - // HowToControlThis::WithType(_, _c) => { - // quote! {#name: self.#name.iter().map(|x| x.eval_and_expand_vec(w, #debug_name)).collect::, _>>()?.into_iter().flatten().collect()} - // } - // HowToControlThis::WithRecurse(_, _) => { - // quote! {#name: self.#name.iter().map(|x| x.eval_and_expand_vec(w, #debug_name)).collect::, _>>()?.into_iter().flatten().collect()} - // } - // HowToControlThis::WithNone(_) => { - // quote! {#name: self.#name.clone()} - // } - // } }; let for_to_control = { @@ -710,18 +719,45 @@ impl GenFinal for FieldTokensLivecode { match wrapper { VecDepth::NotAVec => unreachable!("not a vec in a vec?"), VecDepth::Vec => { - quote! { #name: self.#name.iter().map(|x| murrelet_livecode::types::ControlVecElement::raw(x.to_control())).collect::>() } + if inner_is_lazy_struct { + // For Vec> we convert + // each lazy element directly into its deser control + // representation. + quote! { #name: self.#name.iter().map(|x| x.to_control()).collect::>() } + } else { + quote! { #name: self.#name.iter().map(|x| murrelet_livecode::types::ControlVecElement::raw(x.to_control())).collect::>() } + } } VecDepth::VecVec => { - quote! { - #name: { - let mut result = Vec::with_capacity(self.#name.len()); - for internal_row in &self.#name { - result.push( - internal_row.iter().map(|x| murrelet_livecode::types::ControlVecElement::raw(x.to_control())).collect::>() - ) + if inner_is_lazy_struct { + quote! { + #name: { + let mut result = Vec::with_capacity(self.#name.len()); + for internal_row in &self.#name { + result.push( + internal_row + .iter() + .map(|x| x.to_control()) + .collect::>() + ) + } + result + } + } + } else { + quote! { + #name: { + let mut result = Vec::with_capacity(self.#name.len()); + for internal_row in &self.#name { + result.push( + internal_row + .iter() + .map(|x| murrelet_livecode::types::ControlVecElement::raw(x.to_control())) + .collect::>() + ) + } + result } - result } } } @@ -729,18 +765,6 @@ impl GenFinal for FieldTokensLivecode { } else { quote! {#name: self.#name.clone()} } - - // match infer { - // HowToControlThis::WithType(_, _c) => { - // quote! {#name: self.#name.iter().map(|x| murrelet_livecode::types::ControlVecElement::raw(x.to_control())).collect::>()} - // } - // HowToControlThis::WithRecurse(_, _) => { - // quote! {#name: self.#name.iter().map(|x| murrelet_livecode::types::ControlVecElement::raw(x.to_control())).collect::>()} - // } - // HowToControlThis::WithNone(_) => { - // quote! {#name: self.#name.clone()} - // } - // } }; let for_variable_idents = { From 6be0cff47353b4428ed4e5b4623f3b6157885c10 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Nov 2025 17:17:32 -0500 Subject: [PATCH 105/149] closer --- murrelet_livecode/src/types.rs | 57 ++++++++++++++++++- .../examples/tests.rs | 36 ++++++------ .../src/derive_lazy.rs | 16 +++--- 3 files changed, 80 insertions(+), 29 deletions(-) diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index bfbdfdd..06bca72 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -14,7 +14,6 @@ use crate::{ livecode::{ ControlF32, GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeToControl, LivecodeVariable, }, - nestedit::NestEditable, state::LivecodeWorldState, unitcells::UnitCellIdx, }; @@ -329,7 +328,10 @@ pub struct LazyVecElementRepeat { what: Vec>, } impl LazyVecElementRepeat { - pub fn eval_and_expand_vec(&self, ctx: &MixedEvalDefs) -> LivecodeResult> + pub fn lazy_eval_and_expand_vec( + &self, + ctx: &MixedEvalDefs, + ) -> LivecodeResult> where Source: IsLazy, { @@ -353,7 +355,7 @@ impl LazyVecElementRepeat { result.push(o); } LazyControlVecElement::Repeat(c) => { - let o = c.eval_and_expand_vec(&ctx)?; + let o = c.lazy_eval_and_expand_vec(&ctx)?; result.extend(o.into_iter()); } } @@ -565,6 +567,37 @@ where Repeat(LazyVecElementRepeat), } +impl LazyControlVecElement +where + Source: Clone + Debug + crate::lazy::IsLazy, +{ + pub fn eval_lazy_single(&self, expr: &MixedEvalDefs) -> LivecodeResult { + match self { + LazyControlVecElement::Single(s) => s.eval_lazy(expr).map(|x| x), + LazyControlVecElement::Repeat(s) => { + let vv = s.lazy_eval_and_expand_vec(expr)?; + vv.into_iter() + .next() + .ok_or(LivecodeError::raw("eval_lazy_single failed")) + } + } + } +} + +impl IsLazy for LazyControlVecElement +where + Source: Clone + Debug + crate::lazy::IsLazy, +{ + type Target = Vec; + + fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult { + match self { + LazyControlVecElement::Single(s) => s.eval_lazy(expr).map(|x| vec![x]), + LazyControlVecElement::Repeat(s) => s.lazy_eval_and_expand_vec(expr), + } + } +} + // impl ControlVecElement impl ControlVecElement where @@ -610,6 +643,24 @@ where // } } +impl LazyControlVecElement +where + LazyElement: Clone + Debug + IsLazy, +{ + pub fn lazy_eval_and_expand_vec( + &self, + ctx: &MixedEvalDefs, + ) -> LivecodeResult> + where + LazyElement: IsLazy, + { + match self { + LazyControlVecElement::Single(c) => Ok(vec![c.eval_lazy(ctx)?]), + LazyControlVecElement::Repeat(c) => c.lazy_eval_and_expand_vec(ctx), + } + } +} + // chatgpt #[cfg(feature = "schemars")] impl schemars::JsonSchema for ControlVecElement diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index 75ddf2a..19ccc73 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -9,9 +9,9 @@ use murrelet_livecode_derive::{Livecode, LivecodeOnly, NestEdit}; #[derive(Debug, Clone, Livecode, Lerpable, Default)] pub struct BasicTypes { a_number: f32, - b_color: MurreletColor, #[lerpable(func = "lerpify_vec2")] c_vec2: Vec2, + b_color: MurreletColor, something: Vec, // #[lerpable(func = "lerpify_vec_vec2")] // list_of_vec2: Vec, @@ -27,23 +27,23 @@ fn empty_string_lazy() -> String { String::new() } -#[derive(Debug, Clone, Livecode, Lerpable, Default)] -pub struct BasicTypesWithDefaults { - #[livecode(serde_default = "zeros")] - a_number: f32, - b_color: MurreletColor, - #[livecode(serde_default = "0")] - #[lerpable(func = "lerpify_vec2")] - c_vec2: Vec2, - something: Vec, - #[lerpable(func = "lerpify_vec_vec2")] - list_of_vec2: Vec, - #[livecode(kind = "none", serde_default = "empty_string")] - label: String, - #[livecode(kind = "none")] - #[lerpable(method = "skip")] - b: HashMap, -} +// #[derive(Debug, Clone, Livecode, Lerpable, Default)] +// pub struct BasicTypesWithDefaults { +// #[livecode(serde_default = "zeros")] +// a_number: f32, +// b_color: MurreletColor, +// #[livecode(serde_default = "0")] +// #[lerpable(func = "lerpify_vec2")] +// c_vec2: Vec2, +// something: Vec, +// #[lerpable(func = "lerpify_vec_vec2")] +// list_of_vec2: Vec, +// #[livecode(kind = "none", serde_default = "empty_string")] +// label: String, +// #[livecode(kind = "none")] +// #[lerpable(method = "skip")] +// b: HashMap, +// } // #[derive(Debug, Clone, Livecode, Lerpable, Default)] // struct TestLazy { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index 66fc076..71f5994 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -15,13 +15,13 @@ impl LazyFieldType { ControlType::Bool => quote! {murrelet_livecode::lazy::LazyNodeF32}, // we'll just check if it's above 0 ControlType::F32 => quote! {murrelet_livecode::lazy::LazyNodeF32}, ControlType::F32_2 => { - quote! {Vec} + quote! {Vec>} } ControlType::F32_3 => { - quote! {Vec} + quote! {Vec>} } ControlType::Color => { - quote! {Vec} + quote! {Vec>} } ControlType::LazyNodeF32 => { // already lazy... @@ -76,13 +76,13 @@ impl LazyFieldType { let orig_ty = idents.orig_ty(); match self.0 { ControlType::F32_2 => { - quote! { #name: glam::vec2(self.#name[0].eval_lazy(ctx)? as f32, self.#name[1].eval_lazy(ctx)? as f32)} + quote! { #name: glam::vec2(self.#name[0].eval_lazy_single(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)? as f32)} } ControlType::F32_3 => { - quote! {#name: glam::vec3(self.#name[0].eval_lazy(ctx)? as f32, self.#name[1].eval_lazy(ctx)? as f32, self.#name[2].eval_lazy(ctx)? as f32)} + quote! {#name: glam::vec3(self.#name[0].eval_lazy_single(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)? as f32, self.#name[2].eval_lazy(ctx)? as f32)} } ControlType::Color => { - quote! {#name: murrelet_common::MurreletColor::hsva(self.#name[0].eval_lazy(ctx)? as f32, self.#name[1].eval_lazy(ctx)? as f32, self.#name[2].eval_lazy(ctx)? as f32, self.#name[3].eval_lazy(ctx)? as f32)} + quote! {#name: murrelet_common::MurreletColor::hsva(self.#name[0].eval_lazy_single(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)? as f32, self.#name[2].eval_lazy_single(ctx)? as f32, self.#name[3].eval_lazy_single(ctx)? as f32)} } ControlType::Bool => quote! {#name: self.#name.eval_lazy(ctx)? > 0.0}, ControlType::LazyNodeF32 => quote! {#name: self.#name.add_more_defs(ctx)? }, @@ -500,7 +500,7 @@ impl GenFinal for FieldTokensLazy { #name: self.#name .iter() .map(|item| { - let expanded = item.eval_and_expand_vec(ctx)?; + let expanded = item.lazy_eval_and_expand_vec(ctx)?; expanded.into_iter() .map(|#x_ident| #c_expr) .collect::, _>>() @@ -516,7 +516,7 @@ impl GenFinal for FieldTokensLazy { #name: self.#name .iter() .map(|item| { - let expanded = item.eval_and_expand_vec(ctx)?; + let expanded = item.lazy_eval_and_expand_vec(ctx)?; expanded .into_iter() .map(|x| x.eval_lazy(ctx)) From 8b2c1590d51baf1b0e9b9c8beccc1b9fcdb94f65 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Nov 2025 18:11:17 -0500 Subject: [PATCH 106/149] wip --- murrelet_livecode/src/types.rs | 48 +++++++------------ .../examples/tests.rs | 6 +-- .../src/derive_lazy.rs | 10 ++-- .../src/derive_livecode.rs | 4 -- 4 files changed, 26 insertions(+), 42 deletions(-) diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 06bca72..221b807 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -328,13 +328,7 @@ pub struct LazyVecElementRepeat { what: Vec>, } impl LazyVecElementRepeat { - pub fn lazy_eval_and_expand_vec( - &self, - ctx: &MixedEvalDefs, - ) -> LivecodeResult> - where - Source: IsLazy, - { + pub fn lazy_expand_vec(&self, ctx: &MixedEvalDefs) -> LivecodeResult> { let mut result = Vec::with_capacity(self.repeat.len(ctx)? * self.what.len()); let prefix = if self.prefix.is_empty() { @@ -351,11 +345,11 @@ impl LazyVecElementRepeat { for src in &self.what { match src { LazyControlVecElement::Single(c) => { - let o = c.eval_lazy(&ctx)?; - result.push(o); + // let o = c.eval_lazy(&ctx)?; + result.push(c.clone()); } LazyControlVecElement::Repeat(c) => { - let o = c.lazy_eval_and_expand_vec(&ctx)?; + let o = c.lazy_expand_vec(&ctx)?; result.extend(o.into_iter()); } } @@ -567,15 +561,15 @@ where Repeat(LazyVecElementRepeat), } -impl LazyControlVecElement +impl LazyControlVecElement where - Source: Clone + Debug + crate::lazy::IsLazy, + Source: Clone + Debug + crate::lazy::IsLazy, { - pub fn eval_lazy_single(&self, expr: &MixedEvalDefs) -> LivecodeResult { + pub fn eval_lazy_single(&self, expr: &MixedEvalDefs) -> LivecodeResult { match self { - LazyControlVecElement::Single(s) => s.eval_lazy(expr).map(|x| x), + LazyControlVecElement::Single(s) => Ok(s.clone()), LazyControlVecElement::Repeat(s) => { - let vv = s.lazy_eval_and_expand_vec(expr)?; + let vv = s.lazy_expand_vec(expr)?; vv.into_iter() .next() .ok_or(LivecodeError::raw("eval_lazy_single failed")) @@ -584,16 +578,16 @@ where } } -impl IsLazy for LazyControlVecElement +impl LazyControlVecElement where - Source: Clone + Debug + crate::lazy::IsLazy, + Source: Clone + Debug + crate::lazy::IsLazy, { - type Target = Vec; + // type Target = Vec; - fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult { + fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult> { match self { - LazyControlVecElement::Single(s) => s.eval_lazy(expr).map(|x| vec![x]), - LazyControlVecElement::Repeat(s) => s.lazy_eval_and_expand_vec(expr), + LazyControlVecElement::Single(s) => Ok(vec![s.clone()]), + LazyControlVecElement::Repeat(s) => s.lazy_expand_vec(expr), } } } @@ -647,16 +641,10 @@ impl LazyControlVecElement where LazyElement: Clone + Debug + IsLazy, { - pub fn lazy_eval_and_expand_vec( - &self, - ctx: &MixedEvalDefs, - ) -> LivecodeResult> - where - LazyElement: IsLazy, - { + pub fn lazy_expand_vec(&self, ctx: &MixedEvalDefs) -> LivecodeResult> { match self { - LazyControlVecElement::Single(c) => Ok(vec![c.eval_lazy(ctx)?]), - LazyControlVecElement::Repeat(c) => c.lazy_eval_and_expand_vec(ctx), + LazyControlVecElement::Single(c) => Ok(vec![c.clone()]), + LazyControlVecElement::Repeat(c) => c.lazy_expand_vec(ctx), } } } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index 19ccc73..9d214cb 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -11,7 +11,7 @@ pub struct BasicTypes { a_number: f32, #[lerpable(func = "lerpify_vec2")] c_vec2: Vec2, - b_color: MurreletColor, + // b_color: MurreletColor, something: Vec, // #[lerpable(func = "lerpify_vec_vec2")] // list_of_vec2: Vec, @@ -36,8 +36,8 @@ fn empty_string_lazy() -> String { // #[lerpable(func = "lerpify_vec2")] // c_vec2: Vec2, // something: Vec, -// #[lerpable(func = "lerpify_vec_vec2")] -// list_of_vec2: Vec, +// // #[lerpable(func = "lerpify_vec_vec2")] +// // list_of_vec2: Vec, // #[livecode(kind = "none", serde_default = "empty_string")] // label: String, // #[livecode(kind = "none")] diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index 71f5994..dfcd383 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -76,13 +76,13 @@ impl LazyFieldType { let orig_ty = idents.orig_ty(); match self.0 { ControlType::F32_2 => { - quote! { #name: glam::vec2(self.#name[0].eval_lazy_single(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)? as f32)} + quote! { #name: glam::vec2(self.#name[0].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32)} } ControlType::F32_3 => { - quote! {#name: glam::vec3(self.#name[0].eval_lazy_single(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)? as f32, self.#name[2].eval_lazy(ctx)? as f32)} + quote! {#name: glam::vec3(self.#name[0].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[2].eval_lazy(ctx)? as f32)} } ControlType::Color => { - quote! {#name: murrelet_common::MurreletColor::hsva(self.#name[0].eval_lazy_single(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)? as f32, self.#name[2].eval_lazy_single(ctx)? as f32, self.#name[3].eval_lazy_single(ctx)? as f32)} + quote! {#name: murrelet_common::MurreletColor::hsva(self.#name[0].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[2].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[3].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32)} } ControlType::Bool => quote! {#name: self.#name.eval_lazy(ctx)? > 0.0}, ControlType::LazyNodeF32 => quote! {#name: self.#name.add_more_defs(ctx)? }, @@ -500,7 +500,7 @@ impl GenFinal for FieldTokensLazy { #name: self.#name .iter() .map(|item| { - let expanded = item.lazy_eval_and_expand_vec(ctx)?; + let expanded = item.lazy_expand_vec(ctx)?; expanded.into_iter() .map(|#x_ident| #c_expr) .collect::, _>>() @@ -516,7 +516,7 @@ impl GenFinal for FieldTokensLazy { #name: self.#name .iter() .map(|item| { - let expanded = item.lazy_eval_and_expand_vec(ctx)?; + let expanded = item.lazy_expand_vec(ctx)?; expanded .into_iter() .map(|x| x.eval_lazy(ctx)) diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 77cf8f8..000e14e 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -623,10 +623,6 @@ impl GenFinal for FieldTokensLivecode { let how_to_control_internal = parsed_type_info.how_to_control_internal(); let wrapper = parsed_type_info.wrapper_type(); - // Special-case: when the Vec's internal type is itself a lazy - // struct (e.g. Vec> inside a - // Lazy* struct), we want to use DeserLazyControlVecElement as the - // control element type instead of ControlVecElement. let inner_is_lazy_struct = parsed_type_info .second_how_to .map(|h| h.is_lazy()) From c4049f47ebf9212c4d8ca0598749544bd53248ba Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Nov 2025 22:22:51 -0500 Subject: [PATCH 107/149] back to vec2 --- murrelet_draw/src/style.rs | 16 +++++--- murrelet_livecode/src/livecode.rs | 21 +++++----- murrelet_livecode/src/types.rs | 25 ++++++++++++ .../examples/tests.rs | 27 +++++++------ .../src/derive_lazy.rs | 38 +++++++++++++------ .../src/derive_livecode.rs | 14 ++++++- .../murrelet_livecode_derive/src/parser.rs | 12 +++++- 7 files changed, 111 insertions(+), 42 deletions(-) diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index cea9367..424d88b 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -11,7 +11,11 @@ use lerpable::Lerpable; use md5::{Digest, Md5}; use murrelet_common::*; use murrelet_gui::{CanMakeGUI, MurreletGUI}; -use murrelet_livecode::{lazy::ControlLazyNodeF32, livecode::ControlF32, types::ControlVecElement}; +use murrelet_livecode::{ + lazy::ControlLazyNodeF32, + livecode::ControlF32, + types::{ControlVecElement, DeserLazyControlVecElement}, +}; use murrelet_livecode_derive::Livecode; use styleconf::StyleConf; @@ -24,12 +28,12 @@ fn _black() -> [ControlF32; 4] { ] } -fn _black_lazy() -> Vec> { +fn _black_lazy() -> Vec> { vec![ - ControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::Single(ControlLazyNodeF32::Float(1.0)), + DeserLazyControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), + DeserLazyControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), + DeserLazyControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), + DeserLazyControlVecElement::Single(ControlLazyNodeF32::Float(1.0)), ] } diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index 8bd92db..d701860 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -20,6 +20,7 @@ use crate::lazy::LazyNodeF32; use crate::state::LivecodeWorldState; use crate::types::AdditionalContextNode; use crate::types::ControlVecElement; +use crate::types::DeserLazyControlVecElement; use crate::types::LivecodeError; use crate::types::LivecodeResult; @@ -395,20 +396,20 @@ pub fn _auto_default_f32_1_lazy() -> ControlLazyNodeF32 { // this is to handle the Vec ones, which goes up to length 4 for color // and doesn't care if there are too many -pub fn _auto_default_f32_vec0_lazy() -> Vec> { +pub fn _auto_default_f32_vec0_lazy() -> Vec> { vec![ - ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), + DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), + DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), + DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), + DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), ] } -pub fn _auto_default_f32_vec1_lazy() -> Vec> { +pub fn _auto_default_f32_vec1_lazy() -> Vec> { vec![ - ControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), - ControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), - ControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), - ControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), + DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), + DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), + DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), + DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), ] } diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 221b807..ccddc6e 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -208,6 +208,15 @@ where Single(Source), Repeat(DeserLazyControlVecElementRepeat), } + +impl DeserLazyControlVecElement +where + Source: Clone + Debug, +{ + pub fn raw(c: Source) -> Self { + Self::Single(c) + } +} impl DeserLazyControlVecElement { pub fn o( &self, @@ -561,6 +570,22 @@ where Repeat(LazyVecElementRepeat), } +impl IsLazy for LazyControlVecElement +where + Source: Clone + Debug + IsLazy, +{ + // A single lazy control element expands to a vector of evaluated items. + type Target = Vec; + + fn eval_lazy(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + // First expand structure (repeats) to Vec, still lazy + let expanded: Vec = self.lazy_expand_vec(ctx)?; + + // Then evaluate each inner lazy to its final type + expanded.into_iter().map(|s| s.eval_lazy(ctx)).collect() + } +} + impl LazyControlVecElement where Source: Clone + Debug + crate::lazy::IsLazy, diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index 9d214cb..92239c8 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -11,21 +11,23 @@ pub struct BasicTypes { a_number: f32, #[lerpable(func = "lerpify_vec2")] c_vec2: Vec2, - // b_color: MurreletColor, + #[lerpable(func = "lerpify_vec3")] + c_vec3: Vec3, + b_color: MurreletColor, something: Vec, - // #[lerpable(func = "lerpify_vec_vec2")] - // list_of_vec2: Vec, + #[lerpable(func = "lerpify_vec_vec2")] + list_of_vec2: Vec, // option_f32: Option, // option_vec2: Option, } -fn empty_string() -> String { - String::new() -} +// fn empty_string() -> String { +// String::new() +// } -fn empty_string_lazy() -> String { - String::new() -} +// fn empty_string_lazy() -> String { +// String::new() +// } // #[derive(Debug, Clone, Livecode, Lerpable, Default)] // pub struct BasicTypesWithDefaults { @@ -36,8 +38,8 @@ fn empty_string_lazy() -> String { // #[lerpable(func = "lerpify_vec2")] // c_vec2: Vec2, // something: Vec, -// // #[lerpable(func = "lerpify_vec_vec2")] -// // list_of_vec2: Vec, +// // // #[lerpable(func = "lerpify_vec_vec2")] +// // // list_of_vec2: Vec, // #[livecode(kind = "none", serde_default = "empty_string")] // label: String, // #[livecode(kind = "none")] @@ -138,6 +140,9 @@ fn empty_string_lazy() -> String { // #[derive(Clone, Debug, Default, Livecode, Lerpable)] // pub struct NewTypeWithType(f32); +// #[derive(Clone, Debug, Default, Livecode, Lerpable)] +// pub struct NewTypeWithTypeVec2(#[lerpable(func = "lerpify_vec2")] Vec2); + // #[derive(Clone, Debug, Default, Livecode, Lerpable)] // pub struct NewTypeWithVec(Vec); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index dfcd383..c954054 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -15,13 +15,13 @@ impl LazyFieldType { ControlType::Bool => quote! {murrelet_livecode::lazy::LazyNodeF32}, // we'll just check if it's above 0 ControlType::F32 => quote! {murrelet_livecode::lazy::LazyNodeF32}, ControlType::F32_2 => { - quote! {Vec>} + quote! {Vec} } ControlType::F32_3 => { - quote! {Vec>} + quote! {Vec} } ControlType::Color => { - quote! {Vec>} + quote! {Vec} } ControlType::LazyNodeF32 => { // already lazy... @@ -43,13 +43,13 @@ impl LazyFieldType { ) -> TokenStream2 { match self.0 { ControlType::F32_2 => { - quote! { murrelet_livecode::lazy::eval_lazy_vec2(#ident, ctx) } + quote! { murrelet_livecode::lazy::eval_lazy_vec2(&#ident, ctx) } } ControlType::F32_3 => { - quote! { murrelet_livecode::lazy::eval_lazy_vec3(#ident, ctx) } + quote! { murrelet_livecode::lazy::eval_lazy_vec3(&#ident, ctx) } } ControlType::Color => { - quote! { murrelet_livecode::lazy::eval_lazy_color(#ident, ctx) } + quote! { murrelet_livecode::lazy::eval_lazy_color(&#ident, ctx) } } ControlType::Bool => quote! {#ident.eval_lazy(ctx)? > 0.0}, ControlType::AnglePi => { @@ -76,13 +76,27 @@ impl LazyFieldType { let orig_ty = idents.orig_ty(); match self.0 { ControlType::F32_2 => { - quote! { #name: glam::vec2(self.#name[0].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32)} + quote! { #name: glam::vec2( + self.#name[0].eval_lazy(ctx)? as f32, + self.#name[1].eval_lazy(ctx)? as f32 + )} } ControlType::F32_3 => { - quote! {#name: glam::vec3(self.#name[0].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[2].eval_lazy(ctx)? as f32)} + quote! { + #name: glam::vec3( + self.#name[0].eval_lazy(ctx)? as f32, + self.#name[1].eval_lazy(ctx)? as f32, + self.#name[2].eval_lazy(ctx)? as f32 + ) + } } ControlType::Color => { - quote! {#name: murrelet_common::MurreletColor::hsva(self.#name[0].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[1].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[2].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32, self.#name[3].eval_lazy_single(ctx)?.eval_lazy(ctx)? as f32)} + quote! {#name: murrelet_common::MurreletColor::hsva( + self.#name[0].eval_lazy(ctx)? as f32, + self.#name[1].eval_lazy(ctx)? as f32, + self.#name[2].eval_lazy(ctx)? as f32, + self.#name[3].eval_lazy(ctx)? as f32 + )} } ControlType::Bool => quote! {#name: self.#name.eval_lazy(ctx)? > 0.0}, ControlType::LazyNodeF32 => quote! {#name: self.#name.add_more_defs(ctx)? }, @@ -474,7 +488,7 @@ impl GenFinal for FieldTokensLazy { let name = Self::new_ident(target_type.clone()); quote! {#name} } - e => panic!("need vec something {:?}", e), + e => panic!("lazy1 need vec something {:?}", e), }; let new_ty = match wrapper { @@ -533,7 +547,7 @@ impl GenFinal for FieldTokensLazy { let name = Self::new_ident(target_type.clone()); quote! {#name: self.#name.clone()} } - e => panic!("need vec something {:?}", e), + e => panic!("lazy2 need vec something {:?}", e), } // match wrapper { @@ -570,7 +584,7 @@ impl GenFinal for FieldTokensLazy { let name = Self::new_ident(internal_type); quote! {#name} } - e => panic!("need vec something {:?}", e), + e => panic!("lazy3 need vec something {:?}", e), }; quote! {Vec<#new_ty>} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 000e14e..47dda83 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -641,11 +641,18 @@ impl GenFinal for FieldTokensLivecode { let name = Self::new_ident(original_internal_type.clone()); quote! {#name} } + // HowToControlThis::WithRecurse(_, RecursiveControlType::Vec) => { + // // for things like Lazy Vec2... + // println!("parsed_type_info {:?}", parsed_type_info); + // let original_internal_type = parsed_type_info.internal_type(); + // let lazy_inner = Self::new_ident(original_internal_type.clone()); + // quote! { Vec<#lazy_inner> } + // } HowToControlThis::WithNone(_) => { let target_type = parsed_type_info.internal_type(); quote! {#target_type} } - e => panic!("need vec something {:?}", e), + e => panic!("(livecode, recurse_struct_vec) need vec something {:?}", e), }; let vec_elem_type: TokenStream2 = if inner_is_lazy_struct { @@ -995,7 +1002,10 @@ impl GenFinal for FieldTokensLivecode { let original_internal_type = parsed_type_info.internal_type(); quote! {#original_internal_type} } - e => panic!("need vec something {:?}", e), + e => panic!( + "(livecode, newtype_recurse_struct_vec) need vec something {:?}", + e + ), }; let new_ty = match wrapper { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index 8cfb571..8572d34 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -757,10 +757,12 @@ pub(crate) struct DataFromType { pub(crate) main_type: syn::Ident, pub(crate) second_type: Option, pub(crate) third_type: Option, // so we coulddd use a vec her + pub(crate) fourth_type: Option, // so we coulddd use a vec her pub(crate) main_how_to: HowToControlThis, pub(crate) second_how_to: Option, pub(crate) third_how_to: Option, // so we coulddd use a vec her + pub(crate) fourth_how_to: Option, // so we coulddd use a vec her } impl DataFromType { fn new_from_list(types: Vec) -> DataFromType { @@ -769,6 +771,7 @@ impl DataFromType { let main_type = types[0].clone(); let second_type = types.get(1).cloned(); let third_type = types.get(2).cloned(); + let fourth_type = types.get(3).cloned(); let main_how_to = HowToControlThis::from_type_str(&main_type.to_string()); let second_how_to = second_type @@ -777,19 +780,26 @@ impl DataFromType { let third_how_to = third_type .as_ref() .map(|x| HowToControlThis::from_type_str(&x.to_string())); + let fourth_how_to = fourth_type + .as_ref() + .map(|x| HowToControlThis::from_type_str(&x.to_string())); Self { main_type, second_type, third_type, + fourth_type, main_how_to, second_how_to, third_how_to, + fourth_how_to } } pub(crate) fn how_to_control_internal(&self) -> &HowToControlThis { - if let Some(third) = &self.third_how_to { + if let Some(fourth) = &self.fourth_how_to { + fourth + } else if let Some(third) = &self.third_how_to { third } else if let Some(second) = &self.second_how_to { second From 2af694f6b7151c53da9ef8df5d379fe4c9841c42 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Nov 2025 23:08:10 -0500 Subject: [PATCH 108/149] bit by bit --- murrelet_livecode/src/livecode.rs | 10 ++ .../examples/tests.rs | 14 +- .../src/derive_lazy.rs | 5 + .../src/derive_livecode.rs | 142 +++++++++++++++++- .../murrelet_livecode_derive/src/parser.rs | 19 ++- 5 files changed, 181 insertions(+), 9 deletions(-) diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index d701860..d39a3f1 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -67,6 +67,16 @@ impl LivecodeFromWorld for [ControlF32; 4] { } } +impl LivecodeFromWorld> for Vec +where + Source: LivecodeFromWorld, +{ + fn o(&self, w: &LivecodeWorldState) -> LivecodeResult> { + self.iter().map(|x| x.o(w)).collect::, _>>() + } + +} + pub trait LivecodeToControl { fn to_control(&self) -> ControlT; } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index 92239c8..65eecb2 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -8,13 +8,13 @@ use murrelet_livecode_derive::{Livecode, LivecodeOnly, NestEdit}; #[derive(Debug, Clone, Livecode, Lerpable, Default)] pub struct BasicTypes { - a_number: f32, - #[lerpable(func = "lerpify_vec2")] - c_vec2: Vec2, - #[lerpable(func = "lerpify_vec3")] - c_vec3: Vec3, - b_color: MurreletColor, - something: Vec, + // a_number: f32, + // #[lerpable(func = "lerpify_vec2")] + // c_vec2: Vec2, + // #[lerpable(func = "lerpify_vec3")] + // c_vec3: Vec3, + // b_color: MurreletColor, + // something: Vec, #[lerpable(func = "lerpify_vec_vec2")] list_of_vec2: Vec, // option_f32: Option, diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index c954054..0b38797 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -497,6 +497,11 @@ impl GenFinal for FieldTokensLazy { quote! {Vec>} } VecDepth::VecVec => todo!(), + VecDepth::VecControlVec => { + quote! { Vec>> } + + }, + }; quote! {#back_to_quote #name: #new_ty} }; diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 47dda83..a0de136 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -7,6 +7,7 @@ pub(crate) fn update_to_control_ident(name: syn::Ident) -> syn::Ident { prefix_ident("Control", name) } +#[derive(Debug)] pub(crate) struct LivecodeFieldType(pub ControlType); impl LivecodeFieldType { @@ -24,6 +25,24 @@ impl LivecodeFieldType { } } + pub fn to_token_lazy(&self) -> TokenStream2 { + println!("laz"); + println!("self {:?}", self.0); + match self.0 { + ControlType::F32_2 => quote! { Vec }, + ControlType::F32_3 => quote! { Vec }, + ControlType::Color => quote! { Vec }, + ControlType::ColorUnclamped => { + quote! { Vec } + } + ControlType::F32 => quote! {murrelet_livecode::livecode::ControlF32}, + ControlType::Bool => quote! {murrelet_livecode::livecode::ControlBool}, + ControlType::AnglePi => quote! {murrelet_livecode::livecode::ControlF32}, + // ControlType::EvalExpr => quote! {murrelet_livecode::expr::ControlExprF32}, + ControlType::LazyNodeF32 => quote! {murrelet_livecode::lazy::ControlLazyNodeF32}, + } + } + // usually can call for_world directly, but this is useful in Vec<> pub(crate) fn for_world_no_name( &self, @@ -615,6 +634,7 @@ impl GenFinal for FieldTokensLivecode { // Vec, Vec fn from_recurse_struct_vec(idents: StructIdents) -> FieldTokensLivecode { + println!("idents {:?}", idents); let serde = idents.serde(); let name = idents.name(); let orig_ty = idents.orig_ty(); @@ -622,21 +642,34 @@ impl GenFinal for FieldTokensLivecode { let parsed_type_info = ident_from_type(&orig_ty); let how_to_control_internal = parsed_type_info.how_to_control_internal(); let wrapper = parsed_type_info.wrapper_type(); + println!("wrapper {:?}", wrapper); let inner_is_lazy_struct = parsed_type_info .second_how_to .map(|h| h.is_lazy()) .unwrap_or(false); + println!("inner_is_lazy_struct {:?}", inner_is_lazy_struct); + let for_struct: TokenStream2 = { let src_type = match how_to_control_internal { - HowToControlThis::WithType(_, c) => LivecodeFieldType(*c).to_token(), + HowToControlThis::WithType(_, c) => { + println!("c"); + if inner_is_lazy_struct { + LivecodeFieldType(*c).to_token_lazy() + } else { + LivecodeFieldType(*c).to_token() + } + } HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => { + println!("rs"); let target_type = parsed_type_info.internal_type(); let name = Self::new_ident(target_type.clone()); quote! {#name} } HowToControlThis::WithRecurse(_, RecursiveControlType::StructLazy) => { + println!("rsl"); + let original_internal_type = parsed_type_info.internal_type(); let name = Self::new_ident(original_internal_type.clone()); quote! {#name} @@ -649,12 +682,16 @@ impl GenFinal for FieldTokensLivecode { // quote! { Vec<#lazy_inner> } // } HowToControlThis::WithNone(_) => { + + let target_type = parsed_type_info.internal_type(); quote! {#target_type} } e => panic!("(livecode, recurse_struct_vec) need vec something {:?}", e), }; + println!("src_type {:?}", src_type); + let vec_elem_type: TokenStream2 = if inner_is_lazy_struct { quote! {murrelet_livecode::types::DeserLazyControlVecElement} } else { @@ -669,6 +706,10 @@ impl GenFinal for FieldTokensLivecode { VecDepth::VecVec => { quote! { Vec>> } } + VecDepth::VecControlVec => { + quote! { Vec<#vec_elem_type>> } + + }, }; quote! {#serde #name: #new_ty} @@ -711,6 +752,27 @@ impl GenFinal for FieldTokensLivecode { } } } + + VecDepth::VecControlVec => { + println!("HHHHHHH"); + quote! { + #name: { + let mut result = Vec::with_capacity(self.#name.len()); + for internal_row in &self.#name { + result.push( + internal_row.eval_and_expand_vec(w) + // internal_row.iter() + // .map(|x| x.eval_and_expand_vec(w)) + // .collect::, _>>()? + // .into_iter() + // .flatten() + // .collect() + ) + } + result + } + } + } } } else { quote! {#name: self.#name.clone()} @@ -764,6 +826,41 @@ impl GenFinal for FieldTokensLivecode { } } } + VecDepth::VecControlVec => { + if inner_is_lazy_struct { + println!("HHH"); + + quote! { + #name: { + let mut result = Vec::with_capacity(self.#name.len()); + for internal_row in &self.#name { + result.push( + internal_row + .iter() + .map(|x| x.to_control()) + .collect::>() + ) + } + result + } + } + } else { + quote! { + #name: { + let mut result = Vec::with_capacity(self.#name.len()); + for internal_row in &self.#name { + result.push( + internal_row + .iter() + .map(|x| murrelet_livecode::types::ControlVecElement::raw(x.to_control())) + .collect::>() + ) + } + result + } + } + } + } } } else { quote! {#name: self.#name.clone()} @@ -790,6 +887,21 @@ impl GenFinal for FieldTokensLivecode { } } } + VecDepth::VecControlVec => { + println!("HHHHH"); + + quote! { + { + let mut result = Vec::with_capacity(self.#name.len()); + for internal_row in &self.#name { + result.extend( + internal_row.iter().map(|x| x.variable_identifiers()).into_iter().flatten().collect::>().into_iter() + ); + } + result + } + } + } } } else { quote! {vec![]} @@ -816,6 +928,19 @@ impl GenFinal for FieldTokensLivecode { } } } + VecDepth::VecControlVec => { + quote! { + { + let mut result = Vec::with_capacity(self.#name.len()); + for internal_row in &self.#name { + result.extend( + internal_row.iter().map(|x| x.function_identifiers()).into_iter().flatten().collect::>().into_iter() + ); + } + result + } + } + } } } else { quote! {vec![]} @@ -1008,10 +1133,24 @@ impl GenFinal for FieldTokensLivecode { ), }; + let inner_is_lazy_struct = parsed_type_info + .second_how_to + .map(|h| h.is_lazy()) + .unwrap_or(false); + + + let vec_elem_type: TokenStream2 = if inner_is_lazy_struct { + quote! {murrelet_livecode::types::DeserLazyControlVecElement} + } else { + quote! {murrelet_livecode::types::ControlVecElement} + }; + let new_ty = match wrapper { VecDepth::NotAVec => unreachable!("huh, parsing a not-vec in the vec function"), // why is it in this function? VecDepth::Vec => quote! {Vec<#internal_type>}, VecDepth::VecVec => quote! {Vec>}, + VecDepth::VecControlVec => quote! { Vec<#vec_elem_type>> } +, }; quote! {#serde #new_ty} }; @@ -1023,6 +1162,7 @@ impl GenFinal for FieldTokensLivecode { quote! {self.0.iter().map(|x| x.o(w)).collect::, _>>()?} } VecDepth::VecVec => unimplemented!(), + VecDepth::VecControlVec => unimplemented!() } } else { quote! {self.0.clone()} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index 8572d34..f4c3848 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -826,7 +826,22 @@ impl DataFromType { Some(HowToControlThis::WithRecurse(_, RecursiveControlType::Vec)) => { VecDepth::VecVec } - Some(_) => VecDepth::Vec, + Some(_) => { + + let s = self.second_type.as_ref().map(|x| x.to_string()).clone(); + if s.unwrap().starts_with("LazyControlVecElement") { + VecDepth::VecControlVec + } else if matches!(self.third_how_to, Some(HowToControlThis::WithRecurse(_, RecursiveControlType::Vec))) { + VecDepth::VecControlVec + } else { + + + println!("self.second {:?}", self.second_type); + + VecDepth::Vec + } + + }, None => unreachable!("vec should have a type??"), } } @@ -835,9 +850,11 @@ impl DataFromType { } } +#[derive(Debug)] pub(crate) enum VecDepth { NotAVec, Vec, + VecControlVec, // nested vec, but outside is a control vec VecVec, } From 0163cbdf8bb4c97693b3e3f523415a1d5be3330e Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Nov 2025 23:14:49 -0500 Subject: [PATCH 109/149] mwip --- murrelet_livecode/src/types.rs | 13 ------------- .../murrelet_livecode_derive/src/derive_livecode.rs | 6 +++++- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index ccddc6e..08e6201 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -232,19 +232,6 @@ impl DeserLazyControlVecElement { }; Ok(a) } - - // just to match the interface used by the derive macros for Vec fields - pub fn eval_and_expand_vec( - &self, - w: &LivecodeWorldState, - ) -> LivecodeResult>> - where - Source: LivecodeFromWorld, - Target: IsLazy, - { - let a = self.o(w)?; - Ok(vec![a]) - } } // chatgpt diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index a0de136..acc0bc0 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -759,8 +759,12 @@ impl GenFinal for FieldTokensLivecode { #name: { let mut result = Vec::with_capacity(self.#name.len()); for internal_row in &self.#name { + + // Vec + let item = internal_row.o(w)?; + result.push( - internal_row.eval_and_expand_vec(w) + item // internal_row.iter() // .map(|x| x.eval_and_expand_vec(w)) // .collect::, _>>()? From bc0d5a4d6fb11b23e5ef624eb831f96041668727 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Nov 2025 23:31:06 -0500 Subject: [PATCH 110/149] it might work --- murrelet_livecode/src/livecode.rs | 16 +++++ .../src/derive_livecode.rs | 58 +++++++++---------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index d39a3f1..d1a7f8d 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -157,6 +157,12 @@ impl LivecodeToControl for u64 { } } +impl LivecodeToControl> for Vec where Source: LivecodeToControl { + fn to_control(&self) -> Vec { + self.iter().map(|x| x.to_control()).collect_vec() + } +} + impl LivecodeToControl for LazyNodeF32 { fn to_control(&self) -> ControlLazyNodeF32 { ControlLazyNodeF32::new(self.n().cloned().unwrap()) @@ -316,6 +322,16 @@ impl GetLivecodeIdentifiers for [ControlF32; 4] { } } +impl GetLivecodeIdentifiers for Vec where T: GetLivecodeIdentifiers { + fn variable_identifiers(&self) -> Vec { + self.iter().map(|x| x.variable_identifiers()).flatten().collect_vec() + } + + fn function_identifiers(&self) -> Vec { + self.iter().map(|x| x.function_identifiers()).flatten().collect_vec() + } +} + impl GetLivecodeIdentifiers for String { fn variable_identifiers(&self) -> Vec { vec![] diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index acc0bc0..dc87055 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -759,19 +759,9 @@ impl GenFinal for FieldTokensLivecode { #name: { let mut result = Vec::with_capacity(self.#name.len()); for internal_row in &self.#name { - - // Vec + // DeserLazyControlVecElement -> LazyControlVecElement let item = internal_row.o(w)?; - - result.push( - item - // internal_row.iter() - // .map(|x| x.eval_and_expand_vec(w)) - // .collect::, _>>()? - // .into_iter() - // .flatten() - // .collect() - ) + result.push(item) } result } @@ -789,10 +779,8 @@ impl GenFinal for FieldTokensLivecode { VecDepth::NotAVec => unreachable!("not a vec in a vec?"), VecDepth::Vec => { if inner_is_lazy_struct { - // For Vec> we convert - // each lazy element directly into its deser control - // representation. - quote! { #name: self.#name.iter().map(|x| x.to_control()).collect::>() } + + quote! { #name: self.#name.iter().map(|x| murrelet_livecode::types::DeserLazyControlVecElement::raw(x.to_control())).collect::>() } } else { quote! { #name: self.#name.iter().map(|x| murrelet_livecode::types::ControlVecElement::raw(x.to_control())).collect::>() } } @@ -803,12 +791,16 @@ impl GenFinal for FieldTokensLivecode { #name: { let mut result = Vec::with_capacity(self.#name.len()); for internal_row in &self.#name { - result.push( - internal_row - .iter() - .map(|x| x.to_control()) - .collect::>() - ) + + let item = internal_row.to_control(); + + + result.push(murrelet_livecode::types::DeserLazyControlVecElement::raw(item)) + // internal_row + // .iter() + // .map(|x| x.to_control()) + // .collect::>() + // ) } result } @@ -832,17 +824,18 @@ impl GenFinal for FieldTokensLivecode { } VecDepth::VecControlVec => { if inner_is_lazy_struct { - println!("HHH"); + println!("HHaH"); quote! { #name: { let mut result = Vec::with_capacity(self.#name.len()); for internal_row in &self.#name { - result.push( - internal_row - .iter() - .map(|x| x.to_control()) - .collect::>() + let c = internal_row.to_control(); + result.push(c + // internal_row + // .iter() + // .map(|x| x.to_control()) + // .collect::>() ) } result @@ -898,8 +891,10 @@ impl GenFinal for FieldTokensLivecode { { let mut result = Vec::with_capacity(self.#name.len()); for internal_row in &self.#name { + let items = internal_row.variable_identifiers(); result.extend( - internal_row.iter().map(|x| x.variable_identifiers()).into_iter().flatten().collect::>().into_iter() + items + // internal_row.iter().map(|x| x.variable_identifiers()).into_iter().flatten().collect::>().into_iter() ); } result @@ -937,8 +932,9 @@ impl GenFinal for FieldTokensLivecode { { let mut result = Vec::with_capacity(self.#name.len()); for internal_row in &self.#name { - result.extend( - internal_row.iter().map(|x| x.function_identifiers()).into_iter().flatten().collect::>().into_iter() + let item = internal_row.function_identifiers(); + result.extend(item + // internal_row.iter().map(|x| x.function_identifiers()).into_iter().flatten().collect::>().into_iter() ); } result From 26cb955180f56f7b61fe5242423164872fb55e27 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Nov 2025 23:50:20 -0500 Subject: [PATCH 111/149] wip --- murrelet_livecode/src/livecode.rs | 20 ++++----- .../examples/tests.rs | 44 +++++++++---------- .../src/derive_livecode.rs | 32 ++++++++------ .../murrelet_livecode_derive/src/parser.rs | 6 +-- 4 files changed, 53 insertions(+), 49 deletions(-) diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index d1a7f8d..f9e27d7 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -422,20 +422,20 @@ pub fn _auto_default_f32_1_lazy() -> ControlLazyNodeF32 { // this is to handle the Vec ones, which goes up to length 4 for color // and doesn't care if there are too many -pub fn _auto_default_f32_vec0_lazy() -> Vec> { +pub fn _auto_default_f32_vec0_lazy() -> Vec> { vec![ - DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), - DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), - DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), - DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), + ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), + ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), + ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), + ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), ] } -pub fn _auto_default_f32_vec1_lazy() -> Vec> { +pub fn _auto_default_f32_vec1_lazy() -> Vec> { vec![ - DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), - DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), - DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), - DeserLazyControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), + ControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), + ControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), + ControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), + ControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), ] } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index 65eecb2..d1f97a5 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -8,35 +8,35 @@ use murrelet_livecode_derive::{Livecode, LivecodeOnly, NestEdit}; #[derive(Debug, Clone, Livecode, Lerpable, Default)] pub struct BasicTypes { - // a_number: f32, - // #[lerpable(func = "lerpify_vec2")] - // c_vec2: Vec2, - // #[lerpable(func = "lerpify_vec3")] - // c_vec3: Vec3, - // b_color: MurreletColor, - // something: Vec, + a_number: f32, + #[lerpable(func = "lerpify_vec2")] + c_vec2: Vec2, + #[lerpable(func = "lerpify_vec3")] + c_vec3: Vec3, + b_color: MurreletColor, + something: Vec, #[lerpable(func = "lerpify_vec_vec2")] list_of_vec2: Vec, // option_f32: Option, // option_vec2: Option, } -// fn empty_string() -> String { -// String::new() -// } +fn empty_string() -> String { + String::new() +} -// fn empty_string_lazy() -> String { -// String::new() -// } +fn empty_string_lazy() -> String { + String::new() +} -// #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// pub struct BasicTypesWithDefaults { -// #[livecode(serde_default = "zeros")] -// a_number: f32, -// b_color: MurreletColor, -// #[livecode(serde_default = "0")] -// #[lerpable(func = "lerpify_vec2")] -// c_vec2: Vec2, +#[derive(Debug, Clone, Livecode, Lerpable, Default)] +pub struct BasicTypesWithDefaults { + #[livecode(serde_default = "zeros")] + a_number: f32, + b_color: MurreletColor, + #[livecode(serde_default = "0")] + #[lerpable(func = "lerpify_vec2")] + c_vec2_serde_default: Vec2, // something: Vec, // // // #[lerpable(func = "lerpify_vec_vec2")] // // // list_of_vec2: Vec, @@ -45,7 +45,7 @@ pub struct BasicTypes { // #[livecode(kind = "none")] // #[lerpable(method = "skip")] // b: HashMap, -// } +} // #[derive(Debug, Clone, Livecode, Lerpable, Default)] // struct TestLazy { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index dc87055..fc6fc98 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -682,8 +682,6 @@ impl GenFinal for FieldTokensLivecode { // quote! { Vec<#lazy_inner> } // } HowToControlThis::WithNone(_) => { - - let target_type = parsed_type_info.internal_type(); quote! {#target_type} } @@ -708,8 +706,7 @@ impl GenFinal for FieldTokensLivecode { } VecDepth::VecControlVec => { quote! { Vec<#vec_elem_type>> } - - }, + } }; quote! {#serde #name: #new_ty} @@ -720,13 +717,23 @@ impl GenFinal for FieldTokensLivecode { match wrapper { VecDepth::NotAVec => unreachable!("not a vec in a vec?"), VecDepth::Vec => { - quote! { - #name: self.#name.iter() + // println!("HIII"); + if inner_is_lazy_struct { + quote! { + #name: self.#name.iter() + .map(|x| x.o(w)) + .collect::, _>>()? + + } + } else { + quote! { + #name: self.#name.iter() .map(|x| x.eval_and_expand_vec(w)) .collect::, _>>()? .into_iter() .flatten() .collect()} + } // quote! { // #name: self.#name.iter() @@ -779,8 +786,9 @@ impl GenFinal for FieldTokensLivecode { VecDepth::NotAVec => unreachable!("not a vec in a vec?"), VecDepth::Vec => { if inner_is_lazy_struct { + println!("SSS"); - quote! { #name: self.#name.iter().map(|x| murrelet_livecode::types::DeserLazyControlVecElement::raw(x.to_control())).collect::>() } + quote! { #name: self.#name.iter().map(|x| x.to_control()).collect::>() } } else { quote! { #name: self.#name.iter().map(|x| murrelet_livecode::types::ControlVecElement::raw(x.to_control())).collect::>() } } @@ -824,7 +832,7 @@ impl GenFinal for FieldTokensLivecode { } VecDepth::VecControlVec => { if inner_is_lazy_struct { - println!("HHaH"); + println!("HHaH"); quote! { #name: { @@ -885,7 +893,7 @@ impl GenFinal for FieldTokensLivecode { } } VecDepth::VecControlVec => { - println!("HHHHH"); + println!("HHHHH"); quote! { { @@ -1138,7 +1146,6 @@ impl GenFinal for FieldTokensLivecode { .map(|h| h.is_lazy()) .unwrap_or(false); - let vec_elem_type: TokenStream2 = if inner_is_lazy_struct { quote! {murrelet_livecode::types::DeserLazyControlVecElement} } else { @@ -1149,8 +1156,7 @@ impl GenFinal for FieldTokensLivecode { VecDepth::NotAVec => unreachable!("huh, parsing a not-vec in the vec function"), // why is it in this function? VecDepth::Vec => quote! {Vec<#internal_type>}, VecDepth::VecVec => quote! {Vec>}, - VecDepth::VecControlVec => quote! { Vec<#vec_elem_type>> } -, + VecDepth::VecControlVec => quote! { Vec<#vec_elem_type>> }, }; quote! {#serde #new_ty} }; @@ -1162,7 +1168,7 @@ impl GenFinal for FieldTokensLivecode { quote! {self.0.iter().map(|x| x.o(w)).collect::, _>>()?} } VecDepth::VecVec => unimplemented!(), - VecDepth::VecControlVec => unimplemented!() + VecDepth::VecControlVec => unimplemented!(), } } else { quote! {self.0.clone()} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index f4c3848..c904c93 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -828,10 +828,8 @@ impl DataFromType { } Some(_) => { - let s = self.second_type.as_ref().map(|x| x.to_string()).clone(); - if s.unwrap().starts_with("LazyControlVecElement") { - VecDepth::VecControlVec - } else if matches!(self.third_how_to, Some(HowToControlThis::WithRecurse(_, RecursiveControlType::Vec))) { + // let s = self.second_type.as_ref().map(|x| x.to_string()).clone(); + if matches!(self.third_how_to, Some(HowToControlThis::WithRecurse(_, RecursiveControlType::Vec))) { VecDepth::VecControlVec } else { From efefc09680fb5d0b8ed45f2962015aad442aa0c7 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 26 Nov 2025 23:51:43 -0500 Subject: [PATCH 112/149] it... all works? --- .../examples/tests.rs | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index d1f97a5..4684427 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -37,32 +37,32 @@ pub struct BasicTypesWithDefaults { #[livecode(serde_default = "0")] #[lerpable(func = "lerpify_vec2")] c_vec2_serde_default: Vec2, -// something: Vec, + something: Vec, // // // #[lerpable(func = "lerpify_vec_vec2")] // // // list_of_vec2: Vec, -// #[livecode(kind = "none", serde_default = "empty_string")] -// label: String, -// #[livecode(kind = "none")] -// #[lerpable(method = "skip")] -// b: HashMap, + #[livecode(kind = "none", serde_default = "empty_string")] + label: String, + #[livecode(kind = "none")] + #[lerpable(method = "skip")] + b: HashMap, } -// #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// struct TestLazy { -// #[lerpable(method = "skip")] -// lazy: LazyBasicTypes, -// } +#[derive(Debug, Clone, Livecode, Lerpable, Default)] +struct TestLazy { + #[lerpable(method = "skip")] + lazy: LazyBasicTypes, +} -// #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// enum EnumTest { -// #[default] -// A, -// B(TestLazy), -// C(#[lerpable(method = "skip")] LazyTestLazy), -// } +#[derive(Debug, Clone, Livecode, Lerpable, Default)] +enum EnumTest { + #[default] + A, + B(TestLazy), + C(#[lerpable(method = "skip")] LazyTestLazy), +} -// #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// struct TestNewType(Vec); +#[derive(Debug, Clone, Livecode, Lerpable, Default)] +struct TestNewType(Vec); // #[derive(Debug, Clone, Livecode, Lerpable, Default)] // struct SequencerTest { @@ -137,16 +137,16 @@ pub struct BasicTypesWithDefaults { // // new type -// #[derive(Clone, Debug, Default, Livecode, Lerpable)] -// pub struct NewTypeWithType(f32); +#[derive(Clone, Debug, Default, Livecode, Lerpable)] +pub struct NewTypeWithType(f32); -// #[derive(Clone, Debug, Default, Livecode, Lerpable)] -// pub struct NewTypeWithTypeVec2(#[lerpable(func = "lerpify_vec2")] Vec2); +#[derive(Clone, Debug, Default, Livecode, Lerpable)] +pub struct NewTypeWithTypeVec2(#[lerpable(func = "lerpify_vec2")] Vec2); -// #[derive(Clone, Debug, Default, Livecode, Lerpable)] -// pub struct NewTypeWithVec(Vec); +#[derive(Clone, Debug, Default, Livecode, Lerpable)] +pub struct NewTypeWithVec(Vec); -// #[derive(Clone, Debug, Default, LivecodeOnly)] -// pub struct NewTypeWithStruct(BasicTypes); +#[derive(Clone, Debug, Default, LivecodeOnly)] +pub struct NewTypeWithStruct(BasicTypes); fn main() {} From 45bda20a55d21fc5172ebffe5e85ad778fac1fa5 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 27 Nov 2025 00:02:55 -0500 Subject: [PATCH 113/149] clean up --- murrelet_draw/src/style.rs | 12 +++---- murrelet_livecode/src/types.rs | 35 ++++++++++++++++++- .../examples/tests.rs | 6 ++-- .../src/derive_livecode.rs | 18 ---------- .../murrelet_livecode_derive/src/parser.rs | 17 ++++----- 5 files changed, 51 insertions(+), 37 deletions(-) diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 424d88b..d14b156 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -14,7 +14,7 @@ use murrelet_gui::{CanMakeGUI, MurreletGUI}; use murrelet_livecode::{ lazy::ControlLazyNodeF32, livecode::ControlF32, - types::{ControlVecElement, DeserLazyControlVecElement}, + types::ControlVecElement, }; use murrelet_livecode_derive::Livecode; use styleconf::StyleConf; @@ -28,12 +28,12 @@ fn _black() -> [ControlF32; 4] { ] } -fn _black_lazy() -> Vec> { +fn _black_lazy() -> Vec> { vec![ - DeserLazyControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), - DeserLazyControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), - DeserLazyControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), - DeserLazyControlVecElement::Single(ControlLazyNodeF32::Float(1.0)), + ControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), + ControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), + ControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), + ControlVecElement::Single(ControlLazyNodeF32::Float(1.0)), ] } diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 08e6201..142445a 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -200,7 +200,6 @@ impl DeserLazyControlVecElementRepeat { } #[derive(Debug, Clone)] -#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum DeserLazyControlVecElement where Source: Clone + Debug, @@ -277,6 +276,40 @@ where } } + +#[cfg(feature = "schemars")] +impl schemars::JsonSchema for DeserLazyControlVecElement +where + Source: schemars::JsonSchema + Clone + Debug, +{ + fn schema_name() -> String { + format!("LazyControlVecElement_{}", Source::schema_name()) + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + use schemars::schema::{Schema, SchemaObject, SubschemaValidation}; + // Variant 1: plain Source (your Single case without a wrapper key) + let single_schema = Source::json_schema(gen); + // Variant 2: the repeat object + let repeat_schema = >::json_schema(gen); + + Schema::Object(SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + one_of: Some(vec![single_schema, repeat_schema]), + ..Default::default() + })), + metadata: Some(Box::new(schemars::schema::Metadata { + description: Some( + "Either a single element (inline) OR a repeat object { repeat, prefix?, what }" + .to_string(), + ), + ..Default::default() + })), + ..Default::default() + }) + } +} + // just an intermediate type...? #[derive(Debug, Clone)] pub enum LazyVecElementRepeatMethod { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index 4684427..35ab002 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -38,13 +38,15 @@ pub struct BasicTypesWithDefaults { #[lerpable(func = "lerpify_vec2")] c_vec2_serde_default: Vec2, something: Vec, -// // // #[lerpable(func = "lerpify_vec_vec2")] -// // // list_of_vec2: Vec, + #[lerpable(func = "lerpify_vec_vec2")] + list_of_vec2: Vec, #[livecode(kind = "none", serde_default = "empty_string")] label: String, #[livecode(kind = "none")] #[lerpable(method = "skip")] b: HashMap, + list_test: Vec + } #[derive(Debug, Clone, Livecode, Lerpable, Default)] diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index fc6fc98..9600ac8 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -26,8 +26,6 @@ impl LivecodeFieldType { } pub fn to_token_lazy(&self) -> TokenStream2 { - println!("laz"); - println!("self {:?}", self.0); match self.0 { ControlType::F32_2 => quote! { Vec }, ControlType::F32_3 => quote! { Vec }, @@ -634,7 +632,6 @@ impl GenFinal for FieldTokensLivecode { // Vec, Vec fn from_recurse_struct_vec(idents: StructIdents) -> FieldTokensLivecode { - println!("idents {:?}", idents); let serde = idents.serde(); let name = idents.name(); let orig_ty = idents.orig_ty(); @@ -642,19 +639,15 @@ impl GenFinal for FieldTokensLivecode { let parsed_type_info = ident_from_type(&orig_ty); let how_to_control_internal = parsed_type_info.how_to_control_internal(); let wrapper = parsed_type_info.wrapper_type(); - println!("wrapper {:?}", wrapper); let inner_is_lazy_struct = parsed_type_info .second_how_to .map(|h| h.is_lazy()) .unwrap_or(false); - println!("inner_is_lazy_struct {:?}", inner_is_lazy_struct); - let for_struct: TokenStream2 = { let src_type = match how_to_control_internal { HowToControlThis::WithType(_, c) => { - println!("c"); if inner_is_lazy_struct { LivecodeFieldType(*c).to_token_lazy() } else { @@ -662,14 +655,11 @@ impl GenFinal for FieldTokensLivecode { } } HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => { - println!("rs"); let target_type = parsed_type_info.internal_type(); let name = Self::new_ident(target_type.clone()); quote! {#name} } HowToControlThis::WithRecurse(_, RecursiveControlType::StructLazy) => { - println!("rsl"); - let original_internal_type = parsed_type_info.internal_type(); let name = Self::new_ident(original_internal_type.clone()); quote! {#name} @@ -688,7 +678,6 @@ impl GenFinal for FieldTokensLivecode { e => panic!("(livecode, recurse_struct_vec) need vec something {:?}", e), }; - println!("src_type {:?}", src_type); let vec_elem_type: TokenStream2 = if inner_is_lazy_struct { quote! {murrelet_livecode::types::DeserLazyControlVecElement} @@ -761,7 +750,6 @@ impl GenFinal for FieldTokensLivecode { } VecDepth::VecControlVec => { - println!("HHHHHHH"); quote! { #name: { let mut result = Vec::with_capacity(self.#name.len()); @@ -786,8 +774,6 @@ impl GenFinal for FieldTokensLivecode { VecDepth::NotAVec => unreachable!("not a vec in a vec?"), VecDepth::Vec => { if inner_is_lazy_struct { - println!("SSS"); - quote! { #name: self.#name.iter().map(|x| x.to_control()).collect::>() } } else { quote! { #name: self.#name.iter().map(|x| murrelet_livecode::types::ControlVecElement::raw(x.to_control())).collect::>() } @@ -832,8 +818,6 @@ impl GenFinal for FieldTokensLivecode { } VecDepth::VecControlVec => { if inner_is_lazy_struct { - println!("HHaH"); - quote! { #name: { let mut result = Vec::with_capacity(self.#name.len()); @@ -893,8 +877,6 @@ impl GenFinal for FieldTokensLivecode { } } VecDepth::VecControlVec => { - println!("HHHHH"); - quote! { { let mut result = Vec::with_capacity(self.#name.len()); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index c904c93..f8ef67b 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -792,7 +792,7 @@ impl DataFromType { main_how_to, second_how_to, third_how_to, - fourth_how_to + fourth_how_to, } } @@ -827,19 +827,16 @@ impl DataFromType { VecDepth::VecVec } Some(_) => { - // let s = self.second_type.as_ref().map(|x| x.to_string()).clone(); - if matches!(self.third_how_to, Some(HowToControlThis::WithRecurse(_, RecursiveControlType::Vec))) { + if matches!( + self.third_how_to, + Some(HowToControlThis::WithRecurse(_, RecursiveControlType::Vec)) + ) { VecDepth::VecControlVec - } else { - - - println!("self.second {:?}", self.second_type); - + } else { VecDepth::Vec } - - }, + } None => unreachable!("vec should have a type??"), } } From af4bcc4738b4ed3cd65224b0a7b9e10da96f93fc Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 27 Nov 2025 21:22:14 -0800 Subject: [PATCH 114/149] wip --- murrelet_livecode/src/lazy.rs | 1 + murrelet_livecode/src/state.rs | 4 ++++ murrelet_livecode/src/types.rs | 43 +++++++++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index d3a19ec..3892f8f 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -95,6 +95,7 @@ impl LazyNodeF32Inner { let c = self.clone(); c.add_expr_values(more_defs.expr_vals()) + // println!("dropping contexts..."); // c.world // .update_with_defs(MixedEvalDefsRef::new(more_defs.clone())); diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index 6114c01..a2dec31 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -220,6 +220,10 @@ impl LivecodeWorldState { builtins_disabled: false, } } + + // pub(crate) fn set_val(&mut self, name: &str, val: LivecodeValue) { + // self.update_with_defs(MixedEvalDefs::new_simple(name, val)); + // } } #[derive(Debug, Clone)] diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 142445a..ebcf0c4 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -3,7 +3,7 @@ use std::{collections::HashSet, fmt::Debug}; use evalexpr::{build_operator_tree, EvalexprError, HashMapContext, Node}; use itertools::Itertools; use lerpable::{step, Lerpable}; -use murrelet_common::{IdxInRange, IdxInRange2d}; +use murrelet_common::{IdxInRange, IdxInRange2d, LivecodeValue}; use murrelet_gui::CanMakeGUI; use serde::{Deserialize, Deserializer}; use thiserror::Error; @@ -73,6 +73,14 @@ pub type LivecodeResult = Result; #[serde(transparent)] pub struct AdditionalContextNode(#[cfg_attr(feature = "schemars", schemars(with = "String"))] Node); +fn _default_ctx() -> AdditionalContextNode { + AdditionalContextNode::new_dummy() +} + +fn _default_ctx_lazy() -> AdditionalContextNode { + AdditionalContextNode::new_dummy() +} + impl Default for AdditionalContextNode { fn default() -> Self { Self(build_operator_tree("").unwrap()) @@ -177,6 +185,8 @@ impl DeserLazyControlVecElementRepeatMethod { #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct DeserLazyControlVecElementRepeat { repeat: DeserLazyControlVecElementRepeatMethod, + #[serde(default = "_default_ctx")] + ctx: AdditionalContextNode, prefix: String, what: Vec>, } @@ -193,6 +203,7 @@ impl DeserLazyControlVecElementRepeat { .collect::, _>>()?; Ok(LazyVecElementRepeat { repeat: self.repeat.o(w)?, + ctx: self.ctx.clone(), prefix: self.prefix.clone(), what, }) @@ -276,7 +287,6 @@ where } } - #[cfg(feature = "schemars")] impl schemars::JsonSchema for DeserLazyControlVecElement where @@ -353,6 +363,7 @@ impl LazyVecElementRepeatMethod { #[derive(Debug, Clone)] pub struct LazyVecElementRepeat { repeat: LazyVecElementRepeatMethod, + ctx: AdditionalContextNode, prefix: String, what: Vec>, } @@ -368,6 +379,7 @@ impl LazyVecElementRepeat { for idx in self.repeat.iter(ctx)? { let mut ctx = ctx.clone(); + ctx.add_node(self.ctx.clone()); let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); ctx.set_vals(expr.with_prefix(&prefix)); @@ -418,6 +430,7 @@ where repeat, prefix: rep.prefix.clone(), what, + ctx: rep.ctx.clone(), }) } } @@ -485,7 +498,11 @@ where } impl ControlVecElementRepeat { - pub fn eval_and_expand_vec(&self, w: &LivecodeWorldState) -> LivecodeResult> + pub fn _eval_and_expand_vec( + &self, + w: &LivecodeWorldState, + offset: usize, + ) -> LivecodeResult<(usize, Vec)> where Source: LivecodeFromWorld, { @@ -497,24 +514,38 @@ impl ControlVecElementRepeat { format!("{}_", self.prefix) }; + let mut offset = offset; + for idx in self.repeat.iter(w)? { let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); - let new_w = w.clone_with_vals(expr, &prefix); + let mut new_w = w.clone_with_vals(expr, &prefix); for src in &self.what { match src { ControlVecElement::Single(c) => { + // just update it and overwrite it... + // new_w.set_val("vseed", LivecodeValue::float(offset as f32)); let o = c.o(&new_w)?; result.push(o); + offset += 1; } ControlVecElement::Repeat(c) => { - let o = c.eval_and_expand_vec(&new_w)?; + let (new_offset, o) = c._eval_and_expand_vec(&new_w, offset)?; result.extend(o.into_iter()); + offset += new_offset; } } } } - Ok(result) + Ok((offset, result)) + } + + pub fn eval_and_expand_vec(&self, w: &LivecodeWorldState) -> LivecodeResult> + where + Source: LivecodeFromWorld, + { + let (_, a) = self._eval_and_expand_vec(w, 0)?; + Ok(a) } } From b0c7d4a01d57736512d2c30337c0ea49c9506cb0 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 29 Nov 2025 00:55:03 -0800 Subject: [PATCH 115/149] wip --- murrelet_common/src/geometry.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 49cae0a..03f2600 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -708,6 +708,10 @@ impl PointToPoint { pub fn end_spot(&self) -> SpotOnCurve { SpotOnCurve::new(self.end, self.angle()) } + + pub fn flip(&self) -> PointToPoint { + PointToPoint::new(self.end, self.start) + } } #[derive(Copy, Clone, Debug)] From a2a8cdb821cc0b7d45e72a50e9fe6d0fc8826650 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 29 Nov 2025 15:21:30 -0800 Subject: [PATCH 116/149] wip --- murrelet_livecode/src/lazy.rs | 15 +++- murrelet_livecode/src/types.rs | 57 ++++++++++-- .../src/derive_lazy.rs | 88 ++++++++++++++++++- 3 files changed, 148 insertions(+), 12 deletions(-) diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 3892f8f..9db8583 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -95,7 +95,6 @@ impl LazyNodeF32Inner { let c = self.clone(); c.add_expr_values(more_defs.expr_vals()) - // println!("dropping contexts..."); // c.world // .update_with_defs(MixedEvalDefsRef::new(more_defs.clone())); @@ -247,12 +246,14 @@ impl Lerpable for LazyNodeF32 { pub trait IsLazy where - Self: Sized, + Self: Sized + Clone, { type Target; fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult; + fn with_more_defs(&self, more_defs: &MixedEvalDefs) -> LivecodeResult; + // without a _, like unitcell.. fn eval_idx_(&self, idx: IdxInRange, prefix: &str) -> LivecodeResult { let vals = ExprWorldContextValues::new_from_idx(idx).with_prefix(&format!("{}", prefix)); @@ -271,6 +272,10 @@ impl IsLazy for LazyNodeF32 { fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult { self.eval_with_ctx(expr) } + + fn with_more_defs(&self, more_defs: &MixedEvalDefs) -> LivecodeResult { + self.add_more_defs(more_defs) + } } impl IsLazy for Vec @@ -281,6 +286,12 @@ where fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult> { self.iter().map(|x| x.eval_lazy(expr)).collect() } + + fn with_more_defs(&self, more_defs: &MixedEvalDefs) -> LivecodeResult { + self.iter() + .map(|item| item.with_more_defs(more_defs)) + .collect::>>() + } } impl crate::unitcells::UnitCellCreator for T diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index ebcf0c4..c5245a7 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -378,26 +378,58 @@ impl LazyVecElementRepeat { }; for idx in self.repeat.iter(ctx)? { - let mut ctx = ctx.clone(); - ctx.add_node(self.ctx.clone()); + let mut scoped_ctx = ctx.clone(); + scoped_ctx.add_node(self.ctx.clone()); let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); - ctx.set_vals(expr.with_prefix(&prefix)); + scoped_ctx.set_vals(expr.with_prefix(&prefix)); for src in &self.what { match src { LazyControlVecElement::Single(c) => { - // let o = c.eval_lazy(&ctx)?; - result.push(c.clone()); + result.push(c.with_more_defs(&scoped_ctx)?); } LazyControlVecElement::Repeat(c) => { - let o = c.lazy_expand_vec(&ctx)?; - result.extend(o.into_iter()); + let mut nested = c.lazy_expand_vec(&scoped_ctx)?; + result.append(&mut nested); } } } } + + // for idx in self.repeat.iter(ctx)? { + // let mut ctx = ctx.clone(); + // ctx.add_node(self.ctx.clone()); + // let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); + // ctx.set_vals(expr.with_prefix(&prefix)); + + // for src in &self.what { + // match src { + // LazyControlVecElement::Single(c) => { + // // let o = c.eval_lazy(&ctx)?; + // result.push(c.clone()); + // } + // LazyControlVecElement::Repeat(c) => { + // let o = c.lazy_expand_vec(&ctx)?; + // result.extend(o.into_iter()); + // } + // } + // } + // } Ok(result) } + + pub fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + Ok(Self { + repeat: self.repeat.clone(), + ctx: self.ctx.clone(), + prefix: self.prefix.clone(), + what: self + .what + .iter() + .map(|elem| elem.with_more_defs(ctx)) + .collect::>>()?, + }) + } } impl LivecodeToControl> @@ -635,6 +667,17 @@ where // Then evaluate each inner lazy to its final type expanded.into_iter().map(|s| s.eval_lazy(ctx)).collect() } + + fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + Ok(match self { + LazyControlVecElement::Single(s) => { + LazyControlVecElement::Single(s.with_more_defs(ctx)?) + } + LazyControlVecElement::Repeat(rep) => { + LazyControlVecElement::Repeat(rep.with_more_defs(ctx)?) + } + }) + } } impl LazyControlVecElement diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index 0b38797..258b879 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -206,6 +206,7 @@ impl LazyFieldType { pub(crate) struct FieldTokensLazy { pub(crate) for_struct: TokenStream2, pub(crate) for_world: TokenStream2, + pub(crate) for_more_defs: TokenStream2, } impl GenFinal for FieldTokensLazy { fn make_newtype_struct_final( @@ -218,6 +219,7 @@ impl GenFinal for FieldTokensLazy { let for_struct = variants.iter().map(|x| x.for_struct.clone()); let for_world = variants.iter().map(|x| x.for_world.clone()); + let for_more_defs = variants.iter().map(|x| x.for_more_defs.clone()); quote! { #[derive(Debug, Clone, Default, murrelet_livecode_derive::LivecodeOnly)] @@ -228,6 +230,9 @@ impl GenFinal for FieldTokensLazy { fn eval_lazy(&self, ctx: &murrelet_livecode::expr::MixedEvalDefs) -> murrelet_livecode::types::LivecodeResult<#name> { Ok(#name(#(#for_world,)*)) } + fn with_more_defs(&self, ctx: &murrelet_livecode::expr::MixedEvalDefs) -> murrelet_livecode::types::LivecodeResult { + Ok(Self(#(#for_more_defs,)*)) + } } } @@ -240,6 +245,7 @@ impl GenFinal for FieldTokensLazy { let for_struct = variants.iter().map(|x| x.for_struct.clone()); let for_world = variants.iter().map(|x| x.for_world.clone()); + let for_more_defs = variants.iter().map(|x| x.for_more_defs.clone()); quote! { #[derive(Debug, Clone, Default, murrelet_livecode_derive::LivecodeOnly)] @@ -254,6 +260,12 @@ impl GenFinal for FieldTokensLazy { #(#for_world,)* }) } + + fn with_more_defs(&self, ctx: &murrelet_livecode::expr::MixedEvalDefs) -> murrelet_livecode::types::LivecodeResult { + Ok(Self { + #(#for_more_defs,)* + }) + } } } } @@ -266,6 +278,7 @@ impl GenFinal for FieldTokensLazy { let for_struct = variants.iter().map(|x| x.for_struct.clone()); let for_world = variants.iter().map(|x| x.for_world.clone()); + let for_more_defs = variants.iter().map(|x| x.for_more_defs.clone()); quote! { #[derive(Debug, Clone, Default, murrelet_livecode_derive::LivecodeOnly)] @@ -285,6 +298,13 @@ impl GenFinal for FieldTokensLazy { #(#for_world,)* }) } + + fn with_more_defs(&self, ctx: &murrelet_livecode::expr::MixedEvalDefs) -> murrelet_livecode::types::LivecodeResult { + Ok(match self { + #new_enum_ident::DefaultNoop => #new_enum_ident::DefaultNoop, + #(#for_more_defs,)* + }) + } } } } @@ -303,9 +323,14 @@ impl GenFinal for FieldTokensLazy { quote! { self.0.clone() } }; + let for_more_defs = { + quote! { self.0.with_more_defs(ctx)? } + }; + FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -326,9 +351,14 @@ impl GenFinal for FieldTokensLazy { quote! { self.0.eval_lazy(ctx)? } }; + let for_more_defs = { + quote! { self.0.for_more_defs(ctx)? } + }; + FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -342,9 +372,14 @@ impl GenFinal for FieldTokensLazy { let for_world = LazyFieldType(ctrl).for_newtype_world(idents.clone()); + let for_more_defs = { + quote! { self.0.with_more_defs(ctx)? } + }; + FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -383,9 +418,16 @@ impl GenFinal for FieldTokensLazy { quote! { #new_enum_ident::#variant_ident(s) => #name::#variant_ident(s.eval_lazy(ctx)?) } }; + let for_more_defs = if is_lazy { + quote! { #new_enum_ident::#variant_ident(s) => #new_enum_ident::#variant_ident(s.clone()) } + } else { + quote! { #new_enum_ident::#variant_ident(s) => #new_enum_ident::#variant_ident(s.with_more_defs(ctx)?) } + }; + FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -401,9 +443,14 @@ impl GenFinal for FieldTokensLazy { quote! { #new_enum_ident::#variant_ident => #name::#variant_ident } }; + let for_more_defs: TokenStream2 = { + quote! { #new_enum_ident::#variant_ident => #new_enum_ident::#variant_ident } + }; + FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -419,9 +466,14 @@ impl GenFinal for FieldTokensLazy { quote! {#name: self.#name.clone()} }; + let for_more_defs: TokenStream2 = { + quote! { #name: self.#name.clone() } + }; + FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -438,9 +490,15 @@ impl GenFinal for FieldTokensLazy { let for_world = LazyFieldType(ctrl).for_world(idents.clone()); + let for_world = LazyFieldType(ctrl).for_world(idents.clone()); + let for_more_defs = { + quote! { #name: self.#name.with_more_defs(ctx)? } + }; + FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -458,10 +516,14 @@ impl GenFinal for FieldTokensLazy { }; let for_world = LazyFieldType(ctrl).for_world_option(idents.clone()); + let for_more_defs = { + quote! { #name: if let Some(value) = &self.#name { Some(value.with_more_defs(ctx)?) } else { None } } + }; FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -499,9 +561,7 @@ impl GenFinal for FieldTokensLazy { VecDepth::VecVec => todo!(), VecDepth::VecControlVec => { quote! { Vec>> } - - }, - + } }; quote! {#back_to_quote #name: #new_ty} }; @@ -564,9 +624,17 @@ impl GenFinal for FieldTokensLazy { // } }; + let for_more_defs = quote! { + #name: self.#name + .iter() + .map(|item| item.with_more_defs(ctx)) + .collect::>>()? + }; + FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -597,10 +665,14 @@ impl GenFinal for FieldTokensLazy { let for_world = { quote! {self.0.iter().map(|x| x.eval_lazy(ctx)).collect::, _>>()?} }; + let for_more_defs = { + quote! { self.0.iter().map(|x| x.with_more_defs(ctx)).collect::>>()? } + }; FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -646,9 +718,14 @@ impl GenFinal for FieldTokensLazy { } }; + let for_more_defs = { + quote! { #name: self.#name.clone() } + }; + FieldTokensLazy { for_struct, for_world, + for_more_defs, } } @@ -671,9 +748,14 @@ impl GenFinal for FieldTokensLazy { quote! {#name: self.#name.eval_lazy(ctx)?} }; + let for_more_defs = { + quote! { #name: self.#name.with_more_defs(ctx)? } + }; + FieldTokensLazy { for_struct, for_world, + for_more_defs, } } From fb3f4b94b7014fdf9d9d0fa930d5d855c102e808 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 29 Nov 2025 21:12:14 -0800 Subject: [PATCH 117/149] wip --- murrelet_common/src/lib.rs | 7 +++++++ murrelet_livecode/src/lazy.rs | 12 +++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 8b29aa1..02963e5 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -418,6 +418,13 @@ impl Rect { pub fn xy(&self) -> Vec2 { self.xy } + + pub fn pos(&self, s: Vec2) -> Vec2 { + vec2( + (s.x - self.left()) / self.w(), + (s.y - self.bottom()) / self.h(), + ) + } } #[derive(Debug, Clone, Copy)] diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 9db8583..040cff8 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use evalexpr::Node; use itertools::Itertools; use lerpable::{step, Lerpable}; -use murrelet_common::{IdxInRange, MurreletColor}; +use murrelet_common::{IdxInRange, LivecodeValue, MurreletColor}; use serde::Deserialize; use crate::{ @@ -236,6 +236,16 @@ impl LazyNodeF32 { LazyNodeF32::NoCtxNode(_) => Err(LivecodeError::Raw("no ctx".to_owned())), } } + + pub fn eval_with_xy(&self, xy: glam::Vec2) -> LivecodeResult { + let expr = + ExprWorldContextValues::new(vec![ + ("x".to_string(), LivecodeValue::float(xy.x)), + ("y".to_string(), LivecodeValue::float(xy.y)), + ]); + + self.eval_with_ctx(&expr.to_mixed_defs()) + } } impl Lerpable for LazyNodeF32 { From ce45dde3e0ae7ec6b8c451c45e429ad605efbdb1 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 10 Dec 2025 16:37:46 -0500 Subject: [PATCH 118/149] wip --- murrelet_draw/src/cubic.rs | 9 +++++ murrelet_draw/src/curve_drawer.rs | 56 +++++++++++++++++++++++++++++-- murrelet_draw/src/lib.rs | 1 + murrelet_draw/src/style.rs | 2 ++ murrelet_draw/src/transform2d.rs | 15 +++++++++ murrelet_svg/src/svg.rs | 16 +++++++-- 6 files changed, 94 insertions(+), 5 deletions(-) diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index 0bfa85e..bd15977 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -145,4 +145,13 @@ impl CubicBezier { self.tangent_at_pct(pct) } } + + pub fn apply_vec2_tranform(&self, f: impl Fn(Vec2) -> Vec2) -> Self { + Self { + from: f(self.from), + ctrl1: f(self.ctrl1), + ctrl2: f(self.ctrl2), + to: f(self.to), + } + } } diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index d3421df..80ca32f 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -3,6 +3,7 @@ use itertools::Itertools; use lerpable::Lerpable; use lyon::{geom::CubicBezierSegment, path::Path}; use murrelet_common::*; +use murrelet_livecode::types::{LivecodeError, LivecodeResult}; use murrelet_livecode_derive::*; use serde::{Deserialize, Serialize}; use svg::node::element::path::Data; @@ -12,9 +13,8 @@ use crate::{ newtypes::*, svg::glam_to_lyon, tesselate::{ - cubic_bezier_path_to_lyon, flatten_cubic_bezier_path, - flatten_cubic_bezier_path_with_tolerance, parse_svg_data_as_vec2, ToVecVec2, - }, + ToVecVec2, cubic_bezier_path_to_lyon, flatten_cubic_bezier_path, flatten_cubic_bezier_path_with_tolerance, parse_svg_data_as_vec2 + }, transform2d::Transform2d, }; #[derive(Debug, Clone, Default, Livecode, Lerpable, Serialize, Deserialize)] @@ -289,6 +289,21 @@ impl CurveDrawer { }) .sum() } + + pub(crate) fn maybe_transform(&self, transform: &Transform2d) -> LivecodeResult { + let mut segments = vec![]; + + if !transform.is_similarity_transform() { + return Err(LivecodeError::raw("not a similarity transform")) + } + + for cd in &self.segments { + // we've ran our check, so we can just do it now.. + segments.push(cd.force_transform(transform)?); + } + + Ok(Self::new(segments, self.closed)) + } } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -455,6 +470,20 @@ impl CurveSegment { fn cubic(c: CubicBezier) -> CurveSegment { Self::CubicBezier(CurveCubicBezier::from_cubic(c)) } + + fn force_transform(&self, transform: &Transform2d) -> Self { + match self { + CurveSegment::Arc(curve_arc) => { + curve_arc.force_transform(transform) + }, + CurveSegment::Points(curve_points) => { + curve_points.force_transform(transform) + }, + CurveSegment::CubicBezier(curve_cubic_bezier) => { + curve_cubic_bezier.force_transform(transform) + }, + } + } } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -598,6 +627,16 @@ impl CurveArc { None } } + + // you should make sure that it's a similarity trnasform before you do this! + fn force_transform(&self, transform: &Transform2d) -> Self { + Self { + loc: transform.transform_vec2(self.loc), + radius: transform.approx_scale() * self.radius, + start_pi: transform.approx_rotate() + self.start_pi, + end_pi: transform.approx_rotate() + self.end_pi, + } + } } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -841,3 +880,14 @@ macro_rules! curve_drawers { v }}; } + +#[macro_export] +macro_rules! mixed_drawable { + ($($expr:expr),* $(,)?) => {{ + let mut v: Vec = Vec::new(); + $( + v.extend($expr.to_mixed_drawables()); + )* + v + }}; +} diff --git a/murrelet_draw/src/lib.rs b/murrelet_draw/src/lib.rs index 14b883e..5cac720 100644 --- a/murrelet_draw/src/lib.rs +++ b/murrelet_draw/src/lib.rs @@ -10,3 +10,4 @@ pub mod style; pub mod svg; pub mod tesselate; pub mod transform2d; +pub mod drawable; diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index d14b156..e161dd4 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -275,6 +275,7 @@ impl MurreletStyleOutlined { closed: true, filled: false, color: MurreletColorStyle::color(self.color), + stroke_color: MurreletColorStyle::color(self.color), // should this be stroke color? stroke_weight: self.stroke_weight, ..Default::default() } @@ -364,6 +365,7 @@ impl MurreletStyleLined { closed: false, filled: false, color: MurreletColorStyle::color(self.color), + stroke_color: MurreletColorStyle::color(self.color), stroke_weight: self.stroke_weight, ..Default::default() } diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index 546b9de..fa8140d 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -280,6 +280,21 @@ impl Transform2d { c.append_transform(&loc); c } + + pub fn is_similarity_transform(&self) -> bool { + for s in &self.0 { + match s { + Transform2dStep::Scale(v2) => { + if !approx_eq_eps(v2.v.x.abs(), v2.v.y.abs(), 1.0e-3) { + return false; + } + } + Transform2dStep::Skew(_) => return false, + _ => {} + } + } + return true; + } } impl Default for ControlTransform2d { diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 61947f7..7829276 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -321,7 +321,13 @@ impl GetSvgAttributes for MurreletStyle { v.add("stroke-linejoin", "round"); v.add("stroke-linecap", "round"); - let sc = self.color.as_color(); + // let sc = self.color.as_color(); + let sc = match self.stroke_color { + MurreletColorStyle::Color(_) | MurreletColorStyle::RgbaFill(_) => { + self.stroke_color.as_color() + } + _ => self.color.as_color(), + }; v.add("stroke", &sc.hex()); if sc.alpha() < 1.0 { v.add("stroke-opacity", &format!("{}", sc.alpha())); @@ -336,7 +342,13 @@ impl GetSvgAttributes for MurreletStyle { v.add("stroke-linejoin", "round"); v.add("stroke-linecap", "round"); - let sc = self.color.as_color(); + // let sc = self.color.as_color(); + let sc = match self.stroke_color { + MurreletColorStyle::Color(_) | MurreletColorStyle::RgbaFill(_) => { + self.stroke_color.as_color() + } + _ => self.color.as_color(), + }; v.add("stroke", &sc.hex()); if sc.alpha() < 1.0 { v.add("stroke-opacity", &format!("{}", sc.alpha())); From 0bb4c9bcfb7d287ac41d22116ba46ac1200a278a Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 10 Dec 2025 22:24:14 -0500 Subject: [PATCH 119/149] iwp --- murrelet_common/src/lib.rs | 2 ++ murrelet_draw/src/curve_drawer.rs | 35 +++++++++++++++++++++---------- murrelet_draw/src/style.rs | 6 +----- murrelet_livecode/src/types.rs | 19 ++++++++++++++++- 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 02963e5..860808b 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -249,6 +249,8 @@ where } } + + pub fn cubic_bezier(start: Vec2, ctrl1: Vec2, ctrl2: Vec2, to: Vec2, t: f32) -> Vec2 { let a = lerp(start, ctrl1, t); let b = lerp(ctrl1, ctrl2, t); diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 80ca32f..14143ce 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -13,8 +13,10 @@ use crate::{ newtypes::*, svg::glam_to_lyon, tesselate::{ - ToVecVec2, cubic_bezier_path_to_lyon, flatten_cubic_bezier_path, flatten_cubic_bezier_path_with_tolerance, parse_svg_data_as_vec2 - }, transform2d::Transform2d, + cubic_bezier_path_to_lyon, flatten_cubic_bezier_path, + flatten_cubic_bezier_path_with_tolerance, parse_svg_data_as_vec2, ToVecVec2, + }, + transform2d::Transform2d, }; #[derive(Debug, Clone, Default, Livecode, Lerpable, Serialize, Deserialize)] @@ -294,12 +296,12 @@ impl CurveDrawer { let mut segments = vec![]; if !transform.is_similarity_transform() { - return Err(LivecodeError::raw("not a similarity transform")) + return Err(LivecodeError::raw("not a similarity transform")); } for cd in &self.segments { // we've ran our check, so we can just do it now.. - segments.push(cd.force_transform(transform)?); + segments.push(cd.force_transform(transform)); } Ok(Self::new(segments, self.closed)) @@ -473,15 +475,13 @@ impl CurveSegment { fn force_transform(&self, transform: &Transform2d) -> Self { match self { - CurveSegment::Arc(curve_arc) => { - curve_arc.force_transform(transform) - }, + CurveSegment::Arc(curve_arc) => CurveSegment::Arc(curve_arc.force_transform(transform)), CurveSegment::Points(curve_points) => { - curve_points.force_transform(transform) - }, + CurveSegment::Points(curve_points.force_transform(transform)) + } CurveSegment::CubicBezier(curve_cubic_bezier) => { - curve_cubic_bezier.force_transform(transform) - }, + CurveSegment::CubicBezier(curve_cubic_bezier.force_transform(transform)) + } } } } @@ -700,6 +700,13 @@ impl CurveCubicBezier { pub fn to_pts(&self, tolerance: f32) -> CurveSegment { CurveSegment::new_simple_points(self.flatten(tolerance)) } + + fn force_transform(&self, transform: &Transform2d) -> CurveCubicBezier { + CurveCubicBezier::from_cubic( + self.to_cubic() + .apply_vec2_tranform(|x| transform.transform_vec2(x)), + ) + } } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -728,6 +735,12 @@ impl CurvePoints { pub fn reverse(&self) -> Self { CurvePoints::new(self.points.iter().cloned().rev().collect_vec()) } + + fn force_transform(&self, transform: &Transform2d) -> CurvePoints { + CurvePoints { + points: transform.transform_many_vec2(self.points()).clone_to_vec(), + } + } } pub trait ToCurveSegment { diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index e161dd4..ff032a8 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -11,11 +11,7 @@ use lerpable::Lerpable; use md5::{Digest, Md5}; use murrelet_common::*; use murrelet_gui::{CanMakeGUI, MurreletGUI}; -use murrelet_livecode::{ - lazy::ControlLazyNodeF32, - livecode::ControlF32, - types::ControlVecElement, -}; +use murrelet_livecode::{lazy::ControlLazyNodeF32, livecode::ControlF32, types::ControlVecElement}; use murrelet_livecode_derive::Livecode; use styleconf::StyleConf; diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index c5245a7..e762508 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -3,7 +3,7 @@ use std::{collections::HashSet, fmt::Debug}; use evalexpr::{build_operator_tree, EvalexprError, HashMapContext, Node}; use itertools::Itertools; use lerpable::{step, Lerpable}; -use murrelet_common::{IdxInRange, IdxInRange2d, LivecodeValue}; +use murrelet_common::{IdxInRange, IdxInRange2d, LivecodeValue, print_expect}; use murrelet_gui::CanMakeGUI; use serde::{Deserialize, Deserializer}; use thiserror::Error; @@ -42,6 +42,23 @@ impl LivecodeError { Self::Raw(s.to_string()) } } + +pub trait IterUnwrapOrPrint { + fn iter_unwrap(&self, err: &str, f: F) -> Vec + where + F: Fn(&T) -> LivecodeResult; +} + +impl IterUnwrapOrPrint for Vec { + fn iter_unwrap(&self, err: &str, f: F) -> Vec + where + F: Fn(&T) -> LivecodeResult, + { + let res: LivecodeResult> = self.iter().map(|d| f(d)).collect(); + print_expect(res, err).unwrap_or(vec![]) + } +} + // impl std::fmt::Display for LivecodeError { // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // match self { From 58d024c245e679b7123ba7f5dfe9d8649e51cece Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 12 Dec 2025 00:20:10 -0500 Subject: [PATCH 120/149] wip --- murrelet_draw/src/drawable.rs | 249 ++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 murrelet_draw/src/drawable.rs diff --git a/murrelet_draw/src/drawable.rs b/murrelet_draw/src/drawable.rs new file mode 100644 index 0000000..a95975c --- /dev/null +++ b/murrelet_draw/src/drawable.rs @@ -0,0 +1,249 @@ +use glam::Vec2; +use itertools::Itertools; +use murrelet_livecode::types::LivecodeResult; + +use crate::{ + curve_drawer::{CurveDrawer, ToCurveDrawer}, + style::styleconf::StyleConf, + transform2d::Transform2d, +}; + +// hm, a new type that attaches a shape to its style, but keeps it agnostic to what is drawing. +#[derive(Clone, Debug)] +pub struct DrawnShape { + cds: Vec, + style: StyleConf, +} + +impl DrawnShape { + pub fn new_vecvec(shape: Vec>, style: StyleConf) -> DrawnShape { + let cds = shape + .into_iter() + .map(|x| CurveDrawer::new_simple_points(x, true)) + .collect_vec(); + Self::new_cds(&cds, style) + } + + pub fn new_cds(cds: &[CurveDrawer], style: StyleConf) -> DrawnShape { + Self { + cds: cds.to_vec(), + style: style.clone(), + } + } + + // pub fn faces(&self) -> Vec { + // self.cds.iter().map(|x| x.to_vec2()).collect_vec() + // } + + pub fn style(&self) -> StyleConf { + self.style.clone() + } + + pub fn set_style(&mut self, style: StyleConf) { + self.style = style; + } + + pub fn curves(&self) -> &[CurveDrawer] { + &self.cds + } + + pub fn maybe_transform(&self, transform: &Transform2d) -> LivecodeResult { + let mut new = vec![]; + for c in &self.cds { + new.push(c.maybe_transform(transform)?); + } + Ok(DrawnShape::new_cds(&new, self.style.clone())) + } + + // pub fn transform_as_vec2(&self, transform: &Transform2d) -> Self { + // let face = self + // .faces() + // .iter() + // .map(|x| { + // let t = transform.transform_many_vec2(x); + // t.clone_to_vec() + // }) + // .collect_vec(); + // DrawnShape::new_vecvec(face, self.style()) + // } +} + +pub trait ToDrawnShapeSegments { + fn to_drawn_shape_closed(&self, style: StyleConf) -> DrawnShape; + fn to_drawn_shape_open(&self, style: StyleConf) -> DrawnShape; + + fn to_drawn_shape_closed_r(&self, style: &StyleConf) -> DrawnShape { + self.to_drawn_shape_closed(style.clone()) + } + + fn to_drawn_shape_open_r(&self, style: &StyleConf) -> DrawnShape { + self.to_drawn_shape_open(style.clone()) + } +} + +impl ToDrawnShapeSegments for T +where + T: ToCurveDrawer, +{ + fn to_drawn_shape_closed(&self, style: StyleConf) -> DrawnShape { + DrawnShape::new_cds(&vec![self.to_cd_closed()], style) + } + + fn to_drawn_shape_open(&self, style: StyleConf) -> DrawnShape { + DrawnShape::new_cds(&vec![self.to_cd_open()], style) + } +} + +// impl ToDrawnShapeSegments for Vec> { +// fn to_drawn_shape_closed(&self, style: StyleConf) -> DrawnShape { +// let cds = self +// .into_iter() +// .map(|x| CurveDrawer::new_simple_points(x, true)) +// .collect_vec(); +// DrawnShape::new_cds(&cds, style) +// } + +// fn to_drawn_shape_open(&self, style: StyleConf) -> DrawnShape { +// let cds = self +// .into_iter() +// .map(|x| CurveDrawer::new_simple_points(x, false)) +// .collect_vec(); +// DrawnShape::new_cds(&cds, style) +// } +// } + +pub trait ToDrawnShape { + fn to_drawn_shape(&self, style: StyleConf) -> DrawnShape; + + fn to_drawn_shape_r(&self, style: &StyleConf) -> DrawnShape { + self.to_drawn_shape(style.clone()) + } +} + +impl ToDrawnShape for CurveDrawer { + fn to_drawn_shape(&self, style: StyleConf) -> DrawnShape { + DrawnShape::new_cds(&vec![self.clone()], style) + } +} + +impl ToDrawnShape for Vec { + fn to_drawn_shape(&self, style: StyleConf) -> DrawnShape { + DrawnShape::new_cds(self, style) + } +} + +#[derive(Clone, Debug)] +pub struct PositionedText { + text: String, + loc: Vec2, +} +impl PositionedText { + pub fn new(text: &str, loc: Vec2) -> Self { + Self { + text: text.to_string(), + loc, + } + } + + fn with_style(&self, style: &StyleConf) -> MixedDrawableShape { + MixedDrawableShape::Text(DrawnTextShape { + text: vec![self.clone()], + style: style.clone(), + }) + } + + pub fn text(&self) -> &str { + &self.text + } + + pub fn loc(&self) -> Vec2 { + self.loc + } +} + +impl ToMixedDrawableWithStyle for Vec { + fn with_style(&self, style: &StyleConf) -> MixedDrawableShape { + MixedDrawableShape::Text(DrawnTextShape { + text: self.clone(), + style: style.clone(), + }) + } +} + +impl ToMixedDrawableWithStyle for Vec { + fn with_style(&self, style: &StyleConf) -> MixedDrawableShape { + MixedDrawableShape::Shape(self.to_drawn_shape_r(style)) + } +} + +pub trait ToMixedDrawableWithStyle { + fn with_style(&self, style: &StyleConf) -> MixedDrawableShape; +} + +#[derive(Clone, Debug)] +pub struct DrawnTextShape { + text: Vec, + style: StyleConf, +} +impl DrawnTextShape { + pub fn positions(&self) -> &[PositionedText] { + &self.text + } +} + +// ergh, need another type to hold type... +#[derive(Clone, Debug)] +pub enum MixedDrawableShape { + Shape(DrawnShape), + Text(DrawnTextShape), +} +impl MixedDrawableShape { + pub fn style(&self) -> StyleConf { + match self { + MixedDrawableShape::Shape(drawn_shape) => drawn_shape.style(), + MixedDrawableShape::Text(drawn_text_shape) => drawn_text_shape.style.clone(), + } + } +} + +pub trait ToMixedDrawable { + fn to_mix_drawable(&self) -> MixedDrawableShape; +} + +impl ToMixedDrawable for DrawnShape { + fn to_mix_drawable(&self) -> MixedDrawableShape { + MixedDrawableShape::Shape(self.clone()) + } +} + +pub trait ToMixedDrawables { + fn to_mixed_drawables(&self) -> Vec; +} + +impl ToMixedDrawables for MixedDrawableShape { + fn to_mixed_drawables(&self) -> Vec { + vec![self.clone()] + } +} + +impl ToMixedDrawables for Vec { + fn to_mixed_drawables(&self) -> Vec { + self.clone() + } +} + +impl ToMixedDrawables for DrawnShape { + fn to_mixed_drawables(&self) -> Vec { + vec![MixedDrawableShape::Shape(self.clone())] + } +} + +impl ToMixedDrawables for Vec { + fn to_mixed_drawables(&self) -> Vec { + let mut v = vec![]; + for x in self.iter() { + v.push(MixedDrawableShape::Shape(x.clone())); + } + v + } +} From f3fdb541f993bf25908762d8673273f426ce30f2 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 13 Dec 2025 17:54:21 -0500 Subject: [PATCH 121/149] refactor bird --- murrelet_common/src/geometry.rs | 5 ++ murrelet_draw/src/curve_drawer.rs | 141 +++++++++++++++++++++++++++++- murrelet_draw/src/tesselate.rs | 77 +++++++++++++--- 3 files changed, 208 insertions(+), 15 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 03f2600..45753af 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -729,6 +729,11 @@ impl LineFromVecAndLen { } } + pub fn new_centered(start: Vec2, angle: Angle, length: L) -> Self { + let first_pt = Self::new(start, angle, -0.5 * length.len()).to_last_point(); + Self::new(first_pt, angle, length) + } + pub fn to_last_point(&self) -> Vec2 { self.start + self.length.len() * self.angle.to_norm_dir() } diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 14143ce..5ac4e6a 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -14,7 +14,8 @@ use crate::{ svg::glam_to_lyon, tesselate::{ cubic_bezier_path_to_lyon, flatten_cubic_bezier_path, - flatten_cubic_bezier_path_with_tolerance, parse_svg_data_as_vec2, ToVecVec2, + flatten_cubic_bezier_path_with_tolerance, parse_svg_data_as_vec2, segment_arc, segment_vec, + ToVecVec2, }, transform2d::Transform2d, }; @@ -484,9 +485,41 @@ impl CurveSegment { } } } + + pub fn extend_before(&self, before_amount: f32) -> Self { + let mut c = self.clone(); + match &mut c { + CurveSegment::Arc(curve_arc) => { + let rads = Angle::new(before_amount / curve_arc.radius); + curve_arc.start_pi = curve_arc.start_pi - rads; + } + CurveSegment::Points(curve_points) => todo!(), + CurveSegment::CubicBezier(curve_cubic_bezier) => todo!(), + } + + c + } + + pub fn extend_after(&self, after_amount: f32) -> Self { + let mut c = self.clone(); + match &mut c { + CurveSegment::Arc(curve_arc) => { + let rads = Angle::new(after_amount / curve_arc.radius); + curve_arc.end_pi = curve_arc.end_pi + rads; + } + CurveSegment::Points(curve_points) => todo!(), + CurveSegment::CubicBezier(curve_cubic_bezier) => todo!(), + } + + c + } + + pub fn extend_both(&self, before_amount: f32, after_amount: f32) -> Self { + self.extend_before(before_amount).extend_after(after_amount) + } } -#[derive(Debug, Clone, Livecode, Lerpable)] +#[derive(Debug, Clone, Copy, Livecode, Lerpable)] pub struct CurveArc { #[livecode(serde_default = "zeros")] pub loc: Vec2, // center of circle @@ -637,6 +670,14 @@ impl CurveArc { end_pi: transform.approx_rotate() + self.end_pi, } } + + pub fn length(&self) -> f32 { + self.radius * (self.end_pi - self.start_pi).angle() + } + + pub fn start_pi(&self) -> AnglePi { + self.start_pi + } } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -789,6 +830,12 @@ impl ToCurveSegment for SpotOnCurve { } } +impl ToCurveSegment for PointToPoint { + fn to_segment(&self) -> CurveSegment { + CurveSegment::new_simple_points(vec![self.start(), self.end()]) + } +} + pub trait ToCurveDrawer { fn to_segments(&self) -> Vec; fn to_cd_closed(&self) -> CurveDrawer { @@ -797,6 +844,51 @@ pub trait ToCurveDrawer { fn to_cd_open(&self) -> CurveDrawer { CurveDrawer::new(self.to_segments(), false) } + + // trying out utility functions + fn to_approx_center(&self) -> Vec2 { + // turn it into rough points and then find the center. + // i'm not sure how to deal with tiny/big things.. + // i think we can assume it's not closed, because closing it won't + // change the bounds + let pts = self.to_rough_points(10.0); + let bm = BoundMetric::new_from_points(&pts); + bm.center() + } + + fn to_approx_point(&self) -> Vec2 { + let pts = self.to_rough_points(10.0); + if pts.is_empty() { + return vec2(0.0, 0.0); + } + pts[pts.len() / 2] + } + + // this one isn't evenly spaced + fn to_rough_points(&self, approx_spacing: f32) -> Vec { + let mut result = vec![]; + for s in &self.to_segments() { + let pts = match s { + CurveSegment::Arc(curve_arc) => { + let (s, _) = segment_arc(curve_arc, 0.0, approx_spacing, 0.0); + s.iter().map(|x| x.loc).collect_vec() + } + CurveSegment::Points(curve_points) => { + let mut v = vec![]; + for (curr, next) in curr_next_no_loop_iter(curve_points.points()) { + let (s, _) = segment_vec(*curr, *next, approx_spacing, 0.0); + v.extend(s); + } + v + } + CurveSegment::CubicBezier(curve_cubic_bezier) => curve_cubic_bezier + .to_cubic() + .to_vec2_line_space(approx_spacing), + }; + result.extend(pts) + } + result + } } impl ToCurveDrawer for CurveSegment { @@ -904,3 +996,48 @@ macro_rules! mixed_drawable { v }}; } + +// i get by with a little help from chatgpt +pub trait IntoIterOf { + type Iter: Iterator; + fn into_iter_of(self) -> Self::Iter; +} + +impl IntoIterOf for Vec { + type Iter = std::vec::IntoIter; + fn into_iter_of(self) -> Self::Iter { + self.into_iter() + } +} + +impl IntoIterOf for Option { + type Iter = std::option::IntoIter; + fn into_iter_of(self) -> Self::Iter { + self.into_iter() + } +} + +impl IntoIterOf for T { + type Iter = std::iter::Once; + fn into_iter_of(self) -> Self::Iter { + std::iter::once(self) + } +} + +pub fn extend_flat(out: &mut Vec, x: X) +where + X: IntoIterOf, +{ + out.extend(x.into_iter_of()); +} + +#[macro_export] +macro_rules! flatten { + ($($expr:expr),* $(,)?) => {{ + let mut v = Vec::new(); + $( + extend_flat(&mut v, $expr); + )* + v + }}; +} diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 62b3c59..0a8b5ac 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, ops}; -use crate::{cubic::CubicBezier, curve_drawer::CubicBezierPath, svg::SvgPathDef}; +use crate::{cubic::CubicBezier, curve_drawer::{CubicBezierPath, CurveArc}, svg::SvgPathDef}; use delaunator::Triangulation; use glam::{vec2, Vec2, Vec2Swizzles}; use itertools::Itertools; @@ -14,7 +14,7 @@ use lyon::{ tessellation::{BuffersBuilder, FillOptions, FillTessellator, FillVertex}, }; use murrelet_common::{ - curr_next_no_loop_iter, triangulate::DefaultVertex, PointToPoint, Polyline, SpotOnCurve, ToVec2, + AnglePi, IsAngle, PointToPoint, Polyline, SpotOnCurve, ToVec2, curr_next_no_loop_iter, triangulate::DefaultVertex }; pub trait ToVecVec2 { @@ -59,6 +59,7 @@ impl ToVecVec2 for CubicBezier { path.into_iter().map(|x| vec2(x.y, x.x)).collect_vec() } + fn to_vec2(&self) -> Vec { self.to_vec2_line_space(1.0) } @@ -156,6 +157,67 @@ pub fn segment_vec(from: Vec2, to: Vec2, line_space: f32, offset: f32) -> (Vec (Vec, f32) { + // going into this curve, we should be all caught up + let multi = if curve.is_ccw() { 1.0 } else { -1.0 }; + let radius = curve.radius.abs(); + + let increase_ratio = (radius + 0.5 * height) / radius; + let mut line_space = line_space / increase_ratio; + + // make sure we always have at least a few points + let max_delta_angle = AnglePi::new(0.1); + line_space = line_space.min(radius * max_delta_angle._angle()); + + // expect line_space to be > 0 + if line_space <= 0.0 { + return (vec![], offset); + } + + // okay! now if we already traveled some distance along this curve (e.g. dist_since_last > 0) + // we need to remove some of it + let diameter = AnglePi::new(2.0).scale(radius).angle(); + + let estimated_size = (diameter / line_space) as usize; + let mut vs = Vec::with_capacity(estimated_size); + //vec![]; + + let mut residual = offset + curve.length(); + + // the last point was shifted back + let mut loc_on_arc = -offset; + + while residual >= line_space { + residual -= line_space; + loc_on_arc += line_space; + + let central_angle_from_start = AnglePi::new(2.0).scale(multi * loc_on_arc / diameter); + let curr_angle = curve.start_pi() + central_angle_from_start; + + let norm_vec = curr_angle.to_norm_dir(); + let curr_point = norm_vec * radius + curve.loc; + + let a = if curve.is_ccw() { + curr_angle.perp_to_right() + } else { + curr_angle.perp_to_left() + }; + + // println!("central_angle_from_start {:?}", central_angle_from_start); + // println!("curr_angle {:?}", curr_angle); + let s = SpotOnCurve::new(curr_point, a); + // println!("s {:?}", s); + vs.push(s); + } + + (vs, residual / increase_ratio) +} + pub fn evenly_split_cubic_bezier(c: &CubicBezier, count: usize) -> Vec { // always include the start (and end) let v = flatten_cubic_bezier_path_with_tolerance(&vec![c.clone()], false, 0.1); @@ -197,19 +259,8 @@ pub fn evenly_split_cubic_bezier(c: &CubicBezier, count: usize) -> Vec Date: Sat, 13 Dec 2025 18:38:20 -0500 Subject: [PATCH 122/149] add caching helpers --- murrelet_livecode/src/cachedcompute.rs | 14 ++ murrelet_livecode/src/lib.rs | 1 + .../examples/tests.rs | 13 ++ .../src/derive_cached.rs | 122 ++++++++++++++++++ .../murrelet_livecode_derive/src/lib.rs | 10 ++ .../murrelet_livecode_derive/src/parser.rs | 24 ++++ 6 files changed, 184 insertions(+) create mode 100644 murrelet_livecode/src/cachedcompute.rs create mode 100644 murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs diff --git a/murrelet_livecode/src/cachedcompute.rs b/murrelet_livecode/src/cachedcompute.rs new file mode 100644 index 0000000..77afe76 --- /dev/null +++ b/murrelet_livecode/src/cachedcompute.rs @@ -0,0 +1,14 @@ +use std::cell::OnceCell; + +#[derive(Clone, Debug)] +pub struct CachedCompute(OnceCell); + +impl CachedCompute { + pub fn new() -> Self { + Self(OnceCell::new()) + } + + pub fn has_been_set(&self) -> bool { + self.0.get().is_some() + } +} diff --git a/murrelet_livecode/src/lib.rs b/murrelet_livecode/src/lib.rs index 326e80b..f76a4a3 100644 --- a/murrelet_livecode/src/lib.rs +++ b/murrelet_livecode/src/lib.rs @@ -7,3 +7,4 @@ pub mod nestedit; pub mod state; pub mod types; pub mod unitcells; +pub mod cachedcompute; diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index 35ab002..146d82b 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -5,6 +5,8 @@ use lerpable::Lerpable; use murrelet_common::*; use murrelet_livecode::{types::AdditionalContextNode, unitcells::*}; use murrelet_livecode_derive::{Livecode, LivecodeOnly, NestEdit}; +use murrelet_livecode::cachedcompute::CachedCompute; +use murrelet_livecode_derive::Cached; #[derive(Debug, Clone, Livecode, Lerpable, Default)] pub struct BasicTypes { @@ -152,3 +154,14 @@ pub struct NewTypeWithVec(Vec); pub struct NewTypeWithStruct(BasicTypes); fn main() {} + + + + +#[derive(Debug, Clone, Cached)] +pub struct BirdOutline { + + back: CachedCompute, + neck_back: CachedCompute>, + +} \ No newline at end of file diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs new file mode 100644 index 0000000..7e241a5 --- /dev/null +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs @@ -0,0 +1,122 @@ +use darling::{ast, FromDeriveInput, FromField, FromVariant}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::DeriveInput; + +use crate::parser::ident_from_type; + +#[derive(Debug, FromField, Clone)] +#[darling(attributes(cached))] +pub(crate) struct LivecodeFieldReceiver { + pub(crate) ident: Option, + pub(crate) ty: syn::Type, +} + +// for enums +#[derive(Debug, FromVariant, Clone)] +#[darling(attributes(cached))] +pub(crate) struct LivecodeVariantReceiver {} + +#[derive(Debug, Clone, FromDeriveInput)] +#[darling(attributes(cached), supports(struct_named))] +pub(crate) struct LivecodeReceiver { + ident: syn::Ident, + data: ast::Data, + // ctrl: Option, // this one should be the struct name... +} + +pub fn impl_cache_traits(ast: DeriveInput) -> TokenStream2 { + let ast_receiver = LivecodeReceiver::from_derive_input(&ast).unwrap(); + + match &ast_receiver.data { + ast::Data::Enum(_) => unreachable!("hm, only works on structs"), + ast::Data::Struct(fields) => parse_cache(&ast_receiver.ident, &fields.fields), + } +} + +fn parse_cache(name: &syn::Ident, fields: &[LivecodeFieldReceiver]) -> TokenStream2 { + + + let mut getter_funcs: Vec = vec![]; + let mut check_funcs: Vec = vec![]; + let mut init_funcs: Vec = vec![]; + let mut to_be_filled_funcs: Vec = vec![]; + let mut conf_arguments: Vec = vec![]; + for f in fields { + if let Some(ident) = &f.ident { + let ident = ident.clone(); + let data = ident_from_type(&f.ty); + + // if it uses our type, we use that is our giveaway + if data.main_type.to_string().eq("CachedCompute") { + // there should be a function called compute_$ident + + let expected_compute_name = format!("compute_{}", ident); + let expected_compute_ident = syn::Ident::new(&expected_compute_name, ident.span()); + + let inside_type = data.inside_type().to_quote(); + + + let func = quote! { + fn #ident(&self) -> &#inside_type { + self.#ident.get_or_init(|| self.#expected_compute_ident()) + } + }; + + getter_funcs.push(func); + + let check = quote!{ + self.#ident.has_been_set() + }; + + check_funcs.push(check); + + let init = quote!{ + self.#ident() + }; + + init_funcs.push(init); + + let to_be_filled = quote!{ + #ident: CachedCompute::new() + }; + + to_be_filled_funcs.push(to_be_filled); + } else { + + // they're passed in with the same name in the arguments + let orig_type = f.ty.clone(); + let new_conf_argument = quote!{ + #ident: #orig_type + }; + conf_arguments.push(new_conf_argument); + let to_be_filled = quote!{ + #ident + }; + to_be_filled_funcs.push(to_be_filled); + + } + } + } + + quote! { + impl #name { + #(#getter_funcs)* + + fn cached_has_been_set(&self) -> bool { + true #( && #check_funcs )* + } + + fn init_all_cached(&self) -> bool { + #(#init_funcs;)* + } + + fn new(&self, #(#conf_arguments,)*) -> Self { + #name { + #(#to_be_filled_funcs,)* + } + } + + } + } +} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs index bb3c4b0..7e36f38 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs @@ -11,6 +11,7 @@ mod derive_livecode; mod derive_nestedit; mod parser; mod toplevel; +mod derive_cached; use darling::FromDeriveInput; // use derive_boop::FieldTokensBoop; @@ -26,6 +27,8 @@ use toplevel::{impl_all_the_traits, top_level_livecode, top_level_livecode_json} use quote::quote; +use crate::derive_cached::impl_cache_traits; + fn livecode_parse_ast(rec: LivecodeReceiver) -> TokenStream2 { FieldTokensLivecode::from_ast(rec) } @@ -116,3 +119,10 @@ pub fn murrelet_livecode_graphics(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as syn::DeriveInput); impl_graphics_trait(ast).into() } + + +#[proc_macro_derive(Cached, attributes(cached))] +pub fn murrelet_cache_compute(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as syn::DeriveInput); + impl_cache_traits(ast).into() +} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index f8ef67b..2ae7aa2 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -843,6 +843,30 @@ impl DataFromType { _ => VecDepth::NotAVec, } } + + pub(crate) fn inside_type(&self) -> Self { + Self { + main_type: self.second_type.clone().unwrap(), + second_type: self.third_type.clone(), + third_type: self.fourth_type.clone(), + fourth_type: None, + main_how_to: self.second_how_to.unwrap(), + second_how_to: self.third_how_to, + third_how_to: self.fourth_how_to, + fourth_how_to: None, + } + } + + pub(crate) fn to_quote(&self) -> TokenStream2 { + let main_type = self.main_type.clone(); + match (&self.second_type, &self.third_type, &self.fourth_type) { + (None, None, None) => quote!{ #main_type }, + (Some(second_type), None, None) => quote!{ #main_type<#second_type> }, + (Some(second_type), Some(third_type), None) => quote!{ #main_type<#second_type<#third_type>> }, + (Some(second_type), Some(third_type), Some(fourth_type)) => quote!{ #main_type<#second_type<#third_type<#fourth_type>>> }, + _ => unreachable!(), + } + } } #[derive(Debug)] From ec62a7e77be4d1d1477ef1f4373ae0e4de2bf601 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 14 Dec 2025 17:05:24 -0500 Subject: [PATCH 123/149] wip --- murrelet_livecode/src/cachedcompute.rs | 7 +++++++ .../murrelet_livecode_derive/src/derive_cached.rs | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/murrelet_livecode/src/cachedcompute.rs b/murrelet_livecode/src/cachedcompute.rs index 77afe76..3c34643 100644 --- a/murrelet_livecode/src/cachedcompute.rs +++ b/murrelet_livecode/src/cachedcompute.rs @@ -11,4 +11,11 @@ impl CachedCompute { pub fn has_been_set(&self) -> bool { self.0.get().is_some() } + + pub fn get_or_init(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + self.0.get_or_init(f) + } } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs index 7e241a5..08e57df 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs @@ -107,11 +107,11 @@ fn parse_cache(name: &syn::Ident, fields: &[LivecodeFieldReceiver]) -> TokenStre true #( && #check_funcs )* } - fn init_all_cached(&self) -> bool { + fn init_all_cached(&self) { #(#init_funcs;)* } - fn new(&self, #(#conf_arguments,)*) -> Self { + fn new(#(#conf_arguments,)*) -> Self { #name { #(#to_be_filled_funcs,)* } From ba408869af4fcfa7eaba7dc451e5618927934579 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 20 Dec 2025 16:20:35 -0800 Subject: [PATCH 124/149] wip --- murrelet_common/src/geometry.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 45753af..6dde4fb 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -712,6 +712,10 @@ impl PointToPoint { pub fn flip(&self) -> PointToPoint { PointToPoint::new(self.end, self.start) } + + pub fn pct_spot(&self, x: f32) -> SpotOnCurve { + SpotOnCurve::new(self.pct(x), self.angle()) + } } #[derive(Copy, Clone, Debug)] @@ -817,3 +821,7 @@ impl ToVec2 for DefaultVertex { self.pos2d() } } + +pub fn sagitta_from_arc_len(radius: f32, central_angle: AnglePi) -> f32 { + radius * (1.0 - (0.5 * central_angle.angle()).cos()) +} From b49d34a9d5268f68c76cf1857f43824c386c6104 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 21 Dec 2025 19:01:36 -0800 Subject: [PATCH 125/149] wip --- murrelet_draw/src/cubic.rs | 2 + murrelet_draw/src/curve_drawer.rs | 86 +++- murrelet_draw/src/drawable.rs | 2 +- murrelet_draw/src/scaffold.rs | 400 ++++++++++++++---- murrelet_gpu/src/compute.rs | 3 +- murrelet_gpu/src/graphics_ref.rs | 2 +- murrelet_livecode/src/livecode.rs | 1 - murrelet_livecode/src/state.rs | 8 +- murrelet_livecode/src/types.rs | 6 +- .../src/derive_lazy.rs | 2 +- 10 files changed, 415 insertions(+), 97 deletions(-) diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index bd15977..5a26b54 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -154,4 +154,6 @@ impl CubicBezier { to: f(self.to), } } + + } diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 5ac4e6a..bcdba84 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -307,6 +307,20 @@ impl CurveDrawer { Ok(Self::new(segments, self.closed)) } + + pub fn segments_pseudo_closed(&self) -> Vec { + let mut segments = self.segments().to_vec(); + // if it's closed, then add the first point, otherwise same as rest + if !self.closed { + return segments; + } else { + // if there's no first point, just return itself (which is empty...) + if let Some(first_point) = self.first_point() { + segments.push(CurveSegment::new_simple_point(first_point)); + } + segments + } + } } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -493,8 +507,8 @@ impl CurveSegment { let rads = Angle::new(before_amount / curve_arc.radius); curve_arc.start_pi = curve_arc.start_pi - rads; } - CurveSegment::Points(curve_points) => todo!(), - CurveSegment::CubicBezier(curve_cubic_bezier) => todo!(), + CurveSegment::Points(_) => todo!(), + CurveSegment::CubicBezier(_) => todo!(), } c @@ -507,8 +521,8 @@ impl CurveSegment { let rads = Angle::new(after_amount / curve_arc.radius); curve_arc.end_pi = curve_arc.end_pi + rads; } - CurveSegment::Points(curve_points) => todo!(), - CurveSegment::CubicBezier(curve_cubic_bezier) => todo!(), + CurveSegment::Points(_) => todo!(), + CurveSegment::CubicBezier(_) => todo!(), } c @@ -672,7 +686,7 @@ impl CurveArc { } pub fn length(&self) -> f32 { - self.radius * (self.end_pi - self.start_pi).angle() + self.radius.abs() * (self.end_pi - self.start_pi).angle() } pub fn start_pi(&self) -> AnglePi { @@ -838,11 +852,17 @@ impl ToCurveSegment for PointToPoint { pub trait ToCurveDrawer { fn to_segments(&self) -> Vec; + fn to_cd(&self, is_closed: bool) -> CurveDrawer { + CurveDrawer::new(self.to_segments(), is_closed) + } + fn to_cd_closed(&self) -> CurveDrawer { - CurveDrawer::new(self.to_segments(), true) + // CurveDrawer::new(self.to_segments(), true) + self.to_cd(true) } fn to_cd_open(&self) -> CurveDrawer { - CurveDrawer::new(self.to_segments(), false) + // CurveDrawer::new(self.to_segments(), false) + self.to_cd(false) } // trying out utility functions @@ -856,6 +876,7 @@ pub trait ToCurveDrawer { bm.center() } + // chooses an arbitrary point on the path, like for a label fn to_approx_point(&self) -> Vec2 { let pts = self.to_rough_points(10.0); if pts.is_empty() { @@ -864,7 +885,7 @@ pub trait ToCurveDrawer { pts[pts.len() / 2] } - // this one isn't evenly spaced + // this one isn't evenly spaced yet fn to_rough_points(&self, approx_spacing: f32) -> Vec { let mut result = vec![]; for s in &self.to_segments() { @@ -889,6 +910,49 @@ pub trait ToCurveDrawer { } result } + + fn to_rough_spots(&self, approx_spacing: f32) -> Vec { + let mut result = vec![]; + let mut curr_offset = 0.0; + for s in &self.to_segments() { + let pts = match s { + CurveSegment::Arc(curve_arc) => { + let (s, new_offset) = segment_arc(curve_arc, 0.0, approx_spacing, curr_offset); + curr_offset = new_offset; + s + } + CurveSegment::Points(curve_points) => { + let mut v = vec![]; + for (curr, next) in curr_next_no_loop_iter(curve_points.points()) { + let (s, new_offset) = + segment_vec(*curr, *next, approx_spacing, curr_offset); + let angle = PointToPoint::new(*curr, *next).angle(); + v.extend(s.iter().map(|x| SpotOnCurve::new(*x, angle))); + curr_offset = new_offset; + } + v + } + CurveSegment::CubicBezier(curve_cubic_bezier) => { + let curve_points = curve_cubic_bezier + .to_cubic() + .to_vec2_line_space(approx_spacing); + + // now treat it like a point, so we can get the offset and angle. + let mut v = vec![]; + for (curr, next) in curr_next_no_loop_iter(&curve_points) { + let (s, new_offset) = + segment_vec(*curr, *next, approx_spacing, curr_offset); + let angle = PointToPoint::new(*curr, *next).angle(); + v.extend(s.iter().map(|x| SpotOnCurve::new(*x, angle))); + curr_offset = new_offset; + } + v + } + }; + result.extend(pts) + } + result + } } impl ToCurveDrawer for CurveSegment { @@ -912,6 +976,12 @@ impl ToCurveDrawer for Vec { } } +impl ToCurveDrawer for CurveDrawer { + fn to_segments(&self) -> Vec { + self.segments_pseudo_closed() + } +} + impl ToCurveDrawer for Option where T: ToCurveDrawer, diff --git a/murrelet_draw/src/drawable.rs b/murrelet_draw/src/drawable.rs index a95975c..cb505a2 100644 --- a/murrelet_draw/src/drawable.rs +++ b/murrelet_draw/src/drawable.rs @@ -145,7 +145,7 @@ impl PositionedText { } } - fn with_style(&self, style: &StyleConf) -> MixedDrawableShape { + pub fn with_style(&self, style: &StyleConf) -> MixedDrawableShape { MixedDrawableShape::Text(DrawnTextShape { text: vec![self.clone()], style: style.clone(), diff --git a/murrelet_draw/src/scaffold.rs b/murrelet_draw/src/scaffold.rs index 53d38bd..8c49e9a 100644 --- a/murrelet_draw/src/scaffold.rs +++ b/murrelet_draw/src/scaffold.rs @@ -1,6 +1,241 @@ +#![allow(dead_code)] use geo::{BooleanOps, BoundingRect, Contains}; use glam::{vec2, Vec2}; use itertools::Itertools; +use murrelet_common::SpotOnCurve; +// use leave_common::prelude::*; +use crate::{curve_drawer::{CurveDrawer, ToCurveDrawer}, drawable::DrawnShape}; + +// pub fn line_to_multipolygon(curves: &[Vec2]) -> geo::MultiPolygon { +// geo::MultiPolygon::new(vec![line_to_polygon(curves)]) +// } + +// pub fn line_to_polygon(curves: &[Vec2]) -> geo::Polygon { +// geo::Polygon::new(vec2_to_line_string(curves), vec![]) +// } + +// fn vec2_to_line_string(vs: &[Vec2]) -> geo::LineString { +// let coords = vs.iter().map(|x| x.to_coord()).collect_vec(); +// geo::LineString::new(coords) +// } + +// pub fn multipolygon_to_vec2(p: &geo::MultiPolygon) -> Vec> { +// p.iter().map(|pp| polygon_to_vec2(pp)).collect_vec() +// } + +// pub fn polygon_to_vec2(p: &geo::Polygon) -> Vec { +// let mut coords = p +// .exterior() +// .coords() +// .into_iter() +// .map(|coord| coord.to_vec2()) +// .collect_vec(); + +// if coords.first() == coords.last() { +// coords.pop(); +// } + +// coords +// } + +// trait ToCoord { +// fn to_coord(&self) -> ::geo::Coord; +// } + +// impl ToCoord for Vec2 { +// fn to_coord(&self) -> ::geo::Coord { +// ::geo::coord! {x: self.x as f64, y: self.y as f64} +// } +// } + +// trait ToVec2Griddable { +// fn to_vec2(&self) -> Vec2; +// } + +// impl ToVec2Griddable for ::geo::Coord { +// fn to_vec2(&self) -> Vec2 { +// let (x, y) = self.x_y(); +// vec2(x as f32, y as f32) +// } +// } + +// #[derive(Debug, Clone)] +// pub struct MaskCacheImpl { +// bounding: geo::Rect, +// polygon: geo::Polygon, +// } +// impl MaskCacheImpl { +// fn center(&self) -> Vec2 { +// 0.5 * (self.bounding.min().to_vec2() + self.bounding.max().to_vec2()) +// } + +// fn contains(&self, v: &Vec2) -> bool { +// self.polygon.contains(&v.to_coord()) +// } + +// // left needs to be inside +// fn last_point_containing(&self, left: &Vec2, right: &Vec2) -> Vec2 { +// let mut left = *left; +// let mut right = *right; +// while left.distance(right) > 0.2 { +// let midpoint = 0.5 * (left + right); +// if self.contains(&midpoint) { +// left = midpoint; +// } else { +// right = midpoint; +// } +// } +// // finished! +// 0.5 * (left + right) +// } +// } + +// #[derive(Debug, Clone)] +// pub enum MaskCache { +// Impl(MaskCacheImpl), +// AlwaysTrue, +// } + +// impl MaskCache { +// fn center(&self) -> Vec2 { +// match self { +// MaskCache::Impl(s) => s.center(), +// MaskCache::AlwaysTrue => Vec2::ZERO, +// } +// } + +// pub fn last_point_containing(&self, left: &Vec2, right: &Vec2) -> Vec2 { +// match self { +// MaskCache::Impl(s) => s.last_point_containing(left, right), +// MaskCache::AlwaysTrue => *right, +// } +// } + +// pub fn new_vec2(curves: &[Vec2]) -> Self { +// let polygon = geo::Polygon::new(vec2_to_line_string(curves), vec![]); + +// MaskCache::Impl(MaskCacheImpl { +// bounding: polygon.bounding_rect().unwrap(), +// polygon, +// }) +// } + +// // pub fn new_cd(cd: CurveDrawer) -> Self { +// // Self::new(&vec![cd]) +// // } + +// // pub fn new(curves: &[CurveDrawer]) -> Self { +// // // first curve is external +// // let (first_curve, rest) = curves.split_first().unwrap(); +// // let first = curve_segment_maker_to_line_string(first_curve); + +// // let mut remaining = vec![]; +// // // add all our points to a hashmap +// // for curve_maker in rest { +// // remaining.push(curve_segment_maker_to_line_string(curve_maker)); +// // } + +// // let polygon = ::geo::Polygon::new(first, remaining); + +// // MaskCache::Impl(MaskCacheImpl { +// // bounding: polygon.bounding_rect().unwrap(), +// // polygon, +// // }) +// // } + +// pub fn contains(&self, v: &Vec2) -> bool { +// match self { +// MaskCache::Impl(x) => x.contains(v), +// MaskCache::AlwaysTrue => true, +// } +// } + +// pub fn noop() -> MaskCache { +// MaskCache::AlwaysTrue +// } + +// pub fn crop(&self, shape: &[Vec2]) -> Vec> { +// match self { +// MaskCache::Impl(x) => { +// let other = line_to_polygon(shape); +// let cropped = x.polygon.intersection(&other); +// multipolygon_to_vec2(&cropped) +// } +// MaskCache::AlwaysTrue => vec![shape.to_vec()], +// } +// } + +// // remove this object from all of the shapes +// pub fn crop_inverse(&self, shape: &[Vec2]) -> Vec> { +// match self { +// MaskCache::Impl(x) => { +// let other = line_to_polygon(shape); +// let cropped = other.difference(&x.polygon); +// multipolygon_to_vec2(&cropped) +// } +// MaskCache::AlwaysTrue => vec![shape.to_vec()], +// } +// } + +// pub fn to_vec2(&self) -> Vec { +// match self { +// MaskCache::Impl(mask_cache_impl) => polygon_to_vec2(&mask_cache_impl.polygon), +// MaskCache::AlwaysTrue => unreachable!(), +// } +// } + +// pub fn crop_line(&self, v: &[Vec2]) -> Vec> { +// let mut all_vals = vec![]; +// let mut s = vec![]; +// let mut last_val = None; +// for c in v.into_iter() { +// if self.contains(&c) { +// last_val = Some(c); +// s.push(*c) +// } else if let Some(x) = last_val { +// let last_point_containing = self.last_point_containing(&x, &c); +// s.push(last_point_containing); + +// all_vals.push(s); +// last_val = None; +// s = vec![]; +// } +// } +// if s.len() > 0 { +// all_vals.push(s); +// } + +// all_vals +// } + +// // pub fn crop_many(&self, v: &[DrawnShape]) -> Vec { +// // let mut cropped = vec![]; +// // for a in v.into_iter() { +// // let mut new_cds = vec![]; +// // for cd in a.faces() { +// // new_cds.extend(self.crop(cd.vertices())); +// // } +// // cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); +// // } +// // return cropped; +// // } + +// // pub fn crop_inverse_many(&self, v: &[DrawnShape]) -> Vec { +// // let mut cropped = vec![]; +// // for a in v.into_iter() { +// // let mut new_cds = vec![]; +// // for cd in a.faces() { +// // new_cds.extend(self.crop_inverse(cd.vertices())); +// // } +// // cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); +// // } +// // return cropped; +// // } +// } + + + +// use crate::curve::{ToFaces, WithLeaveCurveDrawerMethods}; pub fn line_to_multipolygon(curves: &[Vec2]) -> geo::MultiPolygon { geo::MultiPolygon::new(vec![line_to_polygon(curves)]) @@ -10,11 +245,6 @@ pub fn line_to_polygon(curves: &[Vec2]) -> geo::Polygon { geo::Polygon::new(vec2_to_line_string(curves), vec![]) } -fn vec2_to_line_string(vs: &[Vec2]) -> geo::LineString { - let coords = vs.iter().map(|x| x.to_coord()).collect_vec(); - geo::LineString::new(coords) -} - pub fn multipolygon_to_vec2(p: &geo::MultiPolygon) -> Vec> { p.iter().map(|pp| polygon_to_vec2(pp)).collect_vec() } @@ -34,16 +264,6 @@ pub fn polygon_to_vec2(p: &geo::Polygon) -> Vec { coords } -trait ToCoord { - fn to_coord(&self) -> ::geo::Coord; -} - -impl ToCoord for Vec2 { - fn to_coord(&self) -> ::geo::Coord { - ::geo::coord! {x: self.x as f64, y: self.y as f64} - } -} - trait ToVec2Griddable { fn to_vec2(&self) -> Vec2; } @@ -55,6 +275,16 @@ impl ToVec2Griddable for ::geo::Coord { } } +trait ToCoord { + fn to_coord(&self) -> ::geo::Coord; +} + +impl ToCoord for Vec2 { + fn to_coord(&self) -> ::geo::Coord { + ::geo::coord! {x: self.x as f64, y: self.y as f64} + } +} + #[derive(Debug, Clone)] pub struct MaskCacheImpl { bounding: geo::Rect, @@ -86,6 +316,15 @@ impl MaskCacheImpl { } } +fn curve_segment_maker_to_line_string(curve: &CurveDrawer) -> geo::LineString { + vec2_to_line_string(&curve.to_rough_points(1.0)) +} + +fn vec2_to_line_string(vs: &[Vec2]) -> geo::LineString { + let coords = vs.iter().map(|x| x.to_coord()).collect_vec(); + geo::LineString::new(coords) +} + #[derive(Debug, Clone)] pub enum MaskCache { Impl(MaskCacheImpl), @@ -93,7 +332,7 @@ pub enum MaskCache { } impl MaskCache { - fn center(&self) -> Vec2 { + pub fn center(&self) -> Vec2 { match self { MaskCache::Impl(s) => s.center(), MaskCache::AlwaysTrue => Vec2::ZERO, @@ -116,28 +355,35 @@ impl MaskCache { }) } - // pub fn new_cd(cd: CurveDrawer) -> Self { - // Self::new(&vec![cd]) - // } + pub fn new_cd(cd: CurveDrawer) -> Self { + Self::new(&vec![cd]) + } + + pub fn new_interior(outline: CurveDrawer, interior: &[CurveDrawer]) -> Self { + // shh, just a wrapper + let s = vec![vec![outline], interior.to_vec()].concat(); + Self::new(&s) - // pub fn new(curves: &[CurveDrawer]) -> Self { - // // first curve is external - // let (first_curve, rest) = curves.split_first().unwrap(); - // let first = curve_segment_maker_to_line_string(first_curve); + } - // let mut remaining = vec![]; - // // add all our points to a hashmap - // for curve_maker in rest { - // remaining.push(curve_segment_maker_to_line_string(curve_maker)); - // } + pub fn new(curves: &[CurveDrawer]) -> Self { + // first curve is external + let (first_curve, rest) = curves.split_first().unwrap(); + let first = curve_segment_maker_to_line_string(first_curve); - // let polygon = ::geo::Polygon::new(first, remaining); + let mut remaining = vec![]; + // add all our points to a hashmap + for curve_maker in rest { + remaining.push(curve_segment_maker_to_line_string(curve_maker)); + } - // MaskCache::Impl(MaskCacheImpl { - // bounding: polygon.bounding_rect().unwrap(), - // polygon, - // }) - // } + let polygon = ::geo::Polygon::new(first, remaining); + + MaskCache::Impl(MaskCacheImpl { + bounding: polygon.bounding_rect().unwrap(), + polygon, + }) + } pub fn contains(&self, v: &Vec2) -> bool { match self { @@ -180,51 +426,51 @@ impl MaskCache { } } - pub fn crop_line(&self, v: &[Vec2]) -> Vec> { - let mut all_vals = vec![]; - let mut s = vec![]; - let mut last_val = None; - for c in v.into_iter() { - if self.contains(&c) { - last_val = Some(c); - s.push(*c) - } else if let Some(x) = last_val { - let last_point_containing = self.last_point_containing(&x, &c); - s.push(last_point_containing); - - all_vals.push(s); - last_val = None; - s = vec![]; + pub fn crop_many(&self, v: &[DrawnShape]) -> Vec { + let mut cropped = vec![]; + for a in v.into_iter() { + let mut new_cds = vec![]; + for cd in a.curves() { + new_cds.extend(self.crop(&cd.to_rough_points(1.0))); } + cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); } - if s.len() > 0 { - all_vals.push(s); + return cropped; + } + + pub fn crop_inverse_many(&self, v: &[DrawnShape]) -> Vec { + let mut cropped = vec![]; + for a in v.into_iter() { + let mut new_cds = vec![]; + for cd in a.curves() { + new_cds.extend(self.crop_inverse(&cd.to_rough_points(1.0))); + } + cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); } + return cropped; + } - all_vals - } - - // pub fn crop_many(&self, v: &[DrawnShape]) -> Vec { - // let mut cropped = vec![]; - // for a in v.into_iter() { - // let mut new_cds = vec![]; - // for cd in a.faces() { - // new_cds.extend(self.crop(cd.vertices())); - // } - // cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); - // } - // return cropped; - // } - - // pub fn crop_inverse_many(&self, v: &[DrawnShape]) -> Vec { - // let mut cropped = vec![]; - // for a in v.into_iter() { - // let mut new_cds = vec![]; - // for cd in a.faces() { - // new_cds.extend(self.crop_inverse(cd.vertices())); - // } - // cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); - // } - // return cropped; - // } + pub fn ray_intersect(&self, spot: &SpotOnCurve) -> Vec2 { + self.last_point_containing(&spot.loc, &spot.line_to_spot(1000.0)) + } + pub fn rect(&self) -> murrelet_common::Rect { + match self { + MaskCache::Impl(mask_cache_impl) => { + let w = mask_cache_impl.bounding.width(); + let h = mask_cache_impl.bounding.height(); + let center = mask_cache_impl.bounding.center(); + + murrelet_common::Rect::from_xy_wh(center.to_vec2(), vec2(w as f32, h as f32)) + } + MaskCache::AlwaysTrue => todo!(), + } + + // match self { + // MaskCache::Impl(mask_cache_impl) => { + // mask_cache_impl. + // + // }, + // MaskCache::AlwaysTrue => todo!(), + // } + } } diff --git a/murrelet_gpu/src/compute.rs b/murrelet_gpu/src/compute.rs index 0c8ff28..b8f87a0 100644 --- a/murrelet_gpu/src/compute.rs +++ b/murrelet_gpu/src/compute.rs @@ -18,7 +18,7 @@ use crate::{ window::GraphicsWindowConf, }; -struct ComputeBindings { +pub struct ComputeBindings { input: wgpu::Buffer, cell_offsets: wgpu::Buffer, cell_indices: wgpu::Buffer, @@ -163,6 +163,7 @@ pub struct ComputeGraphicsToTexture { // render_pipeline: wgpu::RenderPipeline, pub uniforms: BasicUniform, pub buffers: ComputeBindings, + #[allow(dead_code)] texture: TextureAndDesc, bind_group_layout: wgpu::BindGroupLayout, diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index 7e1a9f5..989d3c2 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -664,7 +664,7 @@ impl GraphicsRefCustom { graphics_rc.update_uniforms_other_tuple(c, more_info) } - #[deprecated(note = "Use render_to_view instead")] + #[deprecated(note = "Use render instead")] pub fn render_to_view(&self, device: &DeviceState, view: &wgpu::TextureView) { // self.graphics.borrow_mut().render(device, view) self.render(device, view) diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index f9e27d7..6821199 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -20,7 +20,6 @@ use crate::lazy::LazyNodeF32; use crate::state::LivecodeWorldState; use crate::types::AdditionalContextNode; use crate::types::ControlVecElement; -use crate::types::DeserLazyControlVecElement; use crate::types::LivecodeError; use crate::types::LivecodeResult; diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index a2dec31..f6f415b 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -8,8 +8,8 @@ use murrelet_common::*; use crate::{ expr::{ - init_evalexpr_func_ctx, lc_val_to_expr, ExprWorldContextValues, IntoExprWorldContext, - MixedEvalDefs, MixedEvalDefsRef, + init_evalexpr_func_ctx, ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefs, + MixedEvalDefsRef, }, types::{AdditionalContextNode, LivecodeResult}, unitcells::UnitCellContext, @@ -200,7 +200,7 @@ impl LivecodeWorldState { self.state.asset_layers_in_key(key) } - pub(crate) fn update_with_simple_defs( + pub fn update_with_simple_defs( &self, more_vals: ExprWorldContextValues, ) -> WorldWithLocalVariables { @@ -255,7 +255,7 @@ impl WorldWithLocalVariables { impl Context for WorldWithLocalVariables { fn get_value(&self, identifier: &str) -> Option<&Value> { // locals win - if let Some((_, v)) = self.locals.iter().find(|(k, v)| k == identifier) { + if let Some((_, v)) = self.locals.iter().find(|(k, _v)| k == identifier) { return Some(v); } // otherwise fallback to global diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index e762508..c685025 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -3,7 +3,7 @@ use std::{collections::HashSet, fmt::Debug}; use evalexpr::{build_operator_tree, EvalexprError, HashMapContext, Node}; use itertools::Itertools; use lerpable::{step, Lerpable}; -use murrelet_common::{IdxInRange, IdxInRange2d, LivecodeValue, print_expect}; +use murrelet_common::{IdxInRange, IdxInRange2d, print_expect}; use murrelet_gui::CanMakeGUI; use serde::{Deserialize, Deserializer}; use thiserror::Error; @@ -567,7 +567,7 @@ impl ControlVecElementRepeat { for idx in self.repeat.iter(w)? { let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); - let mut new_w = w.clone_with_vals(expr, &prefix); + let new_w = w.clone_with_vals(expr, &prefix); for src in &self.what { match src { @@ -720,7 +720,7 @@ where { // type Target = Vec; - fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult> { + pub fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult> { match self { LazyControlVecElement::Single(s) => Ok(vec![s.clone()]), LazyControlVecElement::Repeat(s) => s.lazy_expand_vec(expr), diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index 258b879..5843e08 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -488,7 +488,7 @@ impl GenFinal for FieldTokensLazy { quote! {#back_to_quote #name: #t} }; - let for_world = LazyFieldType(ctrl).for_world(idents.clone()); + let _for_world = LazyFieldType(ctrl).for_world(idents.clone()); let for_world = LazyFieldType(ctrl).for_world(idents.clone()); let for_more_defs = { From a187fc526d89437a12d44c0e51200d438b356f05 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 22 Dec 2025 21:29:26 -0800 Subject: [PATCH 126/149] wip --- murrelet_common/src/lib.rs | 50 +++++++++++++- murrelet_draw/src/compass.rs | 110 ++++++++++++++++++++++++++---- murrelet_draw/src/curve_drawer.rs | 2 +- 3 files changed, 144 insertions(+), 18 deletions(-) diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 860808b..8542ef9 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -249,8 +249,6 @@ where } } - - pub fn cubic_bezier(start: Vec2, ctrl1: Vec2, ctrl2: Vec2, to: Vec2, t: f32) -> Vec2 { let a = lerp(start, ctrl1, t); let b = lerp(ctrl1, ctrl2, t); @@ -1088,3 +1086,51 @@ pub fn rgb_to_hex(r: f32, g: f32, b: f32) -> String { format!("#{:02X}{:02X}{:02X}", r, g, b) } + +pub trait MurreletIterHelpers { + type T; + fn to_iter<'a>(&'a self) -> std::slice::Iter<'a, Self::T>; + fn as_vec_ref<'a>(&'a self) -> &'a Vec; + + fn map_iter_collect(&self, f: F) -> Vec + where + F: Fn(&Self::T) -> U, + { + self.to_iter().map(f).collect_vec() + } + + fn prev_curr_next_loop_iter<'a>( + &'a self, + ) -> Box + 'a> { + prev_curr_next_loop_iter(&self.as_vec_ref()) + } + + fn prev_curr_next_no_loop_iter<'a>( + &'a self, + ) -> Box + 'a> { + prev_curr_next_no_loop_iter(&self.as_vec_ref()) + } + + fn curr_next_loop_iter<'a>( + &'a self, + ) -> Box + 'a> { + curr_next_loop_iter(&self.as_vec_ref()) + } + + fn curr_next_no_loop_iter<'a>( + &'a self, + ) -> Box + 'a> { + curr_next_no_loop_iter(&self.as_vec_ref()) + } +} + +impl MurreletIterHelpers for Vec { + type T = T; + fn to_iter<'a>(&'a self) -> std::slice::Iter<'a, T> { + self.iter() + } + + fn as_vec_ref<'a>(&'a self) -> &'a Vec { + &self + } +} diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index c237da5..6b7dfcb 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -1,10 +1,15 @@ #![allow(dead_code)] +use std::collections::HashMap; + use glam::Vec2; +use itertools::Itertools; use lerpable::Lerpable; use murrelet_common::*; use murrelet_gui::make_gui_angle; use murrelet_gui::make_gui_vec2; use murrelet_gui::MurreletGUI; +use murrelet_gui::MurreletGUISchema; +use murrelet_gui::ValueGUI; use murrelet_livecode_derive::Livecode; use crate::curve_drawer::{CurveArc, CurveDrawer, CurvePoints, CurveSegment}; @@ -33,13 +38,13 @@ fn empty_string() -> String { #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct CompassDir { #[murrelet_gui(func = "make_gui_angle")] - angle_pi: AnglePi, + pub angle_pi: AnglePi, #[livecode(serde_default = "false")] #[murrelet_gui(kind = "skip")] - is_absolute: bool, + pub is_absolute: bool, #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] #[murrelet_gui(kind = "skip")] - label: String, + pub label: String, } impl CompassDir { @@ -54,15 +59,15 @@ impl CompassDir { #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct CompassArc { - radius: f32, + pub radius: f32, #[murrelet_gui(func = "make_gui_angle")] - arc_length: AnglePi, + pub arc_length: AnglePi, #[livecode(serde_default = "false")] #[murrelet_gui(kind = "skip")] - is_absolute: bool, + pub is_absolute: bool, #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] #[murrelet_gui(kind = "skip")] - label: String, + pub label: String, } // impl CompassArc { @@ -71,16 +76,30 @@ pub struct CompassArc { #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct CompassLine { - length: f32, // how far should we head in the current direction + pub length: f32, // how far should we head in the current direction #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] #[murrelet_gui(kind = "skip")] - label: String, + pub label: String, +} + +// #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] +// pub struct CompassRepeat { +// times: usize, +// what: Vec, +// } + +pub fn make_gui_vec_vec2() -> MurreletGUISchema { + MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Vec2)) } #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] -pub struct CompassRepeat { - times: usize, - what: Vec, +pub struct CompassAbsPoints { + #[lerpable(func = "lerpify_vec_vec2")] + #[murrelet_gui(func = "make_gui_vec_vec2")] + pts: Vec, + #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] + #[murrelet_gui(kind = "skip")] + label: String, } #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] @@ -88,10 +107,21 @@ pub enum CompassAction { Angle(CompassDir), // abs Arc(CompassArc), Line(CompassLine), - // Repeat(CompassRepeat), // now this is in the control vec! + AbsPoints(CompassAbsPoints), // Repeat(CompassRepeat), // now this is in the control vec! } impl CompassAction { + pub fn qabspts(pts: &[Vec2]) -> CompassAction { + CompassAction::abspts(pts, "".to_string()) + } + + pub fn abspts(pts: &[Vec2], label: String) -> CompassAction { + CompassAction::AbsPoints(CompassAbsPoints { + pts: pts.to_vec(), + label, + }) + } + pub fn qangle(angle_pi: A) -> CompassAction { CompassAction::angle(angle_pi, false, "".to_string()) } @@ -149,6 +179,7 @@ pub struct InteractiveCompassBuilder { curr_loc: Vec2, curr_angle: AnglePi, so_far: Vec, + references: HashMap, } impl InteractiveCompassBuilder { @@ -158,6 +189,7 @@ impl InteractiveCompassBuilder { curr_loc: Vec2::ZERO, curr_angle: AnglePi::new(0.0), so_far: Vec::new(), + references: HashMap::new(), } } @@ -183,6 +215,10 @@ impl InteractiveCompassBuilder { CompassAction::Arc(x) => { vec![self.add_arc(x)] } + CompassAction::AbsPoints(x) => match self.add_abs_pts(x) { + Some(x) => vec![x], + None => vec![], + }, } } @@ -233,6 +269,10 @@ impl InteractiveCompassBuilder { self.pen_down = true; + if x.label.len() > 0 { + self.references.insert(x.label.clone(), self.to_basic()); + } + // know there's at least one point so we can unwrap CurveSegment::Points(CurvePoints::new(points)) } @@ -278,9 +318,45 @@ impl InteractiveCompassBuilder { self.curr_angle = AnglePi::new(next_angle.angle_pi() % 2.0); self.pen_down = true; + if x.label.len() > 0 { + self.references.insert(x.label.clone(), self.to_basic()); + } + CurveSegment::Arc(a) } + fn add_abs_pts(&mut self, x: &CompassAbsPoints) -> Option { + match x.pts.as_slice() { + [.., penultimate, last] => { + let pt = PointToPoint::new(*penultimate, *last).angle().as_angle_pi(); + self.curr_angle = pt; + self.curr_loc = last.clone(); + } + [last] => { + if last.distance(self.curr_loc) > 0.001 { + let penultimate = self.curr_loc; + let pt = PointToPoint::new(penultimate, *last).angle().as_angle_pi(); + self.curr_angle = pt; + self.curr_loc = last.clone(); + } else { + // not enough points to update the angle... + self.curr_loc = last.clone(); + } + } + _ => { + return None; // not enough items + } + }; + + self.pen_down = true; + + if x.label.len() > 0 { + self.references.insert(x.label.clone(), self.to_basic()); + } + + Some(CurveSegment::Points(CurvePoints::new(x.pts.clone()))) + } + pub fn results(&self) -> Vec { self.so_far.clone() } @@ -289,8 +365,8 @@ impl InteractiveCompassBuilder { self.curr_loc } - pub fn curr_angle(&self) -> f32 { - self.curr_angle.angle_pi() + pub fn curr_angle(&self) -> AnglePi { + self.curr_angle } pub fn set_curr_loc(&mut self, curr_loc: Vec2) { @@ -305,6 +381,10 @@ impl InteractiveCompassBuilder { self.so_far .push(CurveSegment::Points(CurvePoints { points: vec![loc] })) } + + pub fn references(&self) -> Vec<(String, CurveStart)> { + self.references.clone().into_iter().collect_vec() + } } #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index bcdba84..fcfb20e 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -686,7 +686,7 @@ impl CurveArc { } pub fn length(&self) -> f32 { - self.radius.abs() * (self.end_pi - self.start_pi).angle() + (self.radius * (self.end_pi - self.start_pi).angle()).abs() } pub fn start_pi(&self) -> AnglePi { From 1d2454128ef3ee7fdbe5b432399d94c73c17cee1 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Mon, 22 Dec 2025 23:51:20 -0800 Subject: [PATCH 127/149] wip --- murrelet_draw/src/curve_drawer.rs | 120 ++++++++++++++++-- .../src/derive_cached.rs | 2 +- 2 files changed, 109 insertions(+), 13 deletions(-) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index fcfb20e..6a2939d 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -266,6 +266,16 @@ impl CurveDrawer { Some(last_command.last_point()) } + // pub fn first_spot(&self) -> Option { + // let first_command = self.segments().first()?; + // Some(first_command.first_spot()) + // } + + // pub fn last_spot(&self) -> Option { + // let last_command = self.segments().last()?; + // Some(last_command.last_spot()) + // } + pub fn length(&self) -> f32 { self.segments .iter() @@ -429,14 +439,19 @@ impl CurveSegment { } pub fn first_angle(&self) -> Option { - Some(self.first_spot().angle().into()) // todo, return none if it's one point + self.first_spot().map(|x| x.angle().into()) // todo, return none if it's one point } - pub fn first_spot(&self) -> SpotOnCurve { + // pub fn first_spot(&self) -> SpotOnCurve { + // self.first_spot_cd().unwrap() + // } + + // if we have only one point, this is None + pub fn first_spot(&self) -> Option { match self { CurveSegment::Arc(arc) => { let a = CurveArc::new(arc.loc, arc.radius, arc.start_pi, arc.end_pi); - SpotOnCurve::new(a.first_point(), a.start_tangent_angle()) + Some(SpotOnCurve::new(a.first_point(), a.start_tangent_angle())) } CurveSegment::Points(points) => { let vec2s = points.points(); @@ -445,20 +460,26 @@ impl CurveSegment { let second = vec2s[1]; let angle = PointToPoint::new(first, second).angle().perp_to_left(); - SpotOnCurve::new(first, angle) + Some(SpotOnCurve::new(first, angle)) } else { - todo!() + None } } - CurveSegment::CubicBezier(cubic_bezier) => cubic_bezier.to_cubic().start_to_tangent().0, + CurveSegment::CubicBezier(cubic_bezier) => { + Some(cubic_bezier.to_cubic().start_to_tangent().0) + } } } - pub fn last_spot(&self) -> SpotOnCurve { + // pub fn last_spot(&self) -> SpotOnCurve { + // self.last_spot_cd().unwrap() + // } + + pub fn last_spot(&self) -> Option { match self { CurveSegment::Arc(arc) => { let a = CurveArc::new(arc.loc, arc.radius, arc.start_pi, arc.end_pi); - SpotOnCurve::new(a.last_point(), a.end_tangent_angle()) + Some(SpotOnCurve::new(a.last_point(), a.end_tangent_angle())) } CurveSegment::Points(p) => { if p.points().len() >= 2 { @@ -466,12 +487,12 @@ impl CurveSegment { let end = *points.last().unwrap(); let prev = *points.get(points.len() - 2).unwrap(); let angle = PointToPoint::new(prev, end).angle().perp_to_left(); - SpotOnCurve::new(end, angle) + Some(SpotOnCurve::new(end, angle)) } else { - unimplemented!() // need to look at the val before... + None } } - CurveSegment::CubicBezier(c) => c.to_cubic().end_to_tangent().0, + CurveSegment::CubicBezier(c) => Some(c.to_cubic().end_to_tangent().0), } } @@ -850,10 +871,85 @@ impl ToCurveSegment for PointToPoint { } } +pub fn simplify_curve_segments(segments: &[CurveSegment]) -> Vec { + let mut cleaned_segments = vec![]; + + // combine our points together.. + let mut curr_points = vec![]; + for segment in segments { + match segment { + CurveSegment::Arc(_) => { + if !curr_points.is_empty() { + cleaned_segments + .push(CurveSegment::Points(CurvePoints::new(curr_points.clone()))); + curr_points = vec![]; + } + cleaned_segments.push(segment.clone()) + } + CurveSegment::Points(c) => curr_points.extend(c.points.iter()), + CurveSegment::CubicBezier(_) => cleaned_segments.push(segment.clone()), + } + } + if !curr_points.is_empty() { + cleaned_segments.push(CurveSegment::Points(CurvePoints::new(curr_points.clone()))); + } + + cleaned_segments +} + pub trait ToCurveDrawer { fn to_segments(&self) -> Vec; fn to_cd(&self, is_closed: bool) -> CurveDrawer { - CurveDrawer::new(self.to_segments(), is_closed) + CurveDrawer::new(simplify_curve_segments(&self.to_segments()), is_closed) + } + + // assume open + fn last_spot_cd(&self) -> Option { + let cdo = self.to_cd_open(); + let cd = cdo.segments(); + + if let Some(cd_last) = cd.last() { + if let Some(a) = cd_last.last_spot() { + return Some(a); + } else { + let last_point = cd_last.last_point(); + // if there's a second-from-last + if cd.len() >= 2 { + let second_from_last = cd[cd.len() - 2].last_point(); + + if last_point.distance(second_from_last) > 1.0e-6 { + return Some(PointToPoint::new(second_from_last, last_point).end_spot()); + } else { + // last test.. try to grab the last spot + return cd[cd.len() - 2].last_spot(); + } + } + } + } + return None; + } + + fn first_spot_cd(&self) -> Option { + let cdo = self.to_cd_open(); + let cd = cdo.segments(); + + if let Some(cd_first) = cd.first() { + if let Some(a) = cd_first.first_spot() { + return Some(a); + } else { + let first_point = cd_first.first_point(); + // if there's a second-from-first + if cd.len() >= 2 { + let second_spot = cd[1].first_point(); + if first_point.distance(second_spot) > 1.0e-6 { + return Some(PointToPoint::new(first_point, second_spot).start_spot()); + } else { + return cd[1].first_spot(); + } + } + } + } + return None; } fn to_cd_closed(&self) -> CurveDrawer { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs index 08e57df..52a80f5 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs @@ -58,7 +58,7 @@ fn parse_cache(name: &syn::Ident, fields: &[LivecodeFieldReceiver]) -> TokenStre let func = quote! { - fn #ident(&self) -> &#inside_type { + pub fn #ident(&self) -> &#inside_type { self.#ident.get_or_init(|| self.#expected_compute_ident()) } }; From 34efa37bb3188237ade50a044be7b042c635adc4 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Wed, 31 Dec 2025 19:19:46 -0800 Subject: [PATCH 128/149] wip --- murrelet_common/src/transform.rs | 8 ++++++++ murrelet_livecode/src/unitcells.rs | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/murrelet_common/src/transform.rs b/murrelet_common/src/transform.rs index 4ce3f33..6a18545 100644 --- a/murrelet_common/src/transform.rs +++ b/murrelet_common/src/transform.rs @@ -116,6 +116,14 @@ impl SimpleTransform2dStep { Self::Scale(Vec2::ONE * v) } + pub fn reflect_x() -> Self { + Self::Scale(vec2(-1.0, 1.0)) + } + + pub fn reflect_y() -> Self { + Self::Scale(vec2(1.0, -1.0)) + } + pub fn transform(&self) -> Mat3 { match self { Self::Translate(v) => Mat3::from_translation(*v), diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index e2dfe5e..94d6404 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -552,6 +552,20 @@ impl UnitCellContext { } } + pub fn new_full( + idx: UnitCellIdx, + ctx: ExprWorldContextValues, + transform: SimpleTransform2d, + adjust_transform: SimpleTransform2d, + ) -> UnitCellContext { + UnitCellContext { + idx, + ctx: Some(ctx), + detail: UnitCellDetails::new_fancy(transform, adjust_transform, true), + tile_info: None, + } + } + pub fn new_with_base(ctx: UnitCellIdx, detail: UnitCellDetails) -> UnitCellContext { UnitCellContext { idx: ctx, From fca81bccd8e5ab01974a0c668bedf35f2a35528a Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 2 Jan 2026 15:54:05 +0800 Subject: [PATCH 129/149] wip --- murrelet_common/src/lib.rs | 12 +- murrelet_draw/src/cubic.rs | 24 +++- murrelet_draw/src/curve_drawer.rs | 202 +++++++++++++++++++++--------- murrelet_draw/src/tesselate.rs | 18 ++- 4 files changed, 184 insertions(+), 72 deletions(-) diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 8542ef9..e1ad102 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -1088,7 +1088,7 @@ pub fn rgb_to_hex(r: f32, g: f32, b: f32) -> String { } pub trait MurreletIterHelpers { - type T; + type T: Clone; fn to_iter<'a>(&'a self) -> std::slice::Iter<'a, Self::T>; fn as_vec_ref<'a>(&'a self) -> &'a Vec; @@ -1099,6 +1099,14 @@ pub trait MurreletIterHelpers { self.to_iter().map(f).collect_vec() } + fn owned_iter(&self) -> std::vec::IntoIter { + self.to_iter().map(|x| x.clone()).collect_vec().into_iter() + } + + fn take_count(&self, amount: usize) -> Vec { + self.owned_iter().take(amount).collect::>() + } + fn prev_curr_next_loop_iter<'a>( &'a self, ) -> Box + 'a> { @@ -1124,7 +1132,7 @@ pub trait MurreletIterHelpers { } } -impl MurreletIterHelpers for Vec { +impl MurreletIterHelpers for Vec { type T = T; fn to_iter<'a>(&'a self) -> std::slice::Iter<'a, T> { self.iter() diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index 5a26b54..0ee6d79 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -1,6 +1,8 @@ use glam::Vec2; use murrelet_common::{Angle, IsAngle, SpotOnCurve}; +use crate::svg::glam_to_lyon; + #[derive(Debug, Clone, Copy)] pub struct CubicBezier { pub from: Vec2, @@ -9,6 +11,18 @@ pub struct CubicBezier { pub to: Vec2, } impl CubicBezier { + pub fn safe_from_spots_s( + in_spot: SpotOnCurve, + out_spot: SpotOnCurve, + strength: Vec2, + ) -> Option { + if in_spot.loc.distance(out_spot.loc) < 1.0e-3 { + None + } else { + Some(Self::from_spots_s(in_spot, out_spot, strength)) + } + } + pub fn from_spots_s(in_spot: SpotOnCurve, out_spot: SpotOnCurve, strength: Vec2) -> Self { Self::from_spots(in_spot, strength.x, out_spot, strength.y) } @@ -155,5 +169,13 @@ impl CubicBezier { } } - + pub fn approx_length(&self) -> f32 { + let lyon_cubic = lyon::geom::CubicBezierSegment { + from: glam_to_lyon(self.from), + ctrl1: glam_to_lyon(self.ctrl1), + ctrl2: glam_to_lyon(self.ctrl2), + to: glam_to_lyon(self.to), + }; + lyon_cubic.approximate_length(0.1) + } } diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 6a2939d..49c1bd2 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -1,7 +1,7 @@ use glam::*; use itertools::Itertools; use lerpable::Lerpable; -use lyon::{geom::CubicBezierSegment, path::Path}; +use lyon::path::Path; use murrelet_common::*; use murrelet_livecode::types::{LivecodeError, LivecodeResult}; use murrelet_livecode_derive::*; @@ -11,7 +11,6 @@ use svg::node::element::path::Data; use crate::{ cubic::CubicBezier, newtypes::*, - svg::glam_to_lyon, tesselate::{ cubic_bezier_path_to_lyon, flatten_cubic_bezier_path, flatten_cubic_bezier_path_with_tolerance, parse_svg_data_as_vec2, segment_arc, segment_vec, @@ -277,30 +276,7 @@ impl CurveDrawer { // } pub fn length(&self) -> f32 { - self.segments - .iter() - .map(|segment| match segment { - CurveSegment::CubicBezier(c) => { - let lyon_cubic = CubicBezierSegment { - from: glam_to_lyon(c.from), - ctrl1: glam_to_lyon(c.ctrl1), - ctrl2: glam_to_lyon(c.ctrl2), - to: glam_to_lyon(c.to), - }; - lyon_cubic.approximate_length(0.1) - } - CurveSegment::Points(p) => p - .points() - .windows(2) - .map(|pts| pts[0].distance(pts[1])) - .sum(), - CurveSegment::Arc(a) => { - let angle_diff = (a.end_pi.angle_pi() - a.start_pi.angle_pi()).rem_euclid(2.0); - let sweep_angle_rads = angle_diff * std::f32::consts::PI; - a.radius * sweep_angle_rads - } - }) - .sum() + self.segments.iter().map(|segment| segment.length()).sum() } pub(crate) fn maybe_transform(&self, transform: &Transform2d) -> LivecodeResult { @@ -386,29 +362,6 @@ impl CurveSegment { CurveSegment::Arc(CurveArc::new(loc, radius.len(), start, end)) } - // pub fn new_simple_arc_from( - // start: Vec2, - // radius: Rad, - // in_angle: A1, - // angle_length: A2, - // ccw: bool, - // ) -> Self { - // // calculate the center - // let (loc, end_pi) = if ccw { - // ( - // start + in_angle.to_norm_dir() * radius.len(), - // in_angle.as_angle() - angle_length.as_angle(), - // ) - // } else { - // ( - // start - in_angle.to_norm_dir() * radius.len(), - // in_angle.as_angle() + angle_length.as_angle(), - // ) - // }; - - // CurveSegment::Arc(CurveArc::new(loc, radius.len(), in_angle, end_pi)) - // } - pub fn new_simple_circle(loc: Vec2, radius: f32) -> Self { CurveSegment::Arc(CurveArc::new( loc, @@ -442,10 +395,6 @@ impl CurveSegment { self.first_spot().map(|x| x.angle().into()) // todo, return none if it's one point } - // pub fn first_spot(&self) -> SpotOnCurve { - // self.first_spot_cd().unwrap() - // } - // if we have only one point, this is None pub fn first_spot(&self) -> Option { match self { @@ -471,10 +420,6 @@ impl CurveSegment { } } - // pub fn last_spot(&self) -> SpotOnCurve { - // self.last_spot_cd().unwrap() - // } - pub fn last_spot(&self) -> Option { match self { CurveSegment::Arc(arc) => { @@ -552,6 +497,22 @@ impl CurveSegment { pub fn extend_both(&self, before_amount: f32, after_amount: f32) -> Self { self.extend_before(before_amount).extend_after(after_amount) } + + fn length(&self) -> f32 { + match self { + CurveSegment::CubicBezier(c) => c.length(), + CurveSegment::Points(p) => p.length(), + CurveSegment::Arc(a) => a.length(), + } + } + + fn split_segment(&self, pct: f32) -> (CurveSegment, CurveSegment) { + match self { + CurveSegment::CubicBezier(c) => c.split_segment(pct), + CurveSegment::Points(p) => p.split_segment(pct), + CurveSegment::Arc(a) => a.split_segment(pct), + } + } } #[derive(Debug, Clone, Copy, Livecode, Lerpable)] @@ -708,11 +669,31 @@ impl CurveArc { pub fn length(&self) -> f32 { (self.radius * (self.end_pi - self.start_pi).angle()).abs() + // let angle_diff = (self.end_pi.angle_pi() - self.start_pi.angle_pi()).rem_euclid(2.0); + // let sweep_angle_rads = angle_diff * std::f32::consts::PI; + // self.radius * sweep_angle_rads } pub fn start_pi(&self) -> AnglePi { self.start_pi } + + fn split_segment(&self, target_dist: f32) -> (CurveSegment, CurveSegment) { + // find out how far around the arc this pct is + let target_pct = target_dist / self.length(); + let split_pi = self.start_pi.lerpify(&self.end_pi, &target_pct); + let first_arc = CurveArc { + end_pi: split_pi, + ..self.clone() + }; + + let second_arc = CurveArc { + start_pi: split_pi, + ..self.clone() + }; + + (first_arc.to_segment(), second_arc.to_segment()) + } } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -741,6 +722,10 @@ impl CurveCubicBezier { } } + pub fn length(&self) -> f32 { + self.to_cubic().approx_length() + } + pub fn first_point(&self) -> Vec2 { self.from } @@ -783,6 +768,11 @@ impl CurveCubicBezier { .apply_vec2_tranform(|x| transform.transform_vec2(x)), ) } + + fn split_segment(&self, target_dist: f32) -> (CurveSegment, CurveSegment) { + let v = self.to_cubic().to_vec2_line_space(1.0); // todo, figure out how to manage that + CurvePoints::new(v).split_segment(target_dist) + } } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -796,6 +786,13 @@ impl CurvePoints { Self { points } } + pub fn length(&self) -> f32 { + self.points() + .windows(2) + .map(|pts| pts[0].distance(pts[1])) + .sum() + } + pub fn first_point(&self) -> Vec2 { *self.points.first().unwrap() } @@ -817,6 +814,33 @@ impl CurvePoints { points: transform.transform_many_vec2(self.points()).clone_to_vec(), } } + + fn split_segment(&self, target_dist: f32) -> (CurveSegment, CurveSegment) { + let mut add_left = true; + let mut so_far = 0.0; + let mut left = vec![self.first_point()]; + let mut right = vec![]; + for (curr, next) in self.points().curr_next_no_loop_iter() { + if add_left { + let dist = curr.distance(*next); + let dist_after_this_jump = so_far + dist; + if dist_after_this_jump > target_dist { + // split this one! + let split_pct = (dist_after_this_jump - target_dist) / dist; + let lerp_point = PointToPoint::new(*curr, *next).pct(split_pct); + left.push(lerp_point); + right.push(lerp_point); + add_left = false; + } else { + so_far = dist_after_this_jump; + left.push(*next) + } + } else { + right.push(*next) + } + } + (left.to_segment(), right.to_segment()) + } } pub trait ToCurveSegment { @@ -981,6 +1005,13 @@ pub trait ToCurveDrawer { pts[pts.len() / 2] } + fn to_rough_pct(&self, pct: f32) -> Option { + let length = self.to_cd_open().length(); + let dist = pct * length; + + self.to_rough_spots_count(dist, Some(1)).first().copied() + } + // this one isn't evenly spaced yet fn to_rough_points(&self, approx_spacing: f32) -> Vec { let mut result = vec![]; @@ -1008,6 +1039,55 @@ pub trait ToCurveDrawer { } fn to_rough_spots(&self, approx_spacing: f32) -> Vec { + self.to_rough_spots_count(approx_spacing, None) + } + + fn split(&self, pct: f32) -> (Vec, Vec) { + if pct <= 0.0 { + return (self.to_segments(), vec![]); + } else if pct >= 1.0 { + return (vec![], self.to_segments()); + } + + let cd = self.to_cd_open(); + let length = cd.length(); + + let target_dist = length * pct; + + let mut left = vec![]; + let mut right = vec![]; + let mut dist_traveled = 0.0; + let mut add_left = true; + for segment in cd.segments() { + if add_left { + let segment_len = segment.length(); + let dist_traveled_after_this_segment = dist_traveled + segment_len; + if dist_traveled_after_this_segment > target_dist { + // we need to split this next one! + let overshot_pct = + (dist_traveled_after_this_segment - target_dist) / segment_len; + let (left_s, right_s) = segment.split_segment(overshot_pct); + + left.push(left_s); + right.push(right_s); + add_left = false; + } else { + dist_traveled = dist_traveled_after_this_segment; + left.push(segment.clone()) + } + } else { + right.push(segment.clone()) + } + } + + (left, right) + } + + fn to_rough_spots_count( + &self, + approx_spacing: f32, + max_count: Option, + ) -> Vec { let mut result = vec![]; let mut curr_offset = 0.0; for s in &self.to_segments() { @@ -1045,7 +1125,13 @@ pub trait ToCurveDrawer { v } }; - result.extend(pts) + result.extend(pts); + + if let Some(mc) = max_count { + if result.len() > mc { + return result.take_count(mc); + } + } } result } diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 0a8b5ac..2e8da99 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -1,6 +1,10 @@ use std::{collections::HashMap, ops}; -use crate::{cubic::CubicBezier, curve_drawer::{CubicBezierPath, CurveArc}, svg::SvgPathDef}; +use crate::{ + cubic::CubicBezier, + curve_drawer::{CubicBezierPath, CurveArc}, + svg::SvgPathDef, +}; use delaunator::Triangulation; use glam::{vec2, Vec2, Vec2Swizzles}; use itertools::Itertools; @@ -14,7 +18,8 @@ use lyon::{ tessellation::{BuffersBuilder, FillOptions, FillTessellator, FillVertex}, }; use murrelet_common::{ - AnglePi, IsAngle, PointToPoint, Polyline, SpotOnCurve, ToVec2, curr_next_no_loop_iter, triangulate::DefaultVertex + curr_next_no_loop_iter, triangulate::DefaultVertex, AnglePi, IsAngle, PointToPoint, Polyline, + SpotOnCurve, ToVec2, }; pub trait ToVecVec2 { @@ -108,15 +113,6 @@ fn point_from_param3(params: &Vec<&f32>) -> (Pt, Pt, Pt) { ) } -// fn point_from_param4(params: &Vec<&f32>) -> (Pt, Pt, Pt, Pt) { -// ( -// _point_from_params(params, 0), -// _point_from_params(params, 1), -// _point_from_params(params, 2), -// _point_from_params(params, 3), -// ) -// } - pub fn many_pt2_to_vec2(ps: &Vec) -> Vec { ps.iter().map(|p| p.as_vec2()).collect_vec() } From 00e48611e345756d93ee1c47a06795b45b967345 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 2 Jan 2026 17:22:39 +0800 Subject: [PATCH 130/149] wip --- murrelet_common/src/geometry.rs | 6 ++++ murrelet_common/src/lib.rs | 17 +++++++++++ murrelet_draw/src/compass.rs | 49 +++++++++++++++++++++++++------ murrelet_draw/src/curve_drawer.rs | 2 +- murrelet_draw/src/transform2d.rs | 2 +- 5 files changed, 65 insertions(+), 11 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 6dde4fb..17b9b2a 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -525,6 +525,12 @@ impl SpotOnCurve { angle: self.angle, } } + + pub fn travel_2d(&self, dist: Vec2) -> SpotOnCurve { + let a = Angle::new(dist.to_angle()); + let loc = self.travel(dist.x).turn_right_perp().travel(dist.y).loc; + SpotOnCurve::new(loc, a) + } } pub trait ToSpotWithAngle { diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index e1ad102..f4d48f6 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -419,12 +419,29 @@ impl Rect { self.xy } + pub fn center(&self) -> Vec2 { + self.xy() + } + pub fn pos(&self, s: Vec2) -> Vec2 { vec2( (s.x - self.left()) / self.w(), (s.y - self.bottom()) / self.h(), ) } + + pub fn transform_to_other_rect(&self, target_rect: Rect) -> SimpleTransform2d { + let scale = target_rect.w() / self.w(); + + SimpleTransform2d::new(vec![ + // first move to center + SimpleTransform2dStep::translate(-self.center()), + // scale + SimpleTransform2dStep::scale_both(scale), + // then move to target + SimpleTransform2dStep::translate(target_rect.center()), + ]) + } } #[derive(Debug, Clone, Copy)] diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index 6b7dfcb..e99ec32 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -12,6 +12,8 @@ use murrelet_gui::MurreletGUISchema; use murrelet_gui::ValueGUI; use murrelet_livecode_derive::Livecode; +use crate::cubic::CubicBezier; +use crate::curve_drawer::ToCurveSegment; use crate::curve_drawer::{CurveArc, CurveDrawer, CurvePoints, CurveSegment}; #[derive(Debug, Clone, Copy, Livecode, MurreletGUI, Lerpable)] @@ -70,9 +72,17 @@ pub struct CompassArc { pub label: String, } -// impl CompassArc { -// pub fn new(radius: f32, arc_length: f32, is_absolute: bool) -> Self { Self { radius, arc_length, is_absolute } } -// } +#[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] +pub struct CompassBezier { + dist: f32, + #[murrelet_gui(func = "make_gui_angle")] + angle: AnglePi, + #[murrelet_gui(func = "make_gui_vec2")] + strengths: Vec2, + #[livecode(serde_default = "murrelet_livecode::livecode::empty_string")] + #[murrelet_gui(kind = "skip")] + pub label: String, +} #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] pub struct CompassLine { @@ -82,12 +92,6 @@ pub struct CompassLine { pub label: String, } -// #[derive(Debug, Clone, Livecode, MurreletGUI, Lerpable)] -// pub struct CompassRepeat { -// times: usize, -// what: Vec, -// } - pub fn make_gui_vec_vec2() -> MurreletGUISchema { MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Vec2)) } @@ -107,6 +111,7 @@ pub enum CompassAction { Angle(CompassDir), // abs Arc(CompassArc), Line(CompassLine), + Bezier(CompassBezier), AbsPoints(CompassAbsPoints), // Repeat(CompassRepeat), // now this is in the control vec! } @@ -215,6 +220,9 @@ impl InteractiveCompassBuilder { CompassAction::Arc(x) => { vec![self.add_arc(x)] } + CompassAction::Bezier(x) => { + vec![self.add_bezier(x)] + } CompassAction::AbsPoints(x) => match self.add_abs_pts(x) { Some(x) => vec![x], None => vec![], @@ -277,6 +285,29 @@ impl InteractiveCompassBuilder { CurveSegment::Points(CurvePoints::new(points)) } + fn curr_spot(&self) -> SpotOnCurve { + SpotOnCurve::new(self.curr_loc, self.curr_angle) + } + + fn add_bezier(&mut self, x: &CompassBezier) -> CurveSegment { + let first_spot = self.curr_spot(); + + // next point is going to take the current angle, and move in that direction + let last_spot = first_spot.rotate(x.angle).travel(x.dist); + + let bezier = CubicBezier::from_spots_s(first_spot, last_spot, x.strengths); + + self.curr_loc = last_spot.loc; + self.curr_angle = last_spot.angle().as_angle_pi(); + + self.pen_down = true; + + if x.label.len() > 0 { + self.references.insert(x.label.clone(), self.to_basic()); + } + bezier.to_segment() + } + fn use_angle_and_length(&self, angle: A, length: f32) -> Vec2 { angle.as_angle_pi().to_norm_dir() * length } diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 49c1bd2..cda2ade 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -279,7 +279,7 @@ impl CurveDrawer { self.segments.iter().map(|segment| segment.length()).sum() } - pub(crate) fn maybe_transform(&self, transform: &Transform2d) -> LivecodeResult { + pub fn maybe_transform(&self, transform: &Transform2d) -> LivecodeResult { let mut segments = vec![]; if !transform.is_similarity_transform() { diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index fa8140d..2eb1181 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -4,7 +4,7 @@ use std::f32::consts::PI; use glam::*; use itertools::Itertools; use lerpable::Lerpable; -use murrelet_common::lerpify_vec2; +use murrelet_common::{Rect, lerpify_vec2}; use murrelet_common::{ a_pi, approx_eq_eps, mat4_from_mat3_transform, AnglePi, IsAngle, IsPolyline, Polyline, SimpleTransform2d, SimpleTransform2dStep, SpotOnCurve, TransformVec2, From 34bcf9a681faaf5f4469f0f55df04527a8968488 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sat, 10 Jan 2026 18:28:42 +0800 Subject: [PATCH 131/149] wip --- murrelet_draw/src/compass.rs | 41 +++++++++++++++-- murrelet_draw/src/curve_drawer.rs | 74 ++++++++++++++++++++++--------- 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index e99ec32..d709bcb 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] use std::collections::HashMap; +use glam::vec2; use glam::Vec2; use itertools::Itertools; use lerpable::Lerpable; @@ -179,12 +180,30 @@ impl Default for CompassAction { } } +#[derive(Clone, Debug)] +pub enum LastActionNames { + Arc, + Line, + Bezier, + Start, +} +impl LastActionNames { + pub fn from_segment(c: &CurveSegment) -> LastActionNames { + match c { + CurveSegment::Arc(_) => LastActionNames::Arc, + CurveSegment::Points(_) => LastActionNames::Line, + CurveSegment::CubicBezier(_) => LastActionNames::Bezier, + } + } +} + pub struct InteractiveCompassBuilder { pen_down: bool, // if we've drawn one curr_loc: Vec2, curr_angle: AnglePi, so_far: Vec, references: HashMap, + last_action: LastActionNames, } impl InteractiveCompassBuilder { @@ -195,6 +214,7 @@ impl InteractiveCompassBuilder { curr_angle: AnglePi::new(0.0), so_far: Vec::new(), references: HashMap::new(), + last_action: LastActionNames::Start, } } @@ -209,7 +229,7 @@ impl InteractiveCompassBuilder { pub fn new_segments(&mut self, dir: &CompassAction) -> Vec { // here we go! - match dir { + let r = match dir { CompassAction::Angle(x) => { self.set_angle(x); vec![] @@ -227,7 +247,9 @@ impl InteractiveCompassBuilder { Some(x) => vec![x], None => vec![], }, - } + }; + + r } pub fn add_qline(&mut self, length: f32) { @@ -243,8 +265,16 @@ impl InteractiveCompassBuilder { } pub fn add_segment(&mut self, dir: &CompassAction) { - let r = { self.new_segments(dir) }; + let r = self.new_segments(dir); self.so_far.extend(r); + + self.last_action = match dir { + CompassAction::Angle(_) => self.last_action.clone(), // don't change the action + CompassAction::Arc(_) => LastActionNames::Arc, + CompassAction::Line(_) => LastActionNames::Line, + CompassAction::Bezier(_) => LastActionNames::Bezier, + CompassAction::AbsPoints(_) => LastActionNames::Line, + } } fn set_angle(&mut self, dir: &CompassDir) { @@ -267,6 +297,8 @@ impl InteractiveCompassBuilder { let mut points = vec![]; if !self.pen_down { points.push(self.curr_loc) + } else if !matches!(self.last_action, LastActionNames::Line) { + points.push(self.curr_loc) } // next point is going to take the current angle, and move in that direction @@ -295,7 +327,8 @@ impl InteractiveCompassBuilder { // next point is going to take the current angle, and move in that direction let last_spot = first_spot.rotate(x.angle).travel(x.dist); - let bezier = CubicBezier::from_spots_s(first_spot, last_spot, x.strengths); + let bezier = + CubicBezier::from_spots_s(first_spot, last_spot, x.strengths * vec2(1.0, -1.0)); self.curr_loc = last_spot.loc; self.curr_angle = last_spot.angle().as_angle_pi(); diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index cda2ade..a1604bd 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -431,7 +431,7 @@ impl CurveSegment { let points = p.points(); let end = *points.last().unwrap(); let prev = *points.get(points.len() - 2).unwrap(); - let angle = PointToPoint::new(prev, end).angle().perp_to_left(); + let angle = PointToPoint::new(prev, end).angle(); //.perp_to_left(); Some(SpotOnCurve::new(end, angle)) } else { None @@ -506,11 +506,11 @@ impl CurveSegment { } } - fn split_segment(&self, pct: f32) -> (CurveSegment, CurveSegment) { + fn split_segment(&self, dist: f32) -> (CurveSegment, CurveSegment) { match self { - CurveSegment::CubicBezier(c) => c.split_segment(pct), - CurveSegment::Points(p) => p.split_segment(pct), - CurveSegment::Arc(a) => a.split_segment(pct), + CurveSegment::CubicBezier(c) => c.split_segment(dist), + CurveSegment::Points(p) => p.split_segment(dist), + CurveSegment::Arc(a) => a.split_segment(dist), } } } @@ -826,10 +826,11 @@ impl CurvePoints { let dist_after_this_jump = so_far + dist; if dist_after_this_jump > target_dist { // split this one! - let split_pct = (dist_after_this_jump - target_dist) / dist; + let split_pct = 1.0 - ((dist_after_this_jump - target_dist) / dist); let lerp_point = PointToPoint::new(*curr, *next).pct(split_pct); left.push(lerp_point); right.push(lerp_point); + right.push(*next); add_left = false; } else { so_far = dist_after_this_jump; @@ -841,6 +842,14 @@ impl CurvePoints { } (left.to_segment(), right.to_segment()) } + + fn push_pt_beginning(&mut self, pt: Vec2) { + self.points.insert(0, pt); + } + + fn extend_pts(&mut self, points: &[Vec2]) { + self.points.extend_from_slice(points); + } } pub trait ToCurveSegment { @@ -899,24 +908,27 @@ pub fn simplify_curve_segments(segments: &[CurveSegment]) -> Vec { let mut cleaned_segments = vec![]; // combine our points together.. - let mut curr_points = vec![]; for segment in segments { - match segment { - CurveSegment::Arc(_) => { - if !curr_points.is_empty() { - cleaned_segments - .push(CurveSegment::Points(CurvePoints::new(curr_points.clone()))); - curr_points = vec![]; + let segment_first_point = segment.first_point(); + let mut segment = segment.clone(); + if let CurveSegment::Points(curve_points) = &mut segment { + match cleaned_segments.last_mut() { + Some(CurveSegment::Points(pts)) => pts.extend_pts(curve_points.points()), + Some(s) => { + // make sure that the line starts where this one ends or else things + // like segmentation breaks down + if s.last_point().distance(segment_first_point) > 1e-2f32 { + curve_points.push_pt_beginning(s.last_point()); + } + + cleaned_segments.push(segment.clone()) } - cleaned_segments.push(segment.clone()) + None => cleaned_segments.push(segment.clone()), } - CurveSegment::Points(c) => curr_points.extend(c.points.iter()), - CurveSegment::CubicBezier(_) => cleaned_segments.push(segment.clone()), + } else { + cleaned_segments.push(segment.clone()) } } - if !curr_points.is_empty() { - cleaned_segments.push(CurveSegment::Points(CurvePoints::new(curr_points.clone()))); - } cleaned_segments } @@ -1042,11 +1054,21 @@ pub trait ToCurveDrawer { self.to_rough_spots_count(approx_spacing, None) } + fn even_split(&self, target_size: f32) -> (Vec, Vec) { + let cd = self.to_cd_open(); + let length = cd.length(); + + let how_many = (length / target_size).floor(); + let new_size = length / how_many; + + self.split(new_size) + } + fn split(&self, pct: f32) -> (Vec, Vec) { if pct <= 0.0 { - return (self.to_segments(), vec![]); - } else if pct >= 1.0 { return (vec![], self.to_segments()); + } else if pct >= 1.0 { + return (self.to_segments(), vec![]); } let cd = self.to_cd_open(); @@ -1058,15 +1080,22 @@ pub trait ToCurveDrawer { let mut right = vec![]; let mut dist_traveled = 0.0; let mut add_left = true; + + let mut last_pt = None; for segment in cd.segments() { if add_left { let segment_len = segment.length(); let dist_traveled_after_this_segment = dist_traveled + segment_len; + if dist_traveled_after_this_segment > target_dist { // we need to split this next one! let overshot_pct = (dist_traveled_after_this_segment - target_dist) / segment_len; - let (left_s, right_s) = segment.split_segment(overshot_pct); + let split_loc = (1.0 - overshot_pct) * segment_len; + + // points might not know about their starting place, so check for that + + let (left_s, right_s) = segment.split_segment(split_loc); left.push(left_s); right.push(right_s); @@ -1078,6 +1107,7 @@ pub trait ToCurveDrawer { } else { right.push(segment.clone()) } + last_pt = Some(segment.last_point()); } (left, right) From 99d88bad9f0b24637a9cd9fd6e6271ebb84ebcc4 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 11 Jan 2026 21:07:06 +0800 Subject: [PATCH 132/149] wip --- murrelet_livecode/src/expr.rs | 7 ++++++- murrelet_perform/src/perform.rs | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index 291bb5f..4986be0 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -38,7 +38,6 @@ pub fn init_evalexpr_func_ctx() -> LivecodeResult { Ok(Value::Int(a)) } }), - "manymod" => Function::new(move |argument| { let a = argument.as_tuple()?; @@ -54,6 +53,12 @@ pub fn init_evalexpr_func_ctx() -> LivecodeResult { } Ok(Value::Int(result)) }), + "trigger" => Function::new(move |argument| { + let tuple = argument.as_fixed_len_tuple(3)?; + let (val, last_val, rate) = (tuple[0].as_number()?, tuple[1].as_number()?, tuple[2].as_number()?); + let f = (val / rate).floor() > (last_val / rate).floor(); + Ok(Value::Boolean(f)) + }), "clamp" => Function::new(move |argument| { let tuple = argument.as_fixed_len_tuple(3)?; diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 5393e0f..10691c5 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -1167,4 +1167,6 @@ where pub fn run_id(&self) -> u64 { self.run_id } + + } From e88eb9dacc5883d9daa726d142555a88d0e8da41 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 18 Jan 2026 12:17:52 +0800 Subject: [PATCH 133/149] wip --- murrelet_common/src/geometry.rs | 4 ++ murrelet_draw/src/compass.rs | 87 +++++++++++++++++-------------- murrelet_draw/src/curve_drawer.rs | 52 +++++++++++++++--- murrelet_draw/src/transform2d.rs | 2 +- 4 files changed, 98 insertions(+), 47 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 17b9b2a..44f9e36 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -258,6 +258,10 @@ pub trait IsAngle: Sized { fn perp_to_right(&self) -> Self; fn scale(&self, s: f32) -> Self; + fn mod2(&self) -> AnglePi { + AnglePi::new(self.angle_pi() % 2.0) + } + fn flip(&self) -> Self { self.perp_to_left().perp_to_left() } diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index d709bcb..45aa44f 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -17,6 +17,7 @@ use crate::cubic::CubicBezier; use crate::curve_drawer::ToCurveSegment; use crate::curve_drawer::{CurveArc, CurveDrawer, CurvePoints, CurveSegment}; + #[derive(Debug, Clone, Copy, Livecode, MurreletGUI, Lerpable)] pub struct CurveStart { #[murrelet_gui(func = "make_gui_vec2")] @@ -227,6 +228,10 @@ impl InteractiveCompassBuilder { self.add_curve_start_simple(start.loc, start.angle_pi); } + pub fn add_curve_start_spot(&mut self, spot: SpotOnCurve) { + self.add_curve_start_simple(spot.loc, spot.angle); + } + pub fn new_segments(&mut self, dir: &CompassAction) -> Vec { // here we go! let r = match dir { @@ -302,7 +307,7 @@ impl InteractiveCompassBuilder { } // next point is going to take the current angle, and move in that direction - let movement = self.use_angle_and_length(self.curr_angle, x.length); + let movement = self.curr_angle.to_norm_dir() * x.length; self.curr_loc += movement; points.push(self.curr_loc); @@ -341,52 +346,54 @@ impl InteractiveCompassBuilder { bezier.to_segment() } - fn use_angle_and_length(&self, angle: A, length: f32) -> Vec2 { - angle.as_angle_pi().to_norm_dir() * length - } - fn add_arc(&mut self, x: &CompassArc) -> CurveSegment { - let angle_pi = x.arc_length.as_angle_pi(); - let (arc_length, radius) = if angle_pi.is_neg() { - (-angle_pi, -x.radius) - } else { - (angle_pi, x.radius) - }; - - // starting at our current location, move at a right angle to our current angle - // negative goes to the left of the line - let loc = - self.curr_loc + self.use_angle_and_length(self.curr_angle + AnglePi::new(0.5), radius); - - // if radius is negative, go backwards - // end_angle is what we'll update curr angle to, it's always assuming positive radius - let (start, end, next_angle) = if radius < 0.0 { - let next_angle = self.curr_angle - arc_length; - ( - AnglePi::new(1.0) + self.curr_angle - AnglePi::new(0.5), - AnglePi::new(1.0) + next_angle - AnglePi::new(0.5), - next_angle, - ) - } else { - let next_angle = self.curr_angle + arc_length; - ( - self.curr_angle - AnglePi::new(0.5), - next_angle - AnglePi::new(0.5), - next_angle, - ) - }; - - let a = CurveArc::new(loc, radius.abs(), start, end); - - self.curr_loc = a.last_point(); - self.curr_angle = AnglePi::new(next_angle.angle_pi() % 2.0); + // let angle_pi = x.arc_length.as_angle_pi(); + // let (arc_length, radius) = if angle_pi.is_neg() { + // (-angle_pi, -x.radius) + // } else { + // (angle_pi, x.radius) + // }; + + // // starting at our current location, move at a right angle to our current angle + // // negative goes to the left of the line + // let loc = self.curr_loc + radius * self.curr_angle.perp_to_left().to_norm_dir(); + + // // if radius is negative, go backwards + // // end_angle is what we'll update curr angle to, it's always assuming positive radius + // let (start, end, next_angle) = if radius < 0.0 { + // let next_angle = self.curr_angle - arc_length; + // ( + // AnglePi::new(1.0) + self.curr_angle - AnglePi::new(0.5), + // AnglePi::new(1.0) + next_angle - AnglePi::new(0.5), + // next_angle, + // ) + // } else { + // let next_angle = self.curr_angle + arc_length; + // ( + // self.curr_angle - AnglePi::new(0.5), + // next_angle - AnglePi::new(0.5), + // next_angle, + // ) + // }; + + // let a = CurveArc::new(loc, radius.abs(), start, end); + + let arc = CurveArc::from_spot( + SpotOnCurve::new(self.curr_loc, self.curr_angle), + x.radius, + x.arc_length, + ); + let new_spot = arc.last_spot(); + + self.curr_loc = new_spot.loc; + self.curr_angle = new_spot.angle().mod2(); self.pen_down = true; if x.label.len() > 0 { self.references.insert(x.label.clone(), self.to_basic()); } - CurveSegment::Arc(a) + CurveSegment::Arc(arc) } fn add_abs_pts(&mut self, x: &CompassAbsPoints) -> Option { diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index a1604bd..177a48e 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -422,10 +422,7 @@ impl CurveSegment { pub fn last_spot(&self) -> Option { match self { - CurveSegment::Arc(arc) => { - let a = CurveArc::new(arc.loc, arc.radius, arc.start_pi, arc.end_pi); - Some(SpotOnCurve::new(a.last_point(), a.end_tangent_angle())) - } + CurveSegment::Arc(arc) => Some(arc.last_spot()), CurveSegment::Points(p) => { if p.points().len() >= 2 { let points = p.points(); @@ -533,6 +530,43 @@ impl CurveArc { } } + pub fn from_spot( + start_spot: SpotOnCurve, + raw_radius: f32, + raw_arc_length: A, + ) -> CurveArc { + let angle_pi = raw_arc_length.as_angle_pi(); + let (arc_length, radius) = if angle_pi.is_neg() { + (-angle_pi, -raw_radius) + } else { + (angle_pi, raw_radius) + }; + + // starting at our current location, move at a right angle to our current angle + // negative goes to the left of the line + let loc = start_spot.loc + start_spot.angle().perp_to_left().to_norm_dir() * radius; + + // if radius is negative, go backwards + // end_angle is what we'll update curr angle to, it's always assuming positive radius + let (start, end): (AnglePi, AnglePi) = if radius < 0.0 { + let next_angle = start_spot.angle - arc_length; + ( + AnglePi::new(1.0) + start_spot.angle - AnglePi::new(0.5), + AnglePi::new(1.0) + next_angle - AnglePi::new(0.5), + // next_angle.into(), + ) + } else { + let next_angle = start_spot.angle + arc_length; + ( + (start_spot.angle - AnglePi::new(0.5)).into(), + (next_angle - AnglePi::new(0.5)).into(), + // next_angle.into(), + ) + }; + + CurveArc::new(loc, radius.abs(), start, end) + } + pub fn is_in_arc(&self, angle: AnglePi) -> bool { let start_pi = self.start_pi.angle_pi(); let end_pi = self.end_pi.angle_pi(); @@ -605,6 +639,10 @@ impl CurveArc { // AnglePi::new(self.end_pi).into() } + pub fn last_spot(&self) -> SpotOnCurve { + SpotOnCurve::new(self.last_point(), self.end_tangent_angle()) + } + fn start_angle(&self) -> Angle { self.start_pi.as_angle() // AnglePi::new(self.start_pi).into() @@ -694,6 +732,8 @@ impl CurveArc { (first_arc.to_segment(), second_arc.to_segment()) } + + } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -1081,7 +1121,7 @@ pub trait ToCurveDrawer { let mut dist_traveled = 0.0; let mut add_left = true; - let mut last_pt = None; + // let mut last_pt = None; for segment in cd.segments() { if add_left { let segment_len = segment.length(); @@ -1107,7 +1147,7 @@ pub trait ToCurveDrawer { } else { right.push(segment.clone()) } - last_pt = Some(segment.last_point()); + // last_pt = Some(segment.last_point()); } (left, right) diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index 2eb1181..fa8140d 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -4,7 +4,7 @@ use std::f32::consts::PI; use glam::*; use itertools::Itertools; use lerpable::Lerpable; -use murrelet_common::{Rect, lerpify_vec2}; +use murrelet_common::lerpify_vec2; use murrelet_common::{ a_pi, approx_eq_eps, mat4_from_mat3_transform, AnglePi, IsAngle, IsPolyline, Polyline, SimpleTransform2d, SimpleTransform2dStep, SpotOnCurve, TransformVec2, From 6fa8674eae1effe4fcfb0feadd5737b7c63d18b2 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 18 Jan 2026 16:17:27 +0800 Subject: [PATCH 134/149] wip --- murrelet_common/src/geometry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 44f9e36..948118e 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -406,7 +406,7 @@ impl IsLength for PointToPoint { // should combine this with Tangent... -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct SpotOnCurve { pub loc: Vec2, pub angle: Angle, From bc77a96c75c4513cc88118288ef2762d3c771fb9 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 22 Jan 2026 19:40:56 +0800 Subject: [PATCH 135/149] wip --- murrelet_common/src/geometry.rs | 42 ++++- murrelet_common/src/intersection.rs | 54 ++++-- murrelet_common/src/transform.rs | 55 +++++- murrelet_draw/src/curve_drawer.rs | 4 + murrelet_draw/src/draw.rs | 75 ++++---- murrelet_draw/src/style.rs | 160 ++++++++++++----- murrelet_draw/src/svg.rs | 21 +-- murrelet_draw/src/tesselate.rs | 262 +++++++++++++++++++++++++++- murrelet_draw/src/transform2d.rs | 8 +- murrelet_livecode/src/types.rs | 6 +- murrelet_livecode/src/unitcells.rs | 63 +++---- murrelet_perform/src/perform.rs | 18 +- murrelet_svg/src/svg.rs | 2 +- 13 files changed, 617 insertions(+), 153 deletions(-) diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 948118e..0bf0659 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -7,7 +7,7 @@ use lerpable::Lerpable; use serde::{Deserialize, Serialize}; use crate::{ - intersection::{find_intersection_inf, within_segment}, + intersection::{find_intersection_inf, find_intersection_segments, within_segment}, transform::TransformVec2, triangulate::DefaultVertex, SimpleTransform2d, SimpleTransform2dStep, @@ -29,6 +29,12 @@ impl AnglePi { AnglePi(v) } + pub fn from_three_vec2(v1: Vec2, v2: Vec2, v3: Vec2) -> AnglePi { + let a = v1 - v2; + let b = v3 - v2; + Angle::new(a.angle_to(b)).into() + } + pub fn to_transform(&self) -> SimpleTransform2d { SimpleTransform2d::rotate_pi(self.angle_pi()) } @@ -476,6 +482,14 @@ impl SpotOnCurve { .to_last_point() } + pub fn move_left_perp_dist_spot(&self, length: L) -> SpotOnCurve { + SpotOnCurve::new(self.move_left_perp_dist(length), self.angle()) + } + + pub fn move_right_perp_dist_spot(&self, length: L) -> SpotOnCurve { + SpotOnCurve::new(self.move_right_perp_dist(length), self.angle()) + } + pub fn move_right_perp_dist(&self, length: L) -> Vec2 { self.turn_right_perp() .to_line(length.to_length()) @@ -535,6 +549,10 @@ impl SpotOnCurve { let loc = self.travel(dist.x).turn_right_perp().travel(dist.y).loc; SpotOnCurve::new(loc, a) } + + pub fn move_back(&self, dist: f32) -> SpotOnCurve { + self.flip().travel(dist).flip() + } } pub trait ToSpotWithAngle { @@ -680,6 +698,10 @@ impl PointToPoint { find_intersection_inf(self.to_tuple(), other.to_tuple()) } + pub fn find_intersection(&self, other: PointToPoint) -> Option { + find_intersection_segments(self.to_tuple(), other.to_tuple()) + } + pub fn within_segment(self, intersection: Vec2, eps: f32) -> bool { within_segment(self.to_tuple(), intersection, eps) } @@ -711,6 +733,10 @@ impl PointToPoint { self.start + loc * (self.end - self.start) } + pub fn pct_spot(&self, pct: f32) -> SpotOnCurve { + SpotOnCurve::new(self.pct(pct), self.angle()) + } + pub fn start_spot(&self) -> SpotOnCurve { SpotOnCurve::new(self.start, self.angle()) } @@ -723,8 +749,18 @@ impl PointToPoint { PointToPoint::new(self.end, self.start) } - pub fn pct_spot(&self, x: f32) -> SpotOnCurve { - SpotOnCurve::new(self.pct(x), self.angle()) + pub fn shift_right_perp(&self, dist: f32) -> PointToPoint { + Self::new( + self.start_spot().turn_right_perp().travel(dist).loc, + self.end_spot().turn_right_perp().travel(dist).loc, + ) + } + + pub fn shift_left_perp(&self, dist: f32) -> PointToPoint { + Self::new( + self.start_spot().turn_left_perp().travel(dist).loc, + self.end_spot().turn_left_perp().travel(dist).loc, + ) } } diff --git a/murrelet_common/src/intersection.rs b/murrelet_common/src/intersection.rs index 634b4eb..ec0f1bf 100644 --- a/murrelet_common/src/intersection.rs +++ b/murrelet_common/src/intersection.rs @@ -17,20 +17,48 @@ pub fn find_intersection_inf(line0: (Vec2, Vec2), line1: (Vec2, Vec2)) -> Option let y4 = line1_end.y; // first find if the lines intersect - let d: f32 = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); - - if d == 0.0 { - None // the lines are parallel, we're done + let d = (y2 - y1) * (x3 - x4) - (x2 - x1) * (y3 - y4); + let epsilon = 1e-7; + if d.abs() < epsilon { + None // parallel, we're done } else { - let t_num = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4); - let t = t_num / d; - - let px = x1 + t * (x2 - x1); - let py = y1 + t * (y2 - y1); - - let intersection = vec2(px, py); - - Some(intersection) + let intersection_point_f32: Vec2; + + let self_is_vertical = (x1 - x2).abs() < epsilon; + let other_is_vertical = (x3 - x4).abs() < epsilon; + + // some help from gemini 2.5 pro + let pt = if self_is_vertical && other_is_vertical { + // Both vertical and den != 0. This should not happen if logic is sound, + // as two distinct vertical lines would have den = 0. + // This implies they might be collinear and overlapping if den was non-zero due to epsilon, + // but the den check should have caught true parallelism. + // For safety, returning None if this unexpected state is reached. + return None; + } else if self_is_vertical { + // Self is vertical, other is not. + let px = x1; + // Slope of other segment + let m_other = (y4 - y3) / (x4 - x3); + // y = m(x - x_pt) + y_pt. Using (x3,y3) from other segment. + let py = m_other * (px - x3) + y3; + Vec2::new(px, py) + } else if other_is_vertical { + // Other is vertical, self is not. + let px = x3; + // Slope of self segment (safe as it's not vertical) + let m_self = (y2 - y1) / (x2 - x1); + // y = m(x - x_pt) + y_pt. Using (x1,y1) from self segment. + let py = m_self * (px - x1) + y1; + Vec2::new(px, py) + } else { + let t_numerator = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4); + let t = t_numerator / d; + let px = x1 + t * (x2 - x1); + let py = y1 + t * (y2 - y1); + Vec2::new(px, py) + }; + Some(pt) } } diff --git a/murrelet_common/src/transform.rs b/murrelet_common/src/transform.rs index 6a18545..74ae65c 100644 --- a/murrelet_common/src/transform.rs +++ b/murrelet_common/src/transform.rs @@ -169,6 +169,24 @@ impl Lerpable for SimpleTransform2dStep { } } +pub trait Transformable { + fn transform_with(&self, t: &T) -> Self; +} + +impl Transformable for Vec2 { + fn transform_with(&self, t: &T) -> Self { + t.to_simple_transform().transform_vec2(*self) + } +} + +impl Transformable for Vec { + fn transform_with(&self, t: &T) -> Self { + self.into_iter() + .map(|x| t.to_simple_transform().transform_vec2(*x)) + .collect_vec() + } +} + #[derive(Clone, Debug)] pub struct SimpleTransform2d(Vec); impl SimpleTransform2d { @@ -190,18 +208,22 @@ impl SimpleTransform2d { &self.0 } - pub fn add_after(&self, transform_vertex: &SimpleTransform2d) -> SimpleTransform2d { + pub fn add_transform_after(&self, other: &F) -> SimpleTransform2d { // just append let v = self .0 .iter() - .chain(transform_vertex.0.iter()) + .chain(other.to_simple_transform().0.iter()) .cloned() .collect(); SimpleTransform2d(v) } + pub fn add_transform_before(&self, other: &F) -> SimpleTransform2d { + other.to_simple_transform().add_transform_after(self) + } + pub fn ident() -> SimpleTransform2d { SimpleTransform2d(vec![]) } @@ -221,16 +243,41 @@ impl SimpleTransform2d { } } -impl TransformVec2 for SimpleTransform2d { +pub trait ToSimpleTransform { + fn to_simple_transform(&self) -> SimpleTransform2d; +} + +impl ToSimpleTransform for SimpleTransform2d { + fn to_simple_transform(&self) -> SimpleTransform2d { + self.clone() + } +} + +impl TransformVec2 for T +where + T: ToSimpleTransform, +{ fn transform_vec2(&self, v: Vec2) -> Vec2 { + let s = self.to_simple_transform(); + let mut v = v; - for step in &self.0 { + for step in &s.0 { v = step.transform().transform_vec2(v); } v } } +// impl TransformVec2 for SimpleTransform2d { +// fn transform_vec2(&self, v: Vec2) -> Vec2 { +// let mut v = v; +// for step in &self.0 { +// v = step.transform().transform_vec2(v); +// } +// v +// } +// } + impl Lerpable for SimpleTransform2d { fn lerpify(&self, other: &Self, pct: &T) -> Self { Self::new(self.0.lerpify(&other.0, pct)) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 177a48e..7f2841a 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -716,6 +716,10 @@ impl CurveArc { self.start_pi } + pub fn end_pi(&self) -> AnglePi { + self.end_pi + } + fn split_segment(&self, target_dist: f32) -> (CurveSegment, CurveSegment) { // find out how far around the arc this pct is let target_pct = target_dist / self.length(); diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index 01e445a..faabae2 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -1,9 +1,12 @@ // traits, that you can connect to things like Nannou draw -use glam::{vec2, Mat4, Vec2, Vec3}; -use itertools::Itertools; +use glam::{vec2, Vec2, Vec3}; + use lerpable::Lerpable; -use murrelet_common::{IsPolyline, MurreletColor, Polyline, SimpleTransform2d, TransformVec2}; +use murrelet_common::{ + IsPolyline, MurreletColor, Polyline, SimpleTransform2d, ToSimpleTransform, TransformVec2, + Transformable, +}; use murrelet_livecode::unitcells::UnitCellContext; use murrelet_livecode_derive::Livecode; use palette::{named::AQUAMARINE, LinSrgba, Srgb}; @@ -11,7 +14,7 @@ use palette::{named::AQUAMARINE, LinSrgba, Srgb}; use crate::{ newtypes::RGBandANewtype, style::{styleconf::*, StyledPathSvgFill}, - transform2d::ToMat4, + tesselate::{AsLyonTransform, ToLyonPath}, }; #[derive(Debug, Clone, Copy, Livecode, Lerpable, Default)] @@ -344,28 +347,28 @@ pub trait Sdraw: Sized { self.with_svg_style(svg_style) } - fn transform(&self) -> Mat4; + fn transform(&self) -> SimpleTransform2d; - fn set_transform(&self, m: M) -> Self; + fn set_transform(&self, m: &M) -> Self; - fn add_transform_after(&self, t: Mat4) -> Self { - let m = t * self.transform(); - self.set_transform(m) + fn add_transform_after(&self, t: &M) -> Self { + let m = self + .transform() + .add_transform_after(&t.to_simple_transform()); + self.set_transform(&m) } - fn add_transform_before(&self, t: M) -> Self { - let m = self.transform() * t.change_to_mat4(); - self.set_transform(m) + fn add_transform_before(&self, t: &M) -> Self { + let m = self + .transform() + .add_transform_before(&t.to_simple_transform()); + self.set_transform(&m) } - fn transform_points(&self, face: &F) -> Polyline; + fn transform_points(&self, face: &F) -> F; fn maybe_transform_vec2(&self, v: Vec2) -> Option { - self.transform_points(&vec![v]) - .into_iter_vec2() - .collect_vec() - .first() - .cloned() + Some(v.transform_with(&self.transform())) } fn line_space_multi(&self) -> f32; @@ -378,10 +381,10 @@ pub struct CoreSDrawCtxUnitCell { sdraw: CoreSDrawCtx, } impl Sdraw for CoreSDrawCtxUnitCell { - fn transform_points(&self, face: &F) -> Polyline { + fn transform_points(&self, face: &F) -> F { // let mut points = face.clone(); - let mut points = self.sdraw.transform.transform_many_vec2(face); + let mut points = face.transform_with(&self.sdraw.transform); // main difference! apply the unit cell transform points = if self.unit_cell_skew { @@ -409,13 +412,13 @@ impl Sdraw for CoreSDrawCtxUnitCell { } // this only changes the global transform, the unitcell one will still happen - fn transform(&self) -> Mat4 { - self.sdraw.transform + fn transform(&self) -> SimpleTransform2d { + self.sdraw.transform.clone() } - fn set_transform(&self, m: M) -> Self { + fn set_transform(&self, m: &M) -> Self { let mut ctx = self.clone(); - ctx.sdraw.transform = m.change_to_mat4(); + ctx.sdraw.transform = m.to_simple_transform(); ctx } } @@ -477,11 +480,11 @@ impl CoreSDrawCtxUnitCell { pub struct CoreSDrawCtx { svg_style: MurreletStyle, pub frame: f32, - transform: Mat4, // happens before the others + transform: SimpleTransform2d, // happens before the others } impl Sdraw for CoreSDrawCtx { - fn transform_points(&self, face: &F) -> Polyline { - self.transform.transform_many_vec2(face) + fn transform_points(&self, face: &F) -> F { + face.transform_with(&self.transform) } fn with_svg_style(&self, svg_style: MurreletStyle) -> Self { @@ -490,24 +493,24 @@ impl Sdraw for CoreSDrawCtx { ctx } - fn transform(&self) -> Mat4 { - self.transform + fn transform(&self) -> SimpleTransform2d { + self.transform.clone() } - fn set_transform(&self, m: M) -> Self { + fn set_transform(&self, m: &M) -> Self { let mut sdraw = self.clone(); - sdraw.transform = m.change_to_mat4(); + sdraw.transform = m.to_simple_transform(); sdraw } - fn add_transform_after(&self, t: Mat4) -> Self { + fn add_transform_after(&self, t: &M) -> Self { let mut ctx = self.clone(); - ctx.transform = t * ctx.transform; + ctx.transform = ctx.transform.add_transform_after(&t.to_simple_transform()); ctx } - fn add_transform_before(&self, t: M) -> Self { + fn add_transform_before(&self, t: &M) -> Self { let mut ctx = self.clone(); - ctx.transform *= t.change_to_mat4(); + ctx.transform = ctx.transform.add_transform_before(&t.to_simple_transform()); ctx } @@ -531,7 +534,7 @@ impl Sdraw for CoreSDrawCtx { } impl CoreSDrawCtx { - pub fn new(svg_style: MurreletStyle, frame: f32, transform: Mat4) -> Self { + pub fn new(svg_style: MurreletStyle, frame: f32, transform: SimpleTransform2d) -> Self { Self { svg_style, frame, diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index ff032a8..490442e 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -1,9 +1,9 @@ #![allow(dead_code)] use crate::{ - curve_drawer::CurveDrawer, + curve_drawer::{CurveDrawer, ToCurveDrawer}, draw::*, svg::{SvgPathDef, SvgShape, TransformedSvgShape}, - tesselate::parse_svg_path_as_vec2, + tesselate::{parse_svg_path_as_vec2, ToLyonPath}, transform2d::*, }; use glam::*; @@ -491,33 +491,47 @@ pub enum MurreletCurveKinds { #[derive(Debug, Clone)] pub struct MurreletCurve { cd: MurreletCurveKinds, - t: Mat4, + t: SimpleTransform2d, } impl MurreletCurve { pub fn new(cd: CurveDrawer) -> Self { Self { cd: MurreletCurveKinds::CD(cd), - t: Mat4::IDENTITY, + t: SimpleTransform2d::noop(), } } - pub fn transform_with_mat4_after(&self, t: Mat4) -> MurreletCurve { + pub fn transform_after(&self, t: &T) -> MurreletCurve { Self { cd: self.cd.clone(), - t: t * self.t, + t: self.t.add_transform_after(t), } } - pub fn transform_with_mat4_before(&self, t: Mat4) -> MurreletCurve { + pub fn transform_before(&self, t: &T) -> MurreletCurve { Self { cd: self.cd.clone(), - t: self.t * t, + t: self.t.add_transform_before(t), } } + // pub fn transform_with_mat4_after(&self, t: Mat4) -> MurreletCurve { + // Self { + // cd: self.cd.clone(), + // t: t * self.t, + // } + // } + + // pub fn transform_with_mat4_before(&self, t: Mat4) -> MurreletCurve { + // Self { + // cd: self.cd.clone(), + // t: self.t * t, + // } + // } + pub fn mat4(&self) -> Mat4 { - self.t + self.t.to_mat4() } pub fn curve(&self) -> CurveDrawer { @@ -533,7 +547,7 @@ impl MurreletCurve { fn new_transformed_svg(svg: &TransformedSvgShape) -> MurreletCurve { Self { cd: MurreletCurveKinds::Svg(svg.shape.as_path()), - t: svg.t, + t: svg.t.clone(), } } @@ -548,7 +562,11 @@ impl MurreletCurve { TransformedSvgShape::from_shape(crate::svg::SvgShape::Path(pts.clone())) } }; - c.transform_with_mat4_after(self.mat4()) + c.transform_after(&self.t) + } + + pub fn transform(&self) -> SimpleTransform2d { + self.t.clone() } } @@ -576,7 +594,7 @@ impl MurreletPath { match self { MurreletPath::Polyline(c) => MurreletCurve { cd: MurreletCurveKinds::CD(CurveDrawer::new_simple_points(c.clone_to_vec(), false)), - t: Mat4::IDENTITY, + t: SimpleTransform2d::noop(), }, MurreletPath::Curve(c) => c.clone(), MurreletPath::Svg(svg) => MurreletCurve::new_transformed_svg(svg), @@ -584,49 +602,46 @@ impl MurreletPath { } } - pub fn transform_with(&self, t: &T) -> Self { + pub fn transform_with(&self, t: &T) -> Self { match self { MurreletPath::Polyline(x) => MurreletPath::Polyline(x.transform_with(t)), - MurreletPath::Curve(cd) => { - MurreletPath::Svg(cd.to_svg().transform_with_mat4_after(t.change_to_mat4())) - } - MurreletPath::Svg(svg) => { - MurreletPath::Svg(svg.transform_with_mat4_after(t.change_to_mat4())) - } + MurreletPath::Curve(cd) => MurreletPath::Svg(cd.to_svg().transform_after(t)), + MurreletPath::Svg(svg) => MurreletPath::Svg(svg.transform_after(t)), MurreletPath::MaskedCurve(_, _) => todo!(), } } - pub fn transform_with_mat4_after(&self, t: Mat4) -> MurreletPath { + pub fn transform_with_mat4_after( + &self, + t: T, + ) -> MurreletPath { match self { MurreletPath::Polyline(_) => self.transform_with(&t), - MurreletPath::Curve(c) => MurreletPath::Curve(c.transform_with_mat4_after(t)), - MurreletPath::Svg(c) => MurreletPath::Svg(c.transform_with_mat4_after(t)), - MurreletPath::MaskedCurve(mask, curve) => MurreletPath::MaskedCurve( - mask.transform_with_mat4_after(t), - curve.transform_with_mat4_after(t), - ), + MurreletPath::Curve(c) => MurreletPath::Curve(c.transform_after(&t)), + MurreletPath::Svg(c) => MurreletPath::Svg(c.transform_after(&t)), + MurreletPath::MaskedCurve(mask, curve) => { + MurreletPath::MaskedCurve(mask.transform_after(&t), curve.transform_after(&t)) + } } } - pub fn transform_with_mat4_before(&self, t: Mat4) -> MurreletPath { + pub fn transform_with_mat4_before(&self, t: &T) -> MurreletPath { match self { - MurreletPath::Polyline(_) => self.transform_with(&t), - MurreletPath::Curve(c) => MurreletPath::Curve(c.transform_with_mat4_before(t)), - MurreletPath::Svg(c) => MurreletPath::Svg(c.transform_with_mat4_before(t)), - MurreletPath::MaskedCurve(mask, curve) => MurreletPath::MaskedCurve( - mask.transform_with_mat4_before(t), - curve.transform_with_mat4_before(t), - ), + MurreletPath::Polyline(_) => self.transform_with(t), + MurreletPath::Curve(c) => MurreletPath::Curve(c.transform_before(t)), + MurreletPath::Svg(c) => MurreletPath::Svg(c.transform_before(t)), + MurreletPath::MaskedCurve(mask, curve) => { + MurreletPath::MaskedCurve(mask.transform_before(t), curve.transform_before(t)) + } } } pub fn transform(&self) -> Option { match self { MurreletPath::Polyline(_) => None, - MurreletPath::Curve(c) => Some(c.t), - MurreletPath::Svg(c) => Some(c.t), - MurreletPath::MaskedCurve(_mask, c) => Some(c.t), + MurreletPath::Curve(c) => Some(c.t.to_mat4()), + MurreletPath::Svg(c) => Some(c.t.to_mat4()), + MurreletPath::MaskedCurve(_mask, c) => Some(c.t.to_mat4()), } } } @@ -651,6 +666,69 @@ impl MurreletPathAnnotation { } } +pub trait ShapeToStyledPath { + fn from_mstyle(&self, style: &MurreletStyle) -> StyledPath; + fn from_style(&self, style: &StyleConf) -> StyledPath { + self.from_mstyle(&style.to_style()) + } +} + +impl ShapeToStyledPath for Polyline { + fn from_mstyle(&self, style: &MurreletStyle) -> StyledPath { + StyledPath::new_from_path(MurreletPath::Polyline(self.clone()), *style) + } +} + +impl ShapeToStyledPath for CurveDrawer { + fn from_mstyle(&self, style: &MurreletStyle) -> StyledPath { + StyledPath::new_from_path( + MurreletPath::Curve(MurreletCurve::new(self.clone())), + *style, + ) + } +} + +impl ToLyonPath for MurreletPath { + fn approx_vertex_count(&self) -> usize { + match self { + MurreletPath::Polyline(p) => p.len(), + MurreletPath::Curve(cd) => match &cd.cd { + MurreletCurveKinds::CD(curve_drawer) => curve_drawer.approx_vertex_count(), + MurreletCurveKinds::Svg(_) => todo!(), + }, + MurreletPath::Svg(_) => todo!(), + MurreletPath::MaskedCurve(_, _) => todo!(), + } + } + + fn start(&self) -> Vec2 { + match self { + MurreletPath::Polyline(p) => *p.first().unwrap_or(&Vec2::ZERO), + MurreletPath::Curve(cd) => match &cd.cd { + MurreletCurveKinds::CD(curve_drawer) => curve_drawer.start(), + MurreletCurveKinds::Svg(_) => todo!(), + }, + MurreletPath::Svg(_) => todo!(), + MurreletPath::MaskedCurve(_, _) => todo!(), + } + } + + fn add_to_lyon( + &self, + builder: &mut B, + ) -> murrelet_livecode::types::LivecodeResult { + match self { + MurreletPath::Polyline(p) => p.to_cd_open().add_to_lyon(builder), + MurreletPath::Curve(cd) => match &cd.cd { + MurreletCurveKinds::CD(curve_drawer) => curve_drawer.add_to_lyon(builder), + MurreletCurveKinds::Svg(_) => todo!(), + }, + MurreletPath::Svg(_) => todo!(), + MurreletPath::MaskedCurve(_, _) => todo!(), + } + } +} + #[derive(Debug, Clone)] pub struct StyledPath { pub path: MurreletPath, @@ -698,22 +776,22 @@ impl StyledPath { } } - pub fn from_path(path: P) -> StyledPath { + pub fn from_path(path: P) -> StyledPath { StyledPath { - path: MurreletPath::Polyline(path.as_polyline()), + path: MurreletPath::Curve(MurreletCurve::new(path.to_cd_open())), style: MurreletStyle::default(), annotations: MurreletPathAnnotation::noop(), } } - pub fn transform_path(&self, t: &T) -> Self { + pub fn transform_path(&self, t: &T) -> Self { StyledPath { path: self.path.transform_with(t), ..self.clone() } } - pub fn transform_with_mat4_after(&self, t: Mat4) -> Self { + pub fn transform_with_mat4_after(&self, t: T) -> Self { StyledPath { path: self.path.transform_with_mat4_after(t), ..self.clone() diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index 3a020ed..ad0d474 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -3,6 +3,7 @@ use glam::{Mat4, Vec2}; use lerpable::Lerpable; use lyon::geom::{euclid::Point2D, Point}; +use murrelet_common::{SimpleTransform2d, ToSimpleTransform}; use murrelet_gui::MurreletGUI; use murrelet_livecode_derive::Livecode; @@ -295,17 +296,17 @@ impl SvgShape { }) } - pub fn transform(&self, t: Mat4) -> TransformedSvgShape { + pub fn transform(&self, t: &F) -> TransformedSvgShape { TransformedSvgShape { shape: self.clone(), - t, + t: t.to_simple_transform(), } } pub fn as_transform(&self) -> TransformedSvgShape { TransformedSvgShape { shape: self.clone(), - t: Mat4::IDENTITY, + t: SimpleTransform2d::noop(), } } @@ -329,13 +330,13 @@ impl SvgShape { #[derive(Clone, Debug)] pub struct TransformedSvgShape { pub shape: SvgShape, - pub t: Mat4, + pub t: SimpleTransform2d, } impl TransformedSvgShape { pub fn from_shape(shape: SvgShape) -> Self { Self { shape, - t: Mat4::IDENTITY, + t: SimpleTransform2d::noop(), } } @@ -344,21 +345,21 @@ impl TransformedSvgShape { Self { shape, - t: Mat4::IDENTITY, + t: SimpleTransform2d::noop(), } } - pub fn transform_with_mat4_after(&self, t: Mat4) -> Self { + pub fn transform_after(&self, t: &F) -> Self { Self { shape: self.shape.clone(), - t: t * self.t, + t: self.t.add_transform_after(t), } } - pub fn transform_with_mat4_before(&self, t: Mat4) -> Self { + pub fn transform_before(&self, t: &F) -> Self { Self { shape: self.shape.clone(), - t: self.t * t, + t: self.t.add_transform_before(t), } } } diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 2e8da99..39576dc 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -1,26 +1,33 @@ -use std::{collections::HashMap, ops}; +use std::{collections::HashMap, f32::consts::PI, ops}; use crate::{ cubic::CubicBezier, - curve_drawer::{CubicBezierPath, CurveArc}, + curve_drawer::{ + CubicBezierPath, CurveArc, CurveCubicBezier, CurveDrawer, CurvePoints, CurveSegment, + ToCurveDrawer, + }, svg::SvgPathDef, }; use delaunator::Triangulation; use glam::{vec2, Vec2, Vec2Swizzles}; use itertools::Itertools; use kurbo::BezPath; +use lyon::geom::vector; +use lyon::{geom::arc::Arc, math::Transform}; +use lyon::{geom::Angle, path::traits::Build}; use lyon::{ geom::{ euclid::{Point2D, UnknownUnit}, - point, + point, Point, }, - path::{FillRule, Path}, + path::{traits::PathBuilder, FillRule, Path}, tessellation::{BuffersBuilder, FillOptions, FillTessellator, FillVertex}, }; use murrelet_common::{ curr_next_no_loop_iter, triangulate::DefaultVertex, AnglePi, IsAngle, PointToPoint, Polyline, - SpotOnCurve, ToVec2, + SimpleTransform2d, SimpleTransform2dStep, SpotOnCurve, ToVec2, }; +use murrelet_livecode::types::{LivecodeError, LivecodeResult}; pub trait ToVecVec2 { fn to_vec2(&self) -> Vec; @@ -85,6 +92,251 @@ impl ToVecVec2 for CubicBezierPath { } } +pub trait AsLyonTransform { + fn to_lyon_transform(&self) -> Transform { + self.update_lyon_transform(Transform::identity()) + } + + fn update_lyon_transform(&self, t: Transform) -> Transform; +} + +impl AsLyonTransform for SimpleTransform2d { + fn update_lyon_transform(&self, t: Transform) -> Transform { + let mut aa = t; + for t in self.steps() { + aa = t.update_lyon_transform(aa); + } + aa + } +} + +impl AsLyonTransform for SimpleTransform2dStep { + fn update_lyon_transform(&self, t: Transform) -> Transform { + match self { + SimpleTransform2dStep::Translate(v) => t.then_translate(vector(v.x, v.y)), + SimpleTransform2dStep::Rotate(v, a) => { + let vv = vector(v.x, v.y); + let t = t.then_translate(-vv); + let t = t.then_rotate(Angle::radians(a.angle())); + let t = t.then_translate(vv); + t + } + SimpleTransform2dStep::Scale(v) => t.then_scale(v.x, v.y), + SimpleTransform2dStep::Skew(_, _) => unreachable!(), + } + } +} + +pub trait ToLyonPath { + const EPS: f32 = 1e-6f32; + + fn approx_vertex_count(&self) -> usize; + + fn to_lyon_with_transform( + &self, + t: &T, + ) -> LivecodeResult { + let transform = t.to_lyon_transform(); + + let mut lyon_builder = lyon::path::Path::builder().transformed(transform); + + let start = self.start(); + + lyon_builder.begin(v2p(start)); + let is_closed = self._add_to_lyon(start, &mut lyon_builder)?; + lyon_builder.end(is_closed); + + Ok(lyon_builder.build()) + } + + fn to_lyon(&self) -> LivecodeResult { + self.to_lyon_with_transform(&SimpleTransform2d::ident()) + } + + fn start(&self) -> Vec2; + + fn _start(&self) -> Point { + v2p(self.start()) + } + + fn _add_to_lyon(&self, start: Vec2, builder: &mut B) -> LivecodeResult { + // handles when "start" is too far away + if start.distance(self.start()) > Self::EPS { + builder.line_to(self._start()); + } + + self.add_to_lyon(builder) + } + + fn add_to_lyon(&self, builder: &mut B) -> LivecodeResult; +} + +fn v2p(v: Vec2) -> Point { + point(v.x, v.y) +} + +impl ToLyonPath for CurvePoints { + fn start(&self) -> Vec2 { + self.first_point() + } + + fn add_to_lyon(&self, builder: &mut B) -> LivecodeResult { + for p in self.points() { + if p.x.is_nan() || p.y.is_nan() { + return LivecodeError::rawr("nan in CurvePoints"); + } + + builder.line_to(v2p(*p)); + } + + Ok(false) + } + + fn approx_vertex_count(&self) -> usize { + self.points.len() + } +} + +impl ToLyonPath for CubicBezier { + fn start(&self) -> Vec2 { + self.from + } + + fn add_to_lyon(&self, builder: &mut B) -> LivecodeResult { + if self.ctrl1.is_nan() || self.ctrl2.is_nan() || self.to.is_nan() || self.from.is_nan() { + return LivecodeError::rawr("nan in Bezier"); + } + + builder.cubic_bezier_to(v2p(self.ctrl1), v2p(self.ctrl2), v2p(self.to)); + + Ok(false) + } + + fn approx_vertex_count(&self) -> usize { + 4 + } +} + +impl ToLyonPath for CurveCubicBezier { + fn start(&self) -> Vec2 { + self.to_cubic().start() + } + + fn add_to_lyon(&self, builder: &mut B) -> LivecodeResult { + self.to_cubic().add_to_lyon(builder)?; + + Ok(false) + } + + fn approx_vertex_count(&self) -> usize { + self.to_cubic().approx_vertex_count() + } +} + +// chatgpt +fn add_circular_arc(builder: &mut B, c: &CurveArc) { + let cx = c.loc.x; + let cy = c.loc.y; + let r = c.radius; + let start = c.start_pi().angle(); + let end: f32 = c.end_pi().angle(); + + // Angles are in radians. + let mut sweep = end - start; + + + if c.is_ccw() { + if sweep < 0.0 { sweep += 2.0 * PI; } + } else { + if sweep > 0.0 { sweep -= 2.0 * PI; } + } + + let arc = Arc { + center: point(cx, cy), + radii: vector(r, r), + start_angle: Angle::radians(start), + sweep_angle: Angle::radians(sweep), + x_rotation: Angle::radians(0.0), + }; + + // Make sure the current point is at the arc's start. + // builder.line_to(arc.from()); // handled already + + arc.for_each_cubic_bezier(&mut |c| { + builder.cubic_bezier_to(c.ctrl1, c.ctrl2, c.to); + }); + + // builder.end(false); +} + +impl ToLyonPath for CurveArc { + fn start(&self) -> Vec2 { + self.first_point() + } + + fn add_to_lyon(&self, builder: &mut B) -> LivecodeResult { + if self.end_pi.angle_pi().is_nan() + || self.start_pi.angle_pi().is_nan() + || self.radius.is_nan() + || self.loc.x.is_nan() + || self.loc.y.is_nan() + { + return LivecodeError::rawr("nan in CurveArc"); + } + + add_circular_arc(builder, self); + + Ok(false) + } + + fn approx_vertex_count(&self) -> usize { + 4 + } +} + +impl ToLyonPath for CurveSegment { + fn start(&self) -> Vec2 { + self.first_point() + } + + fn add_to_lyon(&self, builder: &mut B) -> LivecodeResult { + match self { + CurveSegment::Arc(c) => c.add_to_lyon(builder), + CurveSegment::Points(c) => c.add_to_lyon(builder), + CurveSegment::CubicBezier(c) => c.add_to_lyon(builder), + } + } + + fn approx_vertex_count(&self) -> usize { + match self { + CurveSegment::Arc(c) => c.approx_vertex_count(), + CurveSegment::Points(c) => c.approx_vertex_count(), + CurveSegment::CubicBezier(c) => c.approx_vertex_count(), + } + } +} + +impl ToLyonPath for CurveDrawer { + fn start(&self) -> Vec2 { + self.first_point().unwrap_or_default() //??? + } + + fn add_to_lyon(&self, builder: &mut B) -> LivecodeResult { + for s in self.segments() { + s.add_to_lyon(builder)?; + } + Ok(self.closed) + } + + fn approx_vertex_count(&self) -> usize { + let mut c = 0; + for s in self.segments() { + c += s.approx_vertex_count(); + } + c + } +} + fn vec2_to_kurbo(v: Vec2) -> kurbo::Point { kurbo::Point::new(v.x as f64, v.y as f64) } diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index fa8140d..4865f94 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -4,7 +4,7 @@ use std::f32::consts::PI; use glam::*; use itertools::Itertools; use lerpable::Lerpable; -use murrelet_common::lerpify_vec2; +use murrelet_common::{ToSimpleTransform, lerpify_vec2}; use murrelet_common::{ a_pi, approx_eq_eps, mat4_from_mat3_transform, AnglePi, IsAngle, IsPolyline, Polyline, SimpleTransform2d, SimpleTransform2dStep, SpotOnCurve, TransformVec2, @@ -297,6 +297,12 @@ impl Transform2d { } } +impl ToSimpleTransform for Transform2d { + fn to_simple_transform(&self) -> SimpleTransform2d { + self.to_simple() + } +} + impl Default for ControlTransform2d { fn default() -> Self { Self(Default::default()) diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index c685025..84a3521 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -3,7 +3,7 @@ use std::{collections::HashSet, fmt::Debug}; use evalexpr::{build_operator_tree, EvalexprError, HashMapContext, Node}; use itertools::Itertools; use lerpable::{step, Lerpable}; -use murrelet_common::{IdxInRange, IdxInRange2d, print_expect}; +use murrelet_common::{print_expect, IdxInRange, IdxInRange2d}; use murrelet_gui::CanMakeGUI; use serde::{Deserialize, Deserializer}; use thiserror::Error; @@ -41,6 +41,10 @@ impl LivecodeError { pub fn raw(s: &str) -> Self { Self::Raw(s.to_string()) } + + pub fn rawr(s: &str) -> LivecodeResult { + LivecodeResult::Err(Self::raw(s)) + } } pub trait IterUnwrapOrPrint { diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index 94d6404..24fbfa2 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -681,12 +681,13 @@ impl UnitCellContext { self.detail.transform_with_skew_mat4() } - pub fn transform_with_skew(&self, v: &F) -> Polyline { - self.detail.transform_with_skew(v) + pub fn transform_with_skew(&self, v: &F) -> F { + v.transform_with(&self.detail.transform_with_skew_mat4()) } pub fn transform_one_point_with_skew(&self, v: Vec2) -> Vec2 { - self.detail.transform_with_skew(&vec![v]).clone_to_vec()[0] + // self.detail.transform_with_skew(&vec![v]).clone_to_vec()[0] + v.transform_with(&self.detail.transform_with_skew_mat4()) } pub fn transform_no_skew_one_point(&self, v: Vec2) -> Vec2 { @@ -694,9 +695,9 @@ impl UnitCellContext { self.detail.transform_no_skew_one_point(v) } - pub fn transform_no_skew(&self, v: &F) -> Polyline { + pub fn transform_no_skew(&self, v: &F) -> F { // also does adjust shape.. - self.detail.transform_no_skew(v) + v.transform_with(&self.detail.transform_no_skew_mat4()) } pub fn transform_no_skew_mat4(&self) -> SimpleTransform2d { @@ -712,12 +713,11 @@ impl IntoExprWorldContext for UnitCellContext { fn as_expr_world_context_values(&self) -> ExprWorldContextValues { let mut ctx_vals = self.idx.as_expr_world_context_values(); - let loc = self.detail.transform_with_skew(&vec![ - vec2(-50.0, -50.0), - vec2(50.0, -50.0), - vec2(50.0, 50.0), - ]); - let locs = loc.into_iter_vec2().collect_vec(); + let locs = vec![vec2(-50.0, -50.0), vec2(50.0, -50.0), vec2(50.0, 50.0)] + .into_iter() + .map(|x| self.detail.transform_with_skew_mat4().transform_vec2(x)) + .collect_vec(); + let width = locs[1].distance(locs[0]); let height = locs[1].distance(locs[2]); @@ -977,22 +977,22 @@ impl UnitCellDetails { } } - fn transform_with_skew(&self, face: &F) -> Polyline { - let vs = face - .into_iter_vec2() - .map(|x| match self { - UnitCellDetails::Wallpaper(d) => d.transform_with_skew(x), - UnitCellDetails::Function(d) => d.transform_with_skew(x), - }) - .collect_vec(); - Polyline::new(vs) - } + // fn transform_with_skew(&self, face: &F) -> Polyline { + // let vs = face + // .into_iter_vec2() + // .map(|x| match self { + // UnitCellDetails::Wallpaper(d) => d.transform_with_skew(x), + // UnitCellDetails::Function(d) => d.transform_with_skew(x), + // }) + // .collect_vec(); + // Polyline::new(vs) + // } pub fn transform_no_skew_one_point(&self, v: Vec2) -> Vec2 { self.transform_no_skew(&vec![v]).clone_to_vec()[0] } - pub fn transform_no_skew(&self, v: &F) -> Polyline { + pub fn transform_no_skew(&self, v: &F) -> F { match self { UnitCellDetails::Wallpaper(w) => w.transform_no_skew(v), UnitCellDetails::Function(_) => todo!(), @@ -1076,16 +1076,17 @@ impl UnitCellDetailsWallpaper { // adjust the shape (symmetry, rotation), translate the center let offset = self.offset(); let new_center = SimpleTransform2d::translate(offset); - self.adjust_shape.add_after(&new_center) + self.adjust_shape.add_transform_after(&new_center) } - pub fn transform_no_skew(&self, v: &F) -> Polyline { + pub fn transform_no_skew(&self, v: &F) -> F { let m = self.transform_no_skew_mat(); - Polyline::new( - v.into_iter_vec2() - .map(|x| m.transform_vec2(x)) - .collect_vec(), - ) + v.transform_with(&m) + // Polyline::new( + // v.into_iter_vec2() + // .map(|x| m.transform_vec2(x)) + // .collect_vec(), + // ) } // how to move the location of something @@ -1108,12 +1109,12 @@ impl UnitCellDetailsWallpaper { .as_wallpaper() .unwrap() .transform_vertex - .add_after(&self.transform_vertex), + .add_transform_after(&self.transform_vertex), adjust_shape: detail .as_wallpaper() .unwrap() .adjust_shape - .add_after(&self.adjust_shape), + .add_transform_after(&self.adjust_shape), is_base: self.is_base && detail.as_wallpaper().unwrap().is_base, }) } diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 10691c5..027058c 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] -use glam::{vec3, Mat4, Vec2}; +use glam::{Vec2, vec2}; use lerpable::Lerpable; -use murrelet_common::{Assets, AssetsRef, LivecodeUsage, LivecodeValue}; +use murrelet_common::{Assets, AssetsRef, LivecodeUsage, LivecodeValue, SimpleTransform2d, SimpleTransform2dStep}; use murrelet_common::{LivecodeSrc, LivecodeSrcUpdateInput, MurreletAppInput}; use murrelet_common::{MurreletColor, TransformVec2}; use murrelet_gui::MurreletGUI; @@ -104,21 +104,25 @@ impl SvgDrawConfig { self.capture_path.clone() } - pub fn transform_for_size(&self) -> Mat4 { + pub fn transform_for_size(&self) -> SimpleTransform2d { if self.should_resize { // okay so we take the width, since that's what looked okay on the screen let size = self.size(); let full_target_width = self.full_target_width() * 1.0; - let translation_to_final = vec3(full_target_width, full_target_width, 0.0); + let translation_to_final = vec2(full_target_width, full_target_width); let s = self.target_size / size; - let scale = vec3(s, s, 1.0); + // let scale = vec3(s, s, 1.0); // aiming for 100mm by 100mm, going from 0 to 10 // operations go right to left! - Mat4::from_translation(translation_to_final) * Mat4::from_scale(scale) + // Mat4::from_translation(translation_to_final) * Mat4::from_scale(scale) + SimpleTransform2d::new(vec![ + SimpleTransform2dStep::translate(translation_to_final), + SimpleTransform2dStep::scale_both(s) + ]) } else { - Mat4::IDENTITY + SimpleTransform2d::noop() } } diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 7829276..7448eb9 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -146,7 +146,7 @@ impl ToSvgData for MurreletCurve { impl ToStyledGroup for TransformedSvgShape { fn to_group(&self, style: &MurreletStyle) -> Option { let mut g = Group::new(); - g = g.set("transform", self.t.to_svg_matrix()); + g = g.set("transform", self.t.to_mat4().to_svg_matrix()); match &self.shape { SvgShape::Rect(s) => { let mut rect = svg::node::element::Rectangle::new() From 9ffdd657662f568082ccbe6749cc22288abef54a Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 27 Jan 2026 17:22:21 +0800 Subject: [PATCH 136/149] wip --- murrelet_common/src/idx.rs | 10 +++++ murrelet_common/src/transform.rs | 65 +++++++++++++++++++++++++++--- murrelet_draw/src/curve_drawer.rs | 24 ++++++----- murrelet_draw/src/draw.rs | 3 +- murrelet_draw/src/drawable.rs | 13 ++++++ murrelet_draw/src/transform2d.rs | 15 +------ murrelet_livecode/src/types.rs | 2 - murrelet_livecode/src/unitcells.rs | 6 +-- murrelet_perform/src/cli.rs | 10 ++--- 9 files changed, 106 insertions(+), 42 deletions(-) diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 9c3047c..9d4088e 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -164,6 +164,16 @@ impl IdxInRange { } } +pub trait IdxInRangeEnum<'a, T> { + fn iter_enum_idx(&'a self) -> Vec<(IdxInRange, &'a T)>; +} + +impl<'a, T> IdxInRangeEnum<'a, T> for &[T] { + fn iter_enum_idx(&'a self) -> Vec<(IdxInRange, &'a T)> { + IdxInRange::enumerate(self.iter()) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct IdxInRange2d { pub i: IdxInRange, diff --git a/murrelet_common/src/transform.rs b/murrelet_common/src/transform.rs index 74ae65c..227738f 100644 --- a/murrelet_common/src/transform.rs +++ b/murrelet_common/src/transform.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use lerpable::Lerpable; use crate::{ - lerp, + approx_eq_eps, lerp, polyline::{IsPolyline, Polyline}, vec_lerp, AnglePi, IsAngle, }; @@ -179,11 +179,20 @@ impl Transformable for Vec2 { } } -impl Transformable for Vec { +// impl Transformable for Vec { +// fn transform_with(&self, t: &T) -> Self { +// self.into_iter() +// .map(|x| t.to_simple_transform().transform_vec2(*x)) +// .collect_vec() +// } +// } + +impl Transformable for Vec +where + A: Transformable, +{ fn transform_with(&self, t: &T) -> Self { - self.into_iter() - .map(|x| t.to_simple_transform().transform_vec2(*x)) - .collect_vec() + self.into_iter().map(|x| x.transform_with(t)).collect_vec() } } @@ -194,6 +203,10 @@ impl SimpleTransform2d { Self(v) } + pub fn rotate(angle_pi: AnglePi) -> Self { + Self(vec![SimpleTransform2dStep::rotate_pi(angle_pi)]) + } + pub fn rotate_pi(angle_pi: f32) -> Self { Self(vec![SimpleTransform2dStep::rotate_pi(AnglePi::new( angle_pi, @@ -241,6 +254,48 @@ impl SimpleTransform2d { pub fn to_mat4(&self) -> Mat4 { mat4_from_mat3_transform(self.to_mat3()) } + + pub fn is_similarity_transform(&self) -> bool { + for s in &self.0 { + match s { + SimpleTransform2dStep::Scale(v2) => { + if !approx_eq_eps(v2.x.abs(), v2.y.abs(), 1.0e-3) { + return false; + } + } + SimpleTransform2dStep::Skew(_, _) => return false, + _ => {} + } + } + return true; + } + + // experimental + pub fn approx_scale(&self) -> f32 { + let mut scale = 1.0; + for a in &self.0 { + match a { + SimpleTransform2dStep::Translate(_) => {} + SimpleTransform2dStep::Rotate(_, _) => {} + SimpleTransform2dStep::Scale(s) => scale *= s.x.max(s.y), + SimpleTransform2dStep::Skew(_, _) => todo!(), + } + } + scale + } + + pub fn approx_rotate(&self) -> AnglePi { + let mut rotate = AnglePi::new(0.0); + for a in &self.0 { + match a { + SimpleTransform2dStep::Translate(_) => {} + SimpleTransform2dStep::Rotate(_, s) => rotate = rotate + *s, + SimpleTransform2dStep::Scale(_) => {} + SimpleTransform2dStep::Skew(_, _) => todo!(), + } + } + rotate + } } pub trait ToSimpleTransform { diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 7f2841a..6b24631 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -279,16 +279,18 @@ impl CurveDrawer { self.segments.iter().map(|segment| segment.length()).sum() } - pub fn maybe_transform(&self, transform: &Transform2d) -> LivecodeResult { + pub fn maybe_transform(&self, transform: &T) -> LivecodeResult { let mut segments = vec![]; + let transform = transform.to_simple_transform(); + if !transform.is_similarity_transform() { return Err(LivecodeError::raw("not a similarity transform")); } for cd in &self.segments { // we've ran our check, so we can just do it now.. - segments.push(cd.force_transform(transform)); + segments.push(cd.force_transform(&transform)); } Ok(Self::new(segments, self.closed)) @@ -451,14 +453,15 @@ impl CurveSegment { Self::CubicBezier(CurveCubicBezier::from_cubic(c)) } - fn force_transform(&self, transform: &Transform2d) -> Self { + fn force_transform(&self, transform: &T) -> Self { + let transform = transform.to_simple_transform(); match self { - CurveSegment::Arc(curve_arc) => CurveSegment::Arc(curve_arc.force_transform(transform)), + CurveSegment::Arc(curve_arc) => CurveSegment::Arc(curve_arc.force_transform(&transform)), CurveSegment::Points(curve_points) => { - CurveSegment::Points(curve_points.force_transform(transform)) + CurveSegment::Points(curve_points.force_transform(&transform)) } CurveSegment::CubicBezier(curve_cubic_bezier) => { - CurveSegment::CubicBezier(curve_cubic_bezier.force_transform(transform)) + CurveSegment::CubicBezier(curve_cubic_bezier.force_transform(&transform)) } } } @@ -696,7 +699,8 @@ impl CurveArc { } // you should make sure that it's a similarity trnasform before you do this! - fn force_transform(&self, transform: &Transform2d) -> Self { + fn force_transform(&self, transform: &T) -> Self { + let transform = transform.to_simple_transform(); Self { loc: transform.transform_vec2(self.loc), radius: transform.approx_scale() * self.radius, @@ -806,13 +810,15 @@ impl CurveCubicBezier { CurveSegment::new_simple_points(self.flatten(tolerance)) } - fn force_transform(&self, transform: &Transform2d) -> CurveCubicBezier { + fn force_transform(&self, transform: &T) -> CurveCubicBezier { CurveCubicBezier::from_cubic( self.to_cubic() .apply_vec2_tranform(|x| transform.transform_vec2(x)), ) } + + fn split_segment(&self, target_dist: f32) -> (CurveSegment, CurveSegment) { let v = self.to_cubic().to_vec2_line_space(1.0); // todo, figure out how to manage that CurvePoints::new(v).split_segment(target_dist) @@ -853,7 +859,7 @@ impl CurvePoints { CurvePoints::new(self.points.iter().cloned().rev().collect_vec()) } - fn force_transform(&self, transform: &Transform2d) -> CurvePoints { + fn force_transform(&self, transform: &SimpleTransform2d) -> CurvePoints { CurvePoints { points: transform.transform_many_vec2(self.points()).clone_to_vec(), } diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index faabae2..e9d6374 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -4,7 +4,7 @@ use glam::{vec2, Vec2, Vec3}; use lerpable::Lerpable; use murrelet_common::{ - IsPolyline, MurreletColor, Polyline, SimpleTransform2d, ToSimpleTransform, TransformVec2, + MurreletColor, SimpleTransform2d, ToSimpleTransform, Transformable, }; use murrelet_livecode::unitcells::UnitCellContext; @@ -14,7 +14,6 @@ use palette::{named::AQUAMARINE, LinSrgba, Srgb}; use crate::{ newtypes::RGBandANewtype, style::{styleconf::*, StyledPathSvgFill}, - tesselate::{AsLyonTransform, ToLyonPath}, }; #[derive(Debug, Clone, Copy, Livecode, Lerpable, Default)] diff --git a/murrelet_draw/src/drawable.rs b/murrelet_draw/src/drawable.rs index cb505a2..fca1aeb 100644 --- a/murrelet_draw/src/drawable.rs +++ b/murrelet_draw/src/drawable.rs @@ -1,5 +1,6 @@ use glam::Vec2; use itertools::Itertools; +use murrelet_common::{ToSimpleTransform, Transformable}; use murrelet_livecode::types::LivecodeResult; use crate::{ @@ -132,6 +133,18 @@ impl ToDrawnShape for Vec { } } +impl Transformable for CurveDrawer { + fn transform_with(&self, t: &T) -> Self { + self.maybe_transform(t).unwrap_or_else(|_| self.clone()) + } +} + +impl Transformable for DrawnShape { + fn transform_with(&self, t: &T) -> Self { + DrawnShape::new_cds(&self.cds.transform_with(t), self.style.clone()) + } +} + #[derive(Clone, Debug)] pub struct PositionedText { text: String, diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index 4865f94..bcab322 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -281,20 +281,7 @@ impl Transform2d { c } - pub fn is_similarity_transform(&self) -> bool { - for s in &self.0 { - match s { - Transform2dStep::Scale(v2) => { - if !approx_eq_eps(v2.v.x.abs(), v2.v.y.abs(), 1.0e-3) { - return false; - } - } - Transform2dStep::Skew(_) => return false, - _ => {} - } - } - return true; - } + } impl ToSimpleTransform for Transform2d { diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 84a3521..18b3088 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -657,8 +657,6 @@ impl ControlVecElementRepeat { pub enum ControlVecElement where Source: Clone + Debug, - // Sequencer: UnitCellCreator, - // ControlSequencer: LivecodeFromWorld, { Single(Source), Repeat(ControlVecElementRepeat), diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index 24fbfa2..3c441ee 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -1082,11 +1082,7 @@ impl UnitCellDetailsWallpaper { pub fn transform_no_skew(&self, v: &F) -> F { let m = self.transform_no_skew_mat(); v.transform_with(&m) - // Polyline::new( - // v.into_iter_vec2() - // .map(|x| m.transform_vec2(x)) - // .collect_vec(), - // ) + } // how to move the location of something diff --git a/murrelet_perform/src/cli.rs b/murrelet_perform/src/cli.rs index bddc0e3..4908089 100644 --- a/murrelet_perform/src/cli.rs +++ b/murrelet_perform/src/cli.rs @@ -41,10 +41,10 @@ impl Default for TextureDimensions { Self { // width: 3000, // height: 2000, - width: 1080, - height: 1080, - // width: 2000, - // height: 2000, + // width: 3840, + // height: 1646, + width: 800, + height: 800, // width: 2000, // height: 2000, // width: 750, @@ -63,7 +63,7 @@ pub struct BaseConfigArgs { #[arg(short, long, default_value_t = Default::default())] pub resolution: TextureDimensions, // window resolution - #[arg(long, default_value_t = 4, value_parser = clap::value_parser!(u32).range(1..=8))] + #[arg(long, default_value_t = 1, value_parser = clap::value_parser!(u32).range(1..=8))] pub texture_multiplier: u32, // controls number of pixels the shaders work on #[arg(long)] From 785c28c82a09b6d3a65fc203afbd63cf697c7708 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Sun, 1 Feb 2026 23:52:18 +0800 Subject: [PATCH 137/149] wip --- murrelet_draw/src/curve_drawer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index 6b24631..e0a3530 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -16,7 +16,6 @@ use crate::{ flatten_cubic_bezier_path_with_tolerance, parse_svg_data_as_vec2, segment_arc, segment_vec, ToVecVec2, }, - transform2d::Transform2d, }; #[derive(Debug, Clone, Default, Livecode, Lerpable, Serialize, Deserialize)] From 484f37a03500f36c80958f4df40067e6ab9113e3 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 3 Feb 2026 02:38:41 -0500 Subject: [PATCH 138/149] wip --- murrelet_common/src/idx.rs | 15 + murrelet_livecode/src/expr.rs | 36 +- murrelet_livecode/src/lazy.rs | 196 +++++++++- murrelet_livecode/src/types.rs | 365 +++++++++++++----- .../murrelet_livecode_derive/Cargo.toml | 2 +- .../examples/tests.rs | 239 ++++++------ .../src/derive_lazy.rs | 19 +- .../src/derive_livecode.rs | 41 +- .../murrelet_livecode_derive/src/parser.rs | 7 +- 9 files changed, 674 insertions(+), 246 deletions(-) diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 9d4088e..c1c54ef 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -31,6 +31,17 @@ impl IdxInRange { } } + pub fn new_last>(total: U) -> IdxInRange + where + >::Error: core::fmt::Debug, + { + IdxInRange { + i: 0, + total: total.try_into().expect("can't convert to u64"), + } + .last_i() + } + pub fn enumerate<'a, T, I>(iter: I) -> Vec<(IdxInRange, T)> where I: ExactSizeIterator, @@ -162,6 +173,10 @@ impl IdxInRange { pub fn is_first(&self) -> bool { self.i == 0 } + + pub fn i_usize(&self) -> usize { + self.i() as usize + } } pub trait IdxInRangeEnum<'a, T> { diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index 4986be0..ff82f28 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -314,10 +314,6 @@ impl ExprWorldContextValues { Self::new(vec![]) } - pub fn to_mixed_defs(&self) -> MixedEvalDefs { - MixedEvalDefs::new_from_expr(self.clone()) - } - pub fn update_ctx(&self, ctx: &mut HashMapContext) -> LivecodeResult<()> { for (identifier, value) in &self.0 { // todo, maybe handle the result here to help dev @@ -485,6 +481,34 @@ impl GuideType { } } +pub trait ToMixedDefs { + fn to_mixed_def(&self) -> MixedEvalDefsRef; +} + +impl ToMixedDefs for ExprWorldContextValues { + fn to_mixed_def(&self) -> MixedEvalDefsRef { + MixedEvalDefs::new_from_expr(self.clone()).to_mixed_def() + } +} + +impl ToMixedDefs for Arc { + fn to_mixed_def(&self) -> MixedEvalDefsRef { + MixedEvalDefsRef(self.clone()) + } +} + +impl ToMixedDefs for MixedEvalDefs { + fn to_mixed_def(&self) -> MixedEvalDefsRef { + MixedEvalDefsRef::new(self.clone()) + } +} + +impl<'a> ToMixedDefs for (&'a str, LivecodeValue) { + fn to_mixed_def(&self) -> MixedEvalDefsRef { + MixedEvalDefs::new_simple(self.0, self.1.clone()).to_mixed_def() + } +} + #[derive(Debug, Clone)] pub struct MixedEvalDefsRef(Arc); impl MixedEvalDefsRef { @@ -505,6 +529,10 @@ impl MixedEvalDefsRef { pub(crate) fn new_from_expr(more_vals: ExprWorldContextValues) -> MixedEvalDefsRef { Self::new(MixedEvalDefs::new_from_expr(more_vals)) } + + pub(crate) fn expr_vals(&self) -> &ExprWorldContextValues { + self.0.expr_vals() + } } #[derive(Debug, Clone)] diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 040cff8..d7e0edf 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -1,17 +1,21 @@ use std::sync::Arc; -use evalexpr::Node; -use itertools::Itertools; -use lerpable::{step, Lerpable}; -use murrelet_common::{IdxInRange, LivecodeValue, MurreletColor}; -use serde::Deserialize; - use crate::{ - expr::{ExprWorldContextValues, MixedEvalDefs}, - livecode::{GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeFunction, LivecodeVariable}, + expr::{ExprWorldContextValues, MixedEvalDefs, ToMixedDefs}, + livecode::{ + ControlF32, GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeFunction, LivecodeToControl, + LivecodeVariable, + }, state::{LivecodeWorldState, WorldWithLocalVariables}, types::{LivecodeError, LivecodeResult}, }; +use evalexpr::Node; +use glam::Vec2; +use itertools::Itertools; +use lerpable::IsLerpingMethod; +use lerpable::{step, Lerpable}; +use murrelet_common::{IdxInRange, LivecodeValue, MurreletColor, MurreletIterHelpers}; +use serde::Deserialize; #[derive(Debug, Deserialize, Clone)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -90,10 +94,10 @@ impl LazyNodeF32Inner { } // options to add more details... - pub fn add_more_defs(&self, more_defs: &MixedEvalDefs) -> Self { + pub fn add_more_defs(&self, more_defs: &M) -> Self { // unreachable!(); let c = self.clone(); - c.add_expr_values(more_defs.expr_vals()) + c.add_expr_values(more_defs.to_mixed_def().expr_vals()) // println!("dropping contexts..."); // c.world @@ -180,7 +184,7 @@ impl LazyNodeF32 { } } - pub fn eval_with_ctx(&self, more_defs: &MixedEvalDefs) -> LivecodeResult { + pub fn eval_with_ctx(&self, more_defs: &M) -> LivecodeResult { // update ctx let with_more_ctx = self.add_more_defs(more_defs)?; @@ -193,7 +197,7 @@ impl LazyNodeF32 { } } - pub fn add_more_defs(&self, more_defs: &MixedEvalDefs) -> LivecodeResult { + pub fn add_more_defs(&self, more_defs: &M) -> LivecodeResult { match self { LazyNodeF32::Uninitialized => { Err(LivecodeError::Raw("uninitialized lazy node".to_owned())) @@ -238,13 +242,12 @@ impl LazyNodeF32 { } pub fn eval_with_xy(&self, xy: glam::Vec2) -> LivecodeResult { - let expr = - ExprWorldContextValues::new(vec![ - ("x".to_string(), LivecodeValue::float(xy.x)), - ("y".to_string(), LivecodeValue::float(xy.y)), - ]); + let expr = ExprWorldContextValues::new(vec![ + ("x".to_string(), LivecodeValue::float(xy.x)), + ("y".to_string(), LivecodeValue::float(xy.y)), + ]); - self.eval_with_ctx(&expr.to_mixed_defs()) + self.eval_with_ctx(&expr) } } @@ -331,8 +334,59 @@ pub fn eval_lazy_vec3(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult< )) } -pub fn eval_lazy_vec2(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult { - Ok(glam::vec2(v[0].eval_lazy(ctx)?, v[1].eval_lazy(ctx)?)) +// pub fn eval_lazy_vec2(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult { +// Ok(glam::vec2(v[0].eval_lazy(ctx)?, v[1].eval_lazy(ctx)?)) +// } + +#[derive(Clone, Debug, Default)] +pub struct LazyVec2 { + x: LazyNodeF32, + y: LazyNodeF32, +} + +impl LazyVec2 { + pub fn new(x: LazyNodeF32, y: LazyNodeF32) -> Self { + Self { x, y } + } +} + +#[derive(Clone, Debug, Default, Deserialize)] +pub struct ControlLazyVec2(Vec); +impl LivecodeFromWorld for ControlLazyVec2 { + fn o(&self, w: &LivecodeWorldState) -> LivecodeResult { + Ok(LazyVec2::new(self.0[0].o(w)?, self.0[1].o(w)?)) + } +} + +impl GetLivecodeIdentifiers for ControlLazyVec2 { + fn variable_identifiers(&self) -> Vec { + self.0.iter().map(|f| f.variable_identifiers()).flatten().collect_vec() + } + + fn function_identifiers(&self) -> Vec { + self.0.iter().map(|f| f.function_identifiers()).flatten().collect_vec() + } +} + +impl LivecodeToControl for LazyVec2 { + fn to_control(&self) -> ControlLazyVec2 { + ControlLazyVec2(vec![self.x.to_control(), self.y.to_control()]) + } +} + +impl IsLazy for LazyVec2 { + type Target = glam::Vec2; + + fn eval_lazy(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + Ok(glam::vec2(self.x.eval_lazy(ctx)?, self.y.eval_lazy(ctx)?)) + } + + fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + Ok(LazyVec2::new( + self.x.with_more_defs(ctx)?, + self.y.with_more_defs(ctx)?, + )) + } } pub fn eval_lazy_f32( @@ -349,3 +403,105 @@ pub fn eval_lazy_f32( }; Ok(result) } + +// can lerp between lazy items, by gathering the pairs + pct, and then evaluating them + +#[derive(Clone, Debug)] +pub struct LazyLerp { + left: WrappedLazyType, + right: WrappedLazyType, + pct: f32, // hm, just convert to the pct... +} + +impl LazyLerp { + fn new(left: WrappedLazyType, right: WrappedLazyType, pct: f32) -> Self { + Self { left, right, pct } + } +} + +// newtype to avoid orphan +#[derive(Clone, Debug)] +pub enum WrappedLazyType { + Single(T), + Lerp(Box>), +} +impl WrappedLazyType +where + T: IsLazy + std::fmt::Debug + Clone, +{ + pub(crate) fn new(x: T) -> Self { + Self::Single(x) + } + + pub(crate) fn new_lerp(left: WrappedLazyType, right: WrappedLazyType, pct: f32) -> Self { + WrappedLazyType::Lerp(Box::new(LazyLerp::new(left, right, pct))) + } +} + +impl IsLazy for LazyLerp +where + T: IsLazy, + T::Target: Lerpable, +{ + type Target = T::Target; + fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult { + let left = self.left.eval_lazy(expr)?; + let right = self.right.eval_lazy(expr)?; + let r = left.lerpify(&right, &self.pct); + + Ok(r) + } + + fn with_more_defs(&self, more_defs: &MixedEvalDefs) -> LivecodeResult { + Ok(Self { + left: self.left.with_more_defs(more_defs)?, + right: self.right.with_more_defs(more_defs)?, + pct: self.pct, + }) + } +} + +impl IsLazy for WrappedLazyType +where + T: IsLazy, + T::Target: Lerpable, +{ + type Target = T::Target; + fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult { + match self { + WrappedLazyType::Single(s) => s.eval_lazy(expr), + WrappedLazyType::Lerp(s) => s.eval_lazy(expr), + } + } + + fn with_more_defs(&self, more_defs: &MixedEvalDefs) -> LivecodeResult { + Ok(match self { + WrappedLazyType::Single(s) => WrappedLazyType::Single(s.with_more_defs(more_defs)?), + WrappedLazyType::Lerp(s) => { + WrappedLazyType::Lerp(Box::new(s.with_more_defs(more_defs)?)) + } + }) + } +} + +impl Lerpable for WrappedLazyType +where + T: IsLazy + Clone + std::fmt::Debug, +{ + fn lerpify(&self, other: &Self, pct: &M) -> Self { + WrappedLazyType::new_lerp(self.clone(), other.clone(), pct.lerp_pct() as f32) + } +} + +impl LivecodeToControl for WrappedLazyType +where + T: LivecodeToControl + IsLazy, +{ + fn to_control(&self) -> ControlT { + match self { + WrappedLazyType::Single(inner) => inner.to_control(), + // hax because it's just to control... + WrappedLazyType::Lerp(lerp) => lerp.left.to_control(), + } + } +} diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 18b3088..433d20d 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -3,14 +3,14 @@ use std::{collections::HashSet, fmt::Debug}; use evalexpr::{build_operator_tree, EvalexprError, HashMapContext, Node}; use itertools::Itertools; use lerpable::{step, Lerpable}; -use murrelet_common::{print_expect, IdxInRange, IdxInRange2d}; +use murrelet_common::{print_expect, IdxInRange, IdxInRange2d, LivecodeValue}; use murrelet_gui::CanMakeGUI; use serde::{Deserialize, Deserializer}; use thiserror::Error; use crate::{ - expr::{IntoExprWorldContext, MixedEvalDefs}, - lazy::{ControlLazyNodeF32, IsLazy, LazyNodeF32}, + expr::{IntoExprWorldContext, MixedEvalDefs, ToMixedDefs}, + lazy::{ControlLazyNodeF32, IsLazy, LazyNodeF32, WrappedLazyType}, livecode::{ ControlF32, GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeToControl, LivecodeVariable, }, @@ -204,29 +204,37 @@ impl DeserLazyControlVecElementRepeatMethod { #[derive(Debug, Clone, Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct DeserLazyControlVecElementRepeat { +pub struct DeserLazyControlVecElementRepeat { repeat: DeserLazyControlVecElementRepeatMethod, #[serde(default = "_default_ctx")] ctx: AdditionalContextNode, prefix: String, - what: Vec>, + what: Vec>, + #[serde(default)] + blend_with_next: usize, } -impl DeserLazyControlVecElementRepeat { - fn o(&self, w: &LivecodeWorldState) -> LivecodeResult> +impl DeserLazyControlVecElementRepeat { + fn o( + &self, + w: &LivecodeWorldState, + ) -> LivecodeResult>> where - Source: LivecodeFromWorld, - Target: IsLazy + Debug + Clone, + DeserSource: LivecodeFromWorld, + LazySource: IsLazy + Debug + Clone, + Target: Lerpable, { let what = self .what .iter() - .map(|x| x.o(w)) + .map(|x| x.o::(w)) .collect::, _>>()?; + Ok(LazyVecElementRepeat { repeat: self.repeat.o(w)?, ctx: self.ctx.clone(), prefix: self.prefix.clone(), what, + blend_with_next: self.blend_with_next, }) } } @@ -248,18 +256,23 @@ where Self::Single(c) } } -impl DeserLazyControlVecElement { - pub fn o( +impl DeserLazyControlVecElement { + pub fn o( &self, w: &LivecodeWorldState, - ) -> LivecodeResult> + ) -> LivecodeResult>> where - Source: LivecodeFromWorld, - Target: IsLazy, + DeserSource: LivecodeFromWorld, + LazySource: Debug + Clone + IsLazy, + Target: Lerpable, { let a = match self { - DeserLazyControlVecElement::Single(a) => LazyControlVecElement::Single(a.o(w)?), - DeserLazyControlVecElement::Repeat(r) => LazyControlVecElement::Repeat(r.o(w)?), + DeserLazyControlVecElement::Single(a) => { + LazyControlVecElement::Single(WrappedLazyType::new(a.o(w)?)) + } + DeserLazyControlVecElement::Repeat(r) => { + LazyControlVecElement::Repeat(r.o::(w)?) + } }; Ok(a) } @@ -387,10 +400,24 @@ pub struct LazyVecElementRepeat { ctx: AdditionalContextNode, prefix: String, what: Vec>, + blend_with_next: usize, } -impl LazyVecElementRepeat { - pub fn lazy_expand_vec(&self, ctx: &MixedEvalDefs) -> LivecodeResult> { - let mut result = Vec::with_capacity(self.repeat.len(ctx)? * self.what.len()); + +impl LazyVecElementRepeat> +where + Inner: Clone + Debug + IsLazy, + Inner::Target: Lerpable, +{ + // expands repeats + applies defs, while preserving WrappedLazyType nodes for blending + pub fn lazy_expand_vec_repeat_element( + &self, + ctx: &MixedEvalDefs, + ) -> LivecodeResult>> + where + Inner::Target: Lerpable, + { + let mut result: Vec> = + Vec::with_capacity(self.repeat.len(ctx)? * self.what.len()); let prefix = if self.prefix.is_empty() { "i_".to_string() @@ -404,42 +431,36 @@ impl LazyVecElementRepeat { let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); scoped_ctx.set_vals(expr.with_prefix(&prefix)); + let mut is_blending: Option = None; + for src in &self.what { match src { LazyControlVecElement::Single(c) => { - result.push(c.with_more_defs(&scoped_ctx)?); + let item = c.with_more_defs(&scoped_ctx)?; + blend_with_list(&mut result, item, &mut is_blending); } LazyControlVecElement::Repeat(c) => { - let mut nested = c.lazy_expand_vec(&scoped_ctx)?; - result.append(&mut nested); + let nested = c.lazy_expand_vec_repeat_element(&scoped_ctx)?; + for item in nested { + blend_with_list(&mut result, item, &mut is_blending); + } + + if c.blend_with_next > 0 { + // blend_with_next is a COUNT (1 => only last item) + is_blending = Some(IdxInRange::new_last(c.blend_with_next - 1)); + } } } } } - // for idx in self.repeat.iter(ctx)? { - // let mut ctx = ctx.clone(); - // ctx.add_node(self.ctx.clone()); - // let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); - // ctx.set_vals(expr.with_prefix(&prefix)); - - // for src in &self.what { - // match src { - // LazyControlVecElement::Single(c) => { - // // let o = c.eval_lazy(&ctx)?; - // result.push(c.clone()); - // } - // LazyControlVecElement::Repeat(c) => { - // let o = c.lazy_expand_vec(&ctx)?; - // result.extend(o.into_iter()); - // } - // } - // } - // } Ok(result) } - pub fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + pub fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult + where + Inner::Target: Lerpable, + { Ok(Self { repeat: self.repeat.clone(), ctx: self.ctx.clone(), @@ -449,10 +470,74 @@ impl LazyVecElementRepeat { .iter() .map(|elem| elem.with_more_defs(ctx)) .collect::>>()?, + blend_with_next: self.blend_with_next, }) } } +// impl LazyVecElementRepeat { +// // this one has mixed evals, so it can evaluate the wrapped items too! +// pub fn lazy_expand_vec_repeat_element(&self, ctx: &MixedEvalDefs) -> LivecodeResult> +// where +// Source::Target: Lerpable, +// { +// let mut result: Vec = Vec::with_capacity(self.repeat.len(ctx)? * self.what.len()); + +// let prefix = if self.prefix.is_empty() { +// "i_".to_string() +// } else { +// format!("{}_", self.prefix) +// }; + +// for idx in self.repeat.iter(ctx)? { +// let mut scoped_ctx = ctx.clone(); +// scoped_ctx.add_node(self.ctx.clone()); +// let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); +// scoped_ctx.set_vals(expr.with_prefix(&prefix)); + +// let mut is_blending: Option = None; + +// for src in &self.what { +// match src { +// LazyControlVecElement::Single(c) => { +// let item = c.with_more_defs(&scoped_ctx)?; +// blend_with_list(&mut result, item, &mut is_blending); +// } +// LazyControlVecElement::Repeat(c) => { +// let nested = c.lazy_expand_vec_repeat_element(&scoped_ctx)?; +// for item in nested.into_iter() { +// blend_with_list(&mut result, item, &mut is_blending); +// } + +// if c.blend_with_next > 0 { +// is_blending = Some(IdxInRange::new_last(c.blend_with_next - 1)); +// } +// } +// } +// } +// } + +// Ok(result) +// } + +// pub fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult +// where +// Source::Target: Lerpable, +// { +// Ok(Self { +// repeat: self.repeat.clone(), +// ctx: self.ctx.clone(), +// prefix: self.prefix.clone(), +// what: self +// .what +// .iter() +// .map(|elem| elem.with_more_defs(ctx)) +// .collect::>>()?, +// blend_with_next: self.blend_with_next, +// }) +// } +// } + impl LivecodeToControl> for LazyControlVecElement where @@ -484,6 +569,7 @@ where prefix: rep.prefix.clone(), what, ctx: rep.ctx.clone(), + blend_with_next: rep.blend_with_next, }) } } @@ -497,6 +583,8 @@ pub struct ControlVecElementRepeat { // #[serde(default)] prefix: String, what: Vec>, + #[serde(default)] + blend_with_next: usize, } // impl GetLivecodeIdentifiers for ControlVecElement @@ -550,6 +638,27 @@ where } } +fn blend_with_list( + result: &mut Vec, + new: Target, + is_blending: &mut Option, +) { + if let Some(curr_offset) = is_blending { + // otherwise, too far back, skip + if curr_offset.i_usize() < result.len() { + let i = result.len() - 1 - curr_offset.i_usize(); + + result[i] = new.lerpify(&result[i], &curr_offset.pct()); + + // if we can subtract 1, that's the next one to check. + // otherwise, set to none. + *is_blending = curr_offset.prev_i(); + } + } else { + result.push(new); + } +} + impl ControlVecElementRepeat { pub fn _eval_and_expand_vec( &self, @@ -558,6 +667,7 @@ impl ControlVecElementRepeat { ) -> LivecodeResult<(usize, Vec)> where Source: LivecodeFromWorld, + Target: Lerpable, { let mut result = Vec::with_capacity(self.repeat.len(w)? * self.what.len()); @@ -571,21 +681,36 @@ impl ControlVecElementRepeat { for idx in self.repeat.iter(w)? { let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); - let new_w = w.clone_with_vals(expr, &prefix); + let mut new_w = w.clone_with_vals(expr, &prefix); + + let mut is_blending: Option = None; for src in &self.what { match src { ControlVecElement::Single(c) => { // just update it and overwrite it... - // new_w.set_val("vseed", LivecodeValue::float(offset as f32)); + new_w.update_with_defs( + ("vseed", LivecodeValue::float(offset as f32)).to_mixed_def(), + ); let o = c.o(&new_w)?; - result.push(o); + + blend_with_list(&mut result, o, &mut is_blending); + offset += 1; } ControlVecElement::Repeat(c) => { let (new_offset, o) = c._eval_and_expand_vec(&new_w, offset)?; - result.extend(o.into_iter()); - offset += new_offset; + + for item in o.into_iter() { + blend_with_list(&mut result, item, &mut is_blending); + } + + if c.blend_with_next > 0 { + is_blending = Some(IdxInRange::new_last(c.blend_with_next - 1)); + } + + // result.extend(o.into_iter()); + offset = new_offset; } } } @@ -596,6 +721,7 @@ impl ControlVecElementRepeat { pub fn eval_and_expand_vec(&self, w: &LivecodeWorldState) -> LivecodeResult> where Source: LivecodeFromWorld, + Target: Lerpable, { let (_, a) = self._eval_and_expand_vec(w, 0)?; Ok(a) @@ -672,18 +798,15 @@ where Repeat(LazyVecElementRepeat), } -impl IsLazy for LazyControlVecElement +impl IsLazy for LazyControlVecElement> where - Source: Clone + Debug + IsLazy, + Inner: Clone + Debug + IsLazy, + Inner::Target: Lerpable, { - // A single lazy control element expands to a vector of evaluated items. - type Target = Vec; + type Target = Vec; fn eval_lazy(&self, ctx: &MixedEvalDefs) -> LivecodeResult { - // First expand structure (repeats) to Vec, still lazy - let expanded: Vec = self.lazy_expand_vec(ctx)?; - - // Then evaluate each inner lazy to its final type + let expanded: Vec> = self.lazy_expand_vec(ctx)?; expanded.into_iter().map(|s| s.eval_lazy(ctx)).collect() } @@ -699,36 +822,78 @@ where } } -impl LazyControlVecElement +impl LazyControlVecElement> where - Source: Clone + Debug + crate::lazy::IsLazy, + Inner: Clone + Debug + IsLazy, + Inner::Target: Lerpable, { - pub fn eval_lazy_single(&self, expr: &MixedEvalDefs) -> LivecodeResult { + pub fn lazy_expand_vec( + &self, + ctx: &MixedEvalDefs, + ) -> LivecodeResult>> { match self { - LazyControlVecElement::Single(s) => Ok(s.clone()), - LazyControlVecElement::Repeat(s) => { - let vv = s.lazy_expand_vec(expr)?; - vv.into_iter() - .next() - .ok_or(LivecodeError::raw("eval_lazy_single failed")) - } + LazyControlVecElement::Single(c) => Ok(vec![c.clone()]), + LazyControlVecElement::Repeat(c) => c.lazy_expand_vec_repeat_element(ctx), } } } -impl LazyControlVecElement -where - Source: Clone + Debug + crate::lazy::IsLazy, -{ - // type Target = Vec; +// impl IsLazy for LazyControlVecElement +// where +// Source: Clone + Debug + IsLazy + Lerpable, +// Source::Target: Lerpable, +// { +// type Target = Vec; - pub fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult> { - match self { - LazyControlVecElement::Single(s) => Ok(vec![s.clone()]), - LazyControlVecElement::Repeat(s) => s.lazy_expand_vec(expr), - } - } -} +// fn eval_lazy(&self, ctx: &MixedEvalDefs) -> LivecodeResult { +// let expanded: Vec = self.lazy_expand_vec(ctx)?; +// expanded.into_iter().map(|s| s.eval_lazy(ctx)).collect() +// } + +// fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult { +// Ok(match self { +// LazyControlVecElement::Single(s) => { +// LazyControlVecElement::Single(s.with_more_defs(ctx)?) +// } +// LazyControlVecElement::Repeat(rep) => { +// LazyControlVecElement::Repeat(rep.with_more_defs(ctx)?) +// } +// }) +// } +// } + +// impl LazyControlVecElement +// where +// Source: Clone + Debug + crate::lazy::IsLazy + Lerpable, +// { +// pub fn eval_lazy_single(&self, expr: &MixedEvalDefs) -> LivecodeResult +// where +// Source::Target: Lerpable, +// { +// match self { +// LazyControlVecElement::Single(s) => Ok(s.clone()), +// LazyControlVecElement::Repeat(s) => { +// let vv = s.lazy_expand_vec_repeat_element(expr)?; +// vv.into_iter() +// .next() +// .ok_or(LivecodeError::raw("eval_lazy_single failed")) +// } +// } +// } +// } + +// impl LazyControlVecElement +// where +// Source: Clone + Debug + crate::lazy::IsLazy + Lerpable, +// Target: Lerpable, +// { +// pub fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult> { +// match self { +// LazyControlVecElement::Single(s) => Ok(vec![s.clone()]), +// LazyControlVecElement::Repeat(s) => s.lazy_expand_vec_repeat_element(expr), +// } +// } +// } // impl ControlVecElement impl ControlVecElement @@ -744,6 +909,7 @@ where pub fn eval_and_expand_vec(&self, w: &LivecodeWorldState) -> LivecodeResult> where Source: LivecodeFromWorld, + Target: Lerpable, { match self { ControlVecElement::Single(c) => Ok(vec![c.o(w)?]), @@ -775,17 +941,38 @@ where // } } -impl LazyControlVecElement -where - LazyElement: Clone + Debug + IsLazy, -{ - pub fn lazy_expand_vec(&self, ctx: &MixedEvalDefs) -> LivecodeResult> { - match self { - LazyControlVecElement::Single(c) => Ok(vec![c.clone()]), - LazyControlVecElement::Repeat(c) => c.lazy_expand_vec(ctx), - } - } -} +// impl LazyControlVecElement> +// where +// Inner: Clone + Debug + IsLazy, +// { +// pub fn lazy_expand_vec( +// &self, +// ctx: &MixedEvalDefs, +// ) -> LivecodeResult>> +// where +// Inner::Target: Lerpable, +// { +// match self { +// LazyControlVecElement::Single(c) => Ok(vec![c.clone()]), +// LazyControlVecElement::Repeat(c) => c.lazy_expand_vec_repeat_element(ctx), +// } +// } +// } + +// impl LazyControlVecElement +// where +// LazyElement: Clone + Debug + IsLazy + Lerpable, +// { +// pub fn lazy_expand_vec(&self, ctx: &MixedEvalDefs) -> LivecodeResult> +// where +// LazyElement::Target: Lerpable, +// { +// match self { +// LazyControlVecElement::Single(c) => Ok(vec![c.clone()]), +// LazyControlVecElement::Repeat(c) => c.lazy_expand_vec_repeat_element(ctx), +// } +// } +// } // chatgpt #[cfg(feature = "schemars")] diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml index 51046d6..d875b46 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml +++ b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml @@ -27,7 +27,7 @@ murrelet_livecode = { version = "0.1.2", default-features = false } murrelet_common = "0.1.2" glam = { version = "0.28.0", features = ["serde"] } palette = "0.7.6" -lerpable = "0.0.2" +lerpable = { version = "0.0.2", features = ["glam"] } schemars = "0.8.21" murrelet_gui = { version = "0.1.2", features = ["glam"] } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index 146d82b..d8e6d40 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -11,14 +11,11 @@ use murrelet_livecode_derive::Cached; #[derive(Debug, Clone, Livecode, Lerpable, Default)] pub struct BasicTypes { a_number: f32, - #[lerpable(func = "lerpify_vec2")] c_vec2: Vec2, - #[lerpable(func = "lerpify_vec3")] - c_vec3: Vec3, - b_color: MurreletColor, - something: Vec, - #[lerpable(func = "lerpify_vec_vec2")] - list_of_vec2: Vec, + // c_vec3: Vec3, + // b_color: MurreletColor, + // something: Vec, + // list_of_vec2: Vec, // option_f32: Option, // option_vec2: Option, } @@ -31,137 +28,139 @@ fn empty_string_lazy() -> String { String::new() } -#[derive(Debug, Clone, Livecode, Lerpable, Default)] -pub struct BasicTypesWithDefaults { - #[livecode(serde_default = "zeros")] - a_number: f32, - b_color: MurreletColor, - #[livecode(serde_default = "0")] - #[lerpable(func = "lerpify_vec2")] - c_vec2_serde_default: Vec2, - something: Vec, - #[lerpable(func = "lerpify_vec_vec2")] - list_of_vec2: Vec, - #[livecode(kind = "none", serde_default = "empty_string")] - label: String, - #[livecode(kind = "none")] - #[lerpable(method = "skip")] - b: HashMap, - list_test: Vec - -} - -#[derive(Debug, Clone, Livecode, Lerpable, Default)] -struct TestLazy { - #[lerpable(method = "skip")] - lazy: LazyBasicTypes, -} - -#[derive(Debug, Clone, Livecode, Lerpable, Default)] -enum EnumTest { - #[default] - A, - B(TestLazy), - C(#[lerpable(method = "skip")] LazyTestLazy), -} +// #[derive(Debug, Clone, Livecode, Lerpable, Default)] +// pub struct BasicTypesWithDefaults { +// #[livecode(serde_default = "zeros")] +// a_number: f32, +// // b_color: MurreletColor, +// // #[livecode(serde_default = "0")] +// // #[lerpable(func = "lerpify_vec2")] +// // c_vec2_serde_default: Vec2, +// // something: Vec, +// // #[lerpable(func = "lerpify_vec_vec2")] +// // list_of_vec2: Vec, +// // #[livecode(kind = "none", serde_default = "empty_string")] +// // label: String, +// // #[livecode(kind = "none")] +// // #[lerpable(method = "skip")] +// // b: HashMap, +// // list_test: Vec -#[derive(Debug, Clone, Livecode, Lerpable, Default)] -struct TestNewType(Vec); +// } // #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// struct SequencerTest { -// sequencer: SimpleSquareSequence, -// ctx: AdditionalContextNode, -// #[livecode(src = "sequencer", ctx = "ctx")] -// node: UnitCells, -// #[livecode(src = "sequencer", ctx = "ctx")] +// struct TestLazy { // #[lerpable(method = "skip")] -// node_two: UnitCells, +// lazy: LazyBasicTypes, // } -// fn make_grid( -// x: usize, -// y: usize, -// cell_size: Vec2, -// offset_alternating: bool, -// ) -> Vec { -// let x_usize = x; -// let y_usize = y; - -// (0..x_usize) -// .flat_map(|x| { -// let x_idx = IdxInRange::new(x, x_usize); -// (0..y_usize).map(move |y| { -// let y_idx = IdxInRange::new(y, y_usize); -// let idx = IdxInRange2d::new_from_idx(x_idx, y_idx); -// let ctx = UnitCellIdx::from_idx2d(idx, 1.0); - -// let mut center = if offset_alternating { -// let mut center = idx.to_alternating_i().center_of_cell(); -// center += vec2(-0.5, 0.0); -// if idx.i.i() % 2 == 1 { -// center += vec2(0.5, 0.5); -// } - -// let offset_angle = AnglePi::new(1.0 / 6.0); -// let diag_scale = offset_angle.to_norm_dir() * cell_size.x / (100.0 * 0.5); - -// center *= diag_scale; -// center -// } else { -// let mut center = idx.center_of_cell(); -// center *= vec2(cell_size.x, cell_size.y) / 100.0; -// center -// }; - -// center *= 100.0; - -// let transform = SimpleTransform2d::new(vec![ -// SimpleTransform2dStep::translate(center), -// SimpleTransform2dStep::scale_both(cell_size.x / 100.0), -// ]); - -// UnitCellContext::new(ctx, transform) -// }) -// }) -// .collect::>() +// #[derive(Debug, Clone, Livecode, Lerpable, Default)] +// enum EnumTest { +// #[default] +// A, +// B(TestLazy), +// C(#[lerpable(method = "skip")] LazyTestLazy), // } +// #[derive(Debug, Clone, Livecode, Lerpable, Default)] +// struct TestNewType(Vec); + +// // #[derive(Debug, Clone, Livecode, Lerpable, Default)] +// // struct SequencerTest { +// // sequencer: SimpleSquareSequence, +// // ctx: AdditionalContextNode, +// // #[livecode(src = "sequencer", ctx = "ctx")] +// // node: UnitCells, +// // #[livecode(src = "sequencer", ctx = "ctx")] +// // #[lerpable(method = "skip")] +// // node_two: UnitCells, +// // } + +// // fn make_grid( +// // x: usize, +// // y: usize, +// // cell_size: Vec2, +// // offset_alternating: bool, +// // ) -> Vec { +// // let x_usize = x; +// // let y_usize = y; + +// // (0..x_usize) +// // .flat_map(|x| { +// // let x_idx = IdxInRange::new(x, x_usize); +// // (0..y_usize).map(move |y| { +// // let y_idx = IdxInRange::new(y, y_usize); +// // let idx = IdxInRange2d::new_from_idx(x_idx, y_idx); +// // let ctx = UnitCellIdx::from_idx2d(idx, 1.0); + +// // let mut center = if offset_alternating { +// // let mut center = idx.to_alternating_i().center_of_cell(); +// // center += vec2(-0.5, 0.0); +// // if idx.i.i() % 2 == 1 { +// // center += vec2(0.5, 0.5); +// // } + +// // let offset_angle = AnglePi::new(1.0 / 6.0); +// // let diag_scale = offset_angle.to_norm_dir() * cell_size.x / (100.0 * 0.5); + +// // center *= diag_scale; +// // center +// // } else { +// // let mut center = idx.center_of_cell(); +// // center *= vec2(cell_size.x, cell_size.y) / 100.0; +// // center +// // }; + +// // center *= 100.0; + +// // let transform = SimpleTransform2d::new(vec![ +// // SimpleTransform2dStep::translate(center), +// // SimpleTransform2dStep::scale_both(cell_size.x / 100.0), +// // ]); + +// // UnitCellContext::new(ctx, transform) +// // }) +// // }) +// // .collect::>() +// // } + +// // #[derive(Clone, Debug, Default, Livecode, Lerpable)] +// // pub struct SimpleSquareSequence { +// // rows: usize, +// // cols: usize, +// // size: f32, +// // } +// // impl UnitCellCreator for SimpleSquareSequence { +// // fn to_unit_cell_ctxs(&self) -> Vec { +// // make_grid(self.cols, self.rows, vec2(self.size, self.size), false) +// // } +// // } + +// // // new type + // #[derive(Clone, Debug, Default, Livecode, Lerpable)] -// pub struct SimpleSquareSequence { -// rows: usize, -// cols: usize, -// size: f32, -// } -// impl UnitCellCreator for SimpleSquareSequence { -// fn to_unit_cell_ctxs(&self) -> Vec { -// make_grid(self.cols, self.rows, vec2(self.size, self.size), false) -// } -// } +// pub struct NewTypeWithType(f32); -// // new type +// #[derive(Clone, Debug, Default, Livecode, Lerpable)] +// pub struct NewTypeWithTypeVec2(#[lerpable(func = "lerpify_vec2")] Vec2); -#[derive(Clone, Debug, Default, Livecode, Lerpable)] -pub struct NewTypeWithType(f32); +// #[derive(Clone, Debug, Default, Livecode, Lerpable)] +// pub struct NewTypeWithVec(Vec); -#[derive(Clone, Debug, Default, Livecode, Lerpable)] -pub struct NewTypeWithTypeVec2(#[lerpable(func = "lerpify_vec2")] Vec2); +// #[derive(Clone, Debug, Default, LivecodeOnly)] +// pub struct NewTypeWithStruct(BasicTypes); -#[derive(Clone, Debug, Default, Livecode, Lerpable)] -pub struct NewTypeWithVec(Vec); -#[derive(Clone, Debug, Default, LivecodeOnly)] -pub struct NewTypeWithStruct(BasicTypes); -fn main() {} +// #[derive(Debug, Clone, Cached)] +// pub struct BirdOutline { -#[derive(Debug, Clone, Cached)] -pub struct BirdOutline { +// back: CachedCompute, +// neck_back: CachedCompute>, - back: CachedCompute, - neck_back: CachedCompute>, +// } -} \ No newline at end of file +fn main() {} \ No newline at end of file diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index 5843e08..fd20e8f 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -15,7 +15,7 @@ impl LazyFieldType { ControlType::Bool => quote! {murrelet_livecode::lazy::LazyNodeF32}, // we'll just check if it's above 0 ControlType::F32 => quote! {murrelet_livecode::lazy::LazyNodeF32}, ControlType::F32_2 => { - quote! {Vec} + quote! {murrelet_livecode::lazy::LazyVec2} } ControlType::F32_3 => { quote! {Vec} @@ -43,7 +43,8 @@ impl LazyFieldType { ) -> TokenStream2 { match self.0 { ControlType::F32_2 => { - quote! { murrelet_livecode::lazy::eval_lazy_vec2(&#ident, ctx) } + // quote! { murrelet_livecode::lazy::eval_lazy_vec2(&#ident, ctx) } + quote! { #ident.eval_lazy(ctx)? } } ControlType::F32_3 => { quote! { murrelet_livecode::lazy::eval_lazy_vec3(&#ident, ctx) } @@ -76,10 +77,12 @@ impl LazyFieldType { let orig_ty = idents.orig_ty(); match self.0 { ControlType::F32_2 => { - quote! { #name: glam::vec2( - self.#name[0].eval_lazy(ctx)? as f32, - self.#name[1].eval_lazy(ctx)? as f32 - )} + quote! { #name: self.#name.eval_lazy(ctx)? } + + // glam::vec2( + // self.#name[0].eval_lazy(ctx)? as f32, + // self.#name[1].eval_lazy(ctx)? as f32 + // )} } ControlType::F32_3 => { quote! { @@ -556,11 +559,11 @@ impl GenFinal for FieldTokensLazy { let new_ty = match wrapper { VecDepth::NotAVec => unreachable!("huh, parsing a not-vec in the vec function"), // why is it in this function? VecDepth::Vec => { - quote! {Vec>} + quote! {Vec>>} } VecDepth::VecVec => todo!(), VecDepth::VecControlVec => { - quote! { Vec>> } + quote! { Vec>>> } } }; quote! {#back_to_quote #name: #new_ty} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 9600ac8..6b59245 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -27,7 +27,7 @@ impl LivecodeFieldType { pub fn to_token_lazy(&self) -> TokenStream2 { match self.0 { - ControlType::F32_2 => quote! { Vec }, + ControlType::F32_2 => quote! { murrelet_livecode::lazy::ControlLazyVec2 }, ControlType::F32_3 => quote! { Vec }, ControlType::Color => quote! { Vec }, ControlType::ColorUnclamped => { @@ -678,7 +678,6 @@ impl GenFinal for FieldTokensLivecode { e => panic!("(livecode, recurse_struct_vec) need vec something {:?}", e), }; - let vec_elem_type: TokenStream2 = if inner_is_lazy_struct { quote! {murrelet_livecode::types::DeserLazyControlVecElement} } else { @@ -1191,6 +1190,42 @@ impl GenFinal for FieldTokensLivecode { } fn from_recurse_struct_lazy(idents: StructIdents) -> Self { - Self::from_recurse_struct_struct(idents) + let serde = idents.serde(); + let name = idents.name(); + let orig_ty = idents.orig_ty(); + + let for_struct = { + let new_ty = { + let DataFromType { main_type, .. } = ident_from_type(&orig_ty); + + println!("main_type.to_string() {:?}", main_type.to_string()); + if main_type.to_string() == "LazyVec2" { + quote! { murrelet_livecode::lazy::ControlLazyVec2 } + } else { + let ref_lc_ident = Self::new_ident(main_type.clone()); + quote! {#ref_lc_ident} + } + // let ref_lc_ident = idents.config.new_ident(main_type.clone()); + }; + + quote! {#serde #name: #new_ty} + }; + let for_world = { + quote! {#name: self.#name.o(w)?} + }; + let for_to_control = { + quote! {#name: self.#name.to_control()} + }; + + let for_variable_idents = quote! { self.#name.variable_identifiers() }; + let for_function_idents = quote! { self.#name.function_identifiers() }; + + FieldTokensLivecode { + for_struct, + for_world, + for_to_control, + for_variable_idents, + for_function_idents, + } } } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index 2ae7aa2..da05399 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -883,7 +883,12 @@ pub fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { let s = path.segments.last().unwrap(); let main_type = s.ident.clone(); - acc.push(main_type); + + println!("main_type {:?}", main_type); + + if main_type.to_string() != "WrappedLazyType" { + acc.push(main_type); + } if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, From 41c092af55b23bd537a0316a0eefa7e8ac772f86 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 3 Feb 2026 03:41:39 -0500 Subject: [PATCH 139/149] wip --- murrelet_livecode/src/lazy.rs | 207 +++++++++++++-- .../examples/tests.rs | 236 +++++++++--------- .../src/derive_lazy.rs | 51 ++-- .../src/derive_livecode.rs | 45 ++-- .../murrelet_livecode_derive/src/parser.rs | 47 ++-- 5 files changed, 397 insertions(+), 189 deletions(-) diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index d7e0edf..8d16909 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -6,6 +6,7 @@ use crate::{ ControlF32, GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeFunction, LivecodeToControl, LivecodeVariable, }, + nestedit::{NestEditable, NestedMod}, state::{LivecodeWorldState, WorldWithLocalVariables}, types::{LivecodeError, LivecodeResult}, }; @@ -317,22 +318,22 @@ where } } -pub fn eval_lazy_color(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult { - Ok(murrelet_common::MurreletColor::hsva( - v[0].eval_lazy(ctx)?, - v[1].eval_lazy(ctx)?, - v[2].eval_lazy(ctx)?, - v[3].eval_lazy(ctx)?, - )) -} +// pub fn eval_lazy_color(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult { +// Ok(murrelet_common::MurreletColor::hsva( +// v[0].eval_lazy(ctx)?, +// v[1].eval_lazy(ctx)?, +// v[2].eval_lazy(ctx)?, +// v[3].eval_lazy(ctx)?, +// )) +// } -pub fn eval_lazy_vec3(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult { - Ok(glam::vec3( - v[0].eval_lazy(ctx)?, - v[1].eval_lazy(ctx)?, - v[2].eval_lazy(ctx)?, - )) -} +// pub fn eval_lazy_vec3(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult { +// Ok(glam::vec3( +// v[0].eval_lazy(ctx)?, +// v[1].eval_lazy(ctx)?, +// v[2].eval_lazy(ctx)?, +// )) +// } // pub fn eval_lazy_vec2(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult { // Ok(glam::vec2(v[0].eval_lazy(ctx)?, v[1].eval_lazy(ctx)?)) @@ -350,7 +351,18 @@ impl LazyVec2 { } } +impl NestEditable for LazyVec2 { + fn nest_update(&self, _mods: NestedMod) -> Self { + self.clone() // noop + } + + fn nest_get(&self, _getter: &[&str]) -> LivecodeResult { + Err(LivecodeError::NestGetExtra("LazyNodeF32".to_owned())) // maybe in the future! + } +} + #[derive(Clone, Debug, Default, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ControlLazyVec2(Vec); impl LivecodeFromWorld for ControlLazyVec2 { fn o(&self, w: &LivecodeWorldState) -> LivecodeResult { @@ -360,11 +372,19 @@ impl LivecodeFromWorld for ControlLazyVec2 { impl GetLivecodeIdentifiers for ControlLazyVec2 { fn variable_identifiers(&self) -> Vec { - self.0.iter().map(|f| f.variable_identifiers()).flatten().collect_vec() + self.0 + .iter() + .map(|f| f.variable_identifiers()) + .flatten() + .collect_vec() } fn function_identifiers(&self) -> Vec { - self.0.iter().map(|f| f.function_identifiers()).flatten().collect_vec() + self.0 + .iter() + .map(|f| f.function_identifiers()) + .flatten() + .collect_vec() } } @@ -389,6 +409,159 @@ impl IsLazy for LazyVec2 { } } +#[derive(Clone, Debug, Default)] +pub struct LazyVec3 { + x: LazyNodeF32, + y: LazyNodeF32, + z: LazyNodeF32, +} + +impl LazyVec3 { + pub fn new(x: LazyNodeF32, y: LazyNodeF32, z: LazyNodeF32) -> Self { + Self { x, y, z } + } +} + +#[derive(Clone, Debug, Default, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct ControlLazyVec3(Vec); +impl LivecodeFromWorld for ControlLazyVec3 { + fn o(&self, w: &LivecodeWorldState) -> LivecodeResult { + Ok(LazyVec3::new( + self.0[0].o(w)?, + self.0[1].o(w)?, + self.0[2].o(w)?, + )) + } +} + +impl GetLivecodeIdentifiers for ControlLazyVec3 { + fn variable_identifiers(&self) -> Vec { + self.0 + .iter() + .map(|f| f.variable_identifiers()) + .flatten() + .collect_vec() + } + + fn function_identifiers(&self) -> Vec { + self.0 + .iter() + .map(|f| f.function_identifiers()) + .flatten() + .collect_vec() + } +} + +impl LivecodeToControl for LazyVec3 { + fn to_control(&self) -> ControlLazyVec3 { + ControlLazyVec3(vec![ + self.x.to_control(), + self.y.to_control(), + self.z.to_control(), + ]) + } +} + +impl IsLazy for LazyVec3 { + type Target = glam::Vec3; + + fn eval_lazy(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + Ok(glam::vec3( + self.x.eval_lazy(ctx)?, + self.y.eval_lazy(ctx)?, + self.z.eval_lazy(ctx)?, + )) + } + + fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + Ok(LazyVec3::new( + self.x.with_more_defs(ctx)?, + self.y.with_more_defs(ctx)?, + self.z.with_more_defs(ctx)?, + )) + } +} + +#[derive(Clone, Debug, Default)] +pub struct LazyMurreletColor { + h: LazyNodeF32, + s: LazyNodeF32, + v: LazyNodeF32, + a: LazyNodeF32, +} + +impl LazyMurreletColor { + pub fn new(h: LazyNodeF32, s: LazyNodeF32, v: LazyNodeF32, a: LazyNodeF32) -> Self { + Self { h, s, v, a } + } +} + +#[derive(Clone, Debug, Default, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct ControlLazyMurreletColor(Vec); +impl LivecodeFromWorld for ControlLazyMurreletColor { + fn o(&self, w: &LivecodeWorldState) -> LivecodeResult { + Ok(LazyMurreletColor::new( + self.0[0].o(w)?, + self.0[1].o(w)?, + self.0[2].o(w)?, + self.0[3].o(w)?, + )) + } +} + +impl GetLivecodeIdentifiers for ControlLazyMurreletColor { + fn variable_identifiers(&self) -> Vec { + self.0 + .iter() + .map(|f| f.variable_identifiers()) + .flatten() + .collect_vec() + } + + fn function_identifiers(&self) -> Vec { + self.0 + .iter() + .map(|f| f.function_identifiers()) + .flatten() + .collect_vec() + } +} + +impl LivecodeToControl for LazyMurreletColor { + fn to_control(&self) -> ControlLazyMurreletColor { + ControlLazyMurreletColor(vec![ + self.h.to_control(), + self.s.to_control(), + self.v.to_control(), + self.a.to_control(), + ]) + } +} + +impl IsLazy for LazyMurreletColor { + type Target = MurreletColor; + + fn eval_lazy(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + Ok(MurreletColor::hsva( + self.h.eval_lazy(ctx)?, + self.s.eval_lazy(ctx)?, + self.v.eval_lazy(ctx)?, + self.a.eval_lazy(ctx)?, + )) + } + + fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult { + Ok(LazyMurreletColor::new( + self.h.with_more_defs(ctx)?, + self.s.with_more_defs(ctx)?, + self.v.with_more_defs(ctx)?, + self.a.with_more_defs(ctx)?, + )) + } +} + pub fn eval_lazy_f32( v: &LazyNodeF32, f32min: Option, diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs index d8e6d40..99ce7b2 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/examples/tests.rs @@ -3,20 +3,21 @@ use std::collections::HashMap; use glam::*; use lerpable::Lerpable; use murrelet_common::*; -use murrelet_livecode::{types::AdditionalContextNode, unitcells::*}; -use murrelet_livecode_derive::{Livecode, LivecodeOnly, NestEdit}; use murrelet_livecode::cachedcompute::CachedCompute; +use murrelet_livecode::{types::AdditionalContextNode, unitcells::*}; use murrelet_livecode_derive::Cached; +use murrelet_livecode_derive::{Livecode, LivecodeOnly, NestEdit}; #[derive(Debug, Clone, Livecode, Lerpable, Default)] pub struct BasicTypes { a_number: f32, c_vec2: Vec2, - // c_vec3: Vec3, - // b_color: MurreletColor, - // something: Vec, - // list_of_vec2: Vec, - // option_f32: Option, + b_angle: AnglePi, + c_vec3: Vec3, + b_color: MurreletColor, + something: Vec, + list_of_vec2: Vec, + option_f32: Option, // option_vec2: Option, } @@ -28,132 +29,129 @@ fn empty_string_lazy() -> String { String::new() } -// #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// pub struct BasicTypesWithDefaults { -// #[livecode(serde_default = "zeros")] -// a_number: f32, -// // b_color: MurreletColor, -// // #[livecode(serde_default = "0")] -// // #[lerpable(func = "lerpify_vec2")] -// // c_vec2_serde_default: Vec2, -// // something: Vec, -// // #[lerpable(func = "lerpify_vec_vec2")] -// // list_of_vec2: Vec, -// // #[livecode(kind = "none", serde_default = "empty_string")] -// // label: String, -// // #[livecode(kind = "none")] -// // #[lerpable(method = "skip")] -// // b: HashMap, -// // list_test: Vec +#[derive(Debug, Clone, Livecode, Lerpable, Default)] +pub struct BasicTypesWithDefaults { + #[livecode(serde_default = "zeros")] + a_number: f32, + b_color: MurreletColor, + #[livecode(serde_default = "0")] + c_vec2_serde_default: Vec2, + something: Vec, + list_of_vec2: Vec, + #[livecode(kind = "none", serde_default = "empty_string")] + label: String, + #[livecode(kind = "none")] + #[lerpable(method = "skip")] + b: HashMap, + list_test: Vec, +} -// } +#[derive(Debug, Clone, Livecode, Lerpable, Default)] +struct TestLazy { + #[lerpable(method = "skip")] + lazy: LazyBasicTypes, + // lazy_test: LazyBasicTypes, +} + +#[derive(Debug, Clone, Livecode, Lerpable, Default)] +enum EnumTest { + #[default] + A, + B(TestLazy), + C(#[lerpable(method = "skip")] LazyTestLazy), + // D(LazyTestLazy), +} + +#[derive(Debug, Clone, Livecode, Lerpable, Default)] +struct TestNewType(Vec); // #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// struct TestLazy { +// struct SequencerTest { +// sequencer: SimpleSquareSequence, +// ctx: AdditionalContextNode, +// #[livecode(src = "sequencer", ctx = "ctx")] +// node: UnitCells, +// #[livecode(src = "sequencer", ctx = "ctx")] // #[lerpable(method = "skip")] -// lazy: LazyBasicTypes, +// node_two: UnitCells, // } -// #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// enum EnumTest { -// #[default] -// A, -// B(TestLazy), -// C(#[lerpable(method = "skip")] LazyTestLazy), +// fn make_grid( +// x: usize, +// y: usize, +// cell_size: Vec2, +// offset_alternating: bool, +// ) -> Vec { +// let x_usize = x; +// let y_usize = y; + +// (0..x_usize) +// .flat_map(|x| { +// let x_idx = IdxInRange::new(x, x_usize); +// (0..y_usize).map(move |y| { +// let y_idx = IdxInRange::new(y, y_usize); +// let idx = IdxInRange2d::new_from_idx(x_idx, y_idx); +// let ctx = UnitCellIdx::from_idx2d(idx, 1.0); + +// let mut center = if offset_alternating { +// let mut center = idx.to_alternating_i().center_of_cell(); +// center += vec2(-0.5, 0.0); +// if idx.i.i() % 2 == 1 { +// center += vec2(0.5, 0.5); +// } + +// let offset_angle = AnglePi::new(1.0 / 6.0); +// let diag_scale = offset_angle.to_norm_dir() * cell_size.x / (100.0 * 0.5); + +// center *= diag_scale; +// center +// } else { +// let mut center = idx.center_of_cell(); +// center *= vec2(cell_size.x, cell_size.y) / 100.0; +// center +// }; + +// center *= 100.0; + +// let transform = SimpleTransform2d::new(vec![ +// SimpleTransform2dStep::translate(center), +// SimpleTransform2dStep::scale_both(cell_size.x / 100.0), +// ]); + +// UnitCellContext::new(ctx, transform) +// }) +// }) +// .collect::>() // } -// #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// struct TestNewType(Vec); - -// // #[derive(Debug, Clone, Livecode, Lerpable, Default)] -// // struct SequencerTest { -// // sequencer: SimpleSquareSequence, -// // ctx: AdditionalContextNode, -// // #[livecode(src = "sequencer", ctx = "ctx")] -// // node: UnitCells, -// // #[livecode(src = "sequencer", ctx = "ctx")] -// // #[lerpable(method = "skip")] -// // node_two: UnitCells, -// // } - -// // fn make_grid( -// // x: usize, -// // y: usize, -// // cell_size: Vec2, -// // offset_alternating: bool, -// // ) -> Vec { -// // let x_usize = x; -// // let y_usize = y; - -// // (0..x_usize) -// // .flat_map(|x| { -// // let x_idx = IdxInRange::new(x, x_usize); -// // (0..y_usize).map(move |y| { -// // let y_idx = IdxInRange::new(y, y_usize); -// // let idx = IdxInRange2d::new_from_idx(x_idx, y_idx); -// // let ctx = UnitCellIdx::from_idx2d(idx, 1.0); - -// // let mut center = if offset_alternating { -// // let mut center = idx.to_alternating_i().center_of_cell(); -// // center += vec2(-0.5, 0.0); -// // if idx.i.i() % 2 == 1 { -// // center += vec2(0.5, 0.5); -// // } - -// // let offset_angle = AnglePi::new(1.0 / 6.0); -// // let diag_scale = offset_angle.to_norm_dir() * cell_size.x / (100.0 * 0.5); - -// // center *= diag_scale; -// // center -// // } else { -// // let mut center = idx.center_of_cell(); -// // center *= vec2(cell_size.x, cell_size.y) / 100.0; -// // center -// // }; - -// // center *= 100.0; - -// // let transform = SimpleTransform2d::new(vec![ -// // SimpleTransform2dStep::translate(center), -// // SimpleTransform2dStep::scale_both(cell_size.x / 100.0), -// // ]); - -// // UnitCellContext::new(ctx, transform) -// // }) -// // }) -// // .collect::>() -// // } - -// // #[derive(Clone, Debug, Default, Livecode, Lerpable)] -// // pub struct SimpleSquareSequence { -// // rows: usize, -// // cols: usize, -// // size: f32, -// // } -// // impl UnitCellCreator for SimpleSquareSequence { -// // fn to_unit_cell_ctxs(&self) -> Vec { -// // make_grid(self.cols, self.rows, vec2(self.size, self.size), false) -// // } -// // } - -// // // new type - // #[derive(Clone, Debug, Default, Livecode, Lerpable)] -// pub struct NewTypeWithType(f32); - -// #[derive(Clone, Debug, Default, Livecode, Lerpable)] -// pub struct NewTypeWithTypeVec2(#[lerpable(func = "lerpify_vec2")] Vec2); - -// #[derive(Clone, Debug, Default, Livecode, Lerpable)] -// pub struct NewTypeWithVec(Vec); +// pub struct SimpleSquareSequence { +// rows: usize, +// cols: usize, +// size: f32, +// } +// impl UnitCellCreator for SimpleSquareSequence { +// fn to_unit_cell_ctxs(&self) -> Vec { +// make_grid(self.cols, self.rows, vec2(self.size, self.size), false) +// } +// } -// #[derive(Clone, Debug, Default, LivecodeOnly)] -// pub struct NewTypeWithStruct(BasicTypes); +// // new type +#[derive(Clone, Debug, Default, Livecode, Lerpable)] +pub struct NewTypeWithType(f32); +#[derive(Clone, Debug, Default, Livecode, Lerpable)] +pub struct NewTypeWithTypeVec2(Vec2); +#[derive(Clone, Debug, Default, Livecode, Lerpable)] +pub struct NewTypeWithVec(Vec); +#[derive(Clone, Debug, Default, LivecodeOnly)] +pub struct NewTypeWithStruct(BasicTypes); +#[derive(Clone, Debug, Default, LivecodeOnly)] +pub struct NewTypeWithStructLazy(LazyBasicTypes); // #[derive(Debug, Clone, Cached)] // pub struct BirdOutline { @@ -163,4 +161,4 @@ fn empty_string_lazy() -> String { // } -fn main() {} \ No newline at end of file +fn main() {} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index fd20e8f..3959864 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -18,10 +18,10 @@ impl LazyFieldType { quote! {murrelet_livecode::lazy::LazyVec2} } ControlType::F32_3 => { - quote! {Vec} + quote! {murrelet_livecode::lazy::LazyVec3} } ControlType::Color => { - quote! {Vec} + quote! {murrelet_livecode::lazy::LazyMurreletColor} } ControlType::LazyNodeF32 => { // already lazy... @@ -44,7 +44,7 @@ impl LazyFieldType { match self.0 { ControlType::F32_2 => { // quote! { murrelet_livecode::lazy::eval_lazy_vec2(&#ident, ctx) } - quote! { #ident.eval_lazy(ctx)? } + quote! { #ident.eval_lazy(ctx) } } ControlType::F32_3 => { quote! { murrelet_livecode::lazy::eval_lazy_vec3(&#ident, ctx) } @@ -85,21 +85,25 @@ impl LazyFieldType { // )} } ControlType::F32_3 => { - quote! { - #name: glam::vec3( - self.#name[0].eval_lazy(ctx)? as f32, - self.#name[1].eval_lazy(ctx)? as f32, - self.#name[2].eval_lazy(ctx)? as f32 - ) - } + quote! { #name: self.#name.eval_lazy(ctx)? } + + // quote! { + // #name: glam::vec3( + // self.#name[0].eval_lazy(ctx)? as f32, + // self.#name[1].eval_lazy(ctx)? as f32, + // self.#name[2].eval_lazy(ctx)? as f32 + // ) + // } } ControlType::Color => { - quote! {#name: murrelet_common::MurreletColor::hsva( - self.#name[0].eval_lazy(ctx)? as f32, - self.#name[1].eval_lazy(ctx)? as f32, - self.#name[2].eval_lazy(ctx)? as f32, - self.#name[3].eval_lazy(ctx)? as f32 - )} + quote! { #name: self.#name.eval_lazy(ctx)? } + + // quote! {#name: murrelet_common::MurreletColor::hsva( + // self.#name[0].eval_lazy(ctx)? as f32, + // self.#name[1].eval_lazy(ctx)? as f32, + // self.#name[2].eval_lazy(ctx)? as f32, + // self.#name[3].eval_lazy(ctx)? as f32 + // )} } ControlType::Bool => quote! {#name: self.#name.eval_lazy(ctx)? > 0.0}, ControlType::LazyNodeF32 => quote! {#name: self.#name.add_more_defs(ctx)? }, @@ -179,12 +183,21 @@ impl LazyFieldType { let orig_ty = idents.orig_ty(); match self.0 { ControlType::F32_2 => { - quote! {vec2(self.0[0].eval_lazy(ctx)? as f32, self.0[1].eval_lazy(ctx)? as f32)} + quote! { self.0.eval_lazy(ctx)? } + } + ControlType::F32_3 => { + quote! { self.0.eval_lazy(ctx)? } } - // ControlType::F32_3 => quote!{murrelet_livecode::livecode::ControlF32::vec3(&self.0, w)}, ControlType::Color => { - quote! {MurreletColor::hsva(self.0[0].eval_lazy(ctx)? as f32, self.0[1].eval_lazy(ctx)? as f32, self.0[2].eval_lazy(ctx)? as f32, self.0[3].eval_lazy(ctx)? as f32)} + quote! { self.0.eval_lazy(ctx)? } } + // ControlType::F32_2 => { + // quote! {vec2(self.0[0].eval_lazy(ctx)? as f32, self.0[1].eval_lazy(ctx)? as f32)} + // } + // // ControlType::F32_3 => quote!{murrelet_livecode::livecode::ControlF32::vec3(&self.0, w)}, + // ControlType::Color => { + // quote! {MurreletColor::hsva(self.0[0].eval_lazy(ctx)? as f32, self.0[1].eval_lazy(ctx)? as f32, self.0[2].eval_lazy(ctx)? as f32, self.0[3].eval_lazy(ctx)? as f32)} + // } // ControlType::LinSrgbaUnclamped => quote!{murrelet_livecode::livecode::ControlF32::hsva_unclamped(&self.0, w)}, ControlType::Bool => quote! {self.0.eval_lazy(ctx)? > 0.0}, ControlType::AnglePi => { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 6b59245..e4b7b97 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -28,10 +28,10 @@ impl LivecodeFieldType { pub fn to_token_lazy(&self) -> TokenStream2 { match self.0 { ControlType::F32_2 => quote! { murrelet_livecode::lazy::ControlLazyVec2 }, - ControlType::F32_3 => quote! { Vec }, - ControlType::Color => quote! { Vec }, + ControlType::F32_3 => quote! { murrelet_livecode::lazy::ControlLazyVec3 }, + ControlType::Color => quote! { murrelet_livecode::lazy::ControlMurreletColor }, ControlType::ColorUnclamped => { - quote! { Vec } + todo!() //quote! { Vec } } ControlType::F32 => quote! {murrelet_livecode::livecode::ControlF32}, ControlType::Bool => quote! {murrelet_livecode::livecode::ControlBool}, @@ -439,8 +439,12 @@ impl GenFinal for FieldTokensLivecode { let internal_type = parsed_type_info.main_type; let for_struct = { - let t = Self::new_ident(internal_type); - quote! {#t} + if internal_type.to_string().starts_with("Lazy") { + catch_special_types(internal_type) + } else { + let t = Self::new_ident(internal_type); + quote! {#t} + } }; let for_world = { quote! { self.0.o(w)? } @@ -661,8 +665,8 @@ impl GenFinal for FieldTokensLivecode { } HowToControlThis::WithRecurse(_, RecursiveControlType::StructLazy) => { let original_internal_type = parsed_type_info.internal_type(); - let name = Self::new_ident(original_internal_type.clone()); - quote! {#name} + + catch_special_types(original_internal_type) } // HowToControlThis::WithRecurse(_, RecursiveControlType::Vec) => { // // for things like Lazy Vec2... @@ -705,7 +709,6 @@ impl GenFinal for FieldTokensLivecode { match wrapper { VecDepth::NotAVec => unreachable!("not a vec in a vec?"), VecDepth::Vec => { - // println!("HIII"); if inner_is_lazy_struct { quote! { #name: self.#name.iter() @@ -1198,13 +1201,13 @@ impl GenFinal for FieldTokensLivecode { let new_ty = { let DataFromType { main_type, .. } = ident_from_type(&orig_ty); - println!("main_type.to_string() {:?}", main_type.to_string()); - if main_type.to_string() == "LazyVec2" { - quote! { murrelet_livecode::lazy::ControlLazyVec2 } - } else { - let ref_lc_ident = Self::new_ident(main_type.clone()); - quote! {#ref_lc_ident} - } + // if main_type.to_string() == "LazyVec2" { + // quote! { murrelet_livecode::lazy::ControlLazyVec2 } + // } else { + // let ref_lc_ident = Self::new_ident(main_type.clone()); + // quote! {#ref_lc_ident} + catch_special_types(main_type.clone()) + // } // let ref_lc_ident = idents.config.new_ident(main_type.clone()); }; @@ -1229,3 +1232,15 @@ impl GenFinal for FieldTokensLivecode { } } } + +fn catch_special_types(original_internal_type: syn::Ident) -> TokenStream2 { + let ctrl_ident = update_to_control_ident(original_internal_type.clone()); + + match original_internal_type.to_string().as_str() { + "LazyVec2" => quote! { murrelet_livecode::lazy::ControlLazyVec2 }, + "LazyNodeF32" => quote! { murrelet_livecode::lazy::ControlLazyNodeF32 }, + "LazyVec3" => quote! { murrelet_livecode::lazy::ControlLazyVec3 }, + "LazyMurreletColor" => quote! { murrelet_livecode::lazy::ControlLazyMurreletColor }, + _ => quote! { #ctrl_ident }, + } +} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index da05399..8733f01 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -2,7 +2,7 @@ use darling::{ast, FromDeriveInput, FromField, FromVariant}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -const DEBUG_THIS: bool = false; +const DEBUG_THIS: bool = true; pub(crate) fn prefix_ident(prefix: &str, name: syn::Ident) -> syn::Ident { let lc_name = format!("{}{}", prefix, name); @@ -374,7 +374,7 @@ impl LivecodeFieldReceiver { } SerdeDefault::DefaultImpl => quote! {#[serde(default)]}, SerdeDefault::Empty => { - // nace and general + // nice and general quote! {#[serde(default="murrelet_livecode::livecode::empty_vec")]} } _ => { @@ -382,18 +382,26 @@ impl LivecodeFieldReceiver { let how = self.how_to_control_this(); if is_lazy { - let serde_func = match &how { - HowToControlThis::WithRecurse(_, RecursiveControlType::Vec) => { - // weird and hardcoded for things like Lazy Vec2, which get turned into Vec... - serde.from_control_type(ControlType::LazyNodeF32, true) - } - _ => serde.from_control_type(how.get_control_type(), false), - }; - let r = lazy_version_of_default_serde(&serde_func); - - quote! {#[serde(default=#r)]} + if matches!( + how, + HowToControlThis::WithRecurse(_, RecursiveControlType::StructLazy) + ) { + quote! { #[serde(default)] } + } else { + let serde_func = match &how { + HowToControlThis::WithRecurse(_, RecursiveControlType::Vec) => { + // weird and hardcoded for things like Lazy Vec2, which get turned into Vec... + serde.from_control_type(ControlType::LazyNodeF32, true) + } + _ => serde.from_control_type(how.get_control_type(), false), + }; + let r = lazy_version_of_default_serde(&serde_func); + + quote! {#[serde(default=#r)]} + } } else { let r = serde.from_control_type(how.get_control_type(), false); + quote! {#[serde(default=#r)]} } } @@ -860,10 +868,14 @@ impl DataFromType { pub(crate) fn to_quote(&self) -> TokenStream2 { let main_type = self.main_type.clone(); match (&self.second_type, &self.third_type, &self.fourth_type) { - (None, None, None) => quote!{ #main_type }, - (Some(second_type), None, None) => quote!{ #main_type<#second_type> }, - (Some(second_type), Some(third_type), None) => quote!{ #main_type<#second_type<#third_type>> }, - (Some(second_type), Some(third_type), Some(fourth_type)) => quote!{ #main_type<#second_type<#third_type<#fourth_type>>> }, + (None, None, None) => quote! { #main_type }, + (Some(second_type), None, None) => quote! { #main_type<#second_type> }, + (Some(second_type), Some(third_type), None) => { + quote! { #main_type<#second_type<#third_type>> } + } + (Some(second_type), Some(third_type), Some(fourth_type)) => { + quote! { #main_type<#second_type<#third_type<#fourth_type>>> } + } _ => unreachable!(), } } @@ -883,9 +895,6 @@ pub fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { let s = path.segments.last().unwrap(); let main_type = s.ident.clone(); - - println!("main_type {:?}", main_type); - if main_type.to_string() != "WrappedLazyType" { acc.push(main_type); } From 3b58fc61769aed5c4d0ca4f83f421ff0f0459573 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Tue, 3 Feb 2026 03:55:58 -0500 Subject: [PATCH 140/149] wip --- murrelet_common/src/intersection.rs | 4 +-- murrelet_draw/src/style.rs | 14 ++++------ murrelet_draw/src/svg.rs | 2 +- murrelet_draw/src/tesselate.rs | 10 ++++--- murrelet_gpu/src/device_state.rs | 2 +- murrelet_gpu/src/graphics_ref.rs | 4 +-- murrelet_gpu/src/window.rs | 2 +- murrelet_livecode/src/lazy.rs | 28 +++++++++++++++++-- murrelet_livecode/src/unitcells.rs | 4 +-- .../src/derive_lazy.rs | 8 ++++-- .../murrelet_livecode_derive/src/parser.rs | 2 +- murrelet_perform/src/perform.rs | 25 +++++++---------- 12 files changed, 63 insertions(+), 42 deletions(-) diff --git a/murrelet_common/src/intersection.rs b/murrelet_common/src/intersection.rs index ec0f1bf..010fd31 100644 --- a/murrelet_common/src/intersection.rs +++ b/murrelet_common/src/intersection.rs @@ -1,4 +1,4 @@ -use glam::{vec2, Vec2}; +use glam::Vec2; use crate::SpotOnCurve; @@ -22,7 +22,7 @@ pub fn find_intersection_inf(line0: (Vec2, Vec2), line1: (Vec2, Vec2)) -> Option if d.abs() < epsilon { None // parallel, we're done } else { - let intersection_point_f32: Vec2; + // let intersection_point_f32: Vec2; let self_is_vertical = (x1 - x2).abs() < epsilon; let other_is_vertical = (x3 - x4).abs() < epsilon; diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 490442e..a6eecdb 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -11,7 +11,10 @@ use lerpable::Lerpable; use md5::{Digest, Md5}; use murrelet_common::*; use murrelet_gui::{CanMakeGUI, MurreletGUI}; -use murrelet_livecode::{lazy::ControlLazyNodeF32, livecode::ControlF32, types::ControlVecElement}; +use murrelet_livecode::{ + lazy::ControlLazyMurreletColor, + livecode::ControlF32, +}; use murrelet_livecode_derive::Livecode; use styleconf::StyleConf; @@ -24,13 +27,8 @@ fn _black() -> [ControlF32; 4] { ] } -fn _black_lazy() -> Vec> { - vec![ - ControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::Single(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::Single(ControlLazyNodeF32::Float(1.0)), - ] +fn _black_lazy() -> ControlLazyMurreletColor { + ControlLazyMurreletColor::new_default(0.0, 0.0, 0.0, 1.0) } #[derive(Copy, Clone, Debug, Livecode, Lerpable, Default)] diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index ad0d474..d636f25 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -1,6 +1,6 @@ // defines the SVG basic shapes -use glam::{Mat4, Vec2}; +use glam::Vec2; use lerpable::Lerpable; use lyon::geom::{euclid::Point2D, Point}; use murrelet_common::{SimpleTransform2d, ToSimpleTransform}; diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 39576dc..794e38d 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -4,7 +4,6 @@ use crate::{ cubic::CubicBezier, curve_drawer::{ CubicBezierPath, CurveArc, CurveCubicBezier, CurveDrawer, CurvePoints, CurveSegment, - ToCurveDrawer, }, svg::SvgPathDef, }; @@ -244,11 +243,14 @@ fn add_circular_arc(builder: &mut B, c: &CurveArc) { // Angles are in radians. let mut sweep = end - start; - if c.is_ccw() { - if sweep < 0.0 { sweep += 2.0 * PI; } + if sweep < 0.0 { + sweep += 2.0 * PI; + } } else { - if sweep > 0.0 { sweep -= 2.0 * PI; } + if sweep > 0.0 { + sweep -= 2.0 * PI; + } } let arc = Arc { diff --git a/murrelet_gpu/src/device_state.rs b/murrelet_gpu/src/device_state.rs index a4c2761..bc8dffb 100644 --- a/murrelet_gpu/src/device_state.rs +++ b/murrelet_gpu/src/device_state.rs @@ -286,7 +286,7 @@ impl<'a> DeviceStateForRender<'a> { } } - pub fn device_state(&self) -> &DeviceState { + pub fn device_state(&self) -> &DeviceState<'_> { &self.device_state } diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index 989d3c2..aff7c6b 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -419,7 +419,7 @@ impl ShaderOptions { } } - fn as_sampler_desc(&self) -> wgpu::SamplerDescriptor { + fn as_sampler_desc(&self) -> wgpu::SamplerDescriptor<'_> { wgpu::SamplerDescriptor { address_mode_u: self.sampler_address_mode_u, address_mode_v: self.sampler_address_mode_v, @@ -1513,7 +1513,7 @@ impl Graphics { ) } - pub fn depth_stencil_attachment(&self) -> Option { + pub fn depth_stencil_attachment(&self) -> Option> { if let Some(TextureFor3d { depth_view, .. }) = &self.textures_for_3d { Some(wgpu::RenderPassDepthStencilAttachment { view: depth_view, diff --git a/murrelet_gpu/src/window.rs b/murrelet_gpu/src/window.rs index c6ea37e..db7478e 100644 --- a/murrelet_gpu/src/window.rs +++ b/murrelet_gpu/src/window.rs @@ -26,7 +26,7 @@ impl<'a> GraphicsWindowConf<'a> { } } - pub fn multi(&self, multiplier: f32) -> GraphicsWindowConf { + pub fn multi(&self, multiplier: f32) -> GraphicsWindowConf<'_> { let [x, y] = self.dims; GraphicsWindowConf { device: self.device, diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index 8d16909..c2f78f6 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use crate::{ expr::{ExprWorldContextValues, MixedEvalDefs, ToMixedDefs}, livecode::{ - ControlF32, GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeFunction, LivecodeToControl, + GetLivecodeIdentifiers, LivecodeFromWorld, LivecodeFunction, LivecodeToControl, LivecodeVariable, }, nestedit::{NestEditable, NestedMod}, @@ -11,11 +11,11 @@ use crate::{ types::{LivecodeError, LivecodeResult}, }; use evalexpr::Node; -use glam::Vec2; + use itertools::Itertools; use lerpable::IsLerpingMethod; use lerpable::{step, Lerpable}; -use murrelet_common::{IdxInRange, LivecodeValue, MurreletColor, MurreletIterHelpers}; +use murrelet_common::{IdxInRange, LivecodeValue, MurreletColor}; use serde::Deserialize; #[derive(Debug, Deserialize, Clone)] @@ -36,6 +36,10 @@ impl ControlLazyNodeF32 { Self::Expr(n) } + pub fn new_f32(n: f32) -> Self { + Self::Float(n) + } + fn result(&self) -> Result { match self { ControlLazyNodeF32::Int(d) => Ok(*d as f32), @@ -364,6 +368,12 @@ impl NestEditable for LazyVec2 { #[derive(Clone, Debug, Default, Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ControlLazyVec2(Vec); + +impl ControlLazyVec2 { + pub fn new(x: ControlLazyNodeF32, y: ControlLazyNodeF32) -> Self { + Self(vec![x, y]) + } +} impl LivecodeFromWorld for ControlLazyVec2 { fn o(&self, w: &LivecodeWorldState) -> LivecodeResult { Ok(LazyVec2::new(self.0[0].o(w)?, self.0[1].o(w)?)) @@ -500,6 +510,18 @@ impl LazyMurreletColor { #[derive(Clone, Debug, Default, Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ControlLazyMurreletColor(Vec); + +impl ControlLazyMurreletColor { + pub fn new_default(h: f32, s: f32, v: f32, a: f32) -> Self { + ControlLazyMurreletColor(vec![ + ControlLazyNodeF32::new_f32(h), + ControlLazyNodeF32::new_f32(s), + ControlLazyNodeF32::new_f32(v), + ControlLazyNodeF32::new_f32(a), + ]) + } +} + impl LivecodeFromWorld for ControlLazyMurreletColor { fn o(&self, w: &LivecodeWorldState) -> LivecodeResult { Ok(LazyMurreletColor::new( diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index 3c441ee..c4536b7 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -213,7 +213,7 @@ impl UnitCells { Self { items } } - pub fn iter(&self) -> std::slice::Iter> { + pub fn iter(&self) -> std::slice::Iter<'_, UnitCell> { self.items.iter() } @@ -1055,7 +1055,7 @@ impl Clone for UnitCellDetailsFunction { } impl UnitCellDetailsFunction { - fn transform_with_skew(&self, x: Vec2) -> Vec2 { + pub fn transform_with_skew(&self, x: Vec2) -> Vec2 { (self.func)(x) } } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index 3959864..8337678 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -47,10 +47,14 @@ impl LazyFieldType { quote! { #ident.eval_lazy(ctx) } } ControlType::F32_3 => { - quote! { murrelet_livecode::lazy::eval_lazy_vec3(&#ident, ctx) } + quote! { #ident.eval_lazy(ctx) } + + // quote! { murrelet_livecode::lazy::eval_lazy_vec3(&#ident, ctx) } } ControlType::Color => { - quote! { murrelet_livecode::lazy::eval_lazy_color(&#ident, ctx) } + quote! { #ident.eval_lazy(ctx) } + + // quote! { murrelet_livecode::lazy::eval_lazy_color(&#ident, ctx) } } ControlType::Bool => quote! {#ident.eval_lazy(ctx)? > 0.0}, ControlType::AnglePi => { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index 8733f01..80a14a0 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -2,7 +2,7 @@ use darling::{ast, FromDeriveInput, FromField, FromVariant}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -const DEBUG_THIS: bool = true; +const DEBUG_THIS: bool = false; pub(crate) fn prefix_ident(prefix: &str, name: syn::Ident) -> syn::Ident { let lc_name = format!("{}{}", prefix, name); diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 027058c..1074a8f 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -1,14 +1,16 @@ #![allow(dead_code)] -use glam::{Vec2, vec2}; +use glam::{vec2, Vec2}; use lerpable::Lerpable; -use murrelet_common::{Assets, AssetsRef, LivecodeUsage, LivecodeValue, SimpleTransform2d, SimpleTransform2dStep}; +use murrelet_common::{ + Assets, AssetsRef, LivecodeUsage, LivecodeValue, SimpleTransform2d, SimpleTransform2dStep, +}; use murrelet_common::{LivecodeSrc, LivecodeSrcUpdateInput, MurreletAppInput}; use murrelet_common::{MurreletColor, TransformVec2}; use murrelet_gui::MurreletGUI; use murrelet_livecode::expr::{MixedEvalDefs, MixedEvalDefsRef}; -use murrelet_livecode::lazy::{ControlLazyNodeF32, LazyNodeF32}; +use murrelet_livecode::lazy::{ControlLazyMurreletColor, ControlLazyNodeF32, LazyNodeF32}; use murrelet_livecode::state::{LivecodeTimingConfig, LivecodeWorldState}; -use murrelet_livecode::types::{AdditionalContextNode, ControlVecElement, LivecodeResult}; +use murrelet_livecode::types::{AdditionalContextNode, LivecodeResult}; use std::collections::{HashMap, HashSet}; use std::fs; @@ -119,7 +121,7 @@ impl SvgDrawConfig { // Mat4::from_translation(translation_to_final) * Mat4::from_scale(scale) SimpleTransform2d::new(vec![ SimpleTransform2dStep::translate(translation_to_final), - SimpleTransform2dStep::scale_both(s) + SimpleTransform2dStep::scale_both(s), ]) } else { SimpleTransform2d::noop() @@ -243,13 +245,8 @@ fn _default_bg_color() -> [ControlF32; 4] { ] } -fn _default_bg_color_lazy() -> Vec> { - vec![ - ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::raw(ControlLazyNodeF32::Float(0.0)), - ControlVecElement::raw(ControlLazyNodeF32::Float(1.0)), - ] +fn _default_bg_color_lazy() -> ControlLazyMurreletColor { + ControlLazyMurreletColor::new_default(0.0, 0.0, 0.0, 1.0) } fn _default_svg_size() -> ControlF32 { @@ -846,7 +843,7 @@ where self.svg_save_path_with_prefix("") } - pub fn to_lil_liveconfig(&self) -> LivecodeResult { + pub fn to_lil_liveconfig(&self) -> LivecodeResult> { Ok(LilLiveConfig { save_path: self.save_path.as_ref(), run_id: self.run_id, @@ -1171,6 +1168,4 @@ where pub fn run_id(&self) -> u64 { self.run_id } - - } From a2b9684666475938fbd720944ece45673f20bf7f Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Thu, 5 Feb 2026 17:21:04 -0500 Subject: [PATCH 141/149] wip --- murrelet_common/src/idx.rs | 8 +- murrelet_draw/src/curve_drawer.rs | 10 +- murrelet_livecode/src/types.rs | 180 ++++++++++++++++-- .../src/derive_lazy.rs | 39 ++-- .../src/derive_livecode.rs | 15 +- 5 files changed, 192 insertions(+), 60 deletions(-) diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index c1c54ef..b53fd48 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -35,11 +35,9 @@ impl IdxInRange { where >::Error: core::fmt::Debug, { - IdxInRange { - i: 0, - total: total.try_into().expect("can't convert to u64"), - } - .last_i() + let total = total.try_into().expect("can't convert to u64"); + assert!(total > 0, "IdxInRange::new_last requires total > 0"); + IdxInRange { i: total - 1, total } } pub fn enumerate<'a, T, I>(iter: I) -> Vec<(IdxInRange, T)> diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index e0a3530..dae56b6 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -348,8 +348,8 @@ impl CurveSegment { CurveSegment::Arc(curve_arc) => CurveSegment::Arc(curve_arc.reverse()), CurveSegment::Points(curve_points) => CurveSegment::Points(curve_points.reverse()), CurveSegment::CubicBezier(c) => { - // CurveSegment::CubicBezier(c.reverse()) - CurveSegment::Points(c.as_points().reverse()) + CurveSegment::CubicBezier(c.reverse()) + //CurveSegment::Points(c.as_points().reverse()) } } } @@ -1263,6 +1263,12 @@ impl ToCurveDrawer for Vec { } } +impl ToCurveDrawer for Vec { + fn to_segments(&self) -> Vec { + self.map_iter_collect(|x| x.to_segment()) + } +} + #[macro_export] macro_rules! curve_segments { ($($expr:expr),* $(,)?) => {{ diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 433d20d..6a109e2 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -179,10 +179,18 @@ impl ControlVecElementRepeatMethod { } } +#[derive(Debug, Clone, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct ControlLazyBlendRepeatMethod { + count: ControlLazyNodeF32, + blend: ControlLazyNodeF32, +} + #[derive(Debug, Clone, Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[serde(untagged)] pub enum DeserLazyControlVecElementRepeatMethod { + Blend(ControlLazyBlendRepeatMethod), Single(ControlLazyNodeF32), Rect([ControlLazyNodeF32; 2]), } @@ -198,6 +206,15 @@ impl DeserLazyControlVecElementRepeatMethod { let y = lazy[1].o(w)?; Ok(LazyVecElementRepeatMethod::Rect([x, y])) } + DeserLazyControlVecElementRepeatMethod::Blend(b) => { + let count = b.count.o(w)?; + let blend = b.blend.o(w)?; + + Ok(LazyVecElementRepeatMethod::Blend(LazyBlendRepeatMethod { + count, + blend, + })) + } } } } @@ -210,8 +227,6 @@ pub struct DeserLazyControlVecElementRepeat { ctx: AdditionalContextNode, prefix: String, what: Vec>, - #[serde(default)] - blend_with_next: usize, } impl DeserLazyControlVecElementRepeat { fn o( @@ -234,7 +249,6 @@ impl DeserLazyControlVecElementRepeat { ctx: self.ctx.clone(), prefix: self.prefix.clone(), what, - blend_with_next: self.blend_with_next, }) } } @@ -354,9 +368,16 @@ where } } +#[derive(Debug, Clone)] +pub struct LazyBlendRepeatMethod { + count: LazyNodeF32, + blend: LazyNodeF32, +} + // just an intermediate type...? #[derive(Debug, Clone)] pub enum LazyVecElementRepeatMethod { + Blend(LazyBlendRepeatMethod), Single(LazyNodeF32), Rect([LazyNodeF32; 2]), } @@ -372,6 +393,10 @@ impl LazyVecElementRepeatMethod { let ry = r[1].eval_lazy(ctx)?; rx * ry } + LazyVecElementRepeatMethod::Blend(b) => { + let ss = b.count.eval_lazy(ctx)?; + ss + } }; Ok(v as usize) } @@ -388,9 +413,30 @@ impl LazyVecElementRepeatMethod { let ry = s[1].eval_lazy(ctx)?; IdxInRange2d::enumerate_counts(rx as usize, ry as usize) } + LazyVecElementRepeatMethod::Blend(b) => IdxInRange::enumerate_count( + b.count.eval_lazy(ctx)? as usize + b.blend.eval_lazy(ctx)? as usize, + ) + .iter() + .map(|x| x.to_2d()) + .collect_vec(), }; Ok(v) } + + fn next_blend(&self, ctx: &MixedEvalDefs) -> Option { + match self { + LazyVecElementRepeatMethod::Blend(b) => { + let blend = b.blend.eval_with_ctx(ctx).unwrap_or_default() as usize; + if blend > 0 { + Some(BlendWith::new(blend)) + } else { + None + } + } + LazyVecElementRepeatMethod::Single(_) => None, + LazyVecElementRepeatMethod::Rect(_) => None, + } + } } // just internal method, if we realize we're looking at a lazy, @@ -400,7 +446,6 @@ pub struct LazyVecElementRepeat { ctx: AdditionalContextNode, prefix: String, what: Vec>, - blend_with_next: usize, } impl LazyVecElementRepeat> @@ -431,7 +476,7 @@ where let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); scoped_ctx.set_vals(expr.with_prefix(&prefix)); - let mut is_blending: Option = None; + let mut is_blending: Option = None; for src in &self.what { match src { @@ -445,9 +490,8 @@ where blend_with_list(&mut result, item, &mut is_blending); } - if c.blend_with_next > 0 { - // blend_with_next is a COUNT (1 => only last item) - is_blending = Some(IdxInRange::new_last(c.blend_with_next - 1)); + if let Some(new_blend) = c.repeat.next_blend(&scoped_ctx) { + is_blending = Some(new_blend); } } } @@ -470,7 +514,6 @@ where .iter() .map(|elem| elem.with_more_defs(ctx)) .collect::>>()?, - blend_with_next: self.blend_with_next, }) } } @@ -560,6 +603,14 @@ where y.to_control(), ]) } + LazyVecElementRepeatMethod::Blend(b) => { + DeserLazyControlVecElementRepeatMethod::Blend( + ControlLazyBlendRepeatMethod { + count: b.count.to_control(), + blend: b.blend.to_control(), + }, + ) + } }; let what = rep.what.iter().map(|e| e.to_control()).collect::>(); @@ -569,7 +620,6 @@ where prefix: rep.prefix.clone(), what, ctx: rep.ctx.clone(), - blend_with_next: rep.blend_with_next, }) } } @@ -638,27 +688,111 @@ where } } +#[derive(Debug, Clone, Copy)] +struct BlendWith { + offset: usize, // 0 = last item + count: usize, // number of items to blend +} + +impl BlendWith { + fn new(count: usize) -> Self { + assert!(count > 0, "blend_with_next requires count > 0"); + Self { + offset: count - 1, + count, + } + } + + fn pct(&self) -> f32 { + // Exclusive endpoints: (0, 1) spread across count items. + // Higher pct for items closer to the end (offset larger). + (self.offset + 1) as f32 / (self.count + 1) as f32 + } + + fn prev(self) -> Option { + if self.offset == 0 { + None + } else { + Some(Self { + offset: self.offset - 1, + count: self.count, + }) + } + } +} + fn blend_with_list( result: &mut Vec, new: Target, - is_blending: &mut Option, + is_blending: &mut Option, ) { if let Some(curr_offset) = is_blending { // otherwise, too far back, skip - if curr_offset.i_usize() < result.len() { - let i = result.len() - 1 - curr_offset.i_usize(); + if curr_offset.offset < result.len() { + let i = result.len() - 1 - curr_offset.offset; result[i] = new.lerpify(&result[i], &curr_offset.pct()); // if we can subtract 1, that's the next one to check. // otherwise, set to none. - *is_blending = curr_offset.prev_i(); + *is_blending = curr_offset.prev(); } } else { result.push(new); } } +pub fn eval_and_expand_vec_list( + items: &[ControlVecElement], + w: &LivecodeWorldState, +) -> LivecodeResult> +where + Source: LivecodeFromWorld + Clone + Debug, + Target: Lerpable, +{ + let mut result: Vec = Vec::new(); + let mut is_blending: Option = None; + + for item in items { + let expanded = item.eval_and_expand_vec(w)?; + for elem in expanded { + blend_with_list(&mut result, elem, &mut is_blending); + } + + let blend_count = item.blend_with_next(); + if blend_count > 0 { + is_blending = Some(BlendWith::new(blend_count)); + } + } + + Ok(result) +} + +pub fn lazy_expand_vec_list( + items: &[LazyControlVecElement>], + ctx: &MixedEvalDefs, +) -> LivecodeResult>> +where + Inner: Clone + Debug + IsLazy, + Inner::Target: Lerpable, +{ + let mut result: Vec> = Vec::new(); + let mut is_blending: Option = None; + + for item in items { + let expanded = item.lazy_expand_vec(ctx)?; + for elem in expanded { + blend_with_list(&mut result, elem, &mut is_blending); + } + + if let Some(blend_count) = item.blend_with_next(ctx) { + is_blending = Some(blend_count); + } + } + + Ok(result) +} + impl ControlVecElementRepeat { pub fn _eval_and_expand_vec( &self, @@ -683,7 +817,7 @@ impl ControlVecElementRepeat { let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); let mut new_w = w.clone_with_vals(expr, &prefix); - let mut is_blending: Option = None; + let mut is_blending: Option = None; for src in &self.what { match src { @@ -706,7 +840,7 @@ impl ControlVecElementRepeat { } if c.blend_with_next > 0 { - is_blending = Some(IdxInRange::new_last(c.blend_with_next - 1)); + is_blending = Some(BlendWith::new(c.blend_with_next)); } // result.extend(o.into_iter()); @@ -836,6 +970,13 @@ where LazyControlVecElement::Repeat(c) => c.lazy_expand_vec_repeat_element(ctx), } } + + fn blend_with_next(&self, ctx: &MixedEvalDefs) -> Option { + match self { + LazyControlVecElement::Single(_) => None, + LazyControlVecElement::Repeat(r) => r.repeat.next_blend(ctx), + } + } } // impl IsLazy for LazyControlVecElement @@ -902,6 +1043,13 @@ where // Sequencer: UnitCellCreator, // ControlSequencer: LivecodeFromWorld, { + fn blend_with_next(&self) -> usize { + match self { + ControlVecElement::Single(_) => 0, + ControlVecElement::Repeat(r) => r.blend_with_next, + } + } + pub fn raw(c: Source) -> Self { Self::Single(c) } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index 8337678..edd4328 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -596,35 +596,24 @@ impl GenFinal for FieldTokensLazy { idents.data.f32max, ); quote! { - #name: self.#name - .iter() - .map(|item| { - let expanded = item.lazy_expand_vec(ctx)?; - expanded.into_iter() - .map(|#x_ident| #c_expr) - .collect::, _>>() - }) - .collect::, _>>()? - .into_iter() - .flatten() - .collect() + #name: { + let expanded = murrelet_livecode::types::lazy_expand_vec_list(&self.#name, ctx)?; + expanded + .into_iter() + .map(|#x_ident| #c_expr) + .collect::, _>>()? + } } } HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => { quote! { - #name: self.#name - .iter() - .map(|item| { - let expanded = item.lazy_expand_vec(ctx)?; - expanded - .into_iter() - .map(|x| x.eval_lazy(ctx)) - .collect::, _>>() - }) - .collect::, _>>()? - .into_iter() - .flatten() - .collect() + #name: { + let expanded = murrelet_livecode::types::lazy_expand_vec_list(&self.#name, ctx)?; + expanded + .into_iter() + .map(|x| x.eval_lazy(ctx)) + .collect::, _>>()? + } } } HowToControlThis::WithNone(_) => { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index e4b7b97..914eee1 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -718,12 +718,8 @@ impl GenFinal for FieldTokensLivecode { } } else { quote! { - #name: self.#name.iter() - .map(|x| x.eval_and_expand_vec(w)) - .collect::, _>>()? - .into_iter() - .flatten() - .collect()} + #name: murrelet_livecode::types::eval_and_expand_vec_list(&self.#name, w)? + } } // quote! { @@ -738,12 +734,7 @@ impl GenFinal for FieldTokensLivecode { let mut result = Vec::with_capacity(self.#name.len()); for internal_row in &self.#name { result.push( - internal_row.iter() - .map(|x| x.eval_and_expand_vec(w)) - .collect::, _>>()? - .into_iter() - .flatten() - .collect() + murrelet_livecode::types::eval_and_expand_vec_list(internal_row, w)? ) } result From b08b2d6e957b26ba154898db238e48b2b31d8e44 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 6 Feb 2026 12:56:48 -0500 Subject: [PATCH 142/149] cr, mostly removing commented out code that i probably wont need again --- murrelet_common/Cargo.toml | 1 - murrelet_common/src/color.rs | 11 - murrelet_common/src/geometry.rs | 17 +- murrelet_common/src/idx.rs | 40 +-- murrelet_common/src/lib.rs | 8 +- murrelet_common/src/metric.rs | 8 - murrelet_common/src/transform.rs | 21 -- murrelet_common/src/triangulate.rs | 112 -------- murrelet_draw/Cargo.toml | 4 +- murrelet_draw/src/compass.rs | 36 --- murrelet_draw/src/cubic.rs | 10 - murrelet_draw/src/curve_drawer.rs | 49 +--- murrelet_draw/src/drawable.rs | 34 --- murrelet_draw/src/newtypes.rs | 1 - murrelet_draw/src/scaffold.rs | 246 +----------------- murrelet_draw/src/sequencers.rs | 1 - murrelet_draw/src/serialize.rs | 17 +- murrelet_draw/src/style.rs | 20 +- murrelet_draw/src/tesselate.rs | 191 -------------- murrelet_gen_derive/src/derive_gen.rs | 75 ++---- murrelet_gen_derive/src/parser.rs | 57 +--- murrelet_gpu/Cargo.toml | 7 +- murrelet_gpu/src/compute.rs | 27 -- murrelet_gpu/src/gpu_livecode.rs | 1 - murrelet_gpu/src/gpu_macros.rs | 111 -------- murrelet_gpu/src/graphics_ref.rs | 191 -------------- murrelet_gpu/src/shader_str.rs | 9 - murrelet_gpu/src/uniforms.rs | 10 - murrelet_gui/examples/tests_schema.rs | 210 +++++++-------- murrelet_gui/src/lib.rs | 8 - murrelet_gui_derive/src/parser.rs | 10 +- murrelet_livecode/src/expr.rs | 19 -- murrelet_livecode/src/lazy.rs | 51 +--- murrelet_livecode/src/livecode.rs | 9 - murrelet_livecode/src/state.rs | 80 +----- murrelet_livecode/src/types.rs | 222 +--------------- murrelet_livecode/src/unitcells.rs | 39 --- .../src/derive_cached.rs | 16 +- .../src/derive_lazy.rs | 35 --- .../src/derive_livecode.rs | 43 +-- .../src/derive_nestedit.rs | 5 - .../murrelet_livecode_derive/src/lib.rs | 4 - murrelet_perform/Cargo.toml | 7 - murrelet_perform/src/perform.rs | 18 -- murrelet_perform/src/reload.rs | 5 - murrelet_schema_derive/src/parser.rs | 9 - murrelet_src_osc/src/osc.rs | 4 - murrelet_svg/Cargo.toml | 1 - murrelet_svg/src/svg.rs | 129 --------- 49 files changed, 195 insertions(+), 2044 deletions(-) diff --git a/murrelet_common/Cargo.toml b/murrelet_common/Cargo.toml index bf16313..7930c07 100644 --- a/murrelet_common/Cargo.toml +++ b/murrelet_common/Cargo.toml @@ -14,7 +14,6 @@ palette = "0.7.6" rand = "0.8" num-traits = "0.2.19" lerpable = "0.0.2" -# murrelet_gui = { version = "0.1.2", features = ["glam"] } serde = { version = "1.0.104", features = ["derive"] } bytemuck = { version = "1.15", features = ["derive"] } diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index cfddcd5..c7a215b 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -5,7 +5,6 @@ use std::{ use glam::{vec3, Vec3}; use lerpable::{IsLerpingMethod, Lerpable}; -// use murrelet_gui::CanMakeGUI; use palette::{rgb::Rgb, FromColor, Hsva, IntoColor, LinSrgb, LinSrgba, Srgb, Srgba, WithAlpha}; use serde::{Deserialize, Serialize}; @@ -111,10 +110,6 @@ impl MurreletColor { // getting info out of it pub fn into_hsva_components(&self) -> [f32; 4] { - // let srgba: Srgba = self.0.into_format().into_color(); - // println!("sRGBA before HSV conversion: {:?}", srgba); - - // let hsva: Hsva = Hsva::from_color(srgba); self.0 } @@ -174,12 +169,6 @@ impl MurreletColor { } } -// impl CanMakeGUI for MurreletColor { -// fn make_gui() -> murrelet_gui::MurreletGUISchema { -// murrelet_gui::MurreletGUISchema::Val(murrelet_gui::ValueGUI::Color) -// } -// } - pub trait MurreletIntoLinSrgba { fn into_murrelet_color(&self) -> MurreletColor; } diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 0bf0659..4d5f105 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -29,12 +29,6 @@ impl AnglePi { AnglePi(v) } - pub fn from_three_vec2(v1: Vec2, v2: Vec2, v3: Vec2) -> AnglePi { - let a = v1 - v2; - let b = v3 - v2; - Angle::new(a.angle_to(b)).into() - } - pub fn to_transform(&self) -> SimpleTransform2d { SimpleTransform2d::rotate_pi(self.angle_pi()) } @@ -72,10 +66,8 @@ impl AnglePi { self.0 < 0.0 } - pub fn transform_vec2(&self, v: Vec2) -> Vec2 { - SimpleTransform2d::rotate_pi(self.angle_pi()) - .to_mat3() - .transform_vector2(v) + pub fn rotate_vec2(&self, v: Vec2) -> Vec2 { + self.to_transform().transform_vec2(v) } } @@ -175,7 +167,7 @@ impl From for Angle { } } -// newtype +// newtype. #[derive(Copy, Clone, PartialEq, PartialOrd)] pub struct Angle(f32); impl Angle { @@ -227,7 +219,7 @@ impl Angle { } pub fn rotate_vec2(&self, v: Vec2) -> Vec2 { - Mat3::from_angle(self.angle()).transform_vec2(v) + self.as_angle_pi().rotate_vec2(v) } pub fn is_vertical(&self) -> bool { @@ -682,7 +674,6 @@ impl PointToPoint { } pub fn midpoint(&self) -> Vec2 { - // 0.5 * (self.start + self.end) self.pct(0.5) } diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index b53fd48..49c698b 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -35,9 +35,7 @@ impl IdxInRange { where >::Error: core::fmt::Debug, { - let total = total.try_into().expect("can't convert to u64"); - assert!(total > 0, "IdxInRange::new_last requires total > 0"); - IdxInRange { i: total - 1, total } + IdxInRange::new(0, total).last_i() } pub fn enumerate<'a, T, I>(iter: I) -> Vec<(IdxInRange, T)> @@ -50,10 +48,24 @@ impl IdxInRange { .collect_vec() } - pub fn enumerate_count(total: usize) -> Vec { + pub fn enumerate_count>(total: U) -> Vec + where + >::Error: core::fmt::Debug, + { + let total = total.try_into().expect("can't convert to u64"); (0..total).map(|i| IdxInRange::new(i, total)).collect_vec() } + pub fn idx_rep(&self) -> IdxMatch { + if self.is_first() { + IdxMatch::First + } else if self.is_last() { + IdxMatch::Last + } else { + IdxMatch::Idx(self.i()) + } + } + pub fn matches(&self, m: &IdxMatch) -> bool { match m { IdxMatch::First => self.i() == 0, @@ -141,10 +153,10 @@ impl IdxInRange { self.total - self.i - 1 } - // scale pub fn s(&self, range: Vec2) -> f32 { - lerp(range.x, range.y, self.pct()) + self.scale(range.x, range.y) } + pub fn scale(&self, start: f32, end: f32) -> f32 { lerp(start, end, self.pct()) } @@ -204,7 +216,12 @@ impl IdxInRange2d { } } - pub fn enumerate_counts(ii: usize, jj: usize) -> Vec { + pub fn enumerate_counts + Copy>(ii: U, jj: U) -> Vec + where + >::Error: core::fmt::Debug, + { + let ii = ii.try_into().expect("can't convert to u64"); + let jj = jj.try_into().expect("can't convert to u64"); let mut v = vec![]; for i in 0..ii { for j in 0..jj { @@ -292,10 +309,6 @@ impl IdxInRange2d { vec2(self.i.half_step_pct(), self.j.half_step_pct()) } - // pub fn lerp_idx(&self, x: f32, y: f32) -> [(usize, usize); 4] { - // self.lerp_idx_u(x as usize, y as usize) - // } - pub fn lerp_idx(&self) -> [(usize, usize); 4] { let x_idx = self.i.i() as usize; let y_idx = self.j.i() as usize; @@ -340,11 +353,6 @@ impl IdxInRange2d { vec2(x, y) } - #[deprecated] - pub fn width(&self) -> f32 { - self.i_total() - } - pub fn i_total(&self) -> f32 { self.i.total as f32 } diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index f4d48f6..87188fa 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -124,8 +124,6 @@ pub fn epoch_time_ms() -> u128 { pub fn epoch_time_us() -> u128 { #[cfg(target_arch = "wasm32")] { - //use wasm_bindgen::JsCast; - #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_namespace = Date)] @@ -143,7 +141,7 @@ pub fn epoch_time_us() -> u128 { .duration_since(UNIX_EPOCH) .expect("wat") .as_micros(); - // (s * 1_000_000.0) as u128 + s } } @@ -558,7 +556,7 @@ impl CustomVars { } pub fn update(&mut self, new: &Self) { - // basically update add, you can never delete >:D + // basically update adds, and you can never delete >:D if let Some(o) = &new.0 { self.0 .get_or_insert_with(Default::default) @@ -1020,6 +1018,8 @@ pub fn lerpify_vec_vec3( lerped.into_iter().map(|v| v.into()).collect_vec() } + +// todo, did i end up using this? #[derive(Clone, Copy, Debug)] pub struct Dim2d { x: usize, // e.g. rows diff --git a/murrelet_common/src/metric.rs b/murrelet_common/src/metric.rs index d7d2b51..6a4aecd 100644 --- a/murrelet_common/src/metric.rs +++ b/murrelet_common/src/metric.rs @@ -111,10 +111,6 @@ impl BoundMetricUsize { self.sum += x; } - // pub fn center(&self) -> f32 { - // 0.5 * (self.right + self.left) - // } - pub fn size(&self) -> usize { self.max - self.min } @@ -134,10 +130,6 @@ impl BoundMetricUsize { pub fn count(&self) -> usize { self.count } - - // pub fn avg(&self) -> f32 { - // self.sum / self.count as f32 - // } } impl Default for BoundMetricUsize { diff --git a/murrelet_common/src/transform.rs b/murrelet_common/src/transform.rs index 227738f..7aea222 100644 --- a/murrelet_common/src/transform.rs +++ b/murrelet_common/src/transform.rs @@ -1,6 +1,3 @@ -//! Since I need to do so much transforms of vec2, this -//! trait just makes it easier to use different types to -//! do that. use glam::{vec2, vec3, Mat2, Mat3, Mat4, Vec2, Vec3}; use itertools::Itertools; use lerpable::Lerpable; @@ -179,14 +176,6 @@ impl Transformable for Vec2 { } } -// impl Transformable for Vec { -// fn transform_with(&self, t: &T) -> Self { -// self.into_iter() -// .map(|x| t.to_simple_transform().transform_vec2(*x)) -// .collect_vec() -// } -// } - impl Transformable for Vec where A: Transformable, @@ -323,16 +312,6 @@ where } } -// impl TransformVec2 for SimpleTransform2d { -// fn transform_vec2(&self, v: Vec2) -> Vec2 { -// let mut v = v; -// for step in &self.0 { -// v = step.transform().transform_vec2(v); -// } -// v -// } -// } - impl Lerpable for SimpleTransform2d { fn lerpify(&self, other: &Self, pct: &T) -> Self { Self::new(self.0.lerpify(&other.0, pct)) diff --git a/murrelet_common/src/triangulate.rs b/murrelet_common/src/triangulate.rs index 1bdce47..dc49aca 100644 --- a/murrelet_common/src/triangulate.rs +++ b/murrelet_common/src/triangulate.rs @@ -1,6 +1,3 @@ -// use glam::{vec2, Vec2, Vec3}; -// use lerpable::Lerpable; - use bytemuck::{Pod, Zeroable}; use glam::{vec2, Vec2, Vec3}; use lerpable::Lerpable; @@ -29,29 +26,6 @@ impl DefaultVertex { glam::vec3(self.position[0], self.position[1], self.position[2]) } - // pub fn from_simple(vs: &VertexSimple) -> Self { - // Self { - // position: vs.position, - // normal: vs.normal, - // face_pos: vs.face_pos, - // } - // } - - // pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { - // Self { - // position, - // normal, - // face_pos, - // } - // } - // pub fn pos(&self) -> [f32; 3] { - // self.position - // } - - // pub fn pos_vec3(&self) -> Vec3 { - // glam::vec3(self.position[0], self.position[1], self.position[2]) - // } - pub fn pos2d(&self) -> Vec2 { vec2(self.position[0], self.position[1]) } @@ -88,69 +62,6 @@ impl Lerpable for DefaultVertex { } } -// unsafe impl Zeroable for DefaultVertex {} -// unsafe impl Pod for DefaultVertex {} - -// impl Lerpable for VertexSimple { -// fn lerpify(&self, other: &Self, pct: &T) -> Self { -// VertexSimple { -// position: [ -// self.position[0].lerpify(&other.position[0], pct), -// self.position[1].lerpify(&other.position[1], pct), -// self.position[2].lerpify(&other.position[2], pct), -// ], -// normal: [ -// self.normal[0].lerpify(&other.normal[0], pct), -// self.normal[1].lerpify(&other.normal[1], pct), -// self.normal[2].lerpify(&other.normal[2], pct), -// ], -// face_pos: [ -// self.face_pos[0].lerpify(&other.face_pos[0], pct), -// self.face_pos[1].lerpify(&other.face_pos[1], pct), -// ], -// } -// } -// } - -// #[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)] -// #[repr(C)] -// pub struct VertexSimple { -// pub position: [f32; 3], -// pub normal: [f32; 3], -// pub face_pos: [f32; 2], -// } - -// impl VertexSimple { -// pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { -// Self { -// position, -// normal, -// face_pos, -// } -// } -// pub fn pos(&self) -> [f32; 3] { -// self.position -// } - -// pub fn pos_vec3(&self) -> Vec3 { -// glam::vec3(self.position[0], self.position[1], self.position[2]) -// } - -// pub fn pos2d(&self) -> Vec2 { -// vec2(self.position[0], self.position[1]) -// } - -// pub fn attrs(&self) -> Vec { -// vec![ -// self.normal[0], -// self.normal[1], -// self.normal[2], -// self.face_pos[0], -// self.face_pos[1], -// ] -// } -// } - #[derive(Debug, Clone)] pub struct Triangulate { pub vertices: Vec, @@ -180,11 +91,6 @@ impl Triangulate { &self.vertices } - // pub fn add_vertex(&mut self, v: [f32; 3], n: [f32; 3], face_pos: [f32; 2]) -> u32 { - // let vv = VertexSimple::new(v, n, face_pos); - // self.add_vertex_simple(vv) - // } - pub fn add_vertex_simple(&mut self, vv: Vertex) -> u32 { self.vertices.push(vv); (self.vertices.len() - 1) as u32 @@ -194,24 +100,6 @@ impl Triangulate { self.order.extend(tri) } - // alternatively can add vertices and then add teh vec - // pub fn add_rect(&mut self, v: &[Vec3; 4], flip: bool) { - // let edge1 = v[0] - v[1]; - // let edge2 = v[3] - v[1]; - // let normal = edge1.cross(edge2).normalize().to_array(); - - // let v0 = self.add_vertex(v[0].to_array(), normal, [1.0, 0.0]); - // let v1 = self.add_vertex(v[1].to_array(), normal, [0.0, 0.0]); - // let v2 = self.add_vertex(v[2].to_array(), normal, [1.0, 1.0]); - // let v3 = self.add_vertex(v[3].to_array(), normal, [0.0, 1.0]); - - // if !flip { - // self.order.extend([v0, v2, v1, v1, v2, v3]) - // } else { - // self.order.extend([v0, v1, v2, v1, v3, v2]) - // } - // } - pub fn set_order(&mut self, u: Vec) { self.order = u; } diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index 9b91914..54672d9 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -14,7 +14,7 @@ schemars = [ "murrelet_livecode_macros/schemars", "murrelet_livecode_derive/schemars", ] -# murrelet_gui = ["dep:murrelet_gui", "murrelet_livecode/murrelet_gui", "murrelet_livecode_macros/murrelet_gui", "murrelet_livecode_derive/murrelet_gui"] + [dependencies] murrelet_common = "0.1.2" @@ -44,4 +44,4 @@ kurbo = "0.11" svg = "0.10.0" lyon = "0.17" delaunator = "1.0.2" -geo = "0.29.0" \ No newline at end of file +geo = "0.29.0" diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index 45aa44f..1bb8ad2 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -17,7 +17,6 @@ use crate::cubic::CubicBezier; use crate::curve_drawer::ToCurveSegment; use crate::curve_drawer::{CurveArc, CurveDrawer, CurvePoints, CurveSegment}; - #[derive(Debug, Clone, Copy, Livecode, MurreletGUI, Lerpable)] pub struct CurveStart { #[murrelet_gui(func = "make_gui_vec2")] @@ -166,10 +165,6 @@ impl CompassAction { pub fn line(length: f32, label: String) -> CompassAction { CompassAction::Line(CompassLine { length, label }) } - - // pub fn repeat(times: usize, what: Vec) -> CompassAction { - // CompassAction::Repeat(CompassRepeat { times, what }) - // } } impl Default for CompassAction { fn default() -> Self { @@ -347,37 +342,6 @@ impl InteractiveCompassBuilder { } fn add_arc(&mut self, x: &CompassArc) -> CurveSegment { - // let angle_pi = x.arc_length.as_angle_pi(); - // let (arc_length, radius) = if angle_pi.is_neg() { - // (-angle_pi, -x.radius) - // } else { - // (angle_pi, x.radius) - // }; - - // // starting at our current location, move at a right angle to our current angle - // // negative goes to the left of the line - // let loc = self.curr_loc + radius * self.curr_angle.perp_to_left().to_norm_dir(); - - // // if radius is negative, go backwards - // // end_angle is what we'll update curr angle to, it's always assuming positive radius - // let (start, end, next_angle) = if radius < 0.0 { - // let next_angle = self.curr_angle - arc_length; - // ( - // AnglePi::new(1.0) + self.curr_angle - AnglePi::new(0.5), - // AnglePi::new(1.0) + next_angle - AnglePi::new(0.5), - // next_angle, - // ) - // } else { - // let next_angle = self.curr_angle + arc_length; - // ( - // self.curr_angle - AnglePi::new(0.5), - // next_angle - AnglePi::new(0.5), - // next_angle, - // ) - // }; - - // let a = CurveArc::new(loc, radius.abs(), start, end); - let arc = CurveArc::from_spot( SpotOnCurve::new(self.curr_loc, self.curr_angle), x.radius, diff --git a/murrelet_draw/src/cubic.rs b/murrelet_draw/src/cubic.rs index 0ee6d79..8764d77 100644 --- a/murrelet_draw/src/cubic.rs +++ b/murrelet_draw/src/cubic.rs @@ -96,40 +96,30 @@ impl CubicBezier { } pub fn start_to_tangent(&self) -> (SpotOnCurve, f32) { - // let side_len = self.from.distance(self.to); - let ctrl_line = self.from - self.ctrl1; let dir = Angle::new(ctrl_line.to_angle()) .as_angle_pi() .normalize_angle(); - // let strength = ctrl_line.length() / side_len; - ( SpotOnCurve { loc: self.from, angle: dir.into(), - // strength, }, ctrl_line.length(), ) } pub fn end_to_tangent(&self) -> (SpotOnCurve, f32) { - // let side_len = self.from.distance(self.to); - let ctrl_line = self.ctrl2 - self.to; let dir = Angle::new(ctrl_line.to_angle()) .as_angle_pi() .normalize_angle(); - // let strength = ctrl_line.length() / side_len; - ( SpotOnCurve { loc: self.to, angle: dir.into(), - // strength, }, ctrl_line.length(), ) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index dae56b6..a24ffc7 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -44,10 +44,7 @@ impl CubicOptionVec2 { #[derive(Debug, Clone, Default, Livecode, Lerpable, Serialize, Deserialize)] pub struct CubicBezierTo { pub ctrl1: CubicOptionVec2, - - // #[serde(serialize_with = "serialize_vec2")] pub ctrl2: Vec2, - // #[serde(serialize_with = "serialize_vec2")] pub to: Vec2, } @@ -85,12 +82,6 @@ impl CubicBezierPath { let svg = self.to_data(); let path = parse_svg_data_as_vec2(&svg, line_space); - // if let Some(a) = path.last() { - // if a.distance(self.to.yx()) > 1.0e-3 { - // path.push(self.to.yx()) - // } - // } - path.into_iter().map(|x| vec2(x.y, x.x)).collect_vec() } @@ -123,7 +114,6 @@ impl CubicBezierPath { if self.closed { let ctrl1 = CubicOptionVec2::none().or_last(from, last_ctrl1); - // let ctrl2 = CubicOptionVec2::none().or_last(self.from, self.ctrl1); let ctrl2_source = first_ctrl1_used.unwrap_or(self.ctrl1); let ctrl2 = CubicOptionVec2::none().or_last(self.from, ctrl2_source); svg.push(CubicBezier::new(from, ctrl1, ctrl2, self.from)); @@ -264,16 +254,6 @@ impl CurveDrawer { Some(last_command.last_point()) } - // pub fn first_spot(&self) -> Option { - // let first_command = self.segments().first()?; - // Some(first_command.first_spot()) - // } - - // pub fn last_spot(&self) -> Option { - // let last_command = self.segments().last()?; - // Some(last_command.last_spot()) - // } - pub fn length(&self) -> f32 { self.segments.iter().map(|segment| segment.length()).sum() } @@ -347,10 +327,7 @@ impl CurveSegment { match self { CurveSegment::Arc(curve_arc) => CurveSegment::Arc(curve_arc.reverse()), CurveSegment::Points(curve_points) => CurveSegment::Points(curve_points.reverse()), - CurveSegment::CubicBezier(c) => { - CurveSegment::CubicBezier(c.reverse()) - //CurveSegment::Points(c.as_points().reverse()) - } + CurveSegment::CubicBezier(c) => CurveSegment::CubicBezier(c.reverse()), } } @@ -429,7 +406,7 @@ impl CurveSegment { let points = p.points(); let end = *points.last().unwrap(); let prev = *points.get(points.len() - 2).unwrap(); - let angle = PointToPoint::new(prev, end).angle(); //.perp_to_left(); + let angle = PointToPoint::new(prev, end).angle(); Some(SpotOnCurve::new(end, angle)) } else { None @@ -455,7 +432,9 @@ impl CurveSegment { fn force_transform(&self, transform: &T) -> Self { let transform = transform.to_simple_transform(); match self { - CurveSegment::Arc(curve_arc) => CurveSegment::Arc(curve_arc.force_transform(&transform)), + CurveSegment::Arc(curve_arc) => { + CurveSegment::Arc(curve_arc.force_transform(&transform)) + } CurveSegment::Points(curve_points) => { CurveSegment::Points(curve_points.force_transform(&transform)) } @@ -555,14 +534,12 @@ impl CurveArc { ( AnglePi::new(1.0) + start_spot.angle - AnglePi::new(0.5), AnglePi::new(1.0) + next_angle - AnglePi::new(0.5), - // next_angle.into(), ) } else { let next_angle = start_spot.angle + arc_length; ( (start_spot.angle - AnglePi::new(0.5)).into(), (next_angle - AnglePi::new(0.5)).into(), - // next_angle.into(), ) }; @@ -601,16 +578,13 @@ impl CurveArc { // useful for svg pub fn is_large_arc(&self) -> bool { - // (self.end_pi.angle_pi() - self.start_pi.angle_pi()).abs() > 1.0 - // (self.end_pi.angle_pi() - self.start_pi.angle_pi()).rem_euclid(2.0) > 1.0 - let ccw_angular_distance = (self.end_pi.angle_pi() - self.start_pi.angle_pi()).rem_euclid(2.0); if self.is_ccw() { ccw_angular_distance > 1.0 } else { - // for CW, the distance is the other way around the circle + // for cw, the distance is the other way around the circle (2.0 - ccw_angular_distance) > 1.0 } } @@ -638,7 +612,6 @@ impl CurveArc { fn end_angle(&self) -> Angle { self.end_pi.as_angle() - // AnglePi::new(self.end_pi).into() } pub fn last_spot(&self) -> SpotOnCurve { @@ -647,7 +620,6 @@ impl CurveArc { fn start_angle(&self) -> Angle { self.start_pi.as_angle() - // AnglePi::new(self.start_pi).into() } pub fn start_tangent_angle(&self) -> Angle { @@ -710,9 +682,6 @@ impl CurveArc { pub fn length(&self) -> f32 { (self.radius * (self.end_pi - self.start_pi).angle()).abs() - // let angle_diff = (self.end_pi.angle_pi() - self.start_pi.angle_pi()).rem_euclid(2.0); - // let sweep_angle_rads = angle_diff * std::f32::consts::PI; - // self.radius * sweep_angle_rads } pub fn start_pi(&self) -> AnglePi { @@ -739,8 +708,6 @@ impl CurveArc { (first_arc.to_segment(), second_arc.to_segment()) } - - } #[derive(Debug, Clone, Livecode, Lerpable)] @@ -816,8 +783,6 @@ impl CurveCubicBezier { ) } - - fn split_segment(&self, target_dist: f32) -> (CurveSegment, CurveSegment) { let v = self.to_cubic().to_vec2_line_space(1.0); // todo, figure out how to manage that CurvePoints::new(v).split_segment(target_dist) @@ -1130,7 +1095,6 @@ pub trait ToCurveDrawer { let mut dist_traveled = 0.0; let mut add_left = true; - // let mut last_pt = None; for segment in cd.segments() { if add_left { let segment_len = segment.length(); @@ -1156,7 +1120,6 @@ pub trait ToCurveDrawer { } else { right.push(segment.clone()) } - // last_pt = Some(segment.last_point()); } (left, right) diff --git a/murrelet_draw/src/drawable.rs b/murrelet_draw/src/drawable.rs index fca1aeb..f14c2bf 100644 --- a/murrelet_draw/src/drawable.rs +++ b/murrelet_draw/src/drawable.rs @@ -32,10 +32,6 @@ impl DrawnShape { } } - // pub fn faces(&self) -> Vec { - // self.cds.iter().map(|x| x.to_vec2()).collect_vec() - // } - pub fn style(&self) -> StyleConf { self.style.clone() } @@ -55,18 +51,6 @@ impl DrawnShape { } Ok(DrawnShape::new_cds(&new, self.style.clone())) } - - // pub fn transform_as_vec2(&self, transform: &Transform2d) -> Self { - // let face = self - // .faces() - // .iter() - // .map(|x| { - // let t = transform.transform_many_vec2(x); - // t.clone_to_vec() - // }) - // .collect_vec(); - // DrawnShape::new_vecvec(face, self.style()) - // } } pub trait ToDrawnShapeSegments { @@ -95,24 +79,6 @@ where } } -// impl ToDrawnShapeSegments for Vec> { -// fn to_drawn_shape_closed(&self, style: StyleConf) -> DrawnShape { -// let cds = self -// .into_iter() -// .map(|x| CurveDrawer::new_simple_points(x, true)) -// .collect_vec(); -// DrawnShape::new_cds(&cds, style) -// } - -// fn to_drawn_shape_open(&self, style: StyleConf) -> DrawnShape { -// let cds = self -// .into_iter() -// .map(|x| CurveDrawer::new_simple_points(x, false)) -// .collect_vec(); -// DrawnShape::new_cds(&cds, style) -// } -// } - pub trait ToDrawnShape { fn to_drawn_shape(&self, style: StyleConf) -> DrawnShape; diff --git a/murrelet_draw/src/newtypes.rs b/murrelet_draw/src/newtypes.rs index 31c920c..ae94dae 100644 --- a/murrelet_draw/src/newtypes.rs +++ b/murrelet_draw/src/newtypes.rs @@ -1,7 +1,6 @@ // these could probably be somewhere else, but they are used by this crate // and just give more livecode defaults to work with instead of always // needing to create a new type. -// use crate::serialize::serialize_vec2; use glam::{Vec2, Vec3}; use lerpable::Lerpable; use murrelet_common::*; diff --git a/murrelet_draw/src/scaffold.rs b/murrelet_draw/src/scaffold.rs index 8c49e9a..f35e6c5 100644 --- a/murrelet_draw/src/scaffold.rs +++ b/murrelet_draw/src/scaffold.rs @@ -1,241 +1,12 @@ #![allow(dead_code)] +use crate::{ + curve_drawer::{CurveDrawer, ToCurveDrawer}, + drawable::DrawnShape, +}; use geo::{BooleanOps, BoundingRect, Contains}; use glam::{vec2, Vec2}; use itertools::Itertools; use murrelet_common::SpotOnCurve; -// use leave_common::prelude::*; -use crate::{curve_drawer::{CurveDrawer, ToCurveDrawer}, drawable::DrawnShape}; - -// pub fn line_to_multipolygon(curves: &[Vec2]) -> geo::MultiPolygon { -// geo::MultiPolygon::new(vec![line_to_polygon(curves)]) -// } - -// pub fn line_to_polygon(curves: &[Vec2]) -> geo::Polygon { -// geo::Polygon::new(vec2_to_line_string(curves), vec![]) -// } - -// fn vec2_to_line_string(vs: &[Vec2]) -> geo::LineString { -// let coords = vs.iter().map(|x| x.to_coord()).collect_vec(); -// geo::LineString::new(coords) -// } - -// pub fn multipolygon_to_vec2(p: &geo::MultiPolygon) -> Vec> { -// p.iter().map(|pp| polygon_to_vec2(pp)).collect_vec() -// } - -// pub fn polygon_to_vec2(p: &geo::Polygon) -> Vec { -// let mut coords = p -// .exterior() -// .coords() -// .into_iter() -// .map(|coord| coord.to_vec2()) -// .collect_vec(); - -// if coords.first() == coords.last() { -// coords.pop(); -// } - -// coords -// } - -// trait ToCoord { -// fn to_coord(&self) -> ::geo::Coord; -// } - -// impl ToCoord for Vec2 { -// fn to_coord(&self) -> ::geo::Coord { -// ::geo::coord! {x: self.x as f64, y: self.y as f64} -// } -// } - -// trait ToVec2Griddable { -// fn to_vec2(&self) -> Vec2; -// } - -// impl ToVec2Griddable for ::geo::Coord { -// fn to_vec2(&self) -> Vec2 { -// let (x, y) = self.x_y(); -// vec2(x as f32, y as f32) -// } -// } - -// #[derive(Debug, Clone)] -// pub struct MaskCacheImpl { -// bounding: geo::Rect, -// polygon: geo::Polygon, -// } -// impl MaskCacheImpl { -// fn center(&self) -> Vec2 { -// 0.5 * (self.bounding.min().to_vec2() + self.bounding.max().to_vec2()) -// } - -// fn contains(&self, v: &Vec2) -> bool { -// self.polygon.contains(&v.to_coord()) -// } - -// // left needs to be inside -// fn last_point_containing(&self, left: &Vec2, right: &Vec2) -> Vec2 { -// let mut left = *left; -// let mut right = *right; -// while left.distance(right) > 0.2 { -// let midpoint = 0.5 * (left + right); -// if self.contains(&midpoint) { -// left = midpoint; -// } else { -// right = midpoint; -// } -// } -// // finished! -// 0.5 * (left + right) -// } -// } - -// #[derive(Debug, Clone)] -// pub enum MaskCache { -// Impl(MaskCacheImpl), -// AlwaysTrue, -// } - -// impl MaskCache { -// fn center(&self) -> Vec2 { -// match self { -// MaskCache::Impl(s) => s.center(), -// MaskCache::AlwaysTrue => Vec2::ZERO, -// } -// } - -// pub fn last_point_containing(&self, left: &Vec2, right: &Vec2) -> Vec2 { -// match self { -// MaskCache::Impl(s) => s.last_point_containing(left, right), -// MaskCache::AlwaysTrue => *right, -// } -// } - -// pub fn new_vec2(curves: &[Vec2]) -> Self { -// let polygon = geo::Polygon::new(vec2_to_line_string(curves), vec![]); - -// MaskCache::Impl(MaskCacheImpl { -// bounding: polygon.bounding_rect().unwrap(), -// polygon, -// }) -// } - -// // pub fn new_cd(cd: CurveDrawer) -> Self { -// // Self::new(&vec![cd]) -// // } - -// // pub fn new(curves: &[CurveDrawer]) -> Self { -// // // first curve is external -// // let (first_curve, rest) = curves.split_first().unwrap(); -// // let first = curve_segment_maker_to_line_string(first_curve); - -// // let mut remaining = vec![]; -// // // add all our points to a hashmap -// // for curve_maker in rest { -// // remaining.push(curve_segment_maker_to_line_string(curve_maker)); -// // } - -// // let polygon = ::geo::Polygon::new(first, remaining); - -// // MaskCache::Impl(MaskCacheImpl { -// // bounding: polygon.bounding_rect().unwrap(), -// // polygon, -// // }) -// // } - -// pub fn contains(&self, v: &Vec2) -> bool { -// match self { -// MaskCache::Impl(x) => x.contains(v), -// MaskCache::AlwaysTrue => true, -// } -// } - -// pub fn noop() -> MaskCache { -// MaskCache::AlwaysTrue -// } - -// pub fn crop(&self, shape: &[Vec2]) -> Vec> { -// match self { -// MaskCache::Impl(x) => { -// let other = line_to_polygon(shape); -// let cropped = x.polygon.intersection(&other); -// multipolygon_to_vec2(&cropped) -// } -// MaskCache::AlwaysTrue => vec![shape.to_vec()], -// } -// } - -// // remove this object from all of the shapes -// pub fn crop_inverse(&self, shape: &[Vec2]) -> Vec> { -// match self { -// MaskCache::Impl(x) => { -// let other = line_to_polygon(shape); -// let cropped = other.difference(&x.polygon); -// multipolygon_to_vec2(&cropped) -// } -// MaskCache::AlwaysTrue => vec![shape.to_vec()], -// } -// } - -// pub fn to_vec2(&self) -> Vec { -// match self { -// MaskCache::Impl(mask_cache_impl) => polygon_to_vec2(&mask_cache_impl.polygon), -// MaskCache::AlwaysTrue => unreachable!(), -// } -// } - -// pub fn crop_line(&self, v: &[Vec2]) -> Vec> { -// let mut all_vals = vec![]; -// let mut s = vec![]; -// let mut last_val = None; -// for c in v.into_iter() { -// if self.contains(&c) { -// last_val = Some(c); -// s.push(*c) -// } else if let Some(x) = last_val { -// let last_point_containing = self.last_point_containing(&x, &c); -// s.push(last_point_containing); - -// all_vals.push(s); -// last_val = None; -// s = vec![]; -// } -// } -// if s.len() > 0 { -// all_vals.push(s); -// } - -// all_vals -// } - -// // pub fn crop_many(&self, v: &[DrawnShape]) -> Vec { -// // let mut cropped = vec![]; -// // for a in v.into_iter() { -// // let mut new_cds = vec![]; -// // for cd in a.faces() { -// // new_cds.extend(self.crop(cd.vertices())); -// // } -// // cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); -// // } -// // return cropped; -// // } - -// // pub fn crop_inverse_many(&self, v: &[DrawnShape]) -> Vec { -// // let mut cropped = vec![]; -// // for a in v.into_iter() { -// // let mut new_cds = vec![]; -// // for cd in a.faces() { -// // new_cds.extend(self.crop_inverse(cd.vertices())); -// // } -// // cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); -// // } -// // return cropped; -// // } -// } - - - -// use crate::curve::{ToFaces, WithLeaveCurveDrawerMethods}; pub fn line_to_multipolygon(curves: &[Vec2]) -> geo::MultiPolygon { geo::MultiPolygon::new(vec![line_to_polygon(curves)]) @@ -363,7 +134,6 @@ impl MaskCache { // shh, just a wrapper let s = vec![vec![outline], interior.to_vec()].concat(); Self::new(&s) - } pub fn new(curves: &[CurveDrawer]) -> Self { @@ -464,13 +234,5 @@ impl MaskCache { } MaskCache::AlwaysTrue => todo!(), } - - // match self { - // MaskCache::Impl(mask_cache_impl) => { - // mask_cache_impl. - // - // }, - // MaskCache::AlwaysTrue => todo!(), - // } } } diff --git a/murrelet_draw/src/sequencers.rs b/murrelet_draw/src/sequencers.rs index ad151ae..bd08074 100644 --- a/murrelet_draw/src/sequencers.rs +++ b/murrelet_draw/src/sequencers.rs @@ -3,7 +3,6 @@ use itertools::Itertools; use lerpable::Lerpable; use murrelet_common::*; use murrelet_livecode::unitcells::{UnitCellContext, UnitCellCreator, UnitCellIdx}; -// use murrelet_livecode_derive::*; use crate::transform2d::{Transform2d, Transform2dStep}; use murrelet_livecode_derive::Livecode; diff --git a/murrelet_draw/src/serialize.rs b/murrelet_draw/src/serialize.rs index 8c280ff..98b4ea0 100644 --- a/murrelet_draw/src/serialize.rs +++ b/murrelet_draw/src/serialize.rs @@ -1,4 +1,3 @@ -// use glam::Vec2; use murrelet_common::MurreletColor; use serde::{ser::SerializeSeq, Serializer}; @@ -15,18 +14,4 @@ where seq.serialize_element(number)?; } seq.end() -} - -// just use glam's serde feature flag! -// pub fn serialize_vec2(x: &Vec2, serializer: S) -> Result -// where -// S: Serializer, -// { -// let xy = [x.x, x.y]; - -// let mut seq = serializer.serialize_seq(Some(xy.len()))?; -// for number in &xy { -// seq.serialize_element(number)?; -// } -// seq.end() -// } \ No newline at end of file +} \ No newline at end of file diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index a6eecdb..934adfb 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -11,10 +11,7 @@ use lerpable::Lerpable; use md5::{Digest, Md5}; use murrelet_common::*; use murrelet_gui::{CanMakeGUI, MurreletGUI}; -use murrelet_livecode::{ - lazy::ControlLazyMurreletColor, - livecode::ControlF32, -}; +use murrelet_livecode::{lazy::ControlLazyMurreletColor, livecode::ControlF32}; use murrelet_livecode_derive::Livecode; use styleconf::StyleConf; @@ -395,7 +392,6 @@ pub mod styleconf { pub fn to_style(&self) -> MurreletStyle { match self { - // StyleConf::Verbose(a) => *a, StyleConf::Fill(a) => a.to_style(), StyleConf::Outline(a) => a.to_style(), StyleConf::Line(a) => a.to_style(), @@ -514,20 +510,6 @@ impl MurreletCurve { } } - // pub fn transform_with_mat4_after(&self, t: Mat4) -> MurreletCurve { - // Self { - // cd: self.cd.clone(), - // t: t * self.t, - // } - // } - - // pub fn transform_with_mat4_before(&self, t: Mat4) -> MurreletCurve { - // Self { - // cd: self.cd.clone(), - // t: self.t * t, - // } - // } - pub fn mat4(&self) -> Mat4 { self.t.to_mat4() } diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 794e38d..780db95 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -81,12 +81,6 @@ impl ToVecVec2 for CubicBezierPath { let svg = self.to_data(); let path = parse_svg_data_as_vec2(&svg, 1.0); - // if let Some(a) = path.last() { - // if a.distance(self.to.yx()) > 1.0e-3 { - // path.push(self.to.yx()) - // } - // } - path.into_iter().map(|x| vec2(x.y, x.x)).collect_vec() } } @@ -261,14 +255,9 @@ fn add_circular_arc(builder: &mut B, c: &CurveArc) { x_rotation: Angle::radians(0.0), }; - // Make sure the current point is at the arc's start. - // builder.line_to(arc.from()); // handled already - arc.for_each_cubic_bezier(&mut |c| { builder.cubic_bezier_to(c.ctrl1, c.ctrl2, c.to); }); - - // builder.end(false); } impl ToLyonPath for CurveArc { @@ -435,7 +424,6 @@ pub fn segment_arc( let estimated_size = (diameter / line_space) as usize; let mut vs = Vec::with_capacity(estimated_size); - //vec![]; let mut residual = offset + curve.length(); @@ -458,10 +446,7 @@ pub fn segment_arc( curr_angle.perp_to_left() }; - // println!("central_angle_from_start {:?}", central_angle_from_start); - // println!("curr_angle {:?}", curr_angle); let s = SpotOnCurve::new(curr_point, a); - // println!("s {:?}", s); vs.push(s); } @@ -488,7 +473,6 @@ pub fn evenly_split_cubic_bezier(c: &CubicBezier, count: usize) -> Vec (Vec, Vec let path = path_builder.build(); let opts = FillOptions::default() - // .with_tolerance(0.1) // we're passing in line segments .with_fill_rule(FillRule::EvenOdd) .with_intersections(true); @@ -740,9 +722,6 @@ fn parse_data(data: &svg::node::element::path::Data, line_space: f32) -> Vec Vec CurveDrawer { -// let mut curve_segments: Vec = vec![]; - -// let mut from = Pt::new(0.0, 0.0); -// let mut close = false; - -// // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths -// for command in data.iter() { -// // println!("{:?}", command); - -// let _ly = match command { -// svg::node::element::path::Command::Move(_pos, params) => { -// let curve: Vec<&f32> = params.iter().collect(); -// from = point_from_param1(&curve); - -// curve_segments.push(CurveSegment::new_simple_point(from.as_vec2())); -// } -// svg::node::element::path::Command::Line(pos, params) => { -// for raw_curve in ¶ms.iter().chunks(2) { -// let curve: Vec<&f32> = raw_curve.collect(); - -// let to = point_for_position(pos, Pt::new(*curve[0], *curve[1]), from); - -// // let line = lyon::geom::LineSegment { -// // from: from.into(), -// // to: to.into(), -// // }; - -// // let length = line.length(); - -// // segment_state.add_segment(line, length); -// curve_segments.push(CurveSegment::new_simple_point(to.as_vec2())); - -// from = to; -// } -// } -// svg::node::element::path::Command::HorizontalLine(_, _) => todo!(), -// svg::node::element::path::Command::VerticalLine( -// svg::node::element::path::Position::Relative, -// params, -// ) => { -// for next_point in params.iter() { -// let to = Pt::new(from.x(), next_point + from.y()); - -// // let line = lyon::geom::LineSegment { -// // from: from.into(), -// // to: to.into(), -// // }; - -// // let length = line.length(); - -// // segment_state.add_segment(line, length); - -// curve_segments.push(CurveSegment::new_simple_point(to.as_vec2())); - -// from = to; -// } -// } -// svg::node::element::path::Command::CubicCurve(pos, params) => { -// for raw_curve in ¶ms.iter().chunks(6) { -// let curve: Vec<&f32> = raw_curve.collect(); -// let (raw_ctrl1, raw_ctrl2, raw_to) = point_from_param3(&curve); - -// let _ctrl1 = point_for_position(pos, raw_ctrl1, from); -// let _ctrl2 = point_for_position(pos, raw_ctrl2, from); -// let _to = point_for_position(pos, raw_to, from); - -// // let line = lyon::geom::CubicBezierSegment { -// // from: from.into(), -// // ctrl1: ctrl1.into(), -// // ctrl2: ctrl2.into(), -// // to: to.into(), -// // }; - -// // let length = line.approximate_length(0.1); - -// // segment_state.add_segment(line, length); - -// todo!("cubic!"); -// // curve_segments.push(CurveSegment::new_simple_point(to.as_vec2())); - -// // prev_ctrl = Some(raw_ctrl2); - -// //from = to; -// } -// } -// svg::node::element::path::Command::SmoothCubicCurve( -// svg::node::element::path::Position::Relative, -// params, -// ) => { -// for raw_curve in ¶ms.iter().chunks(4) { -// let curve: Vec<&f32> = raw_curve.collect(); -// let (raw_ctrl2, raw_to) = point_from_param2(&curve); - -// let ctrl2 = raw_ctrl2 + from; -// let to = raw_to + from; - -// // let ctrl1_raw = prev_ctrl.unwrap(); // better exist -// // let ctrl1 = point(from.x - ctrl1_raw.y, from.y - ctrl1_raw.x); -// let ctrl1 = Pt::new(from.x(), from.y()); // i'm.. surprised this works - -// let _line = lyon::geom::CubicBezierSegment { -// from: from.into(), -// ctrl1: ctrl1.into(), -// ctrl2: ctrl2.into(), -// to: to.into(), -// }; - -// // let length = line.approximate_length(0.1); -// // segment_state.add_segment(line, length); -// todo!("cubic!"); - -// //from = to; -// } -// } -// svg::node::element::path::Command::Close => { -// close = true; -// } -// svg::node::element::path::Command::QuadraticCurve(pos, params) => { -// for raw_curve in ¶ms.iter().chunks(4) { -// let curve: Vec<&f32> = raw_curve.collect(); -// let (raw_ctrl, raw_to) = point_from_param2(&curve); - -// let to = point_for_position(pos, raw_to, from); -// let ctrl = point_for_position(pos, raw_ctrl, from); - -// let _line = lyon::geom::QuadraticBezierSegment { -// from: from.into(), -// ctrl: ctrl.into(), -// to: to.into(), -// }; - -// todo!("quad!"); - -// // let length = line.approximate_length(0.1); - -// // segment_state.add_segment(line, length); - -// //from = to; -// } -// } -// svg::node::element::path::Command::SmoothQuadraticCurve(_, _) => todo!(), -// svg::node::element::path::Command::EllipticalArc(_, _) => todo!(), -// _ => todo!(), -// }; -// } - -// // println!("processed {:?} pts", segment_state.vertices.len()); - -// // segment_state.vertices - -// // todo, figure out closed - -// CurveDrawer::new(curve_segments, close) -// } - pub fn load_all_data(path: T, line_space: f32) -> HashMap>> where T: AsRef, { let map = load_all_data_into_map(path); - // println!("loaded into map"); - let r: HashMap>> = map .iter() .map(|(k, v)| { @@ -1199,12 +1018,10 @@ where for event in svg::open(path, &mut content).unwrap() { if let svg::parser::Event::Tag(_, _, attributes) = event { if let Some(id) = attributes.get("id") { - // println!("loading {:?}", id); recent_id = id.to_string(); } if let Some(path_data) = attributes.get("d") { - // println!("path_data {:?}", path_data); let data = svg::node::element::path::Data::parse(path_data).unwrap(); maps.entry(recent_id.to_owned()).or_default().push(data); } @@ -1220,7 +1037,6 @@ pub fn parse_svg_path_as_vec2(data: &SvgPathDef, line_space: f32) -> Vec { let mut cmds = svg::node::element::path::Data::new(); let (start_x, start_y) = data.svg_move_to(); cmds = cmds.move_to(vec![start_x, start_y]); - // (Command::Move(Position::Absolute, ); for cmd in data.cmds() { match cmd { @@ -1242,13 +1058,6 @@ pub fn parse_svg_path_as_vec2(data: &SvgPathDef, line_space: f32) -> Vec { parse_svg_data_as_vec2(&cmds, line_space) } -// fn point_to_param(pt: &Point2D) -> Vec { -// vec![pt.x, pt.y] -// } - -// fn points_to_param(pts: Vec<&Point2D>) -> Vec { -// pts.iter().map(|pt| point_to_param(*pt)).flatten().collect() -// } // todo, can i combine this with the output? pub struct LayersFromSvg { pub layers: HashMap>, diff --git a/murrelet_gen_derive/src/derive_gen.rs b/murrelet_gen_derive/src/derive_gen.rs index 74d5701..ea524ec 100644 --- a/murrelet_gen_derive/src/derive_gen.rs +++ b/murrelet_gen_derive/src/derive_gen.rs @@ -121,14 +121,6 @@ impl GenFinal for FieldTokensGen { murrelet_gen::prefix_field_names(#receiver_name.to_string(), vec![vec!["[weight]".to_string()], #names].concat()) }); - // for_rn_names.extend( - // variant - // .for_rn_names - // .clone() - // .into_iter() - // .map(|x| quote!( murrelet_gen::prefix_field_names(#x, #receiver_name))), - // ); - let weight = receiver.weight; // hrm, might want to use closures if it's expensive // also the create_variant will modify rn_start_idx for us. @@ -302,25 +294,25 @@ impl GenFinal for FieldTokensGen { } } - // skip - fn from_noop_struct(idents: StructIdents) -> FieldTokensGen { - let field_name = idents.data.ident.unwrap().to_string(); - let ty = idents.data.ty; + // // skip + // fn from_noop_struct(idents: StructIdents) -> FieldTokensGen { + // let field_name = idents.data.ident.unwrap().to_string(); + // let ty = idents.data.ty; - let for_rn_count = quote! { 0 }; - let for_rn_names = quote! { vec![] }; - let for_make_gen = quote! { #field_name: #ty::default() }; - let for_to_dist = quote! { vec![] }; - let for_to_dist_mask = quote! { vec![] }; + // let for_rn_count = quote! { 0 }; + // let for_rn_names = quote! { vec![] }; + // let for_make_gen = quote! { #field_name: #ty::default() }; + // let for_to_dist = quote! { vec![] }; + // let for_to_dist_mask = quote! { vec![] }; - FieldTokensGen { - for_rn_count, - for_make_gen, - for_rn_names, - for_to_dist, - for_to_dist_mask, - } - } + // FieldTokensGen { + // for_rn_count, + // for_make_gen, + // for_rn_names, + // for_to_dist, + // for_to_dist_mask, + // } + // } // f32, Vec2, etc fn from_type_struct(idents: StructIdents, method: &GenMethod) -> FieldTokensGen { @@ -355,10 +347,6 @@ impl GenFinal for FieldTokensGen { let i = inside_type[1].clone(); let inside_type_val: syn::Type = syn::parse_quote! { #i }; - // let mut idents_for_vec = vec![]; - // recursive_ident_from_path(&ty, &mut idents_for_vec); - // let internal_type = idents_for_vec[1].clone(); - let ( for_rn_count_per_item, for_rn_names_per_item, @@ -464,35 +452,6 @@ impl GenFinal for FieldTokensGen { for_to_dist_mask, } } - - // fn from_override_struct( - // idents: StructIdents, - // func: &str, - // rn_names: Vec, - // rn_count: usize, - // ) -> FieldTokensGen { - // let field_name = idents.data.ident.unwrap(); - - // let for_rn_count = quote! { #rn_count }; - - // let strs = strs_to_tokens(rn_names); - // let for_rn_names = quote! { - // #(#strs,)* - // }; - - // let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); - - // let for_make_gen = quote! { #field_name: #method() }; - - // let for_to_dist = quote! { #method_inv() } - - // FieldTokensGen { - // for_rn_count, - // for_rn_names, - // for_make_gen, - // for_to_dist - // } - // } } fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { diff --git a/murrelet_gen_derive/src/parser.rs b/murrelet_gen_derive/src/parser.rs index afdeb1d..904e305 100644 --- a/murrelet_gen_derive/src/parser.rs +++ b/murrelet_gen_derive/src/parser.rs @@ -1,6 +1,5 @@ -use darling::{ast, FromDeriveInput, FromField, FromMeta, FromVariant}; +use darling::{ast, FromDeriveInput, FromField, FromVariant}; use proc_macro2::TokenStream as TokenStream2; -use syn::LitStr; use crate::GenMethod; @@ -17,7 +16,6 @@ where fn from_newtype_struct(_idents: StructIdents, parent_ident: syn::Ident) -> Self; fn from_unnamed_enum(idents: EnumIdents) -> Self; fn from_unit_enum(idents: EnumIdents) -> Self; - fn from_noop_struct(idents: StructIdents) -> Self; fn from_type_struct(idents: StructIdents, how_to_control_this_type: &GenMethod) -> Self; fn from_type_recurse( idents: StructIdents, @@ -73,7 +71,6 @@ where HowToControlThis::Type(how_to_control_this_type) => { Self::from_type_struct(idents, &how_to_control_this_type) } - HowToControlThis::Default => todo!(), HowToControlThis::Recurse(outer, inner) => { Self::from_type_recurse(idents, &outer, &inner) } @@ -133,11 +130,6 @@ where }; match field.how_to_control_this() { - HowToControlThis::Default => { - #[cfg(feature = "debug_logging")] - log::info!("-> from_noop_struct"); - Self::from_noop_struct(idents) - } HowToControlThis::Type(_how_to_control_this_type) => { // Self::from_type_struct(idents, &how_to_control_this_type) Self::from_newtype_struct(idents, name.clone()) @@ -158,39 +150,21 @@ where } } -#[derive(Debug, FromMeta, Clone)] -pub struct OverrideFn { - func: String, - labels: Vec, // this must be the same length as the usages in the func -} - #[derive(Debug, FromField, Clone)] #[darling(attributes(murrelet_gen))] pub(crate) struct LivecodeFieldReceiver { pub(crate) ident: Option, pub(crate) ty: syn::Type, - #[darling(default, rename = "override")] - pub(crate) override_fn: Option, pub(crate) method: GenMethod, #[darling(default)] pub(crate) method_inner: Option, } impl LivecodeFieldReceiver { fn how_to_control_this(&self) -> HowToControlThis { - // if let Some(OverrideFn { func, labels }) = &self.override_fn { - // match func.as_str() { - // "default" => HowToControlThis::Default, - // _ => { - // let label_str: Vec = labels.iter().map(|lit| lit.value()).collect(); - // HowToControlThis::Override(func.clone(), label_str, labels.len()) - // } - // } - // } else if let Some(r) = &self.method_inner { HowToControlThis::Recurse(self.method.clone(), r.clone()) } else if matches!(self.method, GenMethod::VecLength { .. }) { panic!("vec missing inner") - // HowToControlThis::Recurse(self.method.clone(), None) } else { HowToControlThis::Type(self.method.clone()) } @@ -228,32 +202,5 @@ pub struct StructIdents { #[derive(Clone, Debug)] pub(crate) enum HowToControlThis { Type(GenMethod), - Recurse(GenMethod, GenMethod), // one level... defaults to calling its func - Default, // just do the default values - // Override(String, Vec, usize), -} - -#[allow(dead_code)] -pub fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { - match t { - syn::Type::Path(syn::TypePath { path, .. }) => { - let s = path.segments.last().unwrap(); - let main_type = s.ident.clone(); - - acc.push(main_type); - - if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - args, - .. - }) = s.arguments.clone() - { - if let syn::GenericArgument::Type(other_ty) = args.first().unwrap() { - recursive_ident_from_path(other_ty, acc); - } else { - panic!("recursive ident not implemented yet {:?}", args); - } - } - } - x => panic!("no name for type {:?}", x), - } + Recurse(GenMethod, GenMethod), } diff --git a/murrelet_gpu/Cargo.toml b/murrelet_gpu/Cargo.toml index 8ba21c5..2a2df33 100644 --- a/murrelet_gpu/Cargo.toml +++ b/murrelet_gpu/Cargo.toml @@ -17,12 +17,7 @@ schemars = [ "murrelet_livecode/schemars", "murrelet_perform/schemars", ] -# murrelet_gui = [ -# "dep:murrelet_gui", -# "murrelet_draw/murrelet_gui", -# "murrelet_livecode/murrelet_gui", -# "murrelet_perform/murrelet_gui", -# ] + [dependencies] wgpu_for_latest = { package = "wgpu", version = "0.20.1", optional = true } diff --git a/murrelet_gpu/src/compute.rs b/murrelet_gpu/src/compute.rs index b8f87a0..cdfdc07 100644 --- a/murrelet_gpu/src/compute.rs +++ b/murrelet_gpu/src/compute.rs @@ -37,11 +37,6 @@ impl ComputeBindings { indices = vec![0; 2] }; - // let queue = c.queue(); - // queue.write_buffer(&self.cell_offsets, 0, bytemuck::cast_slice(&offsets)); - // queue.write_buffer(&self.cell_indices, 0, bytemuck::cast_slice(&indices)); - // queue.write_buffer(&self.input, 0, bytemuck::cast_slice(&data)); - self.input = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(&data), @@ -157,29 +152,13 @@ pub trait ToAABB { // like Graphics, what's needed to create a compute pipeline pub struct ComputeGraphicsToTexture { name: String, - // conf: GraphicsCreator, - // bind_group: wgpu::BindGroup, - // vertex_buffers: VertexBuffers, - // render_pipeline: wgpu::RenderPipeline, pub uniforms: BasicUniform, pub buffers: ComputeBindings, #[allow(dead_code)] texture: TextureAndDesc, - bind_group_layout: wgpu::BindGroupLayout, pipeline: wgpu::ComputePipeline, - // data: Vec, dims: [u32; 2], - // csr: CSR, - // used internally - // pub input_texture_view: wgpu::TextureView, - // pub input_texture_view_other: Option, - // sampler: wgpu::Sampler, - // bind_group_layout: wgpu::BindGroupLayout, - // // i guess need this to create nannou texture - // pub texture_and_desc: TextureAndDesc, - // pub other_texture_and_desc: Option, - // textures_for_3d: Option, } impl ComputeGraphicsToTexture { fn sync_data( @@ -525,10 +504,4 @@ impl ComputeGraphicsToTextureRef { println!("segments is empty, not doing anything"); } } - - // pub fn control_graphics(&self, conf: &GraphicsConf) -> Vec { - // let ctrl_graphics = (self.control_graphic_fn)(conf); - - // ControlGraphicsRef::new(self.label, ctrl_graphics, Some(self.graphics.clone())) - // } } diff --git a/murrelet_gpu/src/gpu_livecode.rs b/murrelet_gpu/src/gpu_livecode.rs index 4beb222..0760cb8 100644 --- a/murrelet_gpu/src/gpu_livecode.rs +++ b/murrelet_gpu/src/gpu_livecode.rs @@ -71,7 +71,6 @@ impl ControlGraphicsRef { } pub fn update_graphics(&self, c: &GraphicsWindowConf) { - //}, g: &GraphicsRef) { self.graphics .update_uniforms_other_tuple(c, self.control.more_info_other_tuple()); } diff --git a/murrelet_gpu/src/gpu_macros.rs b/murrelet_gpu/src/gpu_macros.rs index c6928d4..739c3eb 100644 --- a/murrelet_gpu/src/gpu_macros.rs +++ b/murrelet_gpu/src/gpu_macros.rs @@ -349,8 +349,6 @@ impl TwoSourcesRender { } impl RenderTrait for TwoSourcesRender { - // type VertexKind = VertexKind; - fn render(&self, device: &DeviceStateForRender) { self.source_main .render(device.device_state(), &self.dest_view().unwrap()); @@ -378,10 +376,6 @@ impl RenderTrait for TwoSourcesRender { fn dest_view_other(&self) -> Option { self.dest.texture_view_other() } - - // fn dest(&self) -> Option> { - // Some(self.dest.clone()) - // } } // holds a gpu pipeline :O @@ -413,8 +407,6 @@ impl RenderTrait for PipelineRender { - // type VertexKind = VertexKind; - fn render(&self, device_state_for_render: &DeviceStateForRender) { // write source to pipeline source self.source.render( @@ -436,9 +428,6 @@ impl Option> { - // Some(self.dest.clone()) - // } } // given a list of inputs, choose which one to use @@ -461,8 +450,6 @@ impl ChoiceRender { } impl RenderTrait for ChoiceRender { - // type VertexKind = VertexKind; - fn render(&self, device: &DeviceStateForRender) { let source = &self.sources[self.choice % self.sources.len()]; let dest = &self.dest; @@ -476,10 +463,6 @@ impl RenderTrait for ChoiceRender { todo!() } - // fn dest(&self) -> Option> { - // Some(self.dest.clone()) - // } - fn is_choice(&self) -> bool { true } @@ -515,8 +498,6 @@ impl PingPongRender { } impl RenderTrait for PingPongRender { - // type VertexKind = VertexKind; - fn render(&self, device: &DeviceStateForRender) { let ping_texture = &self.ping.texture_view(); let pong_texture = &self.pong.texture_view(); @@ -560,59 +541,7 @@ impl RenderTrait for PingPongRender { fn dest_view_other(&self) -> Option { None } - - // fn dest(&self) -> Option> { - // Some(self.pong.clone()) - // } } - -// pub struct TextureViewRender { -// pub source: GraphicsRef, -// pub dest: TextureAndDesc, -// } - -// impl TextureViewRender { -// pub fn new_box( -// source: GraphicsRef, -// dest: wgpu::TextureView, -// ) -> Box> { -// Box::new(TextureViewRender { -// source, -// dest: Arc::new(dest), -// }) -// } -// } - -// impl RenderTrait -// for TextureViewRender -// { -// // type VertexKind = VertexKind; - -// fn render(&self, device: &DeviceStateForRender) { -// let source = &self.source; -// source.render_to_texture(device.device_state(), &self.dest); -// } -// fn debug_print(&self) -> Vec { -// let source = &self.source; -// vec![RenderDebugPrint { -// src: source.name(), -// dest: "texture view!".to_string(), -// }] -// } - -// fn dest_view(&self) -> Option { -// self.dest -// } - -// fn dest_view_other(&self) -> Option { -// None -// } - -// // fn dest(&self) -> Option> { -// // None -// // } -// } - pub struct ComputeTextureRender { pub source: ComputeGraphicsToTextureRef, pub dest: GraphicsRefCustom, @@ -628,8 +557,6 @@ impl ComputeTextureRender { } impl RenderTrait for ComputeTextureRender { - // type VertexKind = VertexKind; - // whenver it's called, it'll increment! check if it's overdue before rendering! fn render(&self, device_state_for_render: &DeviceStateForRender) { let source_texture = &self.source; @@ -654,10 +581,6 @@ impl RenderTrait for ComputeTextureRender Option> { - // Some(self.dest.clone()) - // } } pub struct DisplayRender { @@ -671,8 +594,6 @@ impl DisplayRender { } impl RenderTrait for DisplayRender { - // type VertexKind = VertexKind; - fn render(&self, device: &DeviceStateForRender) { let source = &self.source; source.render_to_texture(device.device_state(), device.display_view()); @@ -692,10 +613,6 @@ impl RenderTrait for DisplayRender { fn dest_view_other(&self) -> Option { None } - - // fn dest(&self) -> Option> { - // None - // } } pub struct GPUPipeline { @@ -731,21 +648,6 @@ impl GPUPipeline { } out } - // pub fn add_control_graphics( - // &mut self, - // _label: &str, - // control_graphics_fn: GraphicsRefWithControlFn, - // ) { - // self.ctrl.push(control_graphics_fn) - // } - - // pub fn control_graphics(&self, t: &GraphicConf) -> Vec> { - // let mut v = vec![]; - // for c in &self.ctrl { - // v.extend(c.control_graphics(t).into_iter()); - // } - // v - // } pub fn set_source(&mut self, src: &str) { self.source = Some(src.to_string()); @@ -831,15 +733,7 @@ impl GPUPipelineRef { self.0.borrow().source() } - // pub fn get_graphic(&self, name: &str) -> Option<&dyn AnyGraphicsRef> { - // // self.0.borrow().get_graphic(name) - // let pipeline = self.0.borrow(); - // pipeline.get_graphic(name).map(f) - // } - pub fn control_graphics(&self, conf: &GraphicsConf) -> Vec> { - //Vec> { - // self.0.borrow().control_graphics(conf) self.0.borrow().control_graphics(conf) } } @@ -882,10 +776,6 @@ impl RenderTrait for SingleTextureRender { fn dest_view_other(&self) -> Option { None } - - // fn dest(&self) -> Option> { - // Some(self.dest.clone()) - // } } // makes it easier to ad control grpahics @@ -940,7 +830,6 @@ macro_rules! build_shader_pipeline { $dest.graphics(), ) ); - // pipeline_add_label!($pipeline, $source); pipeline_add_label!($pipeline, $dest); build_shader_pipeline!(@parse $pipeline ($($tail)*)); diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index aff7c6b..17df4cf 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -63,94 +63,6 @@ impl GraphicsVertex for DefaultVertex { } } -// for each vertex, this is what we'll pass in - -// #[repr(C)] -// #[derive(Clone, Copy, Debug)] -// pub struct DefaultVertex { -// position: [f32; 3], -// normal: [f32; 3], -// face_pos: [f32; 2], -// } - -// impl DefaultVertex { -// pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { -// Self { -// position, -// normal, -// face_pos, -// } -// } -// pub fn pos(&self) -> [f32; 3] { -// self.position -// } - -// pub fn pos_vec3(&self) -> Vec3 { -// glam::vec3(self.position[0], self.position[1], self.position[2]) -// } - -// // pub fn from_simple(vs: &VertexSimple) -> Self { -// // Self { -// // position: vs.position, -// // normal: vs.normal, -// // face_pos: vs.face_pos, -// // } -// // } - -// // pub fn new(position: [f32; 3], normal: [f32; 3], face_pos: [f32; 2]) -> Self { -// // Self { -// // position, -// // normal, -// // face_pos, -// // } -// // } -// // pub fn pos(&self) -> [f32; 3] { -// // self.position -// // } - -// // pub fn pos_vec3(&self) -> Vec3 { -// // glam::vec3(self.position[0], self.position[1], self.position[2]) -// // } - -// pub fn pos2d(&self) -> Vec2 { -// vec2(self.position[0], self.position[1]) -// } - -// pub fn attrs(&self) -> Vec { -// vec![ -// self.normal[0], -// self.normal[1], -// self.normal[2], -// self.face_pos[0], -// self.face_pos[1], -// ] -// } -// } - -// impl Lerpable for DefaultVertex { -// fn lerpify(&self, other: &Self, pct: &T) -> Self { -// VertexSimple { -// position: [ -// self.position[0].lerpify(&other.position[0], pct), -// self.position[1].lerpify(&other.position[1], pct), -// self.position[2].lerpify(&other.position[2], pct), -// ], -// normal: [ -// self.normal[0].lerpify(&other.normal[0], pct), -// self.normal[1].lerpify(&other.normal[1], pct), -// self.normal[2].lerpify(&other.normal[2], pct), -// ], -// face_pos: [ -// self.face_pos[0].lerpify(&other.face_pos[0], pct), -// self.face_pos[1].lerpify(&other.face_pos[1], pct), -// ], -// } -// } -// } - -// unsafe impl Zeroable for DefaultVertex {} -// unsafe impl Pod for DefaultVertex {} - // in the default vertex shader, z is dropped pub const VERTICES: [DefaultVertex; 4] = [ DefaultVertex { @@ -220,66 +132,6 @@ pub struct Scene { view: VertexUniforms, // update this as needed } -// this is the conf that you'll interface with -// #[derive(Debug, Clone)] -// pub struct Triangulate { -// vertices: Vec, -// order: Vec, -// } - -// impl Triangulate { -// pub fn new() -> Self { -// Triangulate { -// vertices: vec![], -// order: vec![], -// } -// } - -// pub fn vertices(&self) -> &[Vertex] { -// &self.vertices -// } - -// pub fn add_vertex(&mut self, v: [f32; 3], n: [f32; 3], face_pos: [f32; 2]) -> u32 { -// let vv = Vertex::new(v, n, face_pos); -// self.vertices.push(vv); -// (self.vertices.len() - 1) as u32 -// } - -// // alternatively can add vertices and then add teh vec -// pub fn add_rect(&mut self, v: &[Vec3; 4], flip: bool) { -// let edge1 = v[0] - v[1]; -// let edge2 = v[3] - v[1]; -// let normal = edge1.cross(edge2).normalize().to_array(); - -// let v0 = self.add_vertex(v[0].to_array(), normal, [1.0, 0.0]); -// let v1 = self.add_vertex(v[1].to_array(), normal, [0.0, 0.0]); -// let v2 = self.add_vertex(v[2].to_array(), normal, [1.0, 1.0]); -// let v3 = self.add_vertex(v[3].to_array(), normal, [0.0, 1.0]); - -// if !flip { -// self.order.extend([v0, v2, v1, v1, v2, v3]) -// } else { -// self.order.extend([v0, v1, v2, v1, v3, v2]) -// } -// } - -// pub fn set_order(&mut self, u: Vec) { -// self.order = u; -// } - -// fn order(&self) -> &[u32] { -// &self.order -// } - -// pub fn indices(&self) -> &[u32] { -// &self.order -// } - -// pub fn add_order(&mut self, collect: &[u32]) { -// self.order.extend_from_slice(collect); -// } -// } - // this is the conf that you'll interface with #[derive(Debug, Clone)] pub struct InputVertexConf { @@ -319,12 +171,6 @@ fn main(@location(0) position: vec3) -> @builtin(position) vec4 { } pub fn with_custom_vertices(mut self, tri: &Triangulate) -> Self { - // self.vertices = tri - // .vertices - // .iter() - // .map(|x| DefaultVertex::from_simple(x)) - // .collect_vec(); - // self.order = tri.order.clone(); self.tri = tri.clone(); self.topology = wgpu::PrimitiveTopology::TriangleList; self @@ -333,19 +179,6 @@ fn main(@location(0) position: vec3) -> @builtin(position) vec4 { pub fn indices(&self) -> u32 { self.tri.order.len() as u32 } - - // pub fn default() -> Self { - // Self { - // vs_mod: VERTEX_SHADER, - // view: VertexUniforms::identity(), - // topology: wgpu::PrimitiveTopology::TriangleList, - // tri: Triangulate:: { - // vertices: VERTICES.to_vec(), - // order: vec![0, 1, 2, 1, 3, 2], - // }, - // is_3d: false, - // } - // } } impl InputVertexConf { @@ -666,20 +499,15 @@ impl GraphicsRefCustom { #[deprecated(note = "Use render instead")] pub fn render_to_view(&self, device: &DeviceState, view: &wgpu::TextureView) { - // self.graphics.borrow_mut().render(device, view) self.render(device, view) } pub fn render(&self, device: &DeviceState, view: &wgpu::TextureView) { - // let view = &other.graphics.borrow_mut().input_texture_view; self.graphics.borrow_mut().render(device, view) } #[deprecated(note = "Use render_to_view instead")] pub fn render_2tex(&self, device: &DeviceState, view: &wgpu::TextureView) { - // let binding = other.graphics.borrow_mut(); - // let view = binding.input_texture_view_other.as_ref().unwrap(); - // self.graphics.borrow_mut().render(device_state, view) self.render(device, view) } @@ -763,11 +591,6 @@ impl GraphicsRefCustom { { let mut g = self.graphics.borrow_mut(); g.conf.input_vertex.tri.vertices = tri.vertices.clone(); - // tri - // .vertices - // .iter() - // .map(|x| DefaultVertex::from_simple(x)) - // .collect_vec(); g.conf.input_vertex.tri.order = tri.order.clone(); let queue = c.device.queue(); @@ -842,25 +665,15 @@ pub struct GraphicsRefWithControlFn { } pub trait AnyGraphicsRef { - // fn name(&self) -> String; fn texture_view(&self) -> wgpu::TextureView; - // fn render_to_texture(&self, dev: &DeviceState, dst: &wgpu::TextureView); } impl AnyGraphicsRef for GraphicsRefCustom where VertexKind: GraphicsVertex + 'static, { - // fn name(&self) -> String { - // self.name() - // } - fn texture_view(&self) -> wgpu::TextureView { self.texture_view() } - - // fn render_to_texture(&self, dev: &DeviceState, dst: &wgpu::TextureView) { - // self.render_to_texture(dev, dst) - // } } impl GraphicsRefWithControlFn { @@ -932,7 +745,6 @@ impl Graphics { let queue = &c.device.queue(); self.uniforms.more_info = more_info; - // println!("{:?}", self.uniform.more_info); queue.write_buffer(&self.uniforms_buffer, 0, self.uniforms.as_bytes()); } @@ -946,7 +758,6 @@ impl Graphics { self.uniforms.more_info = more_info; self.uniforms.more_info_other = more_info_other; - // println!("{:?}", self.uniform.more_info); queue.write_buffer(&self.uniforms_buffer, 0, self.uniforms.as_bytes()); } @@ -1095,7 +906,6 @@ impl Graphics { fn _sampler(device: &wgpu::Device, details: ShaderOptions) -> wgpu::Sampler { let sampler_desc = details.as_sampler_desc(); let sampler = device.create_sampler(&sampler_desc); - // println!("sampler: {:?}, {:?}", sampler, sampler_desc); sampler } @@ -1229,7 +1039,6 @@ impl Graphics { compilation_options: wgpu::PipelineCompilationOptions::default(), }), multiview: None, - // cache: None, }; let main_pipeline = device.create_render_pipeline(&rp_desc); diff --git a/murrelet_gpu/src/shader_str.rs b/murrelet_gpu/src/shader_str.rs index f91a169..d9a9477 100644 --- a/murrelet_gpu/src/shader_str.rs +++ b/murrelet_gpu/src/shader_str.rs @@ -3,12 +3,6 @@ pub const SUFFIX: &str = r#" } "#; -// struct Input { -// a: vec2; -// b: vec2; -// }; - - pub const COMPUTE_TEX: &str = r#" struct BasicUniform { dims: vec4, @@ -453,7 +447,6 @@ pub const PREFIX: &str = r#" fn main(@location(0) tex_coords: vec2, @location(1) shad_info: vec4, @location(2) normal: vec3, @location(3) light_space_pos: vec4, @location(4) world_pos: vec3) -> FragmentOutput { "#; - pub const COMPUTE_FORMAT_STR: &str = r#" @compute @workgroup_size(8, 8) fn main(@builtin(global_invocation_id) gid: vec3) { @@ -484,5 +477,3 @@ fn main(@builtin(global_invocation_id) gid: vec3) { textureStore(out_img, vec2(gid.xy), result); } "#; - - diff --git a/murrelet_gpu/src/uniforms.rs b/murrelet_gpu/src/uniforms.rs index 1a9e70b..bebf658 100644 --- a/murrelet_gpu/src/uniforms.rs +++ b/murrelet_gpu/src/uniforms.rs @@ -79,16 +79,6 @@ impl BasicUniform { mapped_at_creation: false, }) } - - // fn copy_to_buffer( - // &self, - // dest: &wgpu::Buffer, - // device: &wgpu::Device, - // encoder: &mut wgpu::CommandEncoder, - // ) { - // println!("copy to buffer"); - // encoder.copy_buffer_to_buffer(&self.to_buffer(device), 0, dest, 0, self.uniforms_size()); - // } } pub struct UniformsPair { diff --git a/murrelet_gui/examples/tests_schema.rs b/murrelet_gui/examples/tests_schema.rs index acb4070..3004a66 100644 --- a/murrelet_gui/examples/tests_schema.rs +++ b/murrelet_gui/examples/tests_schema.rs @@ -1,109 +1,109 @@ -// use std::collections::HashMap; - -// use murrelet_gui::{CanChangeToGUI, CanMakeGUI, MurreletEnumValGUI, MurreletGUISchema, ValueGUI}; -// use murrelet_schema::*; -// use murrelet_schema_derive::MurreletSchema; - -// #[derive(MurreletSchema)] -// pub struct BasicTypes { -// a_number: f32, -// b_number: usize, -// c_number: u64, -// d_number: i32, -// bool: bool, -// something: Vec, -// s: String, -// referenced_string: String, +use std::collections::HashMap; + +use murrelet_gui::{CanChangeToGUI, CanMakeGUI, MurreletEnumValGUI, MurreletGUISchema, ValueGUI}; +use murrelet_schema::*; +use murrelet_schema_derive::MurreletSchema; + +#[derive(MurreletSchema)] +pub struct BasicTypes { + a_number: f32, + b_number: usize, + c_number: u64, + d_number: i32, + bool: bool, + something: Vec, + s: String, + referenced_string: String, +} + +fn custom_func() -> MurreletSchema { + MurreletSchema::Val(murrelet_schema::MurreletPrimitive::Num) +} + +#[derive(MurreletSchema)] +pub struct OverridesAndRecursive { + a_number: f32, + something: Vec, + #[murrelet_schema(func = "custom_func")] + label: String, + #[murrelet_schema(kind = "skip")] + b: HashMap, +} + +#[derive(MurreletSchema)] +enum EnumTest { + A, + B(OverridesAndRecursive), +} + +#[derive(MurreletSchema)] +struct SimpleNewtype(f32); + +// fn lerp_partial(&self, pct: T) -> Self { +// SimpleNewtype(pct.lerp_pct() as f32) +// } // } -// fn custom_func() -> MurreletSchema { -// MurreletSchema::Val(murrelet_schema::MurreletPrimitive::Num) -// } - -// #[derive(MurreletSchema)] -// pub struct OverridesAndRecursive { -// a_number: f32, -// something: Vec, -// #[murrelet_schema(func = "custom_func")] -// label: String, -// #[murrelet_schema(kind = "skip")] -// b: HashMap, -// } +// #[derive(Debug, Clone, MurreletUX)] + +fn main() { + // let b = BasicTypes{ + // a_number: 1.0, + // b_number: -10.0, + // }; + let test_val = BasicTypes::make_schema().change_to_gui(); + + let basic_types_schema = MurreletGUISchema::Struct( + "BasicTypes".to_string(), + vec![ + ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ("bool".to_owned(), MurreletGUISchema::Val(ValueGUI::Bool)), + ( + "something".to_owned(), + MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), + ), + ("s".to_owned(), MurreletGUISchema::Val(ValueGUI::String)), + ( + "referenced_string".to_owned(), + MurreletGUISchema::Val(ValueGUI::String), + ), + ], + ); + + assert_eq!(test_val, basic_types_schema); + + let test_val = OverridesAndRecursive::make_schema().change_to_gui(); + + let overrides_and_recursive_schema = MurreletGUISchema::Struct( + "OverridesAndRecursive".to_string(), + vec![ + ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), + ( + "something".to_owned(), + MurreletGUISchema::list(basic_types_schema), + ), + ("label".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), // make sure it calls the override + ("b".to_owned(), MurreletGUISchema::Skip), + ], + ); + assert_eq!(test_val, overrides_and_recursive_schema); + + let test_val = EnumTest::make_schema().change_to_gui(); + + assert_eq!( + test_val, + MurreletGUISchema::Enum( + "EnumTest".to_string(), + vec![ + (MurreletEnumValGUI::Unit("A".to_owned())), + (MurreletEnumValGUI::Unnamed("B".to_owned(), overrides_and_recursive_schema)), + ], + false + ) + ); +} -// #[derive(MurreletSchema)] -// enum EnumTest { -// A, -// B(OverridesAndRecursive), -// } - -// #[derive(MurreletSchema)] -// struct SimpleNewtype(f32); - -// // fn lerp_partial(&self, pct: T) -> Self { -// // SimpleNewtype(pct.lerp_pct() as f32) -// // } -// // } - -// // #[derive(Debug, Clone, MurreletUX)] - -// fn main() { -// // let b = BasicTypes{ -// // a_number: 1.0, -// // b_number: -10.0, -// // }; -// let test_val = BasicTypes::make_schema().change_to_gui(); - -// let basic_types_schema = MurreletGUISchema::Struct( -// "BasicTypes".to_string(), -// vec![ -// ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), -// ("b_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), -// ("c_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), -// ("d_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), -// ("bool".to_owned(), MurreletGUISchema::Val(ValueGUI::Bool)), -// ( -// "something".to_owned(), -// MurreletGUISchema::list(MurreletGUISchema::Val(ValueGUI::Num)), -// ), -// ("s".to_owned(), MurreletGUISchema::Val(ValueGUI::String)), -// ( -// "referenced_string".to_owned(), -// MurreletGUISchema::Val(ValueGUI::String), -// ), -// ], -// ); - -// assert_eq!(test_val, basic_types_schema); - -// let test_val = OverridesAndRecursive::make_schema().change_to_gui(); - -// let overrides_and_recursive_schema = MurreletGUISchema::Struct( -// "OverridesAndRecursive".to_string(), -// vec![ -// ("a_number".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), -// ( -// "something".to_owned(), -// MurreletGUISchema::list(basic_types_schema), -// ), -// ("label".to_owned(), MurreletGUISchema::Val(ValueGUI::Num)), // make sure it calls the override -// ("b".to_owned(), MurreletGUISchema::Skip), -// ], -// ); -// assert_eq!(test_val, overrides_and_recursive_schema); - -// let test_val = EnumTest::make_schema().change_to_gui(); - -// assert_eq!( -// test_val, -// MurreletGUISchema::Enum( -// "EnumTest".to_string(), -// vec![ -// (MurreletEnumValGUI::Unit("A".to_owned())), -// (MurreletEnumValGUI::Unnamed("B".to_owned(), overrides_and_recursive_schema)), -// ], -// false -// ) -// ); -// } -fn main() {} diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs index 947915d..f56ed4c 100644 --- a/murrelet_gui/src/lib.rs +++ b/murrelet_gui/src/lib.rs @@ -5,14 +5,6 @@ pub use murrelet_gui_derive::MurreletGUI; use murrelet_schema::{MurreletEnumVal, MurreletPrimitive, MurreletSchema}; use serde::Serialize; -// #[derive(Debug, Error)] -// pub enum MurreletGUIErr { -// #[error("An error occurred decoding GUI to livecode: {0}")] -// GUIToLivecode(String), -// } - -// pub type MurreletGUIResult = Result; - #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub enum ValueGUI { Bool, // should be a ControlBool diff --git a/murrelet_gui_derive/src/parser.rs b/murrelet_gui_derive/src/parser.rs index 23c2511..6aec784 100644 --- a/murrelet_gui_derive/src/parser.rs +++ b/murrelet_gui_derive/src/parser.rs @@ -109,15 +109,7 @@ where let idents = ParsedFieldIdent { name: name.clone() }; - // "external" => quote! {}, - // "internal" => default, - // "untagged" => quote! {#[serde(untagged)]}, - // _ => default, - // let is_external = match &e.enum_tag.map(|x| x.as_str()) { - // Some("external") => true, - // None => false, - // _ => unimplemented!("enum type not implemented") - // }; + let is_untagged = if let Some(enum_tag) = &e.enum_tag { if enum_tag.as_str() == "external" { true diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index ff82f28..f12ae6f 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -381,7 +381,6 @@ impl ExprWorldContextValues { new.set_val(&name, val); } - // Self::new([self.0.clone(), vals.0].concat()) new } @@ -397,24 +396,6 @@ impl ExprWorldContextValues { } } -// impl Context for ExprWorldContextValues { -// fn get_value(&self, identifier: &str) -> Option<&Value> { -// todo!() -// } - -// fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult { -// todo!() -// } - -// fn are_builtin_functions_disabled(&self) -> bool { -// true -// } - -// fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> { - -// } -// } - pub trait IntoExprWorldContext { fn as_expr_world_context_values(&self) -> ExprWorldContextValues; } diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index c2f78f6..fcbd0bf 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -85,35 +85,24 @@ impl GetLivecodeIdentifiers for ControlLazyNodeF32 { // todo, figure out how to only build this context once per unitcell/etc #[derive(Debug, Clone)] pub struct LazyNodeF32Inner { - n: Arc, // what will be evaluated! - world: WorldWithLocalVariables, //LivecodeWorldState, // this is a reference :D - // more_defs: MixedEvalDefs, + n: Arc, // what will be evaluated! + world: WorldWithLocalVariables, // this is a reference :D } impl LazyNodeF32Inner { pub fn new(n: Node, world: LivecodeWorldState) -> Self { Self { n: Arc::new(n), world: world.to_local(), - // more_defs: MixedEvalDefs::new(), } } // options to add more details... pub fn add_more_defs(&self, more_defs: &M) -> Self { - // unreachable!(); let c = self.clone(); c.add_expr_values(more_defs.to_mixed_def().expr_vals()) - - // println!("dropping contexts..."); - // c.world - // .update_with_defs(MixedEvalDefsRef::new(more_defs.clone())); - // c } pub fn add_expr_values(&self, more_vals: &ExprWorldContextValues) -> Self { - // let mut c = self.clone(); - // c.more_defs.set_vals(more_vals); - // c let mut c = self.clone(); c.world.update_with_simple_defs(more_vals); c @@ -121,28 +110,12 @@ impl LazyNodeF32Inner { // internal function to build the ctx fn build_ctx(&self) -> &WorldWithLocalVariables { - // LivecodeResult> { - // self.world.clone_with_mixed_defs(&self.more_defs) - - // self.world.ctx()?; - - // let w = self.world.clone(); - - // let a = w.ctx().as_ref() - - // self.world.ctx() &self.world - - // let copied_world = self.world.ctx().as_ref().clone(); - // let mut ctx = copied_world.clone(); - // self.more_defs.update_ctx(&mut ctx)?; - // Ok(ctx) } // what you'll use pub fn eval(&self) -> LivecodeResult { let ctx = self.build_ctx(); - // let ctx = a.as_ref(); self.n .eval_float_with_context(ctx) @@ -322,26 +295,6 @@ where } } -// pub fn eval_lazy_color(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult { -// Ok(murrelet_common::MurreletColor::hsva( -// v[0].eval_lazy(ctx)?, -// v[1].eval_lazy(ctx)?, -// v[2].eval_lazy(ctx)?, -// v[3].eval_lazy(ctx)?, -// )) -// } - -// pub fn eval_lazy_vec3(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult { -// Ok(glam::vec3( -// v[0].eval_lazy(ctx)?, -// v[1].eval_lazy(ctx)?, -// v[2].eval_lazy(ctx)?, -// )) -// } - -// pub fn eval_lazy_vec2(v: &[LazyNodeF32], ctx: &MixedEvalDefs) -> LivecodeResult { -// Ok(glam::vec2(v[0].eval_lazy(ctx)?, v[1].eval_lazy(ctx)?)) -// } #[derive(Clone, Debug, Default)] pub struct LazyVec2 { diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index 6821199..b13b249 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -563,14 +563,6 @@ pub enum ControlBool { Expr(Node), } impl ControlBool { - // pub fn to_unitcell_control(&self) -> UnitCellControlExprBool { - // match self { - // ControlBool::Raw(x) => UnitCellControlExprBool::Bool(*x), - // ControlBool::Int(x) => UnitCellControlExprBool::Int(*x), - // ControlBool::Float(x) => UnitCellControlExprBool::Float(*x), - // ControlBool::Expr(x) => UnitCellControlExprBool::Expr(x.clone()), - // } - // } pub fn force_from_str(s: &str) -> ControlBool { match build_operator_tree(s) { @@ -583,7 +575,6 @@ impl ControlBool { } pub fn o(&self, w: &LivecodeWorldState) -> LivecodeResult { - // self.to_unitcell_control().eval(w) let a = w.ctx()?; let ctx = a.as_ref(); diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index f6f415b..13e44c5 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -49,12 +49,6 @@ impl CachedHM { })) } - // fn update(&mut self, hm: HashMapContext) { - // // *self = CachedHM::Cached(Arc::new(hm)); - // self.data = Arc::new(hm); - // self.flag = CacheFlag::Cached; - // } - fn clear(&mut self) { self.flag = CacheFlag::NotCached; } @@ -220,16 +214,13 @@ impl LivecodeWorldState { builtins_disabled: false, } } - - // pub(crate) fn set_val(&mut self, name: &str, val: LivecodeValue) { - // self.update_with_defs(MixedEvalDefs::new_simple(name, val)); - // } } +// some chatgpt help #[derive(Debug, Clone)] pub struct WorldWithLocalVariables { - base: Arc, // your cached global ctx (world.ctx()?.as_ref()) - locals: Vec<(String, Value)>, // small slice like &[("loc_pct", 0.42.into())] + base: Arc, + locals: Vec<(String, Value)>, builtins_disabled: bool, } impl WorldWithLocalVariables { @@ -237,12 +228,6 @@ impl WorldWithLocalVariables { let mut locals = more_vals.to_vals(); locals.extend(self.locals.iter().cloned()); self.locals = locals; - - // WorldWithLocalVariables { - // base: self.base.clone(), - // locals, - // builtins_disabled: true, - // } } pub(crate) fn variable_names(&self) -> Vec { @@ -254,11 +239,9 @@ impl WorldWithLocalVariables { impl Context for WorldWithLocalVariables { fn get_value(&self, identifier: &str) -> Option<&Value> { - // locals win if let Some((_, v)) = self.locals.iter().find(|(k, _v)| k == identifier) { return Some(v); } - // otherwise fallback to global self.base.get_value(identifier) } @@ -352,8 +335,6 @@ impl LivecodeWorldStateInner { match self.stage { LivecodeWorldStateStage::Timeless => panic!("checking time in a timeless world"), LivecodeWorldStateStage::World(t) => t, - // LivecodeWorldStateStage::Unit(t) => t, - // LivecodeWorldStateStage::Lazy(t) => t, } } @@ -381,49 +362,6 @@ impl LivecodeWorldStateInner { more_defs.update_ctx(self.ctx_mut()) } - // pub fn clone_with_vals( - // &self, - // expr: ExprWorldContextValues, - // prefix: &str, - // ) -> LivecodeResult { - // let mut lazy = self.clone_to_lazy(); // eh just need to clone - // expr.with_prefix(prefix).update_ctx(lazy.ctx_mut())?; - // Ok(lazy) - // } - - // pub fn clone_to_unitcell( - // &self, - // unit_cell_ctx: &UnitCellContext, - // prefix: &str, - // ) -> LivecodeResult { - // let mut context = self.context.clone(); - // unit_cell_ctx - // .as_expr_world_context_values() - // .with_prefix(prefix) - // .update_ctx(&mut context)?; - - // let r = LivecodeWorldState { - // context, - // stage: self - // .stage - // .add_step(LivecodeWorldStateStage::Unit(self.time())), - // assets: self.assets.clone(), - // }; - - // Ok(r) - // } - - // pub fn clone_to_lazy(&self) -> Self { - // let context = self.context.clone(); - // LivecodeWorldState { - // context, - // stage: self - // .stage - // .add_step(LivecodeWorldStateStage::Lazy(self.time())), - // assets: self.assets.clone(), - // } - // } - pub fn asset_layer(&self, key: &str, layer_idx: usize) -> Option> { self.assets.asset_layer(key, layer_idx).cloned() } @@ -436,9 +374,9 @@ impl LivecodeWorldStateInner { Self::new( &init_evalexpr_func_ctx().unwrap(), &LivecodeSrc::new(vec![]), - LiveCodeTimeInstantInfo::new_dummy(), // time - AdditionalContextNode::new_dummy(), // node - Arc::new(Assets::empty()), // assets + LiveCodeTimeInstantInfo::new_dummy(), + AdditionalContextNode::new_dummy(), + Arc::new(Assets::empty()), ) .unwrap() } @@ -448,9 +386,9 @@ impl LivecodeWorldStateInner { Self::new( &empty_ctx, &LivecodeSrc::new(vec![]), - LiveCodeTimeInstantInfo::new_dummy(), // time - AdditionalContextNode::new_dummy(), // node - Arc::new(Assets::empty()), // assets + LiveCodeTimeInstantInfo::new_dummy(), + AdditionalContextNode::new_dummy(), + Arc::new(Assets::empty()), ) .unwrap() } diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 6a109e2..c5903f8 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -319,14 +319,6 @@ where } } - // match VecUnitCell::deserialize(value.clone()) { - // Ok(repeat) => return Ok(ControlVecElement::Repeat(repeat)), - // Err(e) => { - // // it's gonna fail, so just check what - // errors.push(format!("(repeat {})", e)) - // } - // } - // Both variants failed, return an error with detailed messages Err(serde::de::Error::custom(format!( "ControlVecElement {}", @@ -374,7 +366,7 @@ pub struct LazyBlendRepeatMethod { blend: LazyNodeF32, } -// just an intermediate type...? +// just an intermediate type... #[derive(Debug, Clone)] pub enum LazyVecElementRepeatMethod { Blend(LazyBlendRepeatMethod), @@ -518,69 +510,6 @@ where } } -// impl LazyVecElementRepeat { -// // this one has mixed evals, so it can evaluate the wrapped items too! -// pub fn lazy_expand_vec_repeat_element(&self, ctx: &MixedEvalDefs) -> LivecodeResult> -// where -// Source::Target: Lerpable, -// { -// let mut result: Vec = Vec::with_capacity(self.repeat.len(ctx)? * self.what.len()); - -// let prefix = if self.prefix.is_empty() { -// "i_".to_string() -// } else { -// format!("{}_", self.prefix) -// }; - -// for idx in self.repeat.iter(ctx)? { -// let mut scoped_ctx = ctx.clone(); -// scoped_ctx.add_node(self.ctx.clone()); -// let expr = UnitCellIdx::from_idx2d(idx, 1.0).as_expr_world_context_values(); -// scoped_ctx.set_vals(expr.with_prefix(&prefix)); - -// let mut is_blending: Option = None; - -// for src in &self.what { -// match src { -// LazyControlVecElement::Single(c) => { -// let item = c.with_more_defs(&scoped_ctx)?; -// blend_with_list(&mut result, item, &mut is_blending); -// } -// LazyControlVecElement::Repeat(c) => { -// let nested = c.lazy_expand_vec_repeat_element(&scoped_ctx)?; -// for item in nested.into_iter() { -// blend_with_list(&mut result, item, &mut is_blending); -// } - -// if c.blend_with_next > 0 { -// is_blending = Some(IdxInRange::new_last(c.blend_with_next - 1)); -// } -// } -// } -// } -// } - -// Ok(result) -// } - -// pub fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult -// where -// Source::Target: Lerpable, -// { -// Ok(Self { -// repeat: self.repeat.clone(), -// ctx: self.ctx.clone(), -// prefix: self.prefix.clone(), -// what: self -// .what -// .iter() -// .map(|elem| elem.with_more_defs(ctx)) -// .collect::>>()?, -// blend_with_next: self.blend_with_next, -// }) -// } -// } - impl LivecodeToControl> for LazyControlVecElement where @@ -653,15 +582,6 @@ where .collect::>() .into_iter() .collect_vec(), - // ControlVecElement::UnitCell(c) => vec![ - // c.node.variable_identifiers(), - // c.sequencer.variable_identifiers(), - // ] - // .concat() - // .into_iter() - // .collect::>() - // .into_iter() - // .collect_vec(), } } @@ -675,19 +595,11 @@ where .collect::>() .into_iter() .collect_vec(), - // ControlVecElement::UnitCell(c) => vec![ - // c.node.function_identifiers(), - // c.sequencer.function_identifiers(), - // ] - // .concat() - // .into_iter() - // .collect::>() - // .into_iter() - // .collect_vec(), } } } +// chatgpt wanted to use this instead of IdxInRange, but I can probably switch back to idxinrange... #[derive(Debug, Clone, Copy)] struct BlendWith { offset: usize, // 0 = last item @@ -704,8 +616,6 @@ impl BlendWith { } fn pct(&self) -> f32 { - // Exclusive endpoints: (0, 1) spread across count items. - // Higher pct for items closer to the end (offset larger). (self.offset + 1) as f32 / (self.count + 1) as f32 } @@ -843,7 +753,6 @@ impl ControlVecElementRepeat { is_blending = Some(BlendWith::new(c.blend_with_next)); } - // result.extend(o.into_iter()); offset = new_offset; } } @@ -979,64 +888,6 @@ where } } -// impl IsLazy for LazyControlVecElement -// where -// Source: Clone + Debug + IsLazy + Lerpable, -// Source::Target: Lerpable, -// { -// type Target = Vec; - -// fn eval_lazy(&self, ctx: &MixedEvalDefs) -> LivecodeResult { -// let expanded: Vec = self.lazy_expand_vec(ctx)?; -// expanded.into_iter().map(|s| s.eval_lazy(ctx)).collect() -// } - -// fn with_more_defs(&self, ctx: &MixedEvalDefs) -> LivecodeResult { -// Ok(match self { -// LazyControlVecElement::Single(s) => { -// LazyControlVecElement::Single(s.with_more_defs(ctx)?) -// } -// LazyControlVecElement::Repeat(rep) => { -// LazyControlVecElement::Repeat(rep.with_more_defs(ctx)?) -// } -// }) -// } -// } - -// impl LazyControlVecElement -// where -// Source: Clone + Debug + crate::lazy::IsLazy + Lerpable, -// { -// pub fn eval_lazy_single(&self, expr: &MixedEvalDefs) -> LivecodeResult -// where -// Source::Target: Lerpable, -// { -// match self { -// LazyControlVecElement::Single(s) => Ok(s.clone()), -// LazyControlVecElement::Repeat(s) => { -// let vv = s.lazy_expand_vec_repeat_element(expr)?; -// vv.into_iter() -// .next() -// .ok_or(LivecodeError::raw("eval_lazy_single failed")) -// } -// } -// } -// } - -// impl LazyControlVecElement -// where -// Source: Clone + Debug + crate::lazy::IsLazy + Lerpable, -// Target: Lerpable, -// { -// pub fn eval_lazy(&self, expr: &MixedEvalDefs) -> LivecodeResult> { -// match self { -// LazyControlVecElement::Single(s) => Ok(vec![s.clone()]), -// LazyControlVecElement::Repeat(s) => s.lazy_expand_vec_repeat_element(expr), -// } -// } -// } - -// impl ControlVecElement impl ControlVecElement where Source: Clone + Debug, @@ -1065,64 +916,10 @@ where // ControlVecElement::UnitCell(c) => c.eval_and_expand_vec(w), } } - - // pub fn map_source(&self, f: F) -> LivecodeResult> - // where - // F: Fn(&Source) -> LivecodeResult, - // Target: Clone + Debug, - // { - // match self { - // ControlVecElement::Single(c) => f(c).map(|t| ControlVecElement::Single(t)), - // ControlVecElement::Repeat(r) => Ok({ - // let mapped_what = r - // .what - // .iter() - // .map(|e| e.map_source(&f)) - // .collect::>>()?; - // ControlVecElement::Repeat(ControlVecElementRepeat { - // repeat: r.repeat.clone(), - // prefix: r.prefix.clone(), - // what: mapped_what, - // }) - // }), - // } - // } } -// impl LazyControlVecElement> -// where -// Inner: Clone + Debug + IsLazy, -// { -// pub fn lazy_expand_vec( -// &self, -// ctx: &MixedEvalDefs, -// ) -> LivecodeResult>> -// where -// Inner::Target: Lerpable, -// { -// match self { -// LazyControlVecElement::Single(c) => Ok(vec![c.clone()]), -// LazyControlVecElement::Repeat(c) => c.lazy_expand_vec_repeat_element(ctx), -// } -// } -// } +// chatgpt can implement my deserializers... -// impl LazyControlVecElement -// where -// LazyElement: Clone + Debug + IsLazy + Lerpable, -// { -// pub fn lazy_expand_vec(&self, ctx: &MixedEvalDefs) -> LivecodeResult> -// where -// LazyElement::Target: Lerpable, -// { -// match self { -// LazyControlVecElement::Single(c) => Ok(vec![c.clone()]), -// LazyControlVecElement::Repeat(c) => c.lazy_expand_vec_repeat_element(ctx), -// } -// } -// } - -// chatgpt #[cfg(feature = "schemars")] impl schemars::JsonSchema for ControlVecElement where @@ -1187,9 +984,7 @@ where } } -// chatgpt -// impl<'de, Sequencer, ControlSequencer, Source> Deserialize<'de> -// for ControlVecElement +// chatgpt can implement my deserializers... impl<'de, Source> Deserialize<'de> for ControlVecElement where Source: Deserialize<'de> + Clone + Debug, @@ -1210,7 +1005,6 @@ where Err(e) => errors.push(format!("{}", e)), } - // match ControlVecElementRepeat::deserialize(value.clone()) { Ok(repeat) => return Ok(ControlVecElement::Repeat(repeat)), Err(e) => { @@ -1219,14 +1013,6 @@ where } } - // match VecUnitCell::deserialize(value.clone()) { - // Ok(repeat) => return Ok(ControlVecElement::Repeat(repeat)), - // Err(e) => { - // // it's gonna fail, so just check what - // errors.push(format!("(repeat {})", e)) - // } - // } - // Both variants failed, return an error with detailed messages Err(serde::de::Error::custom(format!( "ControlVecElement {}", diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index c4536b7..5814a14 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -38,27 +38,6 @@ impl TmpUnitCells( -// world_ctx: &'a LivecodeWorldState, -// prefix: &'a str, -// unit_cell_ctx: &'a UnitCellContext, -// maybe_node: Option<&'a AdditionalContextNode>, -// ) -> LivecodeResult { -// // world_ctx is currently just the World, so first attach the unit cell world state - -// let mut world_state = ; - -// // let unit_cell_world_ctx = world_state.ctx_mut(); - -// // now update the unit_cell context to have the node -// // if let Some(node) = maybe_node { -// // node.eval_raw(unit_cell_world_ctx)?; -// // } - -// // great, now we have it built. return it! -// Ok(world_state) -// } - impl TmpUnitCells where CtxSource: UnitCellCreator, @@ -83,9 +62,6 @@ where // - unit cell location // it doesn't have sequencer ctx yet, we'll add that next - // let unit_cell_world_ctx_result = - // create_unit_cell(world_ctx, &self.prefix, ctx, unit_cell_ctx.as_ref()); - let unit_cell_world_ctx_result = world_ctx.clone_to_unitcell(ctx, &self.prefix, unit_cell_ctx); @@ -411,8 +387,6 @@ impl UnitCellLookup { } None } - - // pub fn get_vec2(&self, v: Vec2) {} } pub struct IdxAndOffset { @@ -686,7 +660,6 @@ impl UnitCellContext { } pub fn transform_one_point_with_skew(&self, v: Vec2) -> Vec2 { - // self.detail.transform_with_skew(&vec![v]).clone_to_vec()[0] v.transform_with(&self.detail.transform_with_skew_mat4()) } @@ -977,17 +950,6 @@ impl UnitCellDetails { } } - // fn transform_with_skew(&self, face: &F) -> Polyline { - // let vs = face - // .into_iter_vec2() - // .map(|x| match self { - // UnitCellDetails::Wallpaper(d) => d.transform_with_skew(x), - // UnitCellDetails::Function(d) => d.transform_with_skew(x), - // }) - // .collect_vec(); - // Polyline::new(vs) - // } - pub fn transform_no_skew_one_point(&self, v: Vec2) -> Vec2 { self.transform_no_skew(&vec![v]).clone_to_vec()[0] } @@ -1082,7 +1044,6 @@ impl UnitCellDetailsWallpaper { pub fn transform_no_skew(&self, v: &F) -> F { let m = self.transform_no_skew_mat(); v.transform_with(&m) - } // how to move the location of something diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs index 52a80f5..27d94cf 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_cached.rs @@ -22,7 +22,6 @@ pub(crate) struct LivecodeVariantReceiver {} pub(crate) struct LivecodeReceiver { ident: syn::Ident, data: ast::Data, - // ctrl: Option, // this one should be the struct name... } pub fn impl_cache_traits(ast: DeriveInput) -> TokenStream2 { @@ -35,8 +34,6 @@ pub fn impl_cache_traits(ast: DeriveInput) -> TokenStream2 { } fn parse_cache(name: &syn::Ident, fields: &[LivecodeFieldReceiver]) -> TokenStream2 { - - let mut getter_funcs: Vec = vec![]; let mut check_funcs: Vec = vec![]; let mut init_funcs: Vec = vec![]; @@ -56,7 +53,6 @@ fn parse_cache(name: &syn::Ident, fields: &[LivecodeFieldReceiver]) -> TokenStre let inside_type = data.inside_type().to_quote(); - let func = quote! { pub fn #ident(&self) -> &#inside_type { self.#ident.get_or_init(|| self.#expected_compute_ident()) @@ -65,36 +61,34 @@ fn parse_cache(name: &syn::Ident, fields: &[LivecodeFieldReceiver]) -> TokenStre getter_funcs.push(func); - let check = quote!{ + let check = quote! { self.#ident.has_been_set() }; check_funcs.push(check); - let init = quote!{ + let init = quote! { self.#ident() }; init_funcs.push(init); - let to_be_filled = quote!{ + let to_be_filled = quote! { #ident: CachedCompute::new() }; to_be_filled_funcs.push(to_be_filled); } else { - // they're passed in with the same name in the arguments let orig_type = f.ty.clone(); - let new_conf_argument = quote!{ + let new_conf_argument = quote! { #ident: #orig_type }; conf_arguments.push(new_conf_argument); - let to_be_filled = quote!{ + let to_be_filled = quote! { #ident }; to_be_filled_funcs.push(to_be_filled); - } } } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs index edd4328..5adc75a 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_lazy.rs @@ -30,7 +30,6 @@ impl LazyFieldType { ControlType::AnglePi => { quote! { murrelet_livecode::lazy::LazyNodeF32 } } - // ControlType::LinSrgbaUnclamped => quote!{[murrelet_livecode::livecode::ControlF32; 4]}, _ => panic!("unitcell doesn't have this one yet"), } } @@ -43,18 +42,13 @@ impl LazyFieldType { ) -> TokenStream2 { match self.0 { ControlType::F32_2 => { - // quote! { murrelet_livecode::lazy::eval_lazy_vec2(&#ident, ctx) } quote! { #ident.eval_lazy(ctx) } } ControlType::F32_3 => { quote! { #ident.eval_lazy(ctx) } - - // quote! { murrelet_livecode::lazy::eval_lazy_vec3(&#ident, ctx) } } ControlType::Color => { quote! { #ident.eval_lazy(ctx) } - - // quote! { murrelet_livecode::lazy::eval_lazy_color(&#ident, ctx) } } ControlType::Bool => quote! {#ident.eval_lazy(ctx)? > 0.0}, ControlType::AnglePi => { @@ -82,32 +76,12 @@ impl LazyFieldType { match self.0 { ControlType::F32_2 => { quote! { #name: self.#name.eval_lazy(ctx)? } - - // glam::vec2( - // self.#name[0].eval_lazy(ctx)? as f32, - // self.#name[1].eval_lazy(ctx)? as f32 - // )} } ControlType::F32_3 => { quote! { #name: self.#name.eval_lazy(ctx)? } - - // quote! { - // #name: glam::vec3( - // self.#name[0].eval_lazy(ctx)? as f32, - // self.#name[1].eval_lazy(ctx)? as f32, - // self.#name[2].eval_lazy(ctx)? as f32 - // ) - // } } ControlType::Color => { quote! { #name: self.#name.eval_lazy(ctx)? } - - // quote! {#name: murrelet_common::MurreletColor::hsva( - // self.#name[0].eval_lazy(ctx)? as f32, - // self.#name[1].eval_lazy(ctx)? as f32, - // self.#name[2].eval_lazy(ctx)? as f32, - // self.#name[3].eval_lazy(ctx)? as f32 - // )} } ControlType::Bool => quote! {#name: self.#name.eval_lazy(ctx)? > 0.0}, ControlType::LazyNodeF32 => quote! {#name: self.#name.add_more_defs(ctx)? }, @@ -195,19 +169,10 @@ impl LazyFieldType { ControlType::Color => { quote! { self.0.eval_lazy(ctx)? } } - // ControlType::F32_2 => { - // quote! {vec2(self.0[0].eval_lazy(ctx)? as f32, self.0[1].eval_lazy(ctx)? as f32)} - // } - // // ControlType::F32_3 => quote!{murrelet_livecode::livecode::ControlF32::vec3(&self.0, w)}, - // ControlType::Color => { - // quote! {MurreletColor::hsva(self.0[0].eval_lazy(ctx)? as f32, self.0[1].eval_lazy(ctx)? as f32, self.0[2].eval_lazy(ctx)? as f32, self.0[3].eval_lazy(ctx)? as f32)} - // } - // ControlType::LinSrgbaUnclamped => quote!{murrelet_livecode::livecode::ControlF32::hsva_unclamped(&self.0, w)}, ControlType::Bool => quote! {self.0.eval_lazy(ctx)? > 0.0}, ControlType::AnglePi => { quote! {murrelet_common::AnglePi::new(self.0.eval_lazy(ctx)?)} } - // _ => quote!{self.0.eval_lazy(ctx)? as #orig_ty} _ => { let f32_out = match (idents.data.f32min, idents.data.f32max) { (None, None) => quote! {self.0.eval_lazy(ctx)?}, diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs index 914eee1..95a1102 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_livecode.rs @@ -20,7 +20,6 @@ impl LivecodeFieldType { ControlType::Color => quote! {[murrelet_livecode::livecode::ControlF32; 4]}, ControlType::ColorUnclamped => quote! {[murrelet_livecode::livecode::ControlF32; 4]}, ControlType::AnglePi => quote! {murrelet_livecode::livecode::ControlF32}, - // ControlType::EvalExpr => quote! {murrelet_livecode::expr::ControlExprF32}, ControlType::LazyNodeF32 => quote! {murrelet_livecode::lazy::ControlLazyNodeF32}, } } @@ -36,7 +35,6 @@ impl LivecodeFieldType { ControlType::F32 => quote! {murrelet_livecode::livecode::ControlF32}, ControlType::Bool => quote! {murrelet_livecode::livecode::ControlBool}, ControlType::AnglePi => quote! {murrelet_livecode::livecode::ControlF32}, - // ControlType::EvalExpr => quote! {murrelet_livecode::expr::ControlExprF32}, ControlType::LazyNodeF32 => quote! {murrelet_livecode::lazy::ControlLazyNodeF32}, } } @@ -84,8 +82,6 @@ impl LivecodeFieldType { f32min: Option, f32max: Option, ) -> TokenStream2 { - // let name = idents.name(); - // let orig_ty = idents.orig_ty(); match self.0 { ControlType::F32_2 => quote! {self.#name.map(|name| name.o(w)?)}, ControlType::F32_3 => quote! {self.#name.map(|name| name.o(w)?)}, @@ -429,10 +425,6 @@ impl GenFinal for FieldTokensLivecode { idents: StructIdents, _parent_ident: syn::Ident, ) -> FieldTokensLivecode { - // let serde = idents.serde(false).clone(); - // let name = idents.name().clone(); - // let _orig_type = idents.orig_ty().clone(); - // we need to get the internal struct type let orig_ty = idents.orig_ty(); let parsed_type_info = ident_from_type(&orig_ty); @@ -602,7 +594,6 @@ impl GenFinal for FieldTokensLivecode { fn from_option(idents: StructIdents) -> FieldTokensLivecode { let serde = idents.serde().clone(); let name = idents.name().clone(); - // let _orig_type = idents.orig_ty().clone(); let s = ident_from_type(&idents.orig_ty()); @@ -721,12 +712,6 @@ impl GenFinal for FieldTokensLivecode { #name: murrelet_livecode::types::eval_and_expand_vec_list(&self.#name, w)? } } - - // quote! { - // #name: self.#name.iter() - // .map(|elem| elem.o(|source| source.o(w))) - // .collect::, _>>()? - // } } VecDepth::VecVec => { quote! { @@ -781,13 +766,7 @@ impl GenFinal for FieldTokensLivecode { let item = internal_row.to_control(); - result.push(murrelet_livecode::types::DeserLazyControlVecElement::raw(item)) - // internal_row - // .iter() - // .map(|x| x.to_control()) - // .collect::>() - // ) } result } @@ -816,12 +795,7 @@ impl GenFinal for FieldTokensLivecode { let mut result = Vec::with_capacity(self.#name.len()); for internal_row in &self.#name { let c = internal_row.to_control(); - result.push(c - // internal_row - // .iter() - // .map(|x| x.to_control()) - // .collect::>() - ) + result.push(c) } result } @@ -877,7 +851,6 @@ impl GenFinal for FieldTokensLivecode { let items = internal_row.variable_identifiers(); result.extend( items - // internal_row.iter().map(|x| x.variable_identifiers()).into_iter().flatten().collect::>().into_iter() ); } result @@ -916,9 +889,7 @@ impl GenFinal for FieldTokensLivecode { let mut result = Vec::with_capacity(self.#name.len()); for internal_row in &self.#name { let item = internal_row.function_identifiers(); - result.extend(item - // internal_row.iter().map(|x| x.function_identifiers()).into_iter().flatten().collect::>().into_iter() - ); + result.extend(item); } result } @@ -947,7 +918,6 @@ impl GenFinal for FieldTokensLivecode { let for_struct = { let new_ty = { let DataFromType { main_type, .. } = ident_from_type(&orig_ty); - // let ref_lc_ident = idents.config.new_ident(main_type.clone()); let ref_lc_ident = Self::new_ident(main_type.clone()); quote! {#ref_lc_ident} }; @@ -1191,15 +1161,8 @@ impl GenFinal for FieldTokensLivecode { let for_struct = { let new_ty = { let DataFromType { main_type, .. } = ident_from_type(&orig_ty); - - // if main_type.to_string() == "LazyVec2" { - // quote! { murrelet_livecode::lazy::ControlLazyVec2 } - // } else { - // let ref_lc_ident = Self::new_ident(main_type.clone()); - // quote! {#ref_lc_ident} + // eh, this is hacky catch_special_types(main_type.clone()) - // } - // let ref_lc_ident = idents.config.new_ident(main_type.clone()); }; quote! {#serde #name: #new_ty} diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs index 766dace..347d83e 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/derive_nestedit.rs @@ -146,8 +146,6 @@ impl GenFinal for FieldTokensNestEdit { } fn from_newtype_struct(_idents: StructIdents, parent_ident: syn::Ident) -> FieldTokensNestEdit { - // let name = idents.control_type(); - // these will fall to todo!() let for_nestedit = quote! { #parent_ident(self.0.nest_update(mods)) @@ -302,9 +300,6 @@ impl GenFinal for FieldTokensNestEdit { let name = idents.name(); let yaml_name = name.to_string(); - // let s = ident_from_type(&idents.orig_ty()); - // let ctrl = s.second_how_to.unwrap().get_control_type(); - // we'll just use the trait! (unless it's none, then we bail let for_nestedit = match idents.how_to_control_this() { HowToControlThis::WithNone(_) => quote! { diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs index 7e36f38..c5a9837 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs @@ -14,7 +14,6 @@ mod toplevel; mod derive_cached; use darling::FromDeriveInput; -// use derive_boop::FieldTokensBoop; use derive_graphics_trait::impl_graphics_trait; use derive_lazy::FieldTokensLazy; use derive_livecode::FieldTokensLivecode; @@ -67,10 +66,7 @@ pub fn murrelet_livecode_derive_livecode(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as syn::DeriveInput); let ast_receiver = LivecodeReceiver::from_derive_input(&ast).unwrap(); - // livecode_parse_ast(ast_receiver.clone()).into() - // and then i realized i still need nested and lerpable too.... - let livecode = livecode_parse_ast(ast_receiver.clone()); let nested = nestedit_parse_ast(ast_receiver.clone()); diff --git a/murrelet_perform/Cargo.toml b/murrelet_perform/Cargo.toml index de784c7..19d26ea 100644 --- a/murrelet_perform/Cargo.toml +++ b/murrelet_perform/Cargo.toml @@ -16,13 +16,6 @@ schemars = [ "murrelet_livecode_macros/schemars", "murrelet_livecode_derive/schemars", ] -# murrelet_gui = [ -# "dep:murrelet_gui", -# "murrelet_livecode/murrelet_gui", -# "murrelet_draw/murrelet_gui", -# "murrelet_livecode_macros/murrelet_gui", -# "murrelet_livecode_derive/murrelet_gui", -# ] [dependencies] murrelet_common = "0.1.2" diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 1074a8f..e0d0331 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -114,11 +114,9 @@ impl SvgDrawConfig { let translation_to_final = vec2(full_target_width, full_target_width); let s = self.target_size / size; - // let scale = vec3(s, s, 1.0); // aiming for 100mm by 100mm, going from 0 to 10 // operations go right to left! - // Mat4::from_translation(translation_to_final) * Mat4::from_scale(scale) SimpleTransform2d::new(vec![ SimpleTransform2dStep::translate(translation_to_final), SimpleTransform2dStep::scale_both(s), @@ -700,13 +698,6 @@ where load_funcs: &AssetLoaders, ) -> LivecodeResult> { let controlconfig = ControlConfType::parse(&conf)?; - // .map_err(|err| { - // if let Some(error) = err.location() { - // LivecodeError::SerdeLoc(error, err.to_string()) - // } else { - // LivecodeError::Raw(err.to_string()) - // } - // })?; Self::new_full(controlconfig, None, livecode_src, load_funcs, None) } @@ -828,10 +819,6 @@ where if self.lerp_pct >= 1.0 { // todo, just move it out... if let Some(new_target) = &self.queued_configcontrol { - // self.prev_controlconfig = self.controlconfig.clone(); - // self.controlconfig = new_target.clone(); - // self.lerp_pct = 0.0; - // self.queued_configcontrol = None; self.update_config_directly(new_target.clone())?; } } @@ -897,11 +884,6 @@ where pub fn update_config_to(&mut self, text: &str) -> Result<(), String> { match ControlConfType::cb_reload_and_update_info(&mut self.util, text) { Ok(d) => { - // self.prev_controlconfig = self.controlconfig.clone(); - // self.controlconfig = d; - // self.queued_configcontrol = None; - // self.lerp_pct = 0.0; - // Ok(()) self.update_config_directly(d).map_err(|x| x.to_string()) } Err(e) => Err(e), diff --git a/murrelet_perform/src/reload.rs b/murrelet_perform/src/reload.rs index 4d9cbf3..1dc5348 100644 --- a/murrelet_perform/src/reload.rs +++ b/murrelet_perform/src/reload.rs @@ -163,11 +163,6 @@ pub trait LiveCoderLoader: Sized { } Err(err) => { util.update_info_error(); - // if let Some(error) = err.location() { - // Err(LivecodeError::SerdeLoc(format!("{},{}", location.line(), location.column()), err.to_string())) - // } else { - // Err(LivecodeError::Raw(err.to_string())) - // } Err(err) } } diff --git a/murrelet_schema_derive/src/parser.rs b/murrelet_schema_derive/src/parser.rs index f7b9303..7f69149 100644 --- a/murrelet_schema_derive/src/parser.rs +++ b/murrelet_schema_derive/src/parser.rs @@ -109,15 +109,6 @@ where let idents = ParsedFieldIdent { name: name.clone() }; - // "external" => quote! {}, - // "internal" => default, - // "untagged" => quote! {#[serde(untagged)]}, - // _ => default, - // let is_external = match &e.enum_tag.map(|x| x.as_str()) { - // Some("external") => true, - // None => false, - // _ => unimplemented!("enum type not implemented") - // }; let is_untagged = if let Some(enum_tag) = &e.enum_tag { if enum_tag.as_str() == "external" { true diff --git a/murrelet_src_osc/src/osc.rs b/murrelet_src_osc/src/osc.rs index 5c1a77f..f6b07b5 100644 --- a/murrelet_src_osc/src/osc.rs +++ b/murrelet_src_osc/src/osc.rs @@ -64,8 +64,6 @@ impl IsLivecodeSrc for OscMng { LivecodeValue::Int(_) => {} } } - - // Ok(()) } } @@ -128,8 +126,6 @@ pub struct OscCxn { impl OscCxn { pub fn check_and_maybe_update(&self, r: &mut OscValues) -> Result<(), mpsc::TryRecvError> { self.osc_rx.try_recv().map(|x| { - // r.msg = Some(x); - // println!("osc message {:?}", x); for (name, new_val) in x.to_livecode_vals().into_iter() { diff --git a/murrelet_svg/Cargo.toml b/murrelet_svg/Cargo.toml index d82fce9..0cf6bff 100644 --- a/murrelet_svg/Cargo.toml +++ b/murrelet_svg/Cargo.toml @@ -9,7 +9,6 @@ license = "AGPL-3.0-or-later" [features] schemars = ["murrelet_perform/schemars", "murrelet_draw/schemars"] -# murrelet_gui = ["murrelet_perform/murrelet_gui", "murrelet_draw/murrelet_gui"] [dependencies] murrelet_common = "0.1.2" diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index 7448eb9..de6a2e7 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -209,11 +209,6 @@ impl ToSvgMatrix for Mat4 { } } -// // for a type that means something, e.g. "style fro a shape" -// trait AddSvgAttributes { -// fn add_svg_attributes(&self, p: T) -> T; -// } - // for component types, e.g. "color" trait GetSvgAttributes { fn get_svg_attributes(&self) -> MurreletSvgAttributes; @@ -251,15 +246,6 @@ impl GetSvgAttributes for RGBandANewtype { } } -// impl AddSvgAttributes for MurreletColorStyle { -// // fn add_svg_attributes(&self, p: T) -> T { -// // let mut p = p; -// // p.assign("fill-rule", "evenodd"); -// // update_node(&mut p, &self.get_svg_attributes()); -// // p -// // } -// } - impl GetSvgAttributes for MurreletColorStyle { fn get_svg_attributes(&self) -> MurreletSvgAttributes { match self { @@ -270,13 +256,6 @@ impl GetSvgAttributes for MurreletColorStyle { } } -// impl AddSvgAttributes for MurreletPath { -// fn add_svg_attributes(&self, p: T) -> T { -// let mut p = p; -// update_node(&mut p, &self.get_svg_attributes()); -// p -// } -// } impl GetSvgAttributes for MurreletPath { fn get_svg_attributes(&self) -> MurreletSvgAttributes { if let Some(t) = self.transform() { @@ -360,53 +339,6 @@ impl GetSvgAttributes for MurreletStyle { } } -// impl AddSvgAttributes for MurreletStyle { -// fn add_svg_attributes(&self, p: T) -> T { -// let mut p = p; - -// p.assign("fill-rule", "evenodd"); - -// match self.drawing_plan() { -// murrelet_draw::draw::MurreletDrawPlan::Shader(fill) => { -// p.assign("fill", fill.get_svg_attributes()) -// } -// murrelet_draw::draw::MurreletDrawPlan::DebugPoints(_) => unimplemented!(), -// murrelet_draw::draw::MurreletDrawPlan::FilledClosed => { -// p.assign("fill", self.color.get_svg_attributes()); - -// if self.stroke_weight > 0.0 { -// p.assign("stroke-width", self.stroke_weight); -// p.assign("stroke-linejoin", "round"); -// p.assign("stroke-linecap", "round"); -// p.assign("stroke", self.stroke_color.get_svg_attributes()); -// } -// } -// murrelet_draw::draw::MurreletDrawPlan::Outline => { -// p.assign("fill", "none"); - -// if self.stroke_weight > 0.0 { -// p.assign("stroke-width", self.stroke_weight); -// p.assign("stroke-linejoin", "round"); -// p.assign("stroke-linecap", "round"); -// p.assign("stroke", self.color.get_svg_attributes()); -// } -// } -// murrelet_draw::draw::MurreletDrawPlan::Line => { -// p.assign("fill", "none"); - -// if self.stroke_weight > 0.0 { -// p.assign("stroke-width", self.stroke_weight); -// p.assign("stroke-linejoin", "round"); -// p.assign("stroke-linecap", "round"); -// p.assign("stroke", self.color.get_svg_attributes()); -// } -// } -// } - -// p -// } -// } - impl GetSvgAttributes for StyledPath { fn get_svg_attributes(&self) -> MurreletSvgAttributes { let mut c = self.annotations.get_svg_attributes(); @@ -414,13 +346,6 @@ impl GetSvgAttributes for StyledPath { c.add_other(&self.style.get_svg_attributes()); c } - // fn add_svg_attributes(&self, p: T) -> T { - // let mut p = p; - // p = self.annotations.add_svg_attributes(p); - // p = self.path.add_svg_attributes(p); - // p = self.style.add_svg_attributes(p); - // p - // } } impl GetSvgAttributes for MurreletPathAnnotation { @@ -433,16 +358,6 @@ impl GetSvgAttributes for MurreletPathAnnotation { } } -// impl AddSvgAttributes for MurreletPathAnnotation { -// fn add_svg_attributes(&self, p: T) -> T { -// let mut p = p; -// for (k, v) in self.vals() { -// p.assign(k.to_string(), v.to_string()); -// } -// p -// } -// } - pub trait ToSvgPath { fn make_group(&self) -> Option; fn make_pattern(&self) -> Option<(String, svg::node::element::Pattern)>; @@ -632,13 +547,6 @@ impl SvgDocCreator { doc = doc.add(centering_group); doc - - // for (name, layer) in paths.layers.iter() { - // let (g, _) = self.make_layer(name, layer, true); - // doc.append(g); - // } - - // doc } pub fn create_guides(&self) -> Vec> { @@ -876,11 +784,6 @@ impl SvgPathCache { } } -// pub trait ToSvg { -// fn to_svg(&self) -> Option; - -// } - // why am i doing this again, it is a good question impl ToSvgData for SvgPathDef { fn to_svg_data(&self) -> Option { @@ -954,29 +857,9 @@ impl ToSvgData for CurveDrawer { // special cases for circles! if let Some((hemi1, hemi2)) = a.is_full_circle_then_split() { - // let params = ( - // a.radius, - // a.radius, // same as other rad because it's a circle - // 0.0, // angle of ellipse doesn't matter, so 0 - // if a.is_large_arc() { 1 } else { 0 }, // large arc flag - // if a.is_ccw() { 1 } else { 0 }, // sweep-flag - // last_point.x, - // last_point.y, - // ); - path = path.elliptical_arc_to(hemi1.svg_params().to_vec()); path = path.elliptical_arc_to(hemi2.svg_params().to_vec()); } else { - // let params = ( - // a.radius, - // a.radius, // same as other rad because it's a circle - // 0.0, // angle of ellipse doesn't matter, so 0 - // if a.is_large_arc() { 1 } else { 0 }, // large arc flag - // if a.is_ccw() { 1 } else { 0 }, // sweep-flag - // last_point.x, - // last_point.y, - // ); - path = path.elliptical_arc_to(a.svg_params().to_vec()); } @@ -1017,17 +900,5 @@ impl ToSvgData for Vec { // todo, hmmm, see if we can consolidate this. let cd = CurveDrawer::new_simple_points(self.clone(), false); cd.to_svg_data() - - // // whee, flip y's so we stop drawing everything upside down - - // let mut curr_item: Vec2 = *self.first().unwrap(); - - // let mut data = Data::new().move_to((curr_item.x, -curr_item.y)); - - // for loc in self[1..].iter() { - // data = data.line_by((loc.x - curr_item.x, -(loc.y - curr_item.y))); - // curr_item = *loc; - // } - // Some(data) } } From c95dc2723680ba065d2b4fd6d0a777fdf0d6dcb2 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 6 Feb 2026 13:04:36 -0500 Subject: [PATCH 143/149] clippy --- murrelet_common/src/assets.rs | 2 +- murrelet_common/src/color.rs | 2 +- murrelet_common/src/geometry.rs | 4 +- murrelet_common/src/idx.rs | 2 +- murrelet_common/src/lib.rs | 25 +- murrelet_common/src/transform.rs | 4 +- murrelet_common/src/triangulate.rs | 6 + murrelet_draw/Cargo.toml | 2 +- murrelet_draw/src/compass.rs | 30 +- murrelet_draw/src/curve_drawer.rs | 16 +- murrelet_draw/src/draw.rs | 7 +- murrelet_draw/src/drawable.rs | 6 +- murrelet_draw/src/lib.rs | 2 +- murrelet_draw/src/newtypes.rs | 2 +- murrelet_draw/src/scaffold.rs | 15 +- murrelet_draw/src/sequencers.rs | 8 +- murrelet_draw/src/serialize.rs | 2 +- murrelet_draw/src/style.rs | 2 +- murrelet_draw/src/svg.rs | 2 +- murrelet_draw/src/tesselate.rs | 34 +- murrelet_draw/src/transform2d.rs | 12 +- murrelet_gen/src/lib.rs | 1 - murrelet_gen_derive/src/derive_gen.rs | 4 +- murrelet_gen_derive/src/gen_methods.rs | 492 +++++++++--------- murrelet_gpu/src/compute.rs | 6 +- murrelet_gpu/src/device_state.rs | 8 +- murrelet_gpu/src/editable_shaders.rs | 44 +- murrelet_gpu/src/gpu_macros.rs | 9 +- murrelet_gpu/src/graphics_ref.rs | 28 +- murrelet_gpu/src/lib.rs | 2 +- murrelet_gpu/src/window.rs | 4 +- murrelet_gui/examples/tests_schema.rs | 2 - murrelet_gui/src/lib.rs | 4 +- murrelet_gui_derive/src/parser.rs | 9 +- murrelet_livecode/src/app_src.rs | 2 +- murrelet_livecode/src/cachedcompute.rs | 6 + murrelet_livecode/src/expr.rs | 4 +- murrelet_livecode/src/lazy.rs | 21 +- murrelet_livecode/src/lib.rs | 2 +- murrelet_livecode/src/livecode.rs | 20 +- murrelet_livecode/src/state.rs | 2 +- murrelet_livecode/src/types.rs | 14 +- murrelet_livecode/src/unitcells.rs | 12 +- .../murrelet_livecode_derive/src/lib.rs | 3 +- .../murrelet_livecode_derive/src/parser.rs | 2 +- murrelet_perform/src/asset_loader.rs | 2 +- murrelet_perform/src/load.rs | 4 +- murrelet_perform/src/perform.rs | 43 +- murrelet_perform/src/reload.rs | 12 +- murrelet_schema_derive/src/parser.rs | 6 +- murrelet_svg/src/svg.rs | 41 +- 51 files changed, 480 insertions(+), 514 deletions(-) diff --git a/murrelet_common/src/assets.rs b/murrelet_common/src/assets.rs index 14efe06..5757fa6 100644 --- a/murrelet_common/src/assets.rs +++ b/murrelet_common/src/assets.rs @@ -146,7 +146,7 @@ impl Assets { Self { vectors: VectorLayersAssetLookup::empty(), rasters: RasterAssetLookup::empty(), - json: JsonAssetLookup::empty() + json: JsonAssetLookup::empty(), } } diff --git a/murrelet_common/src/color.rs b/murrelet_common/src/color.rs index c7a215b..bfe212d 100644 --- a/murrelet_common/src/color.rs +++ b/murrelet_common/src/color.rs @@ -163,7 +163,7 @@ impl MurreletColor { } pub fn with_saturation(&self, sat: f32) -> MurreletColor { - let mut c = self.clone(); + let mut c = *self; c.0[1] = sat; c } diff --git a/murrelet_common/src/geometry.rs b/murrelet_common/src/geometry.rs index 4d5f105..180b8c1 100644 --- a/murrelet_common/src/geometry.rs +++ b/murrelet_common/src/geometry.rs @@ -509,7 +509,7 @@ impl SpotOnCurve { } pub fn set_angle>(&self, new: A) -> Self { - let mut c = self.clone(); + let mut c = *self; c.angle = new.into(); c } @@ -849,7 +849,7 @@ pub trait ToVec2 { impl ToVec2 for Vec2 { fn to_vec2(&self) -> Vec2 { - self.clone() + *self } } diff --git a/murrelet_common/src/idx.rs b/murrelet_common/src/idx.rs index 49c698b..2bfc30c 100644 --- a/murrelet_common/src/idx.rs +++ b/murrelet_common/src/idx.rs @@ -336,7 +336,7 @@ impl IdxInRange2d { } pub fn is_alternate(&self) -> bool { - (self.i.i() % 2 != 0) ^ (self.j.i() % 2 == 0) + !self.i.i().is_multiple_of(2) ^ self.j.i().is_multiple_of(2) } pub fn is_last_x(&self) -> bool { diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 87188fa..6d42dc8 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -137,12 +137,12 @@ pub fn epoch_time_us() -> u128 { { use std::time::SystemTime; use std::time::UNIX_EPOCH; - let s = SystemTime::now() + + + SystemTime::now() .duration_since(UNIX_EPOCH) .expect("wat") - .as_micros(); - - s + .as_micros() } } @@ -1018,7 +1018,6 @@ pub fn lerpify_vec_vec3( lerped.into_iter().map(|v| v.into()).collect_vec() } - // todo, did i end up using this? #[derive(Clone, Copy, Debug)] pub struct Dim2d { @@ -1107,7 +1106,7 @@ pub fn rgb_to_hex(r: f32, g: f32, b: f32) -> String { pub trait MurreletIterHelpers { type T: Clone; fn to_iter<'a>(&'a self) -> std::slice::Iter<'a, Self::T>; - fn as_vec_ref<'a>(&'a self) -> &'a Vec; + fn as_vec_ref(&self) -> &Vec; fn map_iter_collect(&self, f: F) -> Vec where @@ -1117,7 +1116,7 @@ pub trait MurreletIterHelpers { } fn owned_iter(&self) -> std::vec::IntoIter { - self.to_iter().map(|x| x.clone()).collect_vec().into_iter() + self.to_iter().cloned().collect_vec().into_iter() } fn take_count(&self, amount: usize) -> Vec { @@ -1127,25 +1126,25 @@ pub trait MurreletIterHelpers { fn prev_curr_next_loop_iter<'a>( &'a self, ) -> Box + 'a> { - prev_curr_next_loop_iter(&self.as_vec_ref()) + prev_curr_next_loop_iter(self.as_vec_ref()) } fn prev_curr_next_no_loop_iter<'a>( &'a self, ) -> Box + 'a> { - prev_curr_next_no_loop_iter(&self.as_vec_ref()) + prev_curr_next_no_loop_iter(self.as_vec_ref()) } fn curr_next_loop_iter<'a>( &'a self, ) -> Box + 'a> { - curr_next_loop_iter(&self.as_vec_ref()) + curr_next_loop_iter(self.as_vec_ref()) } fn curr_next_no_loop_iter<'a>( &'a self, ) -> Box + 'a> { - curr_next_no_loop_iter(&self.as_vec_ref()) + curr_next_no_loop_iter(self.as_vec_ref()) } } @@ -1155,7 +1154,7 @@ impl MurreletIterHelpers for Vec { self.iter() } - fn as_vec_ref<'a>(&'a self) -> &'a Vec { - &self + fn as_vec_ref(&self) -> &Vec { + self } } diff --git a/murrelet_common/src/transform.rs b/murrelet_common/src/transform.rs index 7aea222..7a43811 100644 --- a/murrelet_common/src/transform.rs +++ b/murrelet_common/src/transform.rs @@ -181,7 +181,7 @@ where A: Transformable, { fn transform_with(&self, t: &T) -> Self { - self.into_iter().map(|x| x.transform_with(t)).collect_vec() + self.iter().map(|x| x.transform_with(t)).collect_vec() } } @@ -256,7 +256,7 @@ impl SimpleTransform2d { _ => {} } } - return true; + true } // experimental diff --git a/murrelet_common/src/triangulate.rs b/murrelet_common/src/triangulate.rs index dc49aca..35edd35 100644 --- a/murrelet_common/src/triangulate.rs +++ b/murrelet_common/src/triangulate.rs @@ -68,6 +68,12 @@ pub struct Triangulate { pub order: Vec, } +impl Default for Triangulate { + fn default() -> Self { + Self::new() + } +} + impl Triangulate { pub fn new() -> Self { Triangulate { diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index 54672d9..19ea0e9 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -23,7 +23,7 @@ murrelet_livecode = { version = "0.1.2", default-features = false } murrelet_livecode_macros = { version = "0.1.2", default-features = false } murrelet_livecode_derive = { version = "0.1.2", default-features = false } -lerpable = "0.0.2" +lerpable = { version = "0.0.2", features = ["glam"] } serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index 1bb8ad2..8df7aaf 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -202,6 +202,12 @@ pub struct InteractiveCompassBuilder { last_action: LastActionNames, } +impl Default for InteractiveCompassBuilder { + fn default() -> Self { + Self::new() + } +} + impl InteractiveCompassBuilder { pub fn new() -> Self { Self { @@ -229,7 +235,9 @@ impl InteractiveCompassBuilder { pub fn new_segments(&mut self, dir: &CompassAction) -> Vec { // here we go! - let r = match dir { + + + match dir { CompassAction::Angle(x) => { self.set_angle(x); vec![] @@ -247,9 +255,7 @@ impl InteractiveCompassBuilder { Some(x) => vec![x], None => vec![], }, - }; - - r + } } pub fn add_qline(&mut self, length: f32) { @@ -309,7 +315,7 @@ impl InteractiveCompassBuilder { self.pen_down = true; - if x.label.len() > 0 { + if !x.label.is_empty() { self.references.insert(x.label.clone(), self.to_basic()); } @@ -335,7 +341,7 @@ impl InteractiveCompassBuilder { self.pen_down = true; - if x.label.len() > 0 { + if !x.label.is_empty() { self.references.insert(x.label.clone(), self.to_basic()); } bezier.to_segment() @@ -353,7 +359,7 @@ impl InteractiveCompassBuilder { self.curr_angle = new_spot.angle().mod2(); self.pen_down = true; - if x.label.len() > 0 { + if !x.label.is_empty() { self.references.insert(x.label.clone(), self.to_basic()); } @@ -365,17 +371,17 @@ impl InteractiveCompassBuilder { [.., penultimate, last] => { let pt = PointToPoint::new(*penultimate, *last).angle().as_angle_pi(); self.curr_angle = pt; - self.curr_loc = last.clone(); + self.curr_loc = *last; } [last] => { if last.distance(self.curr_loc) > 0.001 { let penultimate = self.curr_loc; let pt = PointToPoint::new(penultimate, *last).angle().as_angle_pi(); self.curr_angle = pt; - self.curr_loc = last.clone(); + self.curr_loc = *last; } else { // not enough points to update the angle... - self.curr_loc = last.clone(); + self.curr_loc = *last; } } _ => { @@ -385,7 +391,7 @@ impl InteractiveCompassBuilder { self.pen_down = true; - if x.label.len() > 0 { + if !x.label.is_empty() { self.references.insert(x.label.clone(), self.to_basic()); } @@ -444,7 +450,7 @@ impl MurreletCompass { let start = self.start; builder.add_curve_start(start); for w in self.dirs.iter() { - builder.add_segment(&w) + builder.add_segment(w) } CurveDrawer::new(builder.results(), self.closed) diff --git a/murrelet_draw/src/curve_drawer.rs b/murrelet_draw/src/curve_drawer.rs index a24ffc7..a59ffd0 100644 --- a/murrelet_draw/src/curve_drawer.rs +++ b/murrelet_draw/src/curve_drawer.rs @@ -279,7 +279,7 @@ impl CurveDrawer { let mut segments = self.segments().to_vec(); // if it's closed, then add the first point, otherwise same as rest if !self.closed { - return segments; + segments } else { // if there's no first point, just return itself (which is empty...) if let Some(first_point) = self.first_point() { @@ -631,7 +631,7 @@ impl CurveArc { } pub fn set_radius(&self, radius: f32) -> CurveArc { - let mut m = self.clone(); + let mut m = *self; m.radius = radius; m } @@ -698,12 +698,12 @@ impl CurveArc { let split_pi = self.start_pi.lerpify(&self.end_pi, &target_pct); let first_arc = CurveArc { end_pi: split_pi, - ..self.clone() + ..*self }; let second_arc = CurveArc { start_pi: split_pi, - ..self.clone() + ..*self }; (first_arc.to_segment(), second_arc.to_segment()) @@ -769,7 +769,7 @@ impl CurveCubicBezier { } pub fn flatten(&self, tolerance: f32) -> Vec { - flatten_cubic_bezier_path_with_tolerance(&vec![self.to_cubic()], false, tolerance).unwrap() + flatten_cubic_bezier_path_with_tolerance(&[self.to_cubic()], false, tolerance).unwrap() } pub fn to_pts(&self, tolerance: f32) -> CurveSegment { @@ -902,7 +902,7 @@ impl ToCurveSegment for Circle { impl ToCurveSegment for CurveArc { fn to_segment(&self) -> CurveSegment { - CurveSegment::Arc(self.clone()) + CurveSegment::Arc(*self) } } @@ -976,7 +976,7 @@ pub trait ToCurveDrawer { } } } - return None; + None } fn first_spot_cd(&self) -> Option { @@ -999,7 +999,7 @@ pub trait ToCurveDrawer { } } } - return None; + None } fn to_cd_closed(&self) -> CurveDrawer { diff --git a/murrelet_draw/src/draw.rs b/murrelet_draw/src/draw.rs index e9d6374..84ac8c8 100644 --- a/murrelet_draw/src/draw.rs +++ b/murrelet_draw/src/draw.rs @@ -3,10 +3,7 @@ use glam::{vec2, Vec2, Vec3}; use lerpable::Lerpable; -use murrelet_common::{ - MurreletColor, SimpleTransform2d, ToSimpleTransform, - Transformable, -}; +use murrelet_common::{MurreletColor, SimpleTransform2d, ToSimpleTransform, Transformable}; use murrelet_livecode::unitcells::UnitCellContext; use murrelet_livecode_derive::Livecode; use palette::{named::AQUAMARINE, LinSrgba, Srgb}; @@ -56,7 +53,7 @@ impl MurreletColorStyle { pub fn as_color(&self) -> MurreletColor { match self { - MurreletColorStyle::Color(c) => c.clone(), + MurreletColorStyle::Color(c) => *c, MurreletColorStyle::RgbaFill(c) => c.color(), MurreletColorStyle::SvgFill(_) => MurreletColor::white(), // default if we're not drawing the texture } diff --git a/murrelet_draw/src/drawable.rs b/murrelet_draw/src/drawable.rs index f14c2bf..e3e80dd 100644 --- a/murrelet_draw/src/drawable.rs +++ b/murrelet_draw/src/drawable.rs @@ -71,11 +71,11 @@ where T: ToCurveDrawer, { fn to_drawn_shape_closed(&self, style: StyleConf) -> DrawnShape { - DrawnShape::new_cds(&vec![self.to_cd_closed()], style) + DrawnShape::new_cds(&[self.to_cd_closed()], style) } fn to_drawn_shape_open(&self, style: StyleConf) -> DrawnShape { - DrawnShape::new_cds(&vec![self.to_cd_open()], style) + DrawnShape::new_cds(&[self.to_cd_open()], style) } } @@ -89,7 +89,7 @@ pub trait ToDrawnShape { impl ToDrawnShape for CurveDrawer { fn to_drawn_shape(&self, style: StyleConf) -> DrawnShape { - DrawnShape::new_cds(&vec![self.clone()], style) + DrawnShape::new_cds(&[self.clone()], style) } } diff --git a/murrelet_draw/src/lib.rs b/murrelet_draw/src/lib.rs index 5cac720..0853f4a 100644 --- a/murrelet_draw/src/lib.rs +++ b/murrelet_draw/src/lib.rs @@ -2,6 +2,7 @@ pub mod compass; pub mod cubic; pub mod curve_drawer; pub mod draw; +pub mod drawable; pub mod newtypes; pub mod scaffold; pub mod sequencers; @@ -10,4 +11,3 @@ pub mod style; pub mod svg; pub mod tesselate; pub mod transform2d; -pub mod drawable; diff --git a/murrelet_draw/src/newtypes.rs b/murrelet_draw/src/newtypes.rs index ae94dae..2962fbb 100644 --- a/murrelet_draw/src/newtypes.rs +++ b/murrelet_draw/src/newtypes.rs @@ -72,7 +72,7 @@ impl RGBandANewtype { } pub fn with_alpha(&self, alpha: f32) -> Self { - let mut c = self.clone(); + let mut c = *self; c.a = alpha; c } diff --git a/murrelet_draw/src/scaffold.rs b/murrelet_draw/src/scaffold.rs index f35e6c5..ecb5120 100644 --- a/murrelet_draw/src/scaffold.rs +++ b/murrelet_draw/src/scaffold.rs @@ -17,14 +17,13 @@ pub fn line_to_polygon(curves: &[Vec2]) -> geo::Polygon { } pub fn multipolygon_to_vec2(p: &geo::MultiPolygon) -> Vec> { - p.iter().map(|pp| polygon_to_vec2(pp)).collect_vec() + p.iter().map(polygon_to_vec2).collect_vec() } pub fn polygon_to_vec2(p: &geo::Polygon) -> Vec { let mut coords = p .exterior() .coords() - .into_iter() .map(|coord| coord.to_vec2()) .collect_vec(); @@ -127,12 +126,12 @@ impl MaskCache { } pub fn new_cd(cd: CurveDrawer) -> Self { - Self::new(&vec![cd]) + Self::new(&[cd]) } pub fn new_interior(outline: CurveDrawer, interior: &[CurveDrawer]) -> Self { // shh, just a wrapper - let s = vec![vec![outline], interior.to_vec()].concat(); + let s = [vec![outline], interior.to_vec()].concat(); Self::new(&s) } @@ -198,26 +197,26 @@ impl MaskCache { pub fn crop_many(&self, v: &[DrawnShape]) -> Vec { let mut cropped = vec![]; - for a in v.into_iter() { + for a in v.iter() { let mut new_cds = vec![]; for cd in a.curves() { new_cds.extend(self.crop(&cd.to_rough_points(1.0))); } cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); } - return cropped; + cropped } pub fn crop_inverse_many(&self, v: &[DrawnShape]) -> Vec { let mut cropped = vec![]; - for a in v.into_iter() { + for a in v.iter() { let mut new_cds = vec![]; for cd in a.curves() { new_cds.extend(self.crop_inverse(&cd.to_rough_points(1.0))); } cropped.push(DrawnShape::new_vecvec(new_cds, a.style())); } - return cropped; + cropped } pub fn ray_intersect(&self, spot: &SpotOnCurve) -> Vec2 { diff --git a/murrelet_draw/src/sequencers.rs b/murrelet_draw/src/sequencers.rs index bd08074..b7d68c5 100644 --- a/murrelet_draw/src/sequencers.rs +++ b/murrelet_draw/src/sequencers.rs @@ -1,9 +1,9 @@ +use crate::transform2d::{Transform2d, Transform2dStep}; use glam::*; use itertools::Itertools; use lerpable::Lerpable; use murrelet_common::*; use murrelet_livecode::unitcells::{UnitCellContext, UnitCellCreator, UnitCellIdx}; -use crate::transform2d::{Transform2d, Transform2dStep}; use murrelet_livecode_derive::Livecode; const REFERENCE_SIZE: f32 = 100.0; @@ -127,14 +127,14 @@ impl SimpleCountSequence { impl UnitCellCreator for SimpleCountSequence { fn to_unit_cell_ctxs(&self) -> Vec { - let v = (0..self.0) + + (0..self.0) .map(|x| { let idx = IdxInRange::new(x, self.0); let ctx = UnitCellIdx::from_idx1d(idx); UnitCellContext::new(ctx, SimpleTransform2d::ident()) }) - .collect_vec(); - v + .collect_vec() } } diff --git a/murrelet_draw/src/serialize.rs b/murrelet_draw/src/serialize.rs index 98b4ea0..f1e5480 100644 --- a/murrelet_draw/src/serialize.rs +++ b/murrelet_draw/src/serialize.rs @@ -14,4 +14,4 @@ where seq.serialize_element(number)?; } seq.end() -} \ No newline at end of file +} diff --git a/murrelet_draw/src/style.rs b/murrelet_draw/src/style.rs index 934adfb..b2aeb51 100644 --- a/murrelet_draw/src/style.rs +++ b/murrelet_draw/src/style.rs @@ -137,7 +137,7 @@ impl StyledPathSvgFill { } pub fn with_alpha(&self, alpha: f32) -> StyledPathSvgFill { - let mut p = self.clone(); + let mut p = *self; p.alpha = alpha; p } diff --git a/murrelet_draw/src/svg.rs b/murrelet_draw/src/svg.rs index d636f25..e17af2b 100644 --- a/murrelet_draw/src/svg.rs +++ b/murrelet_draw/src/svg.rs @@ -341,7 +341,7 @@ impl TransformedSvgShape { } pub fn from_cd(cd: &CurveDrawer) -> Self { - let shape = SvgShape::Path(SvgPathDef::from_cd(&cd)); + let shape = SvgShape::Path(SvgPathDef::from_cd(cd)); Self { shape, diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index 780db95..e8b1d9c 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -111,8 +111,8 @@ impl AsLyonTransform for SimpleTransform2dStep { let vv = vector(v.x, v.y); let t = t.then_translate(-vv); let t = t.then_rotate(Angle::radians(a.angle())); - let t = t.then_translate(vv); - t + + t.then_translate(vv) } SimpleTransform2dStep::Scale(v) => t.then_scale(v.x, v.y), SimpleTransform2dStep::Skew(_, _) => unreachable!(), @@ -241,10 +241,8 @@ fn add_circular_arc(builder: &mut B, c: &CurveArc) { if sweep < 0.0 { sweep += 2.0 * PI; } - } else { - if sweep > 0.0 { - sweep -= 2.0 * PI; - } + } else if sweep > 0.0 { + sweep -= 2.0 * PI; } let arc = Arc { @@ -455,7 +453,7 @@ pub fn segment_arc( pub fn evenly_split_cubic_bezier(c: &CubicBezier, count: usize) -> Vec { // always include the start (and end) - let v = flatten_cubic_bezier_path_with_tolerance(&vec![c.clone()], false, 0.1); + let v = flatten_cubic_bezier_path_with_tolerance(&[*c], false, 0.1); if count <= 1 { let angle = PointToPoint::new(c.from, c.to).angle(); return vec![c.from, c.to] @@ -498,10 +496,10 @@ pub fn evenly_split_cubic_bezier(c: &CubicBezier, count: usize) -> Vec Vec { let curve: Vec<&f32> = params.iter().collect(); from = point_from_param1(&curve); @@ -916,16 +914,16 @@ impl ops::Add for Pt { } } -impl Into> for Pt { - fn into(self) -> Point2D { - self.pt +impl From for Point2D { + fn from(val: Pt) -> Self { + val.pt } } fn point_for_position(pos: &svg::node::element::path::Position, pt: Pt, from: Pt) -> Pt { match pos { - svg::node::element::path::Position::Absolute => pt.into(), - svg::node::element::path::Position::Relative => (pt + from).into(), + svg::node::element::path::Position::Absolute => pt, + svg::node::element::path::Position::Relative => pt + from , } } @@ -934,6 +932,12 @@ pub struct SegmentState { line_space: f32, dist_towards_next: f32, } +impl Default for SegmentState { + fn default() -> Self { + Self::new() + } +} + impl SegmentState { pub fn new() -> SegmentState { SegmentState { diff --git a/murrelet_draw/src/transform2d.rs b/murrelet_draw/src/transform2d.rs index bcab322..76159a2 100644 --- a/murrelet_draw/src/transform2d.rs +++ b/murrelet_draw/src/transform2d.rs @@ -4,11 +4,11 @@ use std::f32::consts::PI; use glam::*; use itertools::Itertools; use lerpable::Lerpable; -use murrelet_common::{ToSimpleTransform, lerpify_vec2}; use murrelet_common::{ a_pi, approx_eq_eps, mat4_from_mat3_transform, AnglePi, IsAngle, IsPolyline, Polyline, SimpleTransform2d, SimpleTransform2dStep, SpotOnCurve, TransformVec2, }; +use murrelet_common::{lerpify_vec2, ToSimpleTransform}; use murrelet_livecode_derive::Livecode; pub trait ToMat4 { @@ -29,7 +29,7 @@ impl ToMat4 for SimpleTransform2d { impl ToMat4 for Mat4 { fn change_to_mat4(&self) -> Mat4 { - self.clone() + *self } } @@ -41,11 +41,11 @@ impl Transform2d { } pub fn prepend_action(&mut self, actions: &[Transform2dStep]) { - self.0 = vec![actions.to_vec(), self.0.clone()].concat(); + self.0 = [actions.to_vec(), self.0.clone()].concat(); } pub fn prepend_one_action(&mut self, action: Transform2dStep) { - self.0 = vec![vec![action], self.0.clone()].concat(); + self.0 = [vec![action], self.0.clone()].concat(); } pub fn append_one_action(&mut self, action: Transform2dStep) { @@ -53,7 +53,7 @@ impl Transform2d { } pub fn append_action(&mut self, actions: &[Transform2dStep]) { - self.0 = vec![self.0.clone(), actions.to_vec()].concat(); + self.0 = [self.0.clone(), actions.to_vec()].concat(); } pub fn append_transform(&mut self, t: &Transform2d) { @@ -280,8 +280,6 @@ impl Transform2d { c.append_transform(&loc); c } - - } impl ToSimpleTransform for Transform2d { diff --git a/murrelet_gen/src/lib.rs b/murrelet_gen/src/lib.rs index eba3839..b4feb05 100644 --- a/murrelet_gen/src/lib.rs +++ b/murrelet_gen/src/lib.rs @@ -21,7 +21,6 @@ pub trait CanSampleFromDist: Sized { let mut rng = StdRng::seed_from_u64(seed); let rns: Vec = (0..Self::rn_count()) - .into_iter() .map(|_| rng.gen()) .collect(); diff --git a/murrelet_gen_derive/src/derive_gen.rs b/murrelet_gen_derive/src/derive_gen.rs index ea524ec..7f3b228 100644 --- a/murrelet_gen_derive/src/derive_gen.rs +++ b/murrelet_gen_derive/src/derive_gen.rs @@ -358,7 +358,7 @@ impl GenFinal for FieldTokensGen { #for_rn_count_per_item * #max + 1 }; - let for_rn_names_all = (0..*max).into_iter().map(|x| { + let for_rn_names_all = (0..*max).map(|x| { let i_name = x.to_string(); quote! { murrelet_gen::prefix_field_names(#i_name.to_string(), #for_rn_names_per_item) } }); @@ -481,7 +481,7 @@ fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { fn nested_ident(t: &syn::Type) -> Vec { let mut acc = vec![]; recursive_ident_from_path(t, &mut acc); - return acc; + acc } // we need to use turbofish to call an associated function diff --git a/murrelet_gen_derive/src/gen_methods.rs b/murrelet_gen_derive/src/gen_methods.rs index 59ecde2..160f905 100644 --- a/murrelet_gen_derive/src/gen_methods.rs +++ b/murrelet_gen_derive/src/gen_methods.rs @@ -78,279 +78,277 @@ impl GenMethod { match self { GenMethod::Recurse => { - let for_rn_count = quote! { #ty::rn_count() }; - let for_rn_names = quote! { #ty::rn_names() }; - let for_make_gen = quote! {{ - let r = #ty::sample_dist(rn, rn_start_idx); - rn_start_idx += #for_rn_count; - r - }}; - let for_to_dist = quote! { #name.to_dist() }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let for_rn_count = quote! { #ty::rn_count() }; + let for_rn_names = quote! { #ty::rn_names() }; + let for_make_gen = quote! {{ + let r = #ty::sample_dist(rn, rn_start_idx); + rn_start_idx += #for_rn_count; + r + }}; + let for_to_dist = quote! { #name.to_dist() }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::BoolBinomial { pct } => { - let for_rn_count = quote! { 1 }; - let for_rn_names = quote! { vec!["pct".to_string()] }; - let for_make_gen = quote! { { - let result = rn[rn_start_idx] > #pct; - rn_start_idx += #for_rn_count; - result - } }; - let for_to_dist = quote! { - if #name { - vec![1.0] - } else { - vec![0.0] - } - }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + let for_rn_count = quote! { 1 }; + let for_rn_names = quote! { vec!["pct".to_string()] }; + let for_make_gen = quote! { { + let result = rn[rn_start_idx] > #pct; + rn_start_idx += #for_rn_count; + result + } }; + let for_to_dist = quote! { + if #name { + vec![1.0] + } else { + vec![0.0] } + }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::F32Uniform { start, end } => { - let for_rn_count = quote! { 1 }; - let for_rn_names = quote! { vec!["uniform".to_string()] }; - let for_make_gen = quote! { { - let result = rn[rn_start_idx] * (#end - #start) + #start; - rn_start_idx += #for_rn_count; - result #maybe_as - } }; - let for_to_dist = quote! { vec![(#name as f32 - #start) / (#end - #start)] }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let for_rn_count = quote! { 1 }; + let for_rn_names = quote! { vec!["uniform".to_string()] }; + let for_make_gen = quote! { { + let result = rn[rn_start_idx] * (#end - #start) + #start; + rn_start_idx += #for_rn_count; + result #maybe_as + } }; + let for_to_dist = quote! { vec![(#name as f32 - #start) / (#end - #start)] }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::F32UniformPosNeg { start, end } => { - let for_rn_count = quote! { 2 }; - let for_rn_names = quote! { vec!["uniform".to_string(), "sign".to_string()] }; - let for_make_gen = quote! { { - let sgn = if(rn[rn_start_idx] > 0.5) { 1.0 } else { -1.0 }; - let result = rn[rn_start_idx + 1] * (#end - #start) + #start; - rn_start_idx += #for_rn_count; - (sgn * result) #maybe_as - } }; - - let for_to_dist = quote! { vec![ - if #name > 0.0 { 1.0 } else { 0.0 }, - (#name.abs() - #start) / (#end - #start) - ] }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec!["uniform".to_string(), "sign".to_string()] }; + let for_make_gen = quote! { { + let sgn = if(rn[rn_start_idx] > 0.5) { 1.0 } else { -1.0 }; + let result = rn[rn_start_idx + 1] * (#end - #start) + #start; + rn_start_idx += #for_rn_count; + (sgn * result) #maybe_as + } }; + + let for_to_dist = quote! { vec![ + if #name > 0.0 { 1.0 } else { 0.0 }, + (#name.abs() - #start) / (#end - #start) + ] }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::F32Fixed { val } => ( - quote! { 0 }, - quote! {vec![]}, - quote! { #val #maybe_as }, - quote! {vec![]}, - ), + quote! { 0 }, + quote! {vec![]}, + quote! { #val #maybe_as }, + quote! {vec![]}, + ), GenMethod::F32Normal { mu, sigma } => { - let for_rn_count = quote! { 2 }; - let for_rn_names = - quote! { vec![ "BoxMuller1".to_string(), "BoxMuller2".to_string()] }; - let for_make_gen = quote! { { - // avoid nans, make sure u1 is positive and non-zero - let u1 = rn[rn_start_idx].clamp(std::f32::MIN_POSITIVE, 1.0); - let u2 = rn[rn_start_idx + 1].clamp(0.0, 1.0); - rn_start_idx += 2; - - let r = (-2.0 * u1.ln()).sqrt(); - let theta = 2.0 * std::f32::consts::PI * u2; - - #mu + #sigma * r * theta.cos() #maybe_as - } }; - let for_to_dist = quote! { todo!() }; - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let for_rn_count = quote! { 2 }; + let for_rn_names = + quote! { vec![ "BoxMuller1".to_string(), "BoxMuller2".to_string()] }; + let for_make_gen = quote! { { + // avoid nans, make sure u1 is positive and non-zero + let u1 = rn[rn_start_idx].clamp(std::f32::MIN_POSITIVE, 1.0); + let u2 = rn[rn_start_idx + 1].clamp(0.0, 1.0); + rn_start_idx += 2; + + let r = (-2.0 * u1.ln()).sqrt(); + let theta = 2.0 * std::f32::consts::PI * u2; + + #mu + #sigma * r * theta.cos() #maybe_as + } }; + let for_to_dist = quote! { todo!() }; + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::Vec2UniformGridStart { - start_x, - start_y, - width, - height, - } => { - let for_rn_count = quote! { 2 }; - let for_rn_names = quote! { vec![ "x".to_string(), "y".to_string()] }; - let for_make_gen = quote! {{ - let width = rn[rn_start_idx] * #width; - let height = rn[rn_start_idx + 1] * #height; - - rn_start_idx += #for_rn_count; - - glam::vec2(#start_x, #start_y) + glam::vec2(width, height) - }}; - - let for_to_dist = quote! { { - // let c = (#name + 0.5 * glam::vec2(#width, #height)); - // vec![c.x / #width, c.y / #height] - - let c = #name - - glam::vec2(#start_x, #start_y) - + 0.5 * glam::vec2(#width, #height); - vec![c.x / #width, c.y / #height] - }}; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + start_x, + start_y, + width, + height, + } => { + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec![ "x".to_string(), "y".to_string()] }; + let for_make_gen = quote! {{ + let width = rn[rn_start_idx] * #width; + let height = rn[rn_start_idx + 1] * #height; + + rn_start_idx += #for_rn_count; + + glam::vec2(#start_x, #start_y) + glam::vec2(width, height) + }}; + + let for_to_dist = quote! { { + // let c = (#name + 0.5 * glam::vec2(#width, #height)); + // vec![c.x / #width, c.y / #height] + + let c = #name + - glam::vec2(#start_x, #start_y) + + 0.5 * glam::vec2(#width, #height); + vec![c.x / #width, c.y / #height] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::Vec2UniformGrid { - x, - y, - width, - height, - } => { - let for_rn_count = quote! { 2 }; - let for_rn_names = quote! { vec![ "x".to_string(), "y".to_string()] }; - let for_make_gen = quote! {{ - let width = rn[rn_start_idx] * #width; - let height = rn[rn_start_idx + 1] * #height; - - rn_start_idx += #for_rn_count; - - glam::vec2(#x, #y) - 0.5 * glam::vec2(#width, #height) + glam::vec2(width, height) - }}; - - let for_to_dist = quote! { { - // let c = (#name + 0.5 * glam::vec2(#width, #height)); - // vec![c.x / #width, c.y / #height] - - let c = #name - - glam::vec2(#x, #y) - + 0.5 * glam::vec2(#width, #height); - vec![c.x / #width, c.y / #height] - }}; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + x, + y, + width, + height, + } => { + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec![ "x".to_string(), "y".to_string()] }; + let for_make_gen = quote! {{ + let width = rn[rn_start_idx] * #width; + let height = rn[rn_start_idx + 1] * #height; + + rn_start_idx += #for_rn_count; + + glam::vec2(#x, #y) - 0.5 * glam::vec2(#width, #height) + glam::vec2(width, height) + }}; + + let for_to_dist = quote! { { + // let c = (#name + 0.5 * glam::vec2(#width, #height)); + // vec![c.x / #width, c.y / #height] + + let c = #name + - glam::vec2(#x, #y) + + 0.5 * glam::vec2(#width, #height); + vec![c.x / #width, c.y / #height] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::Vec2Circle { x, y, radius } => { - let for_rn_count = quote! { 2 }; - let for_rn_names = quote! { vec![ "theta".to_string(), "rad".to_string()] }; - - let for_make_gen = quote! {{ - let angle = rn[rn_start_idx] * 2.0 * std::f32::consts::PI; - let dist = rn[rn_start_idx + 1]; // sqrt it to even out the sampling - rn_start_idx += #for_rn_count; - glam::vec2(#x, #y) + glam::vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() - }}; - - let for_to_dist = quote! { { - let c = #name - glam::vec2(#x, #y); - let dist = (c.length() / #radius).powi(2); - let mut angle = c.to_angle(); - if angle <= 0.0 { - angle += 2.0 * std::f32::consts::PI - } - angle /= (2.0 * std::f32::consts::PI); - vec![angle, dist] - }}; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + let for_rn_count = quote! { 2 }; + let for_rn_names = quote! { vec![ "theta".to_string(), "rad".to_string()] }; + + let for_make_gen = quote! {{ + let angle = rn[rn_start_idx] * 2.0 * std::f32::consts::PI; + let dist = rn[rn_start_idx + 1]; // sqrt it to even out the sampling + rn_start_idx += #for_rn_count; + glam::vec2(#x, #y) + glam::vec2(angle.cos(), angle.sin()) * #radius * dist.sqrt() + }}; + + let for_to_dist = quote! { { + let c = #name - glam::vec2(#x, #y); + let dist = (c.length() / #radius).powi(2); + let mut angle = c.to_angle(); + if angle <= 0.0 { + angle += 2.0 * std::f32::consts::PI } + angle /= (2.0 * std::f32::consts::PI); + vec![angle, dist] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::ColorNormal => { - let for_rn_count = quote! { 3 }; - - let for_rn_names = - quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string()] }; - - let for_make_gen = quote! {{ - let h = rn[rn_start_idx]; - let s = rn[rn_start_idx + 1]; - let v = rn[rn_start_idx + 2]; - rn_start_idx += #for_rn_count; - murrelet_common::MurreletColor::hsva(h, s, v, 1.0) - }}; - - let for_to_dist = quote! {{ - let [h, s, v, _] = #name.into_hsva_components(); - vec![ - h % 1.0, - s % 1.0, - v % 1.0, - ] - }}; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + let for_rn_count = quote! { 3 }; + + let for_rn_names = + quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string()] }; + + let for_make_gen = quote! {{ + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, 1.0) + }}; + + let for_to_dist = quote! {{ + let [h, s, v, _] = #name.into_hsva_components(); + vec![ + h % 1.0, + s % 1.0, + v % 1.0, + ] + }}; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::ColorTransparency => { - let for_rn_count = quote! { 4 }; - - let for_rn_names = quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string(), "alpha".to_string()] }; - - let for_make_gen = quote! { - { - let h = rn[rn_start_idx]; - let s = rn[rn_start_idx + 1]; - let v = rn[rn_start_idx + 2]; - let a = rn[rn_start_idx + 3]; - rn_start_idx += #for_rn_count; - murrelet_common::MurreletColor::hsva(h, s, v, a) - } - }; - - let for_to_dist = quote! { { - let [h, s, v, a] = #name.into_hsva_components(); - vec![ - h % 1.0, - s % 1.0, - v % 1.0, - a % 1.0, - ] - } }; - - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + let for_rn_count = quote! { 4 }; + + let for_rn_names = quote! { vec![ "hue".to_string(), "sat".to_string(), "val".to_string(), "alpha".to_string()] }; + + let for_make_gen = quote! { + { + let h = rn[rn_start_idx]; + let s = rn[rn_start_idx + 1]; + let v = rn[rn_start_idx + 2]; + let a = rn[rn_start_idx + 3]; + rn_start_idx += #for_rn_count; + murrelet_common::MurreletColor::hsva(h, s, v, a) } + }; + + let for_to_dist = quote! { { + let [h, s, v, a] = #name.into_hsva_components(); + vec![ + h % 1.0, + s % 1.0, + v % 1.0, + a % 1.0, + ] + } }; + + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::VecLength { .. } => { - // this is handled in the vec parser - unreachable!("this location of veclength isn't supported yet!") - } - GenMethod::VecLengthFixed { val } => unreachable!(), + // this is handled in the vec parser + unreachable!("this location of veclength isn't supported yet!") + } + GenMethod::VecLengthFixed { val: _ } => unreachable!(), GenMethod::StringChoice { choices } => { - let one_hot = choices.len(); - let for_rn_count = quote! { #one_hot }; - - // let for_rn_names = quote! { vec![ "hue", "sat", "val", "alpha"] }; - let rn_names = choices.iter().map(|(key, _)| quote! { #key.to_string() }); - let for_rn_names = quote! { vec![#(#rn_names,)*] }; - - let weighted_rns = choices - .iter() - .enumerate() - .map(|(i, (key, weight))| { - quote! { (#key.clone(), #weight * rn[rn_start_idx + #i]) } - }) - .collect::>(); - - let for_make_gen = quote! { { - let result = vec![#(#weighted_rns,)*].into_iter().max_by(|a, b| a.1.partial_cmp(&b.1).unwrap()).expect("empty string choices??"); - rn_start_idx += #for_rn_count; - result.0.to_string() - } }; - - let for_to_dist_choices = choices - .iter() - .map(|(key, _)| { + let one_hot = choices.len(); + let for_rn_count = quote! { #one_hot }; + + // let for_rn_names = quote! { vec![ "hue", "sat", "val", "alpha"] }; + let rn_names = choices.iter().map(|(key, _)| quote! { #key.to_string() }); + let for_rn_names = quote! { vec![#(#rn_names,)*] }; + + let weighted_rns = choices + .iter() + .enumerate() + .map(|(i, (key, weight))| { + quote! { (#key.clone(), #weight * rn[rn_start_idx + #i]) } + }) + .collect::>(); + + let for_make_gen = quote! { { + let result = vec![#(#weighted_rns,)*].into_iter().max_by(|a, b| a.1.partial_cmp(&b.1).unwrap()).expect("empty string choices??"); + rn_start_idx += #for_rn_count; + result.0.to_string() + } }; + + let for_to_dist_choices = choices.keys().map(|key| { quote! { if #key.clone() == #name { result.push(1.0) } else { result.push(0.0) } } }) .collect::>(); - let for_to_dist = quote! { { - let mut result = vec![]; - #(#for_to_dist_choices;)* - result - } }; + let for_to_dist = quote! { { + let mut result = vec![]; + #(#for_to_dist_choices;)* + result + } }; - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::Default => { - let for_rn_count = quote! { 0 }; + let for_rn_count = quote! { 0 }; - let for_rn_names = quote! { vec![] }; + let for_rn_names = quote! { vec![] }; - let for_make_gen = quote! { { - Default::default() - } }; + let for_make_gen = quote! { { + Default::default() + } }; - let for_to_dist = quote! { vec![] }; + let for_to_dist = quote! { vec![] }; - (for_rn_count, for_rn_names, for_make_gen, for_to_dist) - } + (for_rn_count, for_rn_names, for_make_gen, for_to_dist) + } GenMethod::F32Lazy => todo!(), } } diff --git a/murrelet_gpu/src/compute.rs b/murrelet_gpu/src/compute.rs index cdfdc07..198abb3 100644 --- a/murrelet_gpu/src/compute.rs +++ b/murrelet_gpu/src/compute.rs @@ -407,8 +407,8 @@ impl ComputeGraphicsToTexture { let [w, h] = self.dims; // ideally could get this from the texture... const WGX: u32 = 8; const WGY: u32 = 8; - let gx = (w + WGX - 1) / WGX; - let gy = (h + WGY - 1) / WGY; + let gx = w.div_ceil(WGX); + let gy = h.div_ceil(WGY); pass.dispatch_workgroups(gx, gy, 1); } // drop(pass) @@ -440,7 +440,7 @@ impl ComputeGraphicsToTexture { ) -> wgpu::BindGroup { device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, - layout: &bind_group_layout, + layout: bind_group_layout, entries: &[ wgpu::BindGroupEntry { binding: 0, diff --git a/murrelet_gpu/src/device_state.rs b/murrelet_gpu/src/device_state.rs index bc8dffb..2de4bc1 100644 --- a/murrelet_gpu/src/device_state.rs +++ b/murrelet_gpu/src/device_state.rs @@ -54,11 +54,11 @@ impl<'a> DeviceState<'a> { } pub fn device(&self) -> &wgpu::Device { - &self.device + self.device } pub fn queue(&self) -> &wgpu::Queue { - &self.queue + self.queue } } @@ -106,7 +106,7 @@ impl OwnedDeviceState { // borrowing from bevy pub fn align_byte_size(value: u32) -> u32 { - if value % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT != 0 { + if !value.is_multiple_of(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT) { value + (wgpu::COPY_BYTES_PER_ROW_ALIGNMENT - (value % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT)) } else { value @@ -148,7 +148,7 @@ fn write_png_to_texture( println!("buffer_rows {:?}", buffer_rows); // just get the name to name the texture - let p = path.file_name().map(|x| x.to_str()).flatten().unwrap_or(""); + let p = path.file_name().and_then(|x| x.to_str()).unwrap_or(""); // bah, uh, okay copy this to a buffer of the right length let mut padded_img = vec![0; (padded_row * buffer_rows).try_into().unwrap()]; diff --git a/murrelet_gpu/src/editable_shaders.rs b/murrelet_gpu/src/editable_shaders.rs index 6abd8a8..cd57156 100644 --- a/murrelet_gpu/src/editable_shaders.rs +++ b/murrelet_gpu/src/editable_shaders.rs @@ -56,19 +56,11 @@ impl ShaderStrings { } pub fn get_shader_str(&self, _c: &GraphicsWindowConf, name: &str) -> Option { - if let Some(str) = self.shaders.get(name) { - Some(Self::shader(&str)) - } else { - None - } + self.shaders.get(name).map(|str| Self::shader(str)) } pub fn get_shader_str_2tex(&self, _c: &GraphicsWindowConf, name: &str) -> Option { - if let Some(str) = self.shaders.get(name) { - Some(Self::shader2tex(&str)) - } else { - None - } + self.shaders.get(name).map(|str| Self::shader2tex(str)) } pub fn get_shader_str_custom_prefix( @@ -77,11 +69,7 @@ impl ShaderStrings { name: &str, prefix: &str, ) -> Option { - if let Some(str) = self.shaders.get(name) { - Some(Self::shader_custom_prefix(&str, prefix)) - } else { - None - } + self.shaders.get(name).map(|str| Self::shader_custom_prefix(str, prefix)) } pub fn get_graphics_ref( @@ -89,15 +77,9 @@ impl ShaderStrings { c: &GraphicsWindowConf, name: &str, ) -> Option> { - if let Some(str) = self.shaders.get(name) { - Some( - GraphicsCreator::::default() + self.shaders.get(name).map(|str| GraphicsCreator::::default() .with_mag_filter(wgpu::FilterMode::Nearest) - .to_graphics_ref(c, name, &Self::shader(&str)), - ) - } else { - None - } + .to_graphics_ref(c, name, &Self::shader(str))) } pub fn get_graphics_ref_2tex( @@ -105,16 +87,10 @@ impl ShaderStrings { c: &GraphicsWindowConf, name: &str, ) -> Option> { - if let Some(str) = self.shaders.get(name) { - Some( - GraphicsCreator::::default() + self.shaders.get(name).map(|str| GraphicsCreator::::default() .with_mag_filter(wgpu::FilterMode::Nearest) .with_second_texture() - .to_graphics_ref(c, name, &Self::shader2tex(&str)), - ) - } else { - None - } + .to_graphics_ref(c, name, &Self::shader2tex(str))) } pub fn has_changed(&self, other: &ControlShaderStrings) -> bool { @@ -125,11 +101,11 @@ impl ShaderStrings { &self, prev_shaders: &ControlShaderStrings, ) -> bool { - if self.has_changed(&prev_shaders) { + if self.has_changed(prev_shaders) { let mut all_success = true; for (name, shader_str) in self.shaders.iter() { - let t = ShaderStrings::shader_str::(&shader_str); + let t = ShaderStrings::shader_str::(shader_str); if let Err(err) = naga::front::wgsl::parse_str(&t) { println!( "error with shader {:?}, {:?}, not updating until it works!", @@ -160,7 +136,7 @@ impl ControlShaderStrings { ) -> Option { let shaders = self.to_normal(); - let shader_changed_and_compiles = shaders.naga_if_needed::(&prev); + let shader_changed_and_compiles = shaders.naga_if_needed::(prev); if force_reload || shader_changed_and_compiles { // just in case there's lerp, be sure to use the one we tested diff --git a/murrelet_gpu/src/gpu_macros.rs b/murrelet_gpu/src/gpu_macros.rs index 739c3eb..1b059d6 100644 --- a/murrelet_gpu/src/gpu_macros.rs +++ b/murrelet_gpu/src/gpu_macros.rs @@ -427,7 +427,6 @@ impl Option { self.dest.texture_view_other() } - } // given a list of inputs, choose which one to use @@ -504,10 +503,10 @@ impl RenderTrait for PingPongRender { for _ in 0..self.k { self.ping .graphics() - .render(device.device_state(), &pong_texture); + .render(device.device_state(), pong_texture); self.pong .graphics() - .render(device.device_state(), &ping_texture); + .render(device.device_state(), ping_texture); } } @@ -701,8 +700,8 @@ impl GPUPipeline { .source .as_ref() .expect("should have set a source if you're gonna get it source"); - self.get_graphic(&name) - .expect(&format!("gave a source {} that doesn't exist", name)) + self.get_graphic(name) + .unwrap_or_else(|| panic!("gave a source {} that doesn't exist", name)) .texture_view() } } diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index 17df4cf..4741363 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -615,12 +615,12 @@ impl GraphicsRefCustom { // index buffer with 4-byte alignment: recreate if growing const ALIGN: usize = 4; let raw_index = bytemuck::cast_slice::(&g.conf.input_vertex.tri.order); - let (index_bytes, needs_recreate) = if raw_index.len() % ALIGN != 0 { + let (index_bytes, needs_recreate) = if !raw_index.len().is_multiple_of(ALIGN) { // pad to alignment let pad = ALIGN - (raw_index.len() % ALIGN); let mut data = Vec::with_capacity(raw_index.len() + pad); data.extend_from_slice(raw_index); - data.extend(std::iter::repeat(0).take(pad)); + data.extend(std::iter::repeat_n(0, pad)); ( data.into_boxed_slice(), raw_index.len() + pad > old_index_bytes_len, @@ -819,7 +819,7 @@ impl Graphics { let mut bind_group_layout_entries = Vec::new(); bind_group_layout_entries.push(wgpu::BindGroupLayoutEntry { - binding: 0 as u32, // needs to line up with @group(0) @binding(1) + binding: 0_u32, // needs to line up with @group(0) @binding(1) visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Texture { sample_type: wgpu::TextureSampleType::Float { filterable: true }, @@ -832,7 +832,7 @@ impl Graphics { if has_second_texture { bind_group_offset += 1; bind_group_layout_entries.push(wgpu::BindGroupLayoutEntry { - binding: 1 as u32, // needs to line up with @group(0) @binding(0) + binding: 1_u32, // needs to line up with @group(0) @binding(0) visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Texture { sample_type: wgpu::TextureSampleType::Float { filterable: true }, @@ -905,8 +905,8 @@ impl Graphics { fn _sampler(device: &wgpu::Device, details: ShaderOptions) -> wgpu::Sampler { let sampler_desc = details.as_sampler_desc(); - let sampler = device.create_sampler(&sampler_desc); - sampler + + device.create_sampler(&sampler_desc) } fn _bind_group( @@ -925,12 +925,12 @@ impl Graphics { entries.push(wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::TextureView(&input_texture_view), + resource: wgpu::BindingResource::TextureView(input_texture_view), }); if let Some(texture_view_other) = input_texture_view_other { entries.push(wgpu::BindGroupEntry { binding: 1, - resource: wgpu::BindingResource::TextureView(&texture_view_other), + resource: wgpu::BindingResource::TextureView(texture_view_other), }); binding_offset += 1; } @@ -938,7 +938,7 @@ impl Graphics { // next is the sampler entries.push(wgpu::BindGroupEntry { binding: binding_offset + 1, - resource: wgpu::BindingResource::Sampler(&sampler), + resource: wgpu::BindingResource::Sampler(sampler), }); entries.push(wgpu::BindGroupEntry { @@ -1032,7 +1032,7 @@ impl Graphics { depth_stencil, multisample: wgpu::MultisampleState::default(), fragment: Some(wgpu::FragmentState { - module: &fs_mod, + module: fs_mod, entry_point: "main", targets: &color_state, #[cfg(not(feature = "nannou"))] @@ -1041,9 +1041,9 @@ impl Graphics { multiview: None, }; - let main_pipeline = device.create_render_pipeline(&rp_desc); + - main_pipeline + device.create_render_pipeline(&rp_desc) } fn _pipeline_layout( @@ -1363,7 +1363,7 @@ impl Graphics { label: Some("Shadow Pass"), color_attachments: &[], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &shadow_view, + view: shadow_view, depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(1.0), #[cfg(not(feature = "nannou"))] @@ -1379,7 +1379,7 @@ impl Graphics { timestamp_writes: Default::default(), }); shadow_pass.set_pipeline(shadow_pipeline); - shadow_pass.set_bind_group(0, &shadow_bind_group, &[]); + shadow_pass.set_bind_group(0, shadow_bind_group, &[]); shadow_pass.set_vertex_buffer(0, self.vertex_buffers.vertex.slice(..)); shadow_pass.set_index_buffer( self.vertex_buffers.index.slice(..), diff --git a/murrelet_gpu/src/lib.rs b/murrelet_gpu/src/lib.rs index 63373d4..01a4b20 100644 --- a/murrelet_gpu/src/lib.rs +++ b/murrelet_gpu/src/lib.rs @@ -1,9 +1,9 @@ +pub mod compute; pub mod device_state; pub mod editable_shaders; pub mod gpu_livecode; pub mod gpu_macros; pub mod graphics_ref; pub mod shader_str; -pub mod compute; pub mod uniforms; pub mod window; diff --git a/murrelet_gpu/src/window.rs b/murrelet_gpu/src/window.rs index db7478e..8dda2a0 100644 --- a/murrelet_gpu/src/window.rs +++ b/murrelet_gpu/src/window.rs @@ -43,7 +43,7 @@ impl<'a> GraphicsWindowConf<'a> { } pub fn device(&self) -> &wgpu::Device { - &self.device.device() + self.device.device() } pub fn with_dims(&self, dims: [u32; 2]) -> Self { @@ -56,4 +56,4 @@ impl<'a> GraphicsWindowConf<'a> { pub fn queue(&self) -> &wgpu::Queue { self.device.queue() } -} \ No newline at end of file +} diff --git a/murrelet_gui/examples/tests_schema.rs b/murrelet_gui/examples/tests_schema.rs index 3004a66..d526742 100644 --- a/murrelet_gui/examples/tests_schema.rs +++ b/murrelet_gui/examples/tests_schema.rs @@ -105,5 +105,3 @@ fn main() { ) ); } - - diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs index f56ed4c..4e59dbf 100644 --- a/murrelet_gui/src/lib.rs +++ b/murrelet_gui/src/lib.rs @@ -170,9 +170,7 @@ impl CanChangeToGUI for MurreletSchema { ), MurreletSchema::Enum(name, items, b) => MurreletGUISchema::Enum( name.clone(), - items - .iter() - .map(|(_type, x)| change_enum_to_gui(x)) + items.values().map(change_enum_to_gui) .collect_vec(), *b, ), diff --git a/murrelet_gui_derive/src/parser.rs b/murrelet_gui_derive/src/parser.rs index 6aec784..34dec9c 100644 --- a/murrelet_gui_derive/src/parser.rs +++ b/murrelet_gui_derive/src/parser.rs @@ -109,13 +109,8 @@ where let idents = ParsedFieldIdent { name: name.clone() }; - let is_untagged = if let Some(enum_tag) = &e.enum_tag { - if enum_tag.as_str() == "external" { - true - } else { - false - } + enum_tag.as_str() == "external" } else { false }; @@ -187,7 +182,7 @@ impl LivecodeFieldReceiver { } else { panic!("unexpected kind") } - } else if let Some(_) = &self.reference { + } else if self.reference.is_some() { HowToControlThis::Name } else if let Some(func) = &self.func { HowToControlThis::Override(func.to_owned()) diff --git a/murrelet_livecode/src/app_src.rs b/murrelet_livecode/src/app_src.rs index 0de82d5..6c8fabe 100644 --- a/murrelet_livecode/src/app_src.rs +++ b/murrelet_livecode/src/app_src.rs @@ -223,7 +223,7 @@ impl AppInputValues { pub fn key_cycle_bool(&self, key: MurreletKey) -> bool { // just need to check if this one's pressed right now if let Some(k) = self.lookup.get(&key) { - self.keys_cycle[*k] % 2 == 0 + self.keys_cycle[*k].is_multiple_of(2) } else { false } diff --git a/murrelet_livecode/src/cachedcompute.rs b/murrelet_livecode/src/cachedcompute.rs index 3c34643..41fb6a4 100644 --- a/murrelet_livecode/src/cachedcompute.rs +++ b/murrelet_livecode/src/cachedcompute.rs @@ -3,6 +3,12 @@ use std::cell::OnceCell; #[derive(Clone, Debug)] pub struct CachedCompute(OnceCell); +impl Default for CachedCompute { + fn default() -> Self { + Self::new() + } +} + impl CachedCompute { pub fn new() -> Self { Self(OnceCell::new()) diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index f12ae6f..75b1d1f 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -484,9 +484,9 @@ impl ToMixedDefs for MixedEvalDefs { } } -impl<'a> ToMixedDefs for (&'a str, LivecodeValue) { +impl ToMixedDefs for (&str, LivecodeValue) { fn to_mixed_def(&self) -> MixedEvalDefsRef { - MixedEvalDefs::new_simple(self.0, self.1.clone()).to_mixed_def() + MixedEvalDefs::new_simple(self.0, self.1).to_mixed_def() } } diff --git a/murrelet_livecode/src/lazy.rs b/murrelet_livecode/src/lazy.rs index fcbd0bf..7cb018b 100644 --- a/murrelet_livecode/src/lazy.rs +++ b/murrelet_livecode/src/lazy.rs @@ -247,7 +247,7 @@ where // without a _, like unitcell.. fn eval_idx_(&self, idx: IdxInRange, prefix: &str) -> LivecodeResult { - let vals = ExprWorldContextValues::new_from_idx(idx).with_prefix(&format!("{}", prefix)); + let vals = ExprWorldContextValues::new_from_idx(idx).with_prefix(prefix); self.eval_lazy(&MixedEvalDefs::new_from_expr(vals)) } @@ -295,7 +295,6 @@ where } } - #[derive(Clone, Debug, Default)] pub struct LazyVec2 { x: LazyNodeF32, @@ -337,16 +336,14 @@ impl GetLivecodeIdentifiers for ControlLazyVec2 { fn variable_identifiers(&self) -> Vec { self.0 .iter() - .map(|f| f.variable_identifiers()) - .flatten() + .flat_map(|f| f.variable_identifiers()) .collect_vec() } fn function_identifiers(&self) -> Vec { self.0 .iter() - .map(|f| f.function_identifiers()) - .flatten() + .flat_map(|f| f.function_identifiers()) .collect_vec() } } @@ -402,16 +399,14 @@ impl GetLivecodeIdentifiers for ControlLazyVec3 { fn variable_identifiers(&self) -> Vec { self.0 .iter() - .map(|f| f.variable_identifiers()) - .flatten() + .flat_map(|f| f.variable_identifiers()) .collect_vec() } fn function_identifiers(&self) -> Vec { self.0 .iter() - .map(|f| f.function_identifiers()) - .flatten() + .flat_map(|f| f.function_identifiers()) .collect_vec() } } @@ -490,16 +485,14 @@ impl GetLivecodeIdentifiers for ControlLazyMurreletColor { fn variable_identifiers(&self) -> Vec { self.0 .iter() - .map(|f| f.variable_identifiers()) - .flatten() + .flat_map(|f| f.variable_identifiers()) .collect_vec() } fn function_identifiers(&self) -> Vec { self.0 .iter() - .map(|f| f.function_identifiers()) - .flatten() + .flat_map(|f| f.function_identifiers()) .collect_vec() } } diff --git a/murrelet_livecode/src/lib.rs b/murrelet_livecode/src/lib.rs index f76a4a3..7c89077 100644 --- a/murrelet_livecode/src/lib.rs +++ b/murrelet_livecode/src/lib.rs @@ -1,5 +1,6 @@ pub mod app_src; pub mod boop; +pub mod cachedcompute; pub mod expr; pub mod lazy; pub mod livecode; @@ -7,4 +8,3 @@ pub mod nestedit; pub mod state; pub mod types; pub mod unitcells; -pub mod cachedcompute; diff --git a/murrelet_livecode/src/livecode.rs b/murrelet_livecode/src/livecode.rs index b13b249..98d3aae 100644 --- a/murrelet_livecode/src/livecode.rs +++ b/murrelet_livecode/src/livecode.rs @@ -73,7 +73,6 @@ where fn o(&self, w: &LivecodeWorldState) -> LivecodeResult> { self.iter().map(|x| x.o(w)).collect::, _>>() } - } pub trait LivecodeToControl { @@ -156,7 +155,10 @@ impl LivecodeToControl for u64 { } } -impl LivecodeToControl> for Vec where Source: LivecodeToControl { +impl LivecodeToControl> for Vec +where + Source: LivecodeToControl, +{ fn to_control(&self) -> Vec { self.iter().map(|x| x.to_control()).collect_vec() } @@ -321,13 +323,20 @@ impl GetLivecodeIdentifiers for [ControlF32; 4] { } } -impl GetLivecodeIdentifiers for Vec where T: GetLivecodeIdentifiers { +impl GetLivecodeIdentifiers for Vec +where + T: GetLivecodeIdentifiers, +{ fn variable_identifiers(&self) -> Vec { - self.iter().map(|x| x.variable_identifiers()).flatten().collect_vec() + self.iter() + .flat_map(|x| x.variable_identifiers()) + .collect_vec() } fn function_identifiers(&self) -> Vec { - self.iter().map(|x| x.function_identifiers()).flatten().collect_vec() + self.iter() + .flat_map(|x| x.function_identifiers()) + .collect_vec() } } @@ -563,7 +572,6 @@ pub enum ControlBool { Expr(Node), } impl ControlBool { - pub fn force_from_str(s: &str) -> ControlBool { match build_operator_tree(s) { Ok(e) => Self::Expr(e), diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index 13e44c5..3235820 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -558,7 +558,7 @@ impl IsLivecodeSrc for LiveCodeTimeInstantInfo { ("t".to_owned(), LivecodeValue::Float(time as f64)), ( "tease".to_owned(), - LivecodeValue::Float(ease(time.into(), (1.0 / 4.0).into(), 0.0)), + LivecodeValue::Float(ease(time.into(), 1.0 / 4.0 , 0.0)), ), ( "stease".to_owned(), diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index c5903f8..3868dc1 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -58,7 +58,7 @@ impl IterUnwrapOrPrint for Vec { where F: Fn(&T) -> LivecodeResult, { - let res: LivecodeResult> = self.iter().map(|d| f(d)).collect(); + let res: LivecodeResult> = self.iter().map(f).collect(); print_expect(res, err).unwrap_or(vec![]) } } @@ -152,8 +152,8 @@ impl ControlVecElementRepeatMethod { fn len(&self, w: &LivecodeWorldState) -> LivecodeResult { let v = match self { ControlVecElementRepeatMethod::Single(s) => { - let ss = s.o(w)?; - ss + + s.o(w)? } ControlVecElementRepeatMethod::Rect(r) => { let rr = r.o(w)?; @@ -377,8 +377,8 @@ impl LazyVecElementRepeatMethod { fn len(&self, ctx: &MixedEvalDefs) -> LivecodeResult { let v = match self { LazyVecElementRepeatMethod::Single(s) => { - let ss = s.eval_lazy(ctx)?; - ss + + s.eval_lazy(ctx)? } LazyVecElementRepeatMethod::Rect(r) => { let rx = r[0].eval_lazy(ctx)?; @@ -386,8 +386,8 @@ impl LazyVecElementRepeatMethod { rx * ry } LazyVecElementRepeatMethod::Blend(b) => { - let ss = b.count.eval_lazy(ctx)?; - ss + + b.count.eval_lazy(ctx)? } }; Ok(v as usize) diff --git a/murrelet_livecode/src/unitcells.rs b/murrelet_livecode/src/unitcells.rs index 5814a14..b00edbd 100644 --- a/murrelet_livecode/src/unitcells.rs +++ b/murrelet_livecode/src/unitcells.rs @@ -32,7 +32,7 @@ impl TmpUnitCells UnitCellLookup { match neighbor { CellNeighbor::Hex(HexCellNeighbor::Up) => self.get_ij(i, j + 1), CellNeighbor::Hex(HexCellNeighbor::UpLeft) => { - let jj = if i % 2 == 0 { j + 1 } else { j }; + let jj = if i.is_multiple_of(2) { j + 1 } else { j }; self.get_ij(i - 1, jj) } CellNeighbor::Hex(HexCellNeighbor::DownLeft) => { - let jj = if i % 2 == 0 { j } else { j - 1 }; + let jj = if i.is_multiple_of(2) { j } else { j - 1 }; self.get_ij(i - 1, jj) } CellNeighbor::Hex(HexCellNeighbor::Down) => self.get_ij(i, j - 1), CellNeighbor::Hex(HexCellNeighbor::DownRight) => { - let jj = if i % 2 == 0 { j } else { j - 1 }; + let jj = if i.is_multiple_of(2) { j } else { j - 1 }; self.get_ij(i + 1, jj) } CellNeighbor::Hex(HexCellNeighbor::UpRight) => { - let jj = if i % 2 == 0 { j + 1 } else { j }; + let jj = if i.is_multiple_of(2) { j + 1 } else { j }; self.get_ij(i + 1, jj) } @@ -614,7 +614,7 @@ impl UnitCellContext { UnitCellContext { idx: self.idx, - ctx: ctx, + ctx, detail: other.detail.as_wallpaper().unwrap().combine(&self.detail), tile_info: None, } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs index c5a9837..614442d 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/lib.rs @@ -5,13 +5,13 @@ extern crate proc_macro; +mod derive_cached; mod derive_graphics_trait; mod derive_lazy; mod derive_livecode; mod derive_nestedit; mod parser; mod toplevel; -mod derive_cached; use darling::FromDeriveInput; use derive_graphics_trait::impl_graphics_trait; @@ -116,7 +116,6 @@ pub fn murrelet_livecode_graphics(input: TokenStream) -> TokenStream { impl_graphics_trait(ast).into() } - #[proc_macro_derive(Cached, attributes(cached))] pub fn murrelet_cache_compute(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as syn::DeriveInput); diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs index 80a14a0..89e2559 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs +++ b/murrelet_livecode_macros/murrelet_livecode_derive/src/parser.rs @@ -895,7 +895,7 @@ pub fn recursive_ident_from_path(t: &syn::Type, acc: &mut Vec) { let s = path.segments.last().unwrap(); let main_type = s.ident.clone(); - if main_type.to_string() != "WrappedLazyType" { + if main_type != "WrappedLazyType" { acc.push(main_type); } diff --git a/murrelet_perform/src/asset_loader.rs b/murrelet_perform/src/asset_loader.rs index ed3f0b7..583573a 100644 --- a/murrelet_perform/src/asset_loader.rs +++ b/murrelet_perform/src/asset_loader.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, path::Path}; use itertools::Itertools; use lerpable::Lerpable; use murrelet_common::{ - Assets, JsonAssetLookup, RasterAsset, RasterAssetLookup, VectorAsset, VectorLayersAssetLookup + Assets, JsonAssetLookup, RasterAsset, RasterAssetLookup, VectorAsset, VectorLayersAssetLookup, }; use murrelet_gui::CanMakeGUI; use murrelet_livecode_derive::Livecode; diff --git a/murrelet_perform/src/load.rs b/murrelet_perform/src/load.rs index 7262804..e86d17d 100644 --- a/murrelet_perform/src/load.rs +++ b/murrelet_perform/src/load.rs @@ -29,11 +29,11 @@ pub fn preprocess_yaml>(text: &str, loc: P) -> String let pattern = r"(?m)^( *)\[\[([^\[\]]+)\]\]"; - let re = Regex::new(&pattern).unwrap(); + let re = Regex::new(pattern).unwrap(); let mut new_text = text.to_owned(); - for cap in re.captures_iter(&text) { + for cap in re.captures_iter(text) { // println!("cap {:?}", cap); let spaces = cap[1].len(); let filename = cap[2].to_string(); diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index e0d0331..00dd195 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -629,16 +629,12 @@ pub fn svg_save_path(lil_liveconfig: &LilLiveConfig) -> SvgDrawConfig { } pub fn svg_save_path_with_prefix(lil_liveconfig: &LilLiveConfig, prefix: &str) -> SvgDrawConfig { - let capture_path = if let Some(save_path) = lil_liveconfig.save_path { - Some(capture_frame_name( + let capture_path = lil_liveconfig.save_path.map(|save_path| capture_frame_name( save_path, lil_liveconfig.run_id, lil_liveconfig.w.actual_frame_u64(), prefix, - )) - } else { - None - }; + )); SvgDrawConfig::new( lil_liveconfig.app_config.width, @@ -795,13 +791,13 @@ where let w = self.world(); - let mut target = self.controlconfig.o(&w)?; + let mut target = self.controlconfig.o(w)?; let mut lerp_change = 0.0; // todo, make this optional if target.config_app_loc().should_lerp() { if self.lerp_pct < 1.0 { - let old_target = self.prev_controlconfig.o(&w)?; + let old_target = self.prev_controlconfig.o(w)?; target = old_target.lerpify(&target, &self.lerp_pct); } @@ -865,10 +861,10 @@ where .controlconfig .variable_identifiers() .into_iter() - .chain(self.prev_controlconfig.variable_identifiers().into_iter()); + .chain(self.prev_controlconfig.variable_identifiers()); let variables = if let Some(queued) = &self.queued_configcontrol { variables_iter - .chain(queued.variable_identifiers().into_iter()) + .chain(queued.variable_identifiers()) .map(|x| x.name) .collect::>() } else { @@ -883,9 +879,7 @@ where // web one, callback pub fn update_config_to(&mut self, text: &str) -> Result<(), String> { match ControlConfType::cb_reload_and_update_info(&mut self.util, text) { - Ok(d) => { - self.update_config_directly(d).map_err(|x| x.to_string()) - } + Ok(d) => self.update_config_directly(d).map_err(|x| x.to_string()), Err(e) => Err(e), } } @@ -923,7 +917,7 @@ where self.livecode_src.update(&update_input); // todo, set this as a variable? - if app.elapsed_frames() % 1 == 0 { + if app.elapsed_frames().is_multiple_of(1) { let variables = self .controlconfig .variable_identifiers() @@ -950,11 +944,10 @@ where // if we can reload whenever, do that. otherwise only reload on bar - if reload { - if !self.app_config().reload_on_bar() || self.world().time().is_on_bar() { + if reload + && (!self.app_config().reload_on_bar() || self.world().time().is_on_bar()) { self.reload_config(); } - } if self.app_config().should_reset() { self.util.reset_time(); @@ -1026,17 +1019,13 @@ where } pub fn capture_frame_name(&self, frame: u64, prefix: &str) -> Option { - if let Some(save_path) = &self.save_path { - Some(capture_frame_name(&save_path, self.run_id, frame, prefix)) - } else { - None - } + self.save_path.as_ref().map(|save_path| capture_frame_name(save_path, self.run_id, frame, prefix)) } // model.livecode.capture_logic(|img_name: PathBuf| { app.main_window().capture_frame(img_name); } ); pub fn capture(&self, capture_frame_fn: F) -> LivecodeResult<()> where - F: Fn(PathBuf) -> (), + F: Fn(PathBuf), { let frame = self.world().actual_frame_u64(); @@ -1064,7 +1053,7 @@ where pub fn capture_with_fn(&self, capture_frame_fn: F) -> LivecodeResult<()> where - F: Fn(PathBuf) -> (), + F: Fn(PathBuf), { let w = self.world(); @@ -1080,7 +1069,7 @@ where || should_capture { let frame_freq = 1; - if frame % frame_freq == 0 { + if frame.is_multiple_of(frame_freq) { self.capture(capture_frame_fn)?; } } @@ -1097,14 +1086,14 @@ where let w = self.world(); let reload_rate = self.app_config().reload_rate; let reload_rate_says_so = - reload_rate >= 1 && w.actual_frame() as u64 % self.app_config().reload_rate == 0; + reload_rate >= 1 && (w.actual_frame() as u64).is_multiple_of(self.app_config().reload_rate); let config_says_so = self.app_config().reload; reload_rate_says_so || config_says_so } pub fn should_redraw(&self) -> bool { let w = self.world(); - let redraw_says_so = w.actual_frame() as u64 % self.app_config().redraw == 0; + let redraw_says_so = (w.actual_frame() as u64).is_multiple_of(self.app_config().redraw); let save_says_so = self.app_config().svg.save; // might have other things.. redraw_says_so || save_says_so diff --git a/murrelet_perform/src/reload.rs b/murrelet_perform/src/reload.rs index 1dc5348..a7afb23 100644 --- a/murrelet_perform/src/reload.rs +++ b/murrelet_perform/src/reload.rs @@ -86,15 +86,15 @@ pub trait LiveCoderLoader: Sized { let mut latest_time = MurreletTime::epoch(); for entry in - fs::read_dir(dir).map_err(|e| LivecodeError::Io(format!("template error"), e))? + fs::read_dir(dir).map_err(|e| LivecodeError::Io("template error".to_string(), e))? { - let entry = entry.map_err(|e| LivecodeError::Io(format!("template error"), e))?; + let entry = entry.map_err(|e| LivecodeError::Io("template error".to_string(), e))?; let metadata = entry .metadata() - .map_err(|e| LivecodeError::Io(format!("template error"), e))?; + .map_err(|e| LivecodeError::Io("template error".to_string(), e))?; let modified_time_s = metadata .modified() - .map_err(|e| LivecodeError::Io(format!("template error"), e))?; + .map_err(|e| LivecodeError::Io("template error".to_string(), e))?; let modified_time = MurreletTime::from_epoch_time( modified_time_s @@ -115,7 +115,7 @@ pub trait LiveCoderLoader: Sized { fn cb_reload_and_update_info(util: &mut LiveCodeUtil, text: &str) -> Result { util.reset_info(); - match Self::parse(&text) { + match Self::parse(text) { Ok(x) => { util.update_info_reloaded(); Ok(x) @@ -141,7 +141,7 @@ pub trait LiveCoderLoader: Sized { })?; let modified = filename .modified() - .map_err(|err| LivecodeError::Io(format!("error finding modified type"), err))?; + .map_err(|err| LivecodeError::Io("error finding modified type".to_string(), err))?; let current_modified = murrelet_time_from_system(modified); diff --git a/murrelet_schema_derive/src/parser.rs b/murrelet_schema_derive/src/parser.rs index 7f69149..d7b0352 100644 --- a/murrelet_schema_derive/src/parser.rs +++ b/murrelet_schema_derive/src/parser.rs @@ -110,11 +110,7 @@ where let idents = ParsedFieldIdent { name: name.clone() }; let is_untagged = if let Some(enum_tag) = &e.enum_tag { - if enum_tag.as_str() == "external" { - true - } else { - false - } + enum_tag.as_str() == "external" } else { false }; diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index de6a2e7..b834195 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -630,6 +630,12 @@ pub struct SvgLayer { paths: Vec, text: Vec, } +impl Default for SvgLayer { + fn default() -> Self { + Self::new() + } +} + impl SvgLayer { pub fn new() -> SvgLayer { SvgLayer { @@ -713,7 +719,7 @@ impl SvgPathCache { let layer = self .layers .entry(layer.to_owned()) - .or_insert(SvgLayer::new()); + .or_default(); layer.paths.push(StyledPath::from_path(line)); } @@ -721,7 +727,7 @@ impl SvgPathCache { let layer = self .layers .entry(layer.to_owned()) - .or_insert(SvgLayer::new()); + .or_default(); layer.paths.push(StyledPath::from_path( self.config.transform_many_vec2(&line), )); @@ -731,7 +737,7 @@ impl SvgPathCache { let layer = self .layers .entry(layer.to_owned()) - .or_insert(SvgLayer::new()); + .or_default(); layer.paths.push( styled_path .transform_with_mat4_after(self.config.svg_draw_config().transform_for_size()), @@ -742,7 +748,7 @@ impl SvgPathCache { let layer = self .layers .entry(layer.to_owned()) - .or_insert(SvgLayer::new()); + .or_default(); layer .text .push(text.transform_with(self.config.svg_draw_config())); @@ -752,7 +758,7 @@ impl SvgPathCache { let layer = self .layers .entry(layer.to_owned()) - .or_insert(SvgLayer::new()); + .or_default(); layer.clear(); } @@ -867,20 +873,17 @@ impl ToSvgData for CurveDrawer { } murrelet_draw::curve_drawer::CurveSegment::Points(a) => { let maybe_first_and_rest = a.points().split_first(); - match maybe_first_and_rest { - Some((f, rest)) => { - // first make sure we're at the first point - if curr_point != Some(*f) { - path = path.line_to((f.x, f.y)); - } - - for v in rest { - path = path.line_to((v.x, v.y)); - } - - curr_point = Some(a.last_point()) + if let Some((f, rest)) = maybe_first_and_rest { + // first make sure we're at the first point + if curr_point != Some(*f) { + path = path.line_to((f.x, f.y)); } - None => {} + + for v in rest { + path = path.line_to((v.x, v.y)); + } + + curr_point = Some(a.last_point()) } } } @@ -893,7 +896,7 @@ impl ToSvgData for CurveDrawer { // just connect the dots with lines impl ToSvgData for Vec { fn to_svg_data(&self) -> Option { - if self.len() == 0 { + if self.is_empty() { return None; } From 42a55e0e8797af504a14621f2aaac6a9ad90c04d Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 6 Feb 2026 13:12:32 -0500 Subject: [PATCH 144/149] cargo fmt and start to fix schema --- murrelet_common/src/lib.rs | 1 - murrelet_draw/src/compass.rs | 1 - murrelet_draw/src/sequencers.rs | 1 - murrelet_draw/src/tesselate.rs | 4 ++-- murrelet_gen/src/lib.rs | 4 +--- murrelet_gpu/src/editable_shaders.rs | 22 ++++++++++------- murrelet_gpu/src/graphics_ref.rs | 4 +--- murrelet_gui/src/lib.rs | 3 +-- murrelet_livecode/src/state.rs | 2 +- murrelet_livecode/src/types.rs | 15 +++--------- murrelet_perform/src/perform.rs | 21 ++++++++++------- murrelet_schema_derive/src/derive_schema.rs | 26 ++++++++++++++------- murrelet_svg/src/svg.rs | 25 ++++---------------- 13 files changed, 58 insertions(+), 71 deletions(-) diff --git a/murrelet_common/src/lib.rs b/murrelet_common/src/lib.rs index 6d42dc8..bc76d08 100644 --- a/murrelet_common/src/lib.rs +++ b/murrelet_common/src/lib.rs @@ -137,7 +137,6 @@ pub fn epoch_time_us() -> u128 { { use std::time::SystemTime; use std::time::UNIX_EPOCH; - SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/murrelet_draw/src/compass.rs b/murrelet_draw/src/compass.rs index 8df7aaf..4869752 100644 --- a/murrelet_draw/src/compass.rs +++ b/murrelet_draw/src/compass.rs @@ -235,7 +235,6 @@ impl InteractiveCompassBuilder { pub fn new_segments(&mut self, dir: &CompassAction) -> Vec { // here we go! - match dir { CompassAction::Angle(x) => { diff --git a/murrelet_draw/src/sequencers.rs b/murrelet_draw/src/sequencers.rs index b7d68c5..3c06977 100644 --- a/murrelet_draw/src/sequencers.rs +++ b/murrelet_draw/src/sequencers.rs @@ -127,7 +127,6 @@ impl SimpleCountSequence { impl UnitCellCreator for SimpleCountSequence { fn to_unit_cell_ctxs(&self) -> Vec { - (0..self.0) .map(|x| { let idx = IdxInRange::new(x, self.0); diff --git a/murrelet_draw/src/tesselate.rs b/murrelet_draw/src/tesselate.rs index e8b1d9c..3dc4735 100644 --- a/murrelet_draw/src/tesselate.rs +++ b/murrelet_draw/src/tesselate.rs @@ -111,7 +111,7 @@ impl AsLyonTransform for SimpleTransform2dStep { let vv = vector(v.x, v.y); let t = t.then_translate(-vv); let t = t.then_rotate(Angle::radians(a.angle())); - + t.then_translate(vv) } SimpleTransform2dStep::Scale(v) => t.then_scale(v.x, v.y), @@ -923,7 +923,7 @@ impl From for Point2D { fn point_for_position(pos: &svg::node::element::path::Position, pt: Pt, from: Pt) -> Pt { match pos { svg::node::element::path::Position::Absolute => pt, - svg::node::element::path::Position::Relative => pt + from , + svg::node::element::path::Position::Relative => pt + from, } } diff --git a/murrelet_gen/src/lib.rs b/murrelet_gen/src/lib.rs index b4feb05..b360f87 100644 --- a/murrelet_gen/src/lib.rs +++ b/murrelet_gen/src/lib.rs @@ -20,9 +20,7 @@ pub trait CanSampleFromDist: Sized { fn gen_from_seed(seed: u64) -> Self { let mut rng = StdRng::seed_from_u64(seed); - let rns: Vec = (0..Self::rn_count()) - .map(|_| rng.gen()) - .collect(); + let rns: Vec = (0..Self::rn_count()).map(|_| rng.gen()).collect(); Self::sample_dist(&rns, 0) } diff --git a/murrelet_gpu/src/editable_shaders.rs b/murrelet_gpu/src/editable_shaders.rs index cd57156..d7e5028 100644 --- a/murrelet_gpu/src/editable_shaders.rs +++ b/murrelet_gpu/src/editable_shaders.rs @@ -69,7 +69,9 @@ impl ShaderStrings { name: &str, prefix: &str, ) -> Option { - self.shaders.get(name).map(|str| Self::shader_custom_prefix(str, prefix)) + self.shaders + .get(name) + .map(|str| Self::shader_custom_prefix(str, prefix)) } pub fn get_graphics_ref( @@ -77,9 +79,11 @@ impl ShaderStrings { c: &GraphicsWindowConf, name: &str, ) -> Option> { - self.shaders.get(name).map(|str| GraphicsCreator::::default() - .with_mag_filter(wgpu::FilterMode::Nearest) - .to_graphics_ref(c, name, &Self::shader(str))) + self.shaders.get(name).map(|str| { + GraphicsCreator::::default() + .with_mag_filter(wgpu::FilterMode::Nearest) + .to_graphics_ref(c, name, &Self::shader(str)) + }) } pub fn get_graphics_ref_2tex( @@ -87,10 +91,12 @@ impl ShaderStrings { c: &GraphicsWindowConf, name: &str, ) -> Option> { - self.shaders.get(name).map(|str| GraphicsCreator::::default() - .with_mag_filter(wgpu::FilterMode::Nearest) - .with_second_texture() - .to_graphics_ref(c, name, &Self::shader2tex(str))) + self.shaders.get(name).map(|str| { + GraphicsCreator::::default() + .with_mag_filter(wgpu::FilterMode::Nearest) + .with_second_texture() + .to_graphics_ref(c, name, &Self::shader2tex(str)) + }) } pub fn has_changed(&self, other: &ControlShaderStrings) -> bool { diff --git a/murrelet_gpu/src/graphics_ref.rs b/murrelet_gpu/src/graphics_ref.rs index 4741363..8fabff5 100644 --- a/murrelet_gpu/src/graphics_ref.rs +++ b/murrelet_gpu/src/graphics_ref.rs @@ -905,7 +905,7 @@ impl Graphics { fn _sampler(device: &wgpu::Device, details: ShaderOptions) -> wgpu::Sampler { let sampler_desc = details.as_sampler_desc(); - + device.create_sampler(&sampler_desc) } @@ -1041,8 +1041,6 @@ impl Graphics { multiview: None, }; - - device.create_render_pipeline(&rp_desc) } diff --git a/murrelet_gui/src/lib.rs b/murrelet_gui/src/lib.rs index 4e59dbf..d1b2df3 100644 --- a/murrelet_gui/src/lib.rs +++ b/murrelet_gui/src/lib.rs @@ -170,8 +170,7 @@ impl CanChangeToGUI for MurreletSchema { ), MurreletSchema::Enum(name, items, b) => MurreletGUISchema::Enum( name.clone(), - items.values().map(change_enum_to_gui) - .collect_vec(), + items.values().map(change_enum_to_gui).collect_vec(), *b, ), MurreletSchema::List(murrelet_schema) => { diff --git a/murrelet_livecode/src/state.rs b/murrelet_livecode/src/state.rs index 3235820..da70071 100644 --- a/murrelet_livecode/src/state.rs +++ b/murrelet_livecode/src/state.rs @@ -558,7 +558,7 @@ impl IsLivecodeSrc for LiveCodeTimeInstantInfo { ("t".to_owned(), LivecodeValue::Float(time as f64)), ( "tease".to_owned(), - LivecodeValue::Float(ease(time.into(), 1.0 / 4.0 , 0.0)), + LivecodeValue::Float(ease(time.into(), 1.0 / 4.0, 0.0)), ), ( "stease".to_owned(), diff --git a/murrelet_livecode/src/types.rs b/murrelet_livecode/src/types.rs index 3868dc1..55ba821 100644 --- a/murrelet_livecode/src/types.rs +++ b/murrelet_livecode/src/types.rs @@ -151,10 +151,7 @@ pub enum ControlVecElementRepeatMethod { impl ControlVecElementRepeatMethod { fn len(&self, w: &LivecodeWorldState) -> LivecodeResult { let v = match self { - ControlVecElementRepeatMethod::Single(s) => { - - s.o(w)? - } + ControlVecElementRepeatMethod::Single(s) => s.o(w)?, ControlVecElementRepeatMethod::Rect(r) => { let rr = r.o(w)?; rr.x * rr.y @@ -376,19 +373,13 @@ pub enum LazyVecElementRepeatMethod { impl LazyVecElementRepeatMethod { fn len(&self, ctx: &MixedEvalDefs) -> LivecodeResult { let v = match self { - LazyVecElementRepeatMethod::Single(s) => { - - s.eval_lazy(ctx)? - } + LazyVecElementRepeatMethod::Single(s) => s.eval_lazy(ctx)?, LazyVecElementRepeatMethod::Rect(r) => { let rx = r[0].eval_lazy(ctx)?; let ry = r[1].eval_lazy(ctx)?; rx * ry } - LazyVecElementRepeatMethod::Blend(b) => { - - b.count.eval_lazy(ctx)? - } + LazyVecElementRepeatMethod::Blend(b) => b.count.eval_lazy(ctx)?, }; Ok(v as usize) } diff --git a/murrelet_perform/src/perform.rs b/murrelet_perform/src/perform.rs index 00dd195..83b5b37 100644 --- a/murrelet_perform/src/perform.rs +++ b/murrelet_perform/src/perform.rs @@ -629,12 +629,14 @@ pub fn svg_save_path(lil_liveconfig: &LilLiveConfig) -> SvgDrawConfig { } pub fn svg_save_path_with_prefix(lil_liveconfig: &LilLiveConfig, prefix: &str) -> SvgDrawConfig { - let capture_path = lil_liveconfig.save_path.map(|save_path| capture_frame_name( + let capture_path = lil_liveconfig.save_path.map(|save_path| { + capture_frame_name( save_path, lil_liveconfig.run_id, lil_liveconfig.w.actual_frame_u64(), prefix, - )); + ) + }); SvgDrawConfig::new( lil_liveconfig.app_config.width, @@ -944,10 +946,9 @@ where // if we can reload whenever, do that. otherwise only reload on bar - if reload - && (!self.app_config().reload_on_bar() || self.world().time().is_on_bar()) { - self.reload_config(); - } + if reload && (!self.app_config().reload_on_bar() || self.world().time().is_on_bar()) { + self.reload_config(); + } if self.app_config().should_reset() { self.util.reset_time(); @@ -1019,7 +1020,9 @@ where } pub fn capture_frame_name(&self, frame: u64, prefix: &str) -> Option { - self.save_path.as_ref().map(|save_path| capture_frame_name(save_path, self.run_id, frame, prefix)) + self.save_path + .as_ref() + .map(|save_path| capture_frame_name(save_path, self.run_id, frame, prefix)) } // model.livecode.capture_logic(|img_name: PathBuf| { app.main_window().capture_frame(img_name); } ); @@ -1085,8 +1088,8 @@ where pub fn should_reload(&self) -> bool { let w = self.world(); let reload_rate = self.app_config().reload_rate; - let reload_rate_says_so = - reload_rate >= 1 && (w.actual_frame() as u64).is_multiple_of(self.app_config().reload_rate); + let reload_rate_says_so = reload_rate >= 1 + && (w.actual_frame() as u64).is_multiple_of(self.app_config().reload_rate); let config_says_so = self.app_config().reload; reload_rate_says_so || config_says_so } diff --git a/murrelet_schema_derive/src/derive_schema.rs b/murrelet_schema_derive/src/derive_schema.rs index 65a28ac..d215b9e 100644 --- a/murrelet_schema_derive/src/derive_schema.rs +++ b/murrelet_schema_derive/src/derive_schema.rs @@ -39,7 +39,7 @@ impl GenFinal for FieldTokensSchema { impl murrelet_schema::CanMakeSchema for #name { fn make_schema() -> murrelet_schema::MurreletSchema { - let mut v = vec![]; + let mut v = std::collections::BTreeMap::new(); #(#for_make_schema;)* murrelet_schema::MurreletSchema::Struct(#name_str.to_owned(), v) @@ -62,7 +62,10 @@ impl GenFinal for FieldTokensSchema { quote! { impl murrelet_schema::CanMakeSchema for #name { fn make_schema() -> murrelet_schema::MurreletSchema { - murrelet_schema::MurreletSchema::Enum(#name_str.to_owned(), vec![#(#for_make_schema,)*], #is_untagged) + let mut v = std::collections::BTreeMap::new(); + #(#for_make_schema;)* + + murrelet_schema::MurreletSchema::Enum(#name_str.to_owned(), v, #is_untagged) } } @@ -95,7 +98,15 @@ impl GenFinal for FieldTokensSchema { let ty = convert_vec_type(&idents.data.fields.fields.first().unwrap().ty); let variant_ident_str = variant_ident.to_string(); - let for_make_schema = quote! { (murrelet_schema::MurreletEnumVal::Unnamed(#variant_ident_str.to_string(), #ty::make_schema())) }; + let for_make_schema = quote! { + v.insert( + #variant_ident_str.to_string(), + murrelet_schema::MurreletEnumVal::Unnamed( + #variant_ident_str.to_string(), + Box::new(#ty::make_schema()), + ), + ) + }; FieldTokensSchema { for_make_schema } } @@ -105,8 +116,7 @@ impl GenFinal for FieldTokensSchema { let variant_ident = idents.data.ident; let variant_ident_str = variant_ident.to_string(); - let for_make_schema = - quote! { murrelet_schema::MurreletEnumVal::Unit(#variant_ident_str.to_owned()) }; + let for_make_schema = quote! { v.insert(#variant_ident_str.to_owned(), murrelet_schema::MurreletEnumVal::Unit(#variant_ident_str.to_owned())) }; FieldTokensSchema { for_make_schema } } @@ -132,7 +142,7 @@ impl GenFinal for FieldTokensSchema { let field_name = idents.data.ident.unwrap().to_string(); let for_make_schema = - quote! { v.push((#field_name.to_owned(), murrelet_schema::MurreletSchema::Skip)) }; + quote! { v.insert(#field_name.to_owned(), murrelet_schema::MurreletSchema::Skip) }; FieldTokensSchema { for_make_schema } } @@ -149,7 +159,7 @@ impl GenFinal for FieldTokensSchema { let for_make_schema = if is_flat { quote! { v.extend(#kind::make_schema().unwrap_to_struct_fields().into_iter()) } } else { - quote! { v.push((#field_name_str.to_owned(), #kind::make_schema())) } + quote! { v.insert(#field_name_str.to_owned(), #kind::make_schema()) } }; FieldTokensSchema { for_make_schema } @@ -161,7 +171,7 @@ impl GenFinal for FieldTokensSchema { let method: syn::Path = syn::parse_str(func).expect("Custom method is invalid path!"); - let for_make_schema = quote! { v.push((#field_name_str.to_owned(), #method())) }; + let for_make_schema = quote! { v.insert(#field_name_str.to_owned(), #method()) }; FieldTokensSchema { for_make_schema } } diff --git a/murrelet_svg/src/svg.rs b/murrelet_svg/src/svg.rs index b834195..ab76a25 100644 --- a/murrelet_svg/src/svg.rs +++ b/murrelet_svg/src/svg.rs @@ -716,28 +716,19 @@ impl SvgPathCache { } pub fn add_simple_path_no_transform(&mut self, layer: &str, line: Vec) { - let layer = self - .layers - .entry(layer.to_owned()) - .or_default(); + let layer = self.layers.entry(layer.to_owned()).or_default(); layer.paths.push(StyledPath::from_path(line)); } pub fn add_simple_path(&mut self, layer: &str, line: Vec) { - let layer = self - .layers - .entry(layer.to_owned()) - .or_default(); + let layer = self.layers.entry(layer.to_owned()).or_default(); layer.paths.push(StyledPath::from_path( self.config.transform_many_vec2(&line), )); } pub fn add_styled_path(&mut self, layer: &str, styled_path: StyledPath) { - let layer = self - .layers - .entry(layer.to_owned()) - .or_default(); + let layer = self.layers.entry(layer.to_owned()).or_default(); layer.paths.push( styled_path .transform_with_mat4_after(self.config.svg_draw_config().transform_for_size()), @@ -745,20 +736,14 @@ impl SvgPathCache { } pub fn add_styled_text(&mut self, layer: &str, text: StyledText) { - let layer = self - .layers - .entry(layer.to_owned()) - .or_default(); + let layer = self.layers.entry(layer.to_owned()).or_default(); layer .text .push(text.transform_with(self.config.svg_draw_config())); } pub fn clear(&mut self, layer: &str) { - let layer = self - .layers - .entry(layer.to_owned()) - .or_default(); + let layer = self.layers.entry(layer.to_owned()).or_default(); layer.clear(); } From 03385fb4afd96da7e40d4568eb51ce8856781caa Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 6 Feb 2026 14:25:11 -0500 Subject: [PATCH 145/149] wip --- .github/workflows/rust.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8b46379..1fbe883 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,13 +24,13 @@ jobs: sudo apt-get update sudo apt-get install -y libasound2-dev - name: cargo fmt - run: cargo fmt -- --check + run: cargo fmt --all -- --check - name: cargo check - run: cargo check --verbose + run: cargo check --workspace --all-targets --verbose - name: cargo check (wasm) - run: cargo check --verbose --target wasm32-unknown-unknown + run: cargo check --workspace --all-targets --verbose --target wasm32-unknown-unknown - name: cargo test - run: cargo test --verbose + run: cargo test --workspace --verbose From dd441e213222fa53d242dee37e506e703450763b Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 6 Feb 2026 14:34:22 -0500 Subject: [PATCH 146/149] try to fix ci --- .github/workflows/rust.yml | 4 ++-- Cargo.toml | 21 +++++++++++++++++++ murrelet/Cargo.toml | 12 +++++------ murrelet_draw/Cargo.toml | 10 ++++----- murrelet_gen/Cargo.toml | 4 ++-- murrelet_gpu/Cargo.toml | 14 ++++++------- murrelet_gui/Cargo.toml | 8 +++---- murrelet_livecode/Cargo.toml | 4 ++-- murrelet_livecode_macros/Cargo.toml | 4 ++-- .../murrelet_livecode_derive/Cargo.toml | 6 +++--- murrelet_perform/Cargo.toml | 12 +++++------ murrelet_schema/Cargo.toml | 4 ++-- murrelet_src_audio/Cargo.toml | 2 +- murrelet_src_midi/Cargo.toml | 2 +- murrelet_src_osc/Cargo.toml | 2 +- murrelet_svg/Cargo.toml | 6 +++--- 16 files changed, 68 insertions(+), 47 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1fbe883..741c2a5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,7 +17,8 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: - target: wasm32-unknown-unknown + targets: wasm32-unknown-unknown + components: rustfmt - name: Install alsa dependencies run: | @@ -33,4 +34,3 @@ jobs: run: cargo test --workspace --verbose - diff --git a/Cargo.toml b/Cargo.toml index 19f918c..3885baa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,24 @@ members = [ ] resolver = "2" + +[workspace.dependencies] +murrelet = { path = "murrelet", version = "0.1.2" } +murrelet_common = { path = "murrelet_common", version = "0.1.2" } +murrelet_draw = { path = "murrelet_draw", version = "0.1.2" } +murrelet_gpu = { path = "murrelet_gpu", version = "0.1.2" } +murrelet_livecode = { path = "murrelet_livecode", version = "0.1.2" } +murrelet_livecode_macros = { path = "murrelet_livecode_macros", version = "0.1.2" } +murrelet_livecode_derive = { path = "murrelet_livecode_macros/murrelet_livecode_derive", version = "0.1.2" } +murrelet_perform = { path = "murrelet_perform", version = "0.1.2" } +murrelet_src_audio = { path = "murrelet_src_audio", version = "0.1.2" } +murrelet_src_midi = { path = "murrelet_src_midi", version = "0.1.2" } +murrelet_src_osc = { path = "murrelet_src_osc", version = "0.1.2" } +murrelet_svg = { path = "murrelet_svg", version = "0.1.2" } +tinylivecode = { path = "tinylivecode", version = "0.1.2" } +murrelet_schema = { path = "murrelet_schema", version = "0.1.2" } +murrelet_schema_derive = { path = "murrelet_schema_derive", version = "0.1.2" } +murrelet_gui = { path = "murrelet_gui", version = "0.1.2" } +murrelet_gui_derive = { path = "murrelet_gui_derive", version = "0.1.2" } +murrelet_gen = { path = "murrelet_gen", version = "0.1.2" } +murrelet_gen_derive = { path = "murrelet_gen_derive", version = "0.1.2" } diff --git a/murrelet/Cargo.toml b/murrelet/Cargo.toml index 6d6506e..c6f9602 100644 --- a/murrelet/Cargo.toml +++ b/murrelet/Cargo.toml @@ -32,9 +32,9 @@ itertools = "0.10.5" serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" -murrelet_common = "0.1.2" -murrelet_draw = { version = "0.1.2", default-features = false } -murrelet_livecode = { version = "0.1.2", default-features = false } -murrelet_livecode_macros = { version = "0.1.2", default-features = false } -murrelet_livecode_derive = { version = "0.1.2", default-features = false } -murrelet_perform = { version = "0.1.2", default-features = false } +murrelet_common = { workspace = true } +murrelet_draw = { workspace = true, default-features = false } +murrelet_livecode = { workspace = true, default-features = false } +murrelet_livecode_macros = { workspace = true, default-features = false } +murrelet_livecode_derive = { workspace = true, default-features = false } +murrelet_perform = { workspace = true, default-features = false } diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index 19ea0e9..ffba244 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -17,11 +17,11 @@ schemars = [ [dependencies] -murrelet_common = "0.1.2" +murrelet_common = { workspace = true } -murrelet_livecode = { version = "0.1.2", default-features = false } -murrelet_livecode_macros = { version = "0.1.2", default-features = false } -murrelet_livecode_derive = { version = "0.1.2", default-features = false } +murrelet_livecode = { workspace = true, default-features = false } +murrelet_livecode_macros = { workspace = true, default-features = false } +murrelet_livecode_derive = { workspace = true, default-features = false } lerpable = { version = "0.0.2", features = ["glam"] } @@ -38,7 +38,7 @@ md-5 = "0.10.6" hex = "0.4.3" schemars = { version = "0.8.21", optional = true } -murrelet_gui = { version = "0.1.2", features = ["glam", "murrelet"] } +murrelet_gui = { workspace = true, features = ["glam", "murrelet"] } kurbo = "0.11" svg = "0.10.0" diff --git a/murrelet_gen/Cargo.toml b/murrelet_gen/Cargo.toml index dc43fa2..77a2eb7 100644 --- a/murrelet_gen/Cargo.toml +++ b/murrelet_gen/Cargo.toml @@ -7,14 +7,14 @@ edition = "2021" default = [] [dependencies] -murrelet_gen_derive = "0.1.2" +murrelet_gen_derive = { workspace = true } thiserror = "2.0.11" serde = { version = "1.0.104", features = ["derive"] } serde_json = "1.0.48" rand = "0.8" glam = { version = "0.28.0", features = ["serde"] } -murrelet_common = "0.1.2" +murrelet_common = { workspace = true } [[example]] diff --git a/murrelet_gpu/Cargo.toml b/murrelet_gpu/Cargo.toml index 2a2df33..579871e 100644 --- a/murrelet_gpu/Cargo.toml +++ b/murrelet_gpu/Cargo.toml @@ -23,12 +23,12 @@ schemars = [ wgpu_for_latest = { package = "wgpu", version = "0.20.1", optional = true } wgpu_for_nannou = { package = "wgpu", version = "0.17.1", optional = true } -murrelet_common = "0.1.2" -murrelet_livecode = { version = "0.1.2", default-features = false } -murrelet_draw = { version = "0.1.2", default-features = false } -murrelet_livecode_macros = { version = "0.1.2" } -murrelet_livecode_derive = { version = "0.1.2" } -murrelet_perform = { version = "0.1.2", default-features = false } +murrelet_common = { workspace = true } +murrelet_livecode = { workspace = true, default-features = false } +murrelet_draw = { workspace = true, default-features = false } +murrelet_livecode_macros = { workspace = true } +murrelet_livecode_derive = { workspace = true } +murrelet_perform = { workspace = true, default-features = false } lerpable = { version = "0.0.2" } @@ -49,4 +49,4 @@ image = "0.25.2" bytemuck = { version = "1.16.1", features = ["derive"] } schemars = { version = "0.8.21", optional = true } -murrelet_gui = { version = "0.1.2", features = ["glam"] } +murrelet_gui = { workspace = true, features = ["glam"] } diff --git a/murrelet_gui/Cargo.toml b/murrelet_gui/Cargo.toml index 6376820..f51a48a 100644 --- a/murrelet_gui/Cargo.toml +++ b/murrelet_gui/Cargo.toml @@ -9,10 +9,10 @@ glam = ["dep:glam"] murrelet = ["dep:murrelet_common"] [dependencies] -murrelet_gui_derive = "0.1.2" -murrelet_schema = "0.1.2" -murrelet_schema_derive = "0.1.2" -murrelet_common = { version = "0.1.2", optional = true } # used to derive types +murrelet_gui_derive = { workspace = true } +murrelet_schema = { workspace = true } +murrelet_schema_derive = { workspace = true } +murrelet_common = { workspace = true, optional = true } # used to derive types thiserror = "2.0.11" serde = { version = "1.0.104", features = ["derive"] } diff --git a/murrelet_livecode/Cargo.toml b/murrelet_livecode/Cargo.toml index 759d70a..8c4d631 100644 --- a/murrelet_livecode/Cargo.toml +++ b/murrelet_livecode/Cargo.toml @@ -11,8 +11,8 @@ license = "AGPL-3.0-or-later" schemars = ["dep:schemars"] [dependencies] -murrelet_common = "0.1.2" -murrelet_gui = { version = "0.1.2", features = ["glam"] } +murrelet_common = { workspace = true } +murrelet_gui = { workspace = true, features = ["glam"] } lerpable = "0.0.2" diff --git a/murrelet_livecode_macros/Cargo.toml b/murrelet_livecode_macros/Cargo.toml index 6202438..02fbbaf 100644 --- a/murrelet_livecode_macros/Cargo.toml +++ b/murrelet_livecode_macros/Cargo.toml @@ -13,5 +13,5 @@ schemars = ["murrelet_livecode/schemars", "murrelet_livecode_derive/schemars"] [dependencies] -murrelet_livecode_derive = { version = "0.1.2", default-features = false } -murrelet_livecode = { version = "0.1.2", default-features = false } +murrelet_livecode_derive = { workspace = true, default-features = false } +murrelet_livecode = { workspace = true, default-features = false } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml index d875b46..1f7f7d6 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml +++ b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml @@ -20,16 +20,16 @@ quote = "1.0.18" proc-macro2 = "1.0.37" darling = "0.20.3" serde = { version = "1.0.104", features = ["derive"] } -murrelet_livecode = { version = "0.1.2", default-features = false } +murrelet_livecode = { workspace = true, default-features = false } # just for examples... [dev-dependencies] -murrelet_common = "0.1.2" +murrelet_common = { workspace = true } glam = { version = "0.28.0", features = ["serde"] } palette = "0.7.6" lerpable = { version = "0.0.2", features = ["glam"] } schemars = "0.8.21" -murrelet_gui = { version = "0.1.2", features = ["glam"] } +murrelet_gui = { workspace = true, features = ["glam"] } [[example]] name = "tests" diff --git a/murrelet_perform/Cargo.toml b/murrelet_perform/Cargo.toml index 19d26ea..d034d82 100644 --- a/murrelet_perform/Cargo.toml +++ b/murrelet_perform/Cargo.toml @@ -18,11 +18,11 @@ schemars = [ ] [dependencies] -murrelet_common = "0.1.2" -murrelet_livecode = { version = "0.1.2", default-features = false } -murrelet_draw = { version = "0.1.2", default-features = false } -murrelet_livecode_macros = { version = "0.1.2", default-features = false } -murrelet_livecode_derive = { version = "0.1.2", default-features = false } +murrelet_common = { workspace = true } +murrelet_livecode = { workspace = true, default-features = false } +murrelet_draw = { workspace = true, default-features = false } +murrelet_livecode_macros = { workspace = true, default-features = false } +murrelet_livecode_derive = { workspace = true, default-features = false } lerpable = { version = "0.0.2" } serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" @@ -35,6 +35,6 @@ palette = "0.7.6" anyhow = "1.0.86" schemars = { version = "0.8.21", optional = true } -murrelet_gui = { version = "0.1.2", features = ["glam"] } +murrelet_gui = { workspace = true, features = ["glam"] } clap = { version = "4.5.23", features = ["derive"] } diff --git a/murrelet_schema/Cargo.toml b/murrelet_schema/Cargo.toml index 4a3a7f9..3fba5aa 100644 --- a/murrelet_schema/Cargo.toml +++ b/murrelet_schema/Cargo.toml @@ -8,8 +8,8 @@ default = [] glam = ["dep:glam"] [dependencies] -murrelet_schema_derive = "0.1.2" -murrelet_common = "0.1.2" +murrelet_schema_derive = { workspace = true } +murrelet_common = { workspace = true } anyhow = "1.0.86" thiserror = "2.0.11" diff --git a/murrelet_src_audio/Cargo.toml b/murrelet_src_audio/Cargo.toml index c64c577..7126788 100644 --- a/murrelet_src_audio/Cargo.toml +++ b/murrelet_src_audio/Cargo.toml @@ -8,7 +8,7 @@ description = "audio input functions for murrelet, a livecode framework" license = "AGPL-3.0-or-later" [dependencies] -murrelet_common = "0.1.2" +murrelet_common = { workspace = true } cpal = "0.14" rustfft = "6.1.0" glam = { version = "0.28.0", features = ["serde"] } diff --git a/murrelet_src_midi/Cargo.toml b/murrelet_src_midi/Cargo.toml index 277ef2d..43bd3e2 100644 --- a/murrelet_src_midi/Cargo.toml +++ b/murrelet_src_midi/Cargo.toml @@ -8,7 +8,7 @@ description = "MIDI input functions for murrelet, a livecode framework" license = "AGPL-3.0-or-later" [dependencies] -murrelet_common = "0.1.2" +murrelet_common = { workspace = true } midir = "0.8.0" diff --git a/murrelet_src_osc/Cargo.toml b/murrelet_src_osc/Cargo.toml index 454783a..7e00616 100644 --- a/murrelet_src_osc/Cargo.toml +++ b/murrelet_src_osc/Cargo.toml @@ -9,7 +9,7 @@ license = "AGPL-3.0-or-later" [dependencies] -murrelet_common = "0.1.2" +murrelet_common = { workspace = true } uuid = "1.8.0" rosc = "~0.10" diff --git a/murrelet_svg/Cargo.toml b/murrelet_svg/Cargo.toml index 0cf6bff..61f03dc 100644 --- a/murrelet_svg/Cargo.toml +++ b/murrelet_svg/Cargo.toml @@ -11,9 +11,9 @@ license = "AGPL-3.0-or-later" schemars = ["murrelet_perform/schemars", "murrelet_draw/schemars"] [dependencies] -murrelet_common = "0.1.2" -murrelet_perform = { version = "0.1.2", default-features = false } -murrelet_draw = { version = "0.1.2", default-features = false } +murrelet_common = { workspace = true } +murrelet_perform = { workspace = true, default-features = false } +murrelet_draw = { workspace = true, default-features = false } glam = { version = "0.28.0", features = ["serde"] } itertools = "0.10.5" svg = "0.10.0" From c948d391c03a22771ad4bc3588ff2de984fbacc9 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 6 Feb 2026 14:45:45 -0500 Subject: [PATCH 147/149] bump lerpable --- Cargo.toml | 11 ++++++----- murrelet_common/Cargo.toml | 3 ++- murrelet_draw/Cargo.toml | 5 ++++- murrelet_gen/Cargo.toml | 3 +++ murrelet_gpu/Cargo.toml | 5 ++++- murrelet_livecode/Cargo.toml | 5 ++++- .../murrelet_livecode_derive/Cargo.toml | 2 +- murrelet_perform/Cargo.toml | 5 ++++- 8 files changed, 28 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3885baa..86e740b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,12 +26,12 @@ resolver = "2" [workspace.dependencies] murrelet = { path = "murrelet", version = "0.1.2" } murrelet_common = { path = "murrelet_common", version = "0.1.2" } -murrelet_draw = { path = "murrelet_draw", version = "0.1.2" } +murrelet_draw = { path = "murrelet_draw", version = "0.1.2", default-features = false } murrelet_gpu = { path = "murrelet_gpu", version = "0.1.2" } -murrelet_livecode = { path = "murrelet_livecode", version = "0.1.2" } -murrelet_livecode_macros = { path = "murrelet_livecode_macros", version = "0.1.2" } -murrelet_livecode_derive = { path = "murrelet_livecode_macros/murrelet_livecode_derive", version = "0.1.2" } -murrelet_perform = { path = "murrelet_perform", version = "0.1.2" } +murrelet_livecode = { path = "murrelet_livecode", version = "0.1.2", default-features = false } +murrelet_livecode_macros = { path = "murrelet_livecode_macros", version = "0.1.2", default-features = false } +murrelet_livecode_derive = { path = "murrelet_livecode_macros/murrelet_livecode_derive", version = "0.1.2", default-features = false } +murrelet_perform = { path = "murrelet_perform", version = "0.1.2", default-features = false } murrelet_src_audio = { path = "murrelet_src_audio", version = "0.1.2" } murrelet_src_midi = { path = "murrelet_src_midi", version = "0.1.2" } murrelet_src_osc = { path = "murrelet_src_osc", version = "0.1.2" } @@ -43,3 +43,4 @@ murrelet_gui = { path = "murrelet_gui", version = "0.1.2" } murrelet_gui_derive = { path = "murrelet_gui_derive", version = "0.1.2" } murrelet_gen = { path = "murrelet_gen", version = "0.1.2" } murrelet_gen_derive = { path = "murrelet_gen_derive", version = "0.1.2" } +getrandom = { version = "0.2.17", features = ["js"] } diff --git a/murrelet_common/Cargo.toml b/murrelet_common/Cargo.toml index 7930c07..e7b97bc 100644 --- a/murrelet_common/Cargo.toml +++ b/murrelet_common/Cargo.toml @@ -13,9 +13,10 @@ glam = { version = "0.28.0", features = ["serde"] } palette = "0.7.6" rand = "0.8" num-traits = "0.2.19" -lerpable = "0.0.2" +lerpable = "0.0.3" serde = { version = "1.0.104", features = ["derive"] } bytemuck = { version = "1.15", features = ["derive"] } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = "0.2" +getrandom = { workspace = true } diff --git a/murrelet_draw/Cargo.toml b/murrelet_draw/Cargo.toml index ffba244..2e39a85 100644 --- a/murrelet_draw/Cargo.toml +++ b/murrelet_draw/Cargo.toml @@ -23,7 +23,7 @@ murrelet_livecode = { workspace = true, default-features = false } murrelet_livecode_macros = { workspace = true, default-features = false } murrelet_livecode_derive = { workspace = true, default-features = false } -lerpable = { version = "0.0.2", features = ["glam"] } +lerpable = { version = "0.0.3", features = ["glam"] } serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" @@ -45,3 +45,6 @@ svg = "0.10.0" lyon = "0.17" delaunator = "1.0.2" geo = "0.29.0" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { workspace = true } diff --git a/murrelet_gen/Cargo.toml b/murrelet_gen/Cargo.toml index 77a2eb7..ebcdaeb 100644 --- a/murrelet_gen/Cargo.toml +++ b/murrelet_gen/Cargo.toml @@ -20,3 +20,6 @@ murrelet_common = { workspace = true } [[example]] name = "tests" path = "examples/tests.rs" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { workspace = true } diff --git a/murrelet_gpu/Cargo.toml b/murrelet_gpu/Cargo.toml index 579871e..396b804 100644 --- a/murrelet_gpu/Cargo.toml +++ b/murrelet_gpu/Cargo.toml @@ -30,7 +30,7 @@ murrelet_livecode_macros = { workspace = true } murrelet_livecode_derive = { workspace = true } murrelet_perform = { workspace = true, default-features = false } -lerpable = { version = "0.0.2" } +lerpable = { version = "0.0.3" } glam = { version = "0.28.0", features = ["serde"] } palette = "0.7.6" @@ -50,3 +50,6 @@ bytemuck = { version = "1.16.1", features = ["derive"] } schemars = { version = "0.8.21", optional = true } murrelet_gui = { workspace = true, features = ["glam"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { workspace = true } diff --git a/murrelet_livecode/Cargo.toml b/murrelet_livecode/Cargo.toml index 8c4d631..a9ce45d 100644 --- a/murrelet_livecode/Cargo.toml +++ b/murrelet_livecode/Cargo.toml @@ -14,7 +14,7 @@ schemars = ["dep:schemars"] murrelet_common = { workspace = true } murrelet_gui = { workspace = true, features = ["glam"] } -lerpable = "0.0.2" +lerpable = "0.0.3" itertools = "0.10.5" regex = "1.7.3" @@ -29,3 +29,6 @@ serde_yaml = "0.9.17" thiserror = "2.0.11" schemars = { version = "0.8.21", optional = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { workspace = true } diff --git a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml index 1f7f7d6..d465dce 100644 --- a/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml +++ b/murrelet_livecode_macros/murrelet_livecode_derive/Cargo.toml @@ -27,7 +27,7 @@ murrelet_livecode = { workspace = true, default-features = false } murrelet_common = { workspace = true } glam = { version = "0.28.0", features = ["serde"] } palette = "0.7.6" -lerpable = { version = "0.0.2", features = ["glam"] } +lerpable = { version = "0.0.3", features = ["glam"] } schemars = "0.8.21" murrelet_gui = { workspace = true, features = ["glam"] } diff --git a/murrelet_perform/Cargo.toml b/murrelet_perform/Cargo.toml index d034d82..c814239 100644 --- a/murrelet_perform/Cargo.toml +++ b/murrelet_perform/Cargo.toml @@ -23,7 +23,7 @@ murrelet_livecode = { workspace = true, default-features = false } murrelet_draw = { workspace = true, default-features = false } murrelet_livecode_macros = { workspace = true, default-features = false } murrelet_livecode_derive = { workspace = true, default-features = false } -lerpable = { version = "0.0.2" } +lerpable = { version = "0.0.3" } serde = { version = "1.0.104", features = ["derive"] } serde_yaml = "0.9.17" evalexpr = "11.1.0" @@ -38,3 +38,6 @@ schemars = { version = "0.8.21", optional = true } murrelet_gui = { workspace = true, features = ["glam"] } clap = { version = "4.5.23", features = ["derive"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { workspace = true } From 087e1cfca1f451c39722aebe9ffd2a2c878ec7c7 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 6 Feb 2026 15:12:19 -0500 Subject: [PATCH 148/149] use naga directly --- murrelet_gpu/Cargo.toml | 1 + murrelet_gpu/src/editable_shaders.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/murrelet_gpu/Cargo.toml b/murrelet_gpu/Cargo.toml index 396b804..3ea97c8 100644 --- a/murrelet_gpu/Cargo.toml +++ b/murrelet_gpu/Cargo.toml @@ -22,6 +22,7 @@ schemars = [ [dependencies] wgpu_for_latest = { package = "wgpu", version = "0.20.1", optional = true } wgpu_for_nannou = { package = "wgpu", version = "0.17.1", optional = true } +naga = { version = "0.20.0", features = ["wgsl-in"] } murrelet_common = { workspace = true } murrelet_livecode = { workspace = true, default-features = false } diff --git a/murrelet_gpu/src/editable_shaders.rs b/murrelet_gpu/src/editable_shaders.rs index d7e5028..8893b26 100644 --- a/murrelet_gpu/src/editable_shaders.rs +++ b/murrelet_gpu/src/editable_shaders.rs @@ -7,7 +7,7 @@ use crate::{ use lerpable::Lerpable; use murrelet_common::triangulate::DefaultVertex; use murrelet_livecode_derive::Livecode; -use wgpu_for_latest::naga; +use naga; #[cfg(feature = "nannou")] use wgpu_for_nannou as wgpu; From 5d83ef287fdf67683d6f5adcb46dbf6a5e2b0a52 Mon Sep 17 00:00:00 2001 From: Jessica Stringham Date: Fri, 6 Feb 2026 15:18:04 -0500 Subject: [PATCH 149/149] one extra thing --- murrelet_livecode/src/expr.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/murrelet_livecode/src/expr.rs b/murrelet_livecode/src/expr.rs index 75b1d1f..323e877 100644 --- a/murrelet_livecode/src/expr.rs +++ b/murrelet_livecode/src/expr.rs @@ -514,6 +514,10 @@ impl MixedEvalDefsRef { pub(crate) fn expr_vals(&self) -> &ExprWorldContextValues { self.0.expr_vals() } + + pub fn as_defs(&self) -> &MixedEvalDefs { + self.0.as_ref() + } } #[derive(Debug, Clone)]