rive_rs/shapes/paint/
trim_path.rs
1use std::mem;
6use std::rc::Rc;
7
8use crate::component::Component;
9use crate::component_dirt::ComponentDirt;
10use crate::core::{Core, CoreContext, ObjectRef, OnAdded, Property};
11use crate::option_cell::OptionCell;
12use crate::shapes::paint::stroke_effect::StrokeEffect;
13use crate::shapes::paint::Stroke;
14use crate::shapes::{CommandPath, CommandPathBuilder, MetricsPath};
15use crate::status_code::StatusCode;
16
17#[derive(Debug, Default)]
18pub struct TrimPath {
19 component: Component,
20 start: Property<f32>,
21 end: Property<f32>,
22 offset: Property<f32>,
23 mode_value: Property<u64>,
24 command_path: OptionCell<Rc<CommandPath>>,
25}
26
27impl ObjectRef<'_, TrimPath> {
28 pub fn start(&self) -> f32 {
29 self.start.get()
30 }
31
32 pub fn set_start(&self, start: f32) {
33 self.start.set(start);
34 self.invalidate_effect();
35 }
36
37 pub fn end(&self) -> f32 {
38 self.end.get()
39 }
40
41 pub fn set_end(&self, end: f32) {
42 self.end.set(end);
43 self.invalidate_effect();
44 }
45
46 pub fn offset(&self) -> f32 {
47 self.offset.get()
48 }
49
50 pub fn set_offset(&self, offset: f32) {
51 self.offset.set(offset);
52 self.invalidate_effect();
53 }
54
55 pub fn mode_value(&self) -> u64 {
56 self.mode_value.get()
57 }
58
59 pub fn set_mode_value(&self, mode_value: u64) {
60 self.mode_value.set(mode_value);
61 self.invalidate_effect();
62 }
63}
64
65impl StrokeEffect for ObjectRef<'_, TrimPath> {
66 fn effect_path(&self, source: &mut MetricsPath) -> Rc<CommandPath> {
67 if let Some(command_path) = self.command_path.get() {
68 return command_path;
69 }
70
71 let mut render_offset = self.offset().fract();
72 if render_offset.is_sign_negative() {
73 render_offset += 1.0;
74 }
75
76 let total_len = source.compute_length();
79 let mut start_len = total_len * (self.start() + render_offset);
80 let mut end_len = total_len * (self.end() + render_offset);
81
82 if end_len < start_len {
83 mem::swap(&mut start_len, &mut end_len);
84 }
85
86 if start_len > total_len {
87 start_len -= total_len;
88 end_len -= total_len;
89 }
90
91 let mut builder = CommandPathBuilder::new();
92
93 while end_len > 0.0 {
94 if start_len < total_len {
95 source.trimmed(&mut builder, start_len, end_len, true);
96 end_len -= total_len;
97 start_len = 0.0;
98 } else {
99 start_len -= total_len;
100 end_len -= total_len;
101 }
102 }
103
104 let command_path = Rc::new(builder.build());
105 self.command_path.set(Some(command_path.clone()));
106
107 command_path
108 }
109
110 fn invalidate_effect(&self) {
111 self.command_path.set(None);
112 let stroke = self.cast::<Component>().parent().unwrap().cast::<Stroke>();
113
114 stroke.as_ref().outlined_stroke.set(None);
115 stroke
116 .cast::<Component>()
117 .as_ref()
118 .parent()
119 .unwrap()
120 .cast::<Component>()
121 .as_ref()
122 .add_dirt(ComponentDirt::PAINT, false);
123 }
124}
125
126impl Core for TrimPath {
127 parent_types![(component, Component)];
128
129 properties![
130 (114, start, set_start),
131 (115, end, set_end),
132 (116, offset, set_offset),
133 (117, mode_value, set_mode_value),
134 component,
135 ];
136}
137
138impl OnAdded for ObjectRef<'_, TrimPath> {
139 on_added!([on_added_dirty, import], Component);
140
141 fn on_added_clean(&self, _context: &dyn CoreContext) -> StatusCode {
142 if let Some(stroke) =
143 self.cast::<Component>().parent().and_then(|parent| parent.try_cast::<Stroke>())
144 {
145 stroke.as_ref().set_stroke_effect(Some(self.as_object().into()));
146 StatusCode::Ok
147 } else {
148 StatusCode::InvalidObject
149 }
150 }
151}