Skip to content

Commit 3cedd37

Browse files
committed
Make boolean ops support the Text type
1 parent 05b64a7 commit 3cedd37

5 files changed

Lines changed: 79 additions & 57 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node-graph/nodes/gstd/src/text.rs

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
use core_types::blending::BlendMode;
21
use core_types::list::List;
3-
use core_types::{
4-
ATTR_BLEND_MODE, ATTR_EDITOR_LAYER_PATH, ATTR_FONT_SIZE, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TEXT_ALIGN, ATTR_TEXT_CHARACTER_SPACING, ATTR_TEXT_FONT, ATTR_TEXT_LINE_HEIGHT,
5-
ATTR_TEXT_MAX_HEIGHT, ATTR_TEXT_MAX_WIDTH, ATTR_TEXT_TILT, ATTR_TRANSFORM, Ctx,
6-
};
2+
use core_types::{ATTR_FONT_SIZE, ATTR_TEXT_ALIGN, ATTR_TEXT_CHARACTER_SPACING, ATTR_TEXT_FONT, ATTR_TEXT_LINE_HEIGHT, ATTR_TEXT_MAX_HEIGHT, ATTR_TEXT_MAX_WIDTH, ATTR_TEXT_TILT, Ctx};
73
use graph_craft::application_io::resource::Resource;
84
use graphic_types::Vector;
95
pub use text_nodes::*;
@@ -176,53 +172,5 @@ fn text_to_vector(
176172
/// When enabled, each glyph is emitted as its own vector item instead of a single compound path per string.
177173
separate_glyphs: bool,
178174
) -> List<Vector> {
179-
let mut result = List::new();
180-
181-
for index in 0..strings.len() {
182-
let Some(text) = strings.element(index) else { continue };
183-
if text.is_empty() {
184-
continue;
185-
}
186-
187-
let font: Resource = strings.attribute_cloned_or_default(ATTR_TEXT_FONT, index);
188-
189-
let typesetting = TypesettingConfig {
190-
font_size: strings.attribute_cloned_or(ATTR_FONT_SIZE, index, DEFAULT_FONT_SIZE),
191-
line_height_ratio: strings.attribute_cloned_or(ATTR_TEXT_LINE_HEIGHT, index, DEFAULT_LINE_HEIGHT),
192-
character_spacing: strings.attribute_cloned_or(ATTR_TEXT_CHARACTER_SPACING, index, 0.),
193-
max_width: strings.attribute_cloned_or::<Option<f64>>(ATTR_TEXT_MAX_WIDTH, index, None),
194-
max_height: strings.attribute_cloned_or::<Option<f64>>(ATTR_TEXT_MAX_HEIGHT, index, None),
195-
tilt: strings.attribute_cloned_or(ATTR_TEXT_TILT, index, 0.),
196-
align: strings.attribute_cloned_or(ATTR_TEXT_ALIGN, index, TextAlign::default()),
197-
};
198-
199-
let vectors = to_path(text, &font, typesetting, separate_glyphs);
200-
let transform = strings.attribute_cloned_or_default::<glam::DAffine2>(ATTR_TRANSFORM, index);
201-
let layer_path = strings.attribute_cloned_or_default::<List<graph_craft::document::NodeId>>(ATTR_EDITOR_LAYER_PATH, index);
202-
let blend_mode = strings.attribute::<BlendMode>(ATTR_BLEND_MODE, index).copied();
203-
let opacity = strings.attribute::<f64>(ATTR_OPACITY, index).copied();
204-
let opacity_fill = strings.attribute::<f64>(ATTR_OPACITY_FILL, index).copied();
205-
206-
for mut item in vectors.into_iter() {
207-
if transform != glam::DAffine2::IDENTITY {
208-
let local = item.attribute_cloned_or_default::<glam::DAffine2>(ATTR_TRANSFORM);
209-
item.set_attribute(ATTR_TRANSFORM, transform * local);
210-
}
211-
if !layer_path.is_empty() {
212-
item.set_attribute(ATTR_EDITOR_LAYER_PATH, layer_path.clone());
213-
}
214-
if let Some(blend_mode) = blend_mode {
215-
item.set_attribute(ATTR_BLEND_MODE, blend_mode);
216-
}
217-
if let Some(opacity) = opacity {
218-
item.set_attribute(ATTR_OPACITY, opacity);
219-
}
220-
if let Some(opacity_fill) = opacity_fill {
221-
item.set_attribute(ATTR_OPACITY_FILL, opacity_fill);
222-
}
223-
result.push(item);
224-
}
225-
}
226-
227-
result
175+
shape_text_list(&strings, separate_glyphs)
228176
}

node-graph/nodes/path-bool/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ license = "MIT OR Apache-2.0"
1010
# Local dependencies
1111
core-types = { workspace = true }
1212
graphic-types = { workspace = true }
13+
text-nodes = { workspace = true }
1314
node-macro = { workspace = true }
1415
glam = { workspace = true }
1516
linesweeper = { workspace = true }

node-graph/nodes/path-bool/src/lib.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,18 @@ fn flatten_vector(graphic_list: &List<Graphic>) -> List<Vector> {
278278
Item::from_parts(element, attributes)
279279
})
280280
.collect::<Vec<_>>(),
281-
// Text carries no vector geometry until shaped by the 'Text to Vector' node, whose font shaping this crate intentionally doesn't depend on
282-
Graphic::Text(_) => Vec::new(),
281+
Graphic::Text(text) => {
282+
// Shape the glyphs into vectors (each item's own transform is applied), then compose the parent's transform like the other arms
283+
let parent_transform: DAffine2 = graphic_list.attribute_cloned_or_default(ATTR_TRANSFORM, index);
284+
text_nodes::shape_text_list(&text, false)
285+
.into_iter()
286+
.map(|mut sub_vector| {
287+
let current_transform: DAffine2 = sub_vector.attribute_cloned_or_default(ATTR_TRANSFORM);
288+
*sub_vector.attribute_mut_or_insert_default(ATTR_TRANSFORM) = parent_transform * current_transform;
289+
sub_vector
290+
})
291+
.collect::<Vec<_>>()
292+
}
283293
}
284294
})
285295
.collect()

node-graph/nodes/text/src/to_path.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
use super::TypesettingConfig;
22
use super::text_context::TextContext;
3+
use core_types::blending::BlendMode;
34
use core_types::list::List;
4-
use glam::DVec2;
5+
use core_types::uuid::NodeId;
6+
use core_types::{
7+
ATTR_BLEND_MODE, ATTR_EDITOR_LAYER_PATH, ATTR_FONT_SIZE, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TEXT_ALIGN, ATTR_TEXT_CHARACTER_SPACING, ATTR_TEXT_FONT, ATTR_TEXT_LINE_HEIGHT,
8+
ATTR_TEXT_MAX_HEIGHT, ATTR_TEXT_MAX_WIDTH, ATTR_TEXT_TILT, ATTR_TRANSFORM,
9+
};
10+
use glam::{DAffine2, DVec2};
511
use graphene_resource::Resource;
612
use vector_types::Vector;
713

@@ -16,3 +22,59 @@ pub fn bounding_box(text: &str, font: &Resource, typesetting: TypesettingConfig,
1622
pub fn lines_clipping(text: &str, font: &Resource, typesetting: TypesettingConfig) -> bool {
1723
TextContext::with_thread_local(|ctx| ctx.lines_clipping(text, font, typesetting))
1824
}
25+
26+
/// Shapes each string item of a styled `List<String>` into vector geometry, reading its font and typesetting
27+
/// from the item's attributes (as set by the 'Text Layer' node) and re-applying its transform and blending
28+
/// attributes onto the produced paths. With `separate_glyphs`, each glyph becomes its own item.
29+
pub fn shape_text_list(strings: &List<String>, separate_glyphs: bool) -> List<Vector> {
30+
let mut result = List::new();
31+
32+
for index in 0..strings.len() {
33+
let Some(text) = strings.element(index) else { continue };
34+
if text.is_empty() {
35+
continue;
36+
}
37+
38+
let font: Resource = strings.attribute_cloned_or_default(ATTR_TEXT_FONT, index);
39+
40+
let defaults = TypesettingConfig::default();
41+
let typesetting = TypesettingConfig {
42+
font_size: strings.attribute_cloned_or(ATTR_FONT_SIZE, index, defaults.font_size),
43+
line_height_ratio: strings.attribute_cloned_or(ATTR_TEXT_LINE_HEIGHT, index, defaults.line_height_ratio),
44+
character_spacing: strings.attribute_cloned_or(ATTR_TEXT_CHARACTER_SPACING, index, defaults.character_spacing),
45+
max_width: strings.attribute_cloned_or::<Option<f64>>(ATTR_TEXT_MAX_WIDTH, index, defaults.max_width),
46+
max_height: strings.attribute_cloned_or::<Option<f64>>(ATTR_TEXT_MAX_HEIGHT, index, defaults.max_height),
47+
tilt: strings.attribute_cloned_or(ATTR_TEXT_TILT, index, defaults.tilt),
48+
align: strings.attribute_cloned_or(ATTR_TEXT_ALIGN, index, defaults.align),
49+
};
50+
51+
let vectors = to_path(text, &font, typesetting, separate_glyphs);
52+
let transform = strings.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, index);
53+
let layer_path = strings.attribute_cloned_or_default::<List<NodeId>>(ATTR_EDITOR_LAYER_PATH, index);
54+
let blend_mode = strings.attribute::<BlendMode>(ATTR_BLEND_MODE, index).copied();
55+
let opacity = strings.attribute::<f64>(ATTR_OPACITY, index).copied();
56+
let opacity_fill = strings.attribute::<f64>(ATTR_OPACITY_FILL, index).copied();
57+
58+
for mut item in vectors.into_iter() {
59+
if transform != DAffine2::IDENTITY {
60+
let local = item.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM);
61+
item.set_attribute(ATTR_TRANSFORM, transform * local);
62+
}
63+
if !layer_path.is_empty() {
64+
item.set_attribute(ATTR_EDITOR_LAYER_PATH, layer_path.clone());
65+
}
66+
if let Some(blend_mode) = blend_mode {
67+
item.set_attribute(ATTR_BLEND_MODE, blend_mode);
68+
}
69+
if let Some(opacity) = opacity {
70+
item.set_attribute(ATTR_OPACITY, opacity);
71+
}
72+
if let Some(opacity_fill) = opacity_fill {
73+
item.set_attribute(ATTR_OPACITY_FILL, opacity_fill);
74+
}
75+
result.push(item);
76+
}
77+
}
78+
79+
result
80+
}

0 commit comments

Comments
 (0)