Skip to content

Commit 1a49f60

Browse files
committed
WIP: add shear to text node
1 parent 8fc2dc2 commit 1a49f60

File tree

5 files changed

+51
-9
lines changed

5 files changed

+51
-9
lines changed

editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,6 +1555,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
15551555
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().character_spacing), false),
15561556
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_width), false),
15571557
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_height), false),
1558+
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().shear), false),
15581559
],
15591560
..Default::default()
15601561
},
@@ -1608,6 +1609,17 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
16081609
..Default::default()
16091610
}),
16101611
),
1612+
PropertiesRow::with_override(
1613+
"Shear",
1614+
"TODO",
1615+
WidgetOverride::Number(NumberInputSettings {
1616+
min: Some(-85.),
1617+
1618+
max: Some(85.),
1619+
unit: Some("°".to_string()),
1620+
..Default::default()
1621+
}),
1622+
),
16111623
],
16121624
output_names: vec!["Vector".to_string()],
16131625
..Default::default()

editor/src/messages/tool/common_functionality/graph_modification_utils.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,13 +361,15 @@ pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInter
361361
let Some(&TaggedValue::F64(character_spacing)) = inputs[5].as_value() else { return None };
362362
let Some(&TaggedValue::OptionalF64(max_width)) = inputs[6].as_value() else { return None };
363363
let Some(&TaggedValue::OptionalF64(max_height)) = inputs[7].as_value() else { return None };
364+
let Some(&TaggedValue::F64(shear)) = inputs[8].as_value() else { return None };
364365

365366
let typesetting = TypesettingConfig {
366367
font_size,
367368
line_height_ratio,
368369
max_width,
369370
character_spacing,
370371
max_height,
372+
shear,
371373
};
372374
Some((text, font, typesetting))
373375
}

editor/src/messages/tool/tool_messages/text_tool.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub struct TextOptions {
3535
font_name: String,
3636
font_style: String,
3737
fill: ToolColorOptions,
38+
shear: f64,
3839
}
3940

4041
impl Default for TextOptions {
@@ -46,6 +47,7 @@ impl Default for TextOptions {
4647
font_name: graphene_std::consts::DEFAULT_FONT_FAMILY.into(),
4748
font_style: graphene_std::consts::DEFAULT_FONT_STYLE.into(),
4849
fill: ToolColorOptions::new_primary(),
50+
shear: 0.,
4951
}
5052
}
5153
}
@@ -784,6 +786,7 @@ impl Fsm for TextToolFsmState {
784786
max_width: constraint_size.map(|size| size.x),
785787
character_spacing: tool_options.character_spacing,
786788
max_height: constraint_size.map(|size| size.y),
789+
shear: tool_options.shear,
787790
},
788791
font: Font::new(tool_options.font_name.clone(), tool_options.font_style.clone()),
789792
color: tool_options.fill.active_color(),

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

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::vector::PointId;
22
use bezier_rs::{ManipulatorGroup, Subpath};
33
use core::cell::RefCell;
4-
use glam::DVec2;
4+
use glam::{DAffine2, DVec2};
55
use parley::fontique::Blob;
66
use parley::{Alignment, AlignmentOptions, FontContext, GlyphRun, Layout, LayoutContext, LineHeight, PositionedLayoutItem, StyleProperty};
77
use skrifa::GlyphId;
@@ -18,6 +18,7 @@ thread_local! {
1818

1919
struct PathBuilder {
2020
current_subpath: Subpath<PointId>,
21+
glyph_subpaths: Vec<Subpath<PointId>>,
2122
other_subpaths: Vec<Subpath<PointId>>,
2223
origin: DVec2,
2324
scale: f64,
@@ -33,21 +34,29 @@ impl PathBuilder {
3334
self.origin = DVec2::new(x, y);
3435
}
3536

36-
fn draw_glyph(&mut self, glyph: &OutlineGlyph<'_>, size: f32, normalized_coords: &[NormalizedCoord]) {
37+
fn draw_glyph(&mut self, glyph: &OutlineGlyph<'_>, size: f32, normalized_coords: &[NormalizedCoord], style_skew: Option<DAffine2>, skew: DAffine2) {
3738
let location_ref = LocationRef::new(normalized_coords);
3839
let settings = DrawSettings::unhinted(Size::new(size), location_ref);
3940
glyph.draw(settings, self).unwrap();
4041

41-
if !self.current_subpath.is_empty() {
42-
self.other_subpaths.push(core::mem::replace(&mut self.current_subpath, Subpath::new(Vec::new(), false)));
42+
for glyph_subpath in &mut self.glyph_subpaths {
43+
if let Some(style_skew) = style_skew {
44+
glyph_subpath.apply_transform(style_skew);
45+
}
46+
47+
glyph_subpath.apply_transform(skew);
48+
}
49+
50+
if !self.glyph_subpaths.is_empty() {
51+
self.other_subpaths.extend(core::mem::take(&mut self.glyph_subpaths));
4352
}
4453
}
4554
}
4655

4756
impl OutlinePen for PathBuilder {
4857
fn move_to(&mut self, x: f32, y: f32) {
4958
if !self.current_subpath.is_empty() {
50-
self.other_subpaths.push(std::mem::replace(&mut self.current_subpath, Subpath::new(Vec::new(), false)));
59+
self.glyph_subpaths.push(std::mem::replace(&mut self.current_subpath, Subpath::new(Vec::new(), false)));
5160
}
5261
self.current_subpath.push_manipulator_group(ManipulatorGroup::new_anchor_with_id(self.point(x, y), self.id.next_id()));
5362
}
@@ -71,7 +80,7 @@ impl OutlinePen for PathBuilder {
7180

7281
fn close(&mut self) {
7382
self.current_subpath.set_closed(true);
74-
self.other_subpaths.push(std::mem::replace(&mut self.current_subpath, Subpath::new(Vec::new(), false)));
83+
self.glyph_subpaths.push(std::mem::replace(&mut self.current_subpath, Subpath::new(Vec::new(), false)));
7584
}
7685
}
7786

@@ -82,6 +91,7 @@ pub struct TypesettingConfig {
8291
pub character_spacing: f64,
8392
pub max_width: Option<f64>,
8493
pub max_height: Option<f64>,
94+
pub shear: f64,
8595
}
8696

8797
impl Default for TypesettingConfig {
@@ -92,16 +102,28 @@ impl Default for TypesettingConfig {
92102
character_spacing: 0.,
93103
max_width: None,
94104
max_height: None,
105+
shear: 0.,
95106
}
96107
}
97108
}
98109

99-
fn render_glyph_run(glyph_run: &GlyphRun<'_, ()>, path_builder: &mut PathBuilder) {
110+
fn render_glyph_run(glyph_run: &GlyphRun<'_, ()>, path_builder: &mut PathBuilder, shear: f64) {
100111
let mut run_x = glyph_run.offset();
101112
let run_y = glyph_run.baseline();
102113

103114
let run = glyph_run.run();
104115

116+
let skew = DAffine2::from_translation(DVec2::new(0.0, run_y as f64))
117+
* DAffine2::from_cols_array(&[1.0, 0.0, -shear.to_radians().tan() as f64, 1.0, 0.0, 0.0])
118+
* DAffine2::from_translation(DVec2::new(0.0, -run_y as f64));
119+
120+
let synthesis = run.synthesis();
121+
let style_skew = synthesis.skew().map(|angle| {
122+
DAffine2::from_translation(DVec2::new(0.0, run_y as f64))
123+
* DAffine2::from_cols_array(&[1.0, 0.0, -angle.to_radians().tan() as f64, 1.0, 0.0, 0.0])
124+
* DAffine2::from_translation(DVec2::new(0.0, -run_y as f64))
125+
});
126+
105127
let font = run.font();
106128
let font_size = run.font_size();
107129

@@ -120,7 +142,7 @@ fn render_glyph_run(glyph_run: &GlyphRun<'_, ()>, path_builder: &mut PathBuilder
120142
let glyph_id = GlyphId::from(glyph.id);
121143
if let Some(glyph_outline) = outlines.get(glyph_id) {
122144
path_builder.set_origin(glyph_x as f64, glyph_y as f64);
123-
path_builder.draw_glyph(&glyph_outline, font_size, &normalized_coords);
145+
path_builder.draw_glyph(&glyph_outline, font_size, &normalized_coords, style_skew, skew);
124146
}
125147
}
126148
}
@@ -160,6 +182,7 @@ pub fn to_path(str: &str, font_data: Option<Blob<u8>>, typesetting: TypesettingC
160182

161183
let mut path_builder = PathBuilder {
162184
current_subpath: Subpath::new(Vec::new(), false),
185+
glyph_subpaths: Vec::new(),
163186
other_subpaths: Vec::new(),
164187
origin: DVec2::ZERO,
165188
scale: layout.scale() as f64,
@@ -170,7 +193,7 @@ pub fn to_path(str: &str, font_data: Option<Blob<u8>>, typesetting: TypesettingC
170193
for item in line.items() {
171194
match item {
172195
PositionedLayoutItem::GlyphRun(glyph_run) => {
173-
render_glyph_run(&glyph_run, &mut path_builder);
196+
render_glyph_run(&glyph_run, &mut path_builder, typesetting.shear);
174197
}
175198
PositionedLayoutItem::InlineBox(_inline_box) => {
176199
// Render the inline box

node-graph/gstd/src/text.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ fn text<'i: 'n>(
1414
#[default(0.)] character_spacing: f64,
1515
#[default(None)] max_width: Option<f64>,
1616
#[default(None)] max_height: Option<f64>,
17+
#[default(0.)] shear: f64,
1718
) -> VectorDataTable {
1819
let typesetting = TypesettingConfig {
1920
font_size,
2021
line_height_ratio,
2122
character_spacing,
2223
max_width,
2324
max_height,
25+
shear,
2426
};
2527

2628
let font_data = editor.font_cache.get(&font_name).map(|f| load_font(f));

0 commit comments

Comments
 (0)