diff --git a/NEWS.md b/NEWS.md
index c05b935..8271177 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -14,7 +14,7 @@
* `string2path()` now generates the same outline as `string2fill()` and
`string2stroke()` (#69).
-* `path_id` is now 1-origin.
+* `path_id` and `glyph_id` are now 1-origin.
# string2path 0.1.8
diff --git a/R/main.R b/R/main.R
index 24ef3c7..252ba8b 100644
--- a/R/main.R
+++ b/R/main.R
@@ -14,7 +14,7 @@
#' @param tolerance Maximum distance allowed between the curve and its
#' approximation. For more details, please refer to [the documentation of the
#' underlying Rust
-#' library](https://docs.rs/lyon/0.17.5/lyon/#what-is-the-tolerance-variable-in-these-examples).
+#' library](https://docs.rs/lyon_geom/latest/lyon_geom/#flattening).
#'
#' @param line_width Line width of strokes.
#'
diff --git a/README.md b/README.md
index 2470723..53e3890 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
+# string2path
-
# string2path
@@ -9,16 +9,17 @@
[![Lifecycle:
experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental)
[![CRAN
-status](https://www.r-pkg.org/badges/version/string2path)](https://CRAN.R-project.org/package=string2path)
+status](https://www.r-pkg.org/badges/version/string2path.png)](https://CRAN.R-project.org/package=string2path)
[![string2path status
-badge](https://yutannihilation.r-universe.dev/badges/string2path)](https://yutannihilation.r-universe.dev)
+badge](https://yutannihilation.r-universe.dev/badges/string2path.png)](https://yutannihilation.r-universe.dev)
+
The string2path R package converts a text to paths of the outlines of
each glyph, based on a font data. Under the hood, this package is
powered by [the savvy
-framework](https://yutannihilation.github.io/savvy/guide/) to use
-these two Rust crates:
+framework](https://yutannihilation.github.io/savvy/guide/) to use these
+two Rust crates:
- [ttf-parser](https://github.com/RazrFalcon/ttf-parser) for parsing
font data. TrueType font (`.ttf`) and OpenType font (`.otf`) are
@@ -72,7 +73,7 @@ ggplot(d) +
scale_colour_viridis_d(option = "H")
```
-
+
``` r
@@ -88,7 +89,7 @@ ggplot(d) +
transition_reveal(rowid)
```
-
+
#### `dump_fontdb()`
@@ -101,7 +102,7 @@ style (e.g. `"italic"`).
``` r
dump_fontdb()
#> # A tibble: 448 × 5
-#> x y family weight style
+#> source index family weight style
#>
#> 1 "C:\\WINDOWS\\Fonts\\arial.ttf" 0 Arial normal normal
#> 2 "C:\\WINDOWS\\Fonts\\arialbd.ttf" 0 Arial bold normal
@@ -139,7 +140,7 @@ ggplot(d_tmp) +
coord_equal()
```
-
+
### `string2fill()`
@@ -155,7 +156,7 @@ ggplot(d) +
scale_fill_viridis_d(option = "H")
```
-
+
### `string2stroke()`
@@ -174,12 +175,15 @@ for (w in 1:9 * 0.01) {
}
```
-
+
## `tolerance`
`tolerance` controls resolution of the tessellation. You can reduce
-tolerance to get higher resolutions.
+tolerance to get higher resolutions. In most of the cases, `1e-5` ~
+`1e-6` should be enough. For more details, please refer to [lyon’s
+official
+document](https://docs.rs/lyon_geom/latest/lyon_geom/#flattening).
``` r
for (tolerance in c(1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7)) {
@@ -195,24 +199,4 @@ for (tolerance in c(1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7)) {
}
```
-
-
-Note that `tolerance` parameter behaves a bit differently on
-`string2fill()` and `string2stroke()`. But, in either case, 1e-5 ~ 1e-6
-should be enough.
-
-``` r
-for (tolerance in c(1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7)) {
- d <- string2path("abc", "Iosevka SS08", font_weight = "bold", font_style = "italic", tolerance = tolerance)
-
- p <- ggplot(d) +
- geom_path(aes(x, y, group = path_id), colour = "black", linewidth = 0.5) +
- geom_point(aes(x, y, group = path_id), colour = "black", size = 1.5) +
- theme_minimal() +
- coord_equal() +
- ggtitle(paste0("tolerance: ", tolerance))
- plot(p)
-}
-```
-
-
+
diff --git a/README.Rmd b/README.qmd
similarity index 63%
rename from README.Rmd
rename to README.qmd
index 939f920..432147d 100644
--- a/README.Rmd
+++ b/README.qmd
@@ -1,10 +1,12 @@
---
-output: github_document
+title: "string2path"
+format: gfm
+editor: visual
---
-
-
-```{r, include = FALSE}
+```{r}
+#| label: "setup"
+#| include: false
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
@@ -18,19 +20,15 @@ knitr::opts_chunk$set(
# string2path
-[![R-CMD-check](https://github.com/yutannihilation/string2path/workflows/R-CMD-check/badge.svg)](https://github.com/yutannihilation/string2path/actions)
-[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental)
-[![CRAN status](https://www.r-pkg.org/badges/version/string2path)](https://CRAN.R-project.org/package=string2path)
-[![string2path status badge](https://yutannihilation.r-universe.dev/badges/string2path)](https://yutannihilation.r-universe.dev)
-
-The string2path R package converts a text to paths of the outlines of each glyph, based on a font data.
-Under the hood, this package is powered by [the savvy framework][savvy] to use these two Rust crates:
+[![R-CMD-check](https://github.com/yutannihilation/string2path/workflows/R-CMD-check/badge.svg)](https://github.com/yutannihilation/string2path/actions) [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) [![CRAN status](https://www.r-pkg.org/badges/version/string2path)](https://CRAN.R-project.org/package=string2path) [![string2path status badge](https://yutannihilation.r-universe.dev/badges/string2path)](https://yutannihilation.r-universe.dev)
-* [ttf-parser](https://github.com/RazrFalcon/ttf-parser) for parsing font data. TrueType font (`.ttf`) and OpenType font (`.otf`) are supported.
-* [lyon](https://github.com/nical/lyon/) for tessellation of polygons and flattening the curves.
+
+
+The string2path R package converts a text to paths of the outlines of each glyph, based on a font data. Under the hood, this package is powered by [the savvy framework](https://yutannihilation.github.io/savvy/guide/) to use these two Rust crates:
-[savvy]: https://yutannihilation.github.io/savvy/guide/
+- [ttf-parser](https://github.com/RazrFalcon/ttf-parser) for parsing font data. TrueType font (`.ttf`) and OpenType font (`.otf`) are supported.
+- [lyon](https://github.com/nical/lyon/) for tessellation of polygons and flattening the curves.
## Installation
@@ -51,9 +49,7 @@ install.packages("string2path",
)
```
-If you want to install from source, you need to have Rust toolchain installed
-before trying to install this package. See
-for the installation instructions.
+If you want to install from source, you need to have Rust toolchain installed before trying to install this package. See for the installation instructions.
## Example
@@ -91,19 +87,14 @@ ggplot(d) +
#### `dump_fontdb()`
-Note that `"Noto Sans JP"` above (and `"Iosevka SS08"` below) is the font installed
-on my local machine, so the same code might not run on your environment. You can
-use `dump_fontdb()` to see the available combination of font family (e.g. `"Arial"`),
-weight (e.g. `"bold"`), and style (e.g. `"italic"`).
+Note that `"Noto Sans JP"` above (and `"Iosevka SS08"` below) is the font installed on my local machine, so the same code might not run on your environment. You can use `dump_fontdb()` to see the available combination of font family (e.g. `"Arial"`), weight (e.g. `"bold"`), and style (e.g. `"italic"`).
```{r}
#| label: dump
dump_fontdb()
```
-You can also specify the font file directly. Pomicons is a font available on
-[gabrielelana/pomicons](https://github.com/gabrielelana/pomicons), licensed under
-SIL OFL 1.1.
+You can also specify the font file directly. Pomicons is a font available on [gabrielelana/pomicons](https://github.com/gabrielelana/pomicons), licensed under SIL OFL 1.1.
```{r}
#| label: icon_font
@@ -124,7 +115,6 @@ ggplot(d_tmp) +
coord_equal()
```
-
### `string2fill()`
```{r}
@@ -159,11 +149,9 @@ for (w in 1:9 * 0.01) {
}
```
-
-
## `tolerance`
-`tolerance` controls resolution of the tessellation. You can reduce tolerance to get higher resolutions.
+`tolerance` controls resolution of the tessellation. You can reduce tolerance to get higher resolutions. In most of the cases, `1e-5` \~ `1e-6` should be enough. For more details, please refer to [lyon's official document](https://docs.rs/lyon_geom/latest/lyon_geom/#flattening).
```{r}
#| label: example3
@@ -180,23 +168,3 @@ for (tolerance in c(1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7)) {
plot(p)
}
```
-
-Note that `tolerance` parameter behaves a bit differently on `string2fill()` and `string2stroke()`.
-But, in either case, 1e-5 ~ 1e-6 should be enough.
-
-```{r}
-#| label: example4
-#| animation.hook: gifski
-for (tolerance in c(1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7)) {
- d <- string2path("abc", "Iosevka SS08", font_weight = "bold", font_style = "italic", tolerance = tolerance)
-
- p <- ggplot(d) +
- geom_path(aes(x, y, group = path_id), colour = "black", linewidth = 0.5) +
- geom_point(aes(x, y, group = path_id), colour = "black", size = 1.5) +
- theme_minimal() +
- coord_equal() +
- ggtitle(paste0("tolerance: ", tolerance))
- plot(p)
-}
-```
-
diff --git a/man/figures/README-example-1.gif b/man/figures/README-example-1.gif
index 36898ca..63f29c5 100644
Binary files a/man/figures/README-example-1.gif and b/man/figures/README-example-1.gif differ
diff --git a/man/figures/README-example-1.png b/man/figures/README-example-1.png
index 021d667..a44bea4 100644
Binary files a/man/figures/README-example-1.png and b/man/figures/README-example-1.png differ
diff --git a/man/figures/README-example2-1.png b/man/figures/README-example2-1.png
index 9c6b329..ccffdf8 100644
Binary files a/man/figures/README-example2-1.png and b/man/figures/README-example2-1.png differ
diff --git a/man/figures/README-example3-.gif b/man/figures/README-example3-.gif
index 5951f53..961aaf2 100644
Binary files a/man/figures/README-example3-.gif and b/man/figures/README-example3-.gif differ
diff --git a/man/figures/README-icon_font-1.png b/man/figures/README-icon_font-1.png
index 875ee87..8269e72 100644
Binary files a/man/figures/README-icon_font-1.png and b/man/figures/README-icon_font-1.png differ
diff --git a/man/figures/README-string2stroke-.gif b/man/figures/README-string2stroke-.gif
index f51d19f..1e17aef 100644
Binary files a/man/figures/README-string2stroke-.gif and b/man/figures/README-string2stroke-.gif differ
diff --git a/man/string2path.Rd b/man/string2path.Rd
index 40ec842..b2e4b40 100644
--- a/man/string2path.Rd
+++ b/man/string2path.Rd
@@ -45,7 +45,7 @@ string2fill(
\item{font_style}{A font style.}
\item{tolerance}{Maximum distance allowed between the curve and its
-approximation. For more details, please refer to \href{https://docs.rs/lyon/0.17.5/lyon/#what-is-the-tolerance-variable-in-these-examples}{the documentation of the underlying Rust library}.}
+approximation. For more details, please refer to \href{https://docs.rs/lyon_geom/latest/lyon_geom/#flattening}{the documentation of the underlying Rust library}.}
\item{line_width}{Line width of strokes.}
}
diff --git a/src/rust/src/builder.rs b/src/rust/src/builder.rs
index 21d4c11..e6d2eb9 100644
--- a/src/rust/src/builder.rs
+++ b/src/rust/src/builder.rs
@@ -53,6 +53,23 @@ pub struct LyonPathBuilder {
}
impl LyonPathBuilder {
+ fn new_inner(builder: T, tolerance: f32, line_width: f32) -> Self {
+ Self {
+ builders: vec![builder],
+ layer_color: HashMap::new(),
+ cur_layer: 0,
+ cur_glyph_id: 0,
+ cur_path_id: 0,
+ glyph_id_map: HashMap::new(),
+ base_transform: lyon::geom::euclid::Transform2D::identity(),
+ scale_factor: 1.,
+ offset_x: 0.,
+ offset_y: 0.,
+ tolerance,
+ line_width,
+ }
+ }
+
#[inline]
pub fn cur_builder(&mut self) -> &mut T {
&mut self.builders[self.cur_layer]
@@ -71,15 +88,6 @@ impl LyonPathBuilder {
.collect()
}
- // adds offsets to x and y
- pub fn point(&self, x: f32, y: f32) -> lyon::math::Point {
- point(x, y)
- }
-
- pub fn ids(&self) -> [f32; 2] {
- [self.cur_glyph_id as _, self.cur_path_id as _]
- }
-
pub fn update_transform(&mut self) {
let transform = self
.base_transform
@@ -151,18 +159,20 @@ impl LyonPathBuilder {
// For path
-pub type FlattenedPathBuilder = lyon::path::builder::Transformed<
- lyon::path::builder::Flattened,
- lyon::math::Transform,
+pub type FlattenedPathBuilder = lyon::path::builder::NoAttributes<
+ lyon::path::builder::Transformed<
+ lyon::path::builder::Flattened,
+ lyon::math::Transform,
+ >,
>;
impl BuildPath for FlattenedPathBuilder {
fn set_transform(&mut self, transform: lyon::math::Transform) {
- self.set_transform(transform);
+ self.inner_mut().set_transform(transform);
}
fn new_builder(tolerance: f32) -> Self {
- lyon::path::Path::builder_with_attributes(2)
+ lyon::path::Path::builder()
.flattened(tolerance)
.transformed(lyon::geom::euclid::Transform2D::identity())
}
@@ -173,38 +183,23 @@ pub type LyonPathBuilderForPath = LyonPathBuilder;
impl LyonPathBuilderForPath {
pub fn new(tolerance: f32, line_width: f32) -> Self {
let builder = FlattenedPathBuilder::new_builder(tolerance);
- Self {
- builders: vec![builder],
- layer_color: HashMap::new(),
- cur_layer: 0,
- cur_glyph_id: 0,
- cur_path_id: 0,
- glyph_id_map: HashMap::new(),
- base_transform: lyon::geom::euclid::Transform2D::identity(),
- scale_factor: 1.,
- offset_x: 0.,
- offset_y: 0.,
- tolerance,
- line_width,
- }
+ Self::new_inner(builder, tolerance, line_width)
}
}
// For stroke and fill
-pub type NonFlattenedPathBuilder = lyon::path::builder::Transformed<
- lyon::path::path::BuilderWithAttributes,
- lyon::math::Transform,
+pub type NonFlattenedPathBuilder = lyon::path::builder::NoAttributes<
+ lyon::path::builder::Transformed,
>;
impl BuildPath for NonFlattenedPathBuilder {
fn set_transform(&mut self, transform: lyon::math::Transform) {
- self.set_transform(transform);
+ self.inner_mut().set_transform(transform);
}
fn new_builder(_tolerance: f32) -> Self {
- lyon::path::Path::builder_with_attributes(2)
- .transformed(lyon::geom::euclid::Transform2D::identity())
+ lyon::path::Path::builder().transformed(lyon::geom::euclid::Transform2D::identity())
}
}
@@ -213,20 +208,7 @@ pub type LyonPathBuilderForStrokeAndFill = LyonPathBuilder Self {
let builder = NonFlattenedPathBuilder::new_builder(tolerance);
- Self {
- builders: vec![builder],
- layer_color: HashMap::new(),
- cur_layer: 0,
- cur_glyph_id: 0,
- cur_path_id: 0,
- glyph_id_map: HashMap::new(),
- base_transform: lyon::geom::euclid::Transform2D::identity(),
- scale_factor: 1.,
- offset_x: 0.,
- offset_y: 0.,
- tolerance,
- line_width,
- }
+ Self::new_inner(builder, tolerance, line_width)
}
}
@@ -242,32 +224,26 @@ impl ttf_parser::OutlineBuilder for LyonPathBuilder {
self.glyph_id_map
.insert(self.cur_path_id, self.cur_glyph_id);
- let at = self.point(x, y);
- let custom_attributes = &self.ids();
- self.cur_builder().begin(at, custom_attributes);
+ let at = point(x, y);
+ self.cur_builder().begin(at, &[]);
}
fn line_to(&mut self, x: f32, y: f32) {
- let to = self.point(x, y);
- let custom_attributes = &self.ids();
- self.cur_builder().line_to(to, custom_attributes);
+ let to = point(x, y);
+ self.cur_builder().line_to(to, &[]);
}
fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
- let ctrl = self.point(x1, y1);
- let to = self.point(x, y);
- let custom_attributes = &self.ids();
- self.cur_builder()
- .quadratic_bezier_to(ctrl, to, custom_attributes);
+ let ctrl = point(x1, y1);
+ let to = point(x, y);
+ self.cur_builder().quadratic_bezier_to(ctrl, to, &[]);
}
fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
- let ctrl1 = self.point(x1, y1);
- let ctrl2 = self.point(x2, y2);
- let to = self.point(x, y);
- let custom_attributes = &self.ids();
- self.cur_builder()
- .cubic_bezier_to(ctrl1, ctrl2, to, custom_attributes);
+ let ctrl1 = point(x1, y1);
+ let ctrl2 = point(x2, y2);
+ let to = point(x, y);
+ self.cur_builder().cubic_bezier_to(ctrl1, ctrl2, to, &[]);
}
fn close(&mut self) {
diff --git a/src/rust/src/font.rs b/src/rust/src/font.rs
index e767452..7b188b5 100644
--- a/src/rust/src/font.rs
+++ b/src/rust/src/font.rs
@@ -154,6 +154,10 @@ impl LyonPathBuilder {
prev_glyph = None;
continue;
}
+
+ // increment glyph ID for consistency
+ self.cur_glyph_id += 1;
+
// Even when we cannot find glyph_id, fill it with 0.
let cur_glyph = font.glyph_index(c).unwrap_or(GlyphId(0));
@@ -176,7 +180,6 @@ impl LyonPathBuilder {
}
prev_glyph = Some(cur_glyph);
- self.cur_glyph_id += 1;
}
Ok(())
diff --git a/src/rust/src/into_fill_stroke.rs b/src/rust/src/into_fill_stroke.rs
index f8d7be7..2e896d3 100644
--- a/src/rust/src/into_fill_stroke.rs
+++ b/src/rust/src/into_fill_stroke.rs
@@ -4,37 +4,21 @@ use lyon::tessellation::*;
use ttf_parser::RgbaColor;
#[derive(Copy, Clone, Debug)]
-struct Vertex {
- position: lyon::math::Point,
- glyph_id: u32,
- path_id: u32,
-}
+struct Vertex(lyon::math::Point);
// This can have some members so that it can be used in new_vertex(), but I
// don't find any useful usage yet.
struct VertexCtor {}
impl FillVertexConstructor for VertexCtor {
- fn new_vertex(&mut self, mut vertex: FillVertex) -> Vertex {
- let pos = vertex.position();
- let attr = vertex.interpolated_attributes();
- Vertex {
- position: pos,
- glyph_id: attr[0] as _,
- path_id: attr[1] as _,
- }
+ fn new_vertex(&mut self, vertex: FillVertex) -> Vertex {
+ Vertex(vertex.position())
}
}
impl StrokeVertexConstructor for VertexCtor {
- fn new_vertex(&mut self, mut vertex: StrokeVertex) -> Vertex {
- let pos = vertex.position();
- let attr = vertex.interpolated_attributes();
- Vertex {
- position: pos,
- glyph_id: attr[0] as _,
- path_id: attr[1] as _,
- }
+ fn new_vertex(&mut self, vertex: StrokeVertex) -> Vertex {
+ Vertex(vertex.position())
}
}
@@ -51,7 +35,7 @@ impl LyonPathBuilderForStrokeAndFill {
x: Vec::new(),
y: Vec::new(),
glyph_id: Vec::new(),
- path_id: Vec::new(),
+ path_id: None,
triangle_id: Some(Vec::new()),
color,
};
@@ -60,7 +44,14 @@ impl LyonPathBuilderForStrokeAndFill {
let mut tessellator = FillTessellator::new();
let options = FillOptions::tolerance(self.tolerance);
+ let mut cur_path_id: u32 = 0;
for (path, color) in paths {
+ let path_id_inc = path
+ .iter()
+ .filter(|x| matches!(x, path::Event::Begin { .. }))
+ .count();
+ cur_path_id += path_id_inc as u32;
+
let mut geometry: VertexBuffers = VertexBuffers::new();
{
// Compute the tessellation.
@@ -72,7 +63,9 @@ impl LyonPathBuilderForStrokeAndFill {
)
.unwrap();
}
- extract_vertex_buffer(geometry, &mut result, color);
+
+ let cur_glyph_id = *self.glyph_id_map.get(&cur_path_id).unwrap_or(&0) as i32;
+ extract_vertex_buffer(geometry, &mut result, color, cur_glyph_id);
}
result
}
@@ -89,15 +82,23 @@ impl LyonPathBuilderForStrokeAndFill {
x: Vec::new(),
y: Vec::new(),
glyph_id: Vec::new(),
- path_id: Vec::new(),
+ path_id: None,
triangle_id: Some(Vec::new()),
color,
};
+ let mut cur_path_id: u32 = 0;
+
// Will contain the result of the tessellation.
let mut tessellator = StrokeTessellator::new();
let options = StrokeOptions::tolerance(self.tolerance).with_line_width(self.line_width);
for (path, color) in paths {
+ let path_id_inc = path
+ .iter()
+ .filter(|x| matches!(x, path::Event::Begin { .. }))
+ .count();
+ cur_path_id += path_id_inc as u32;
+
let mut geometry: VertexBuffers = VertexBuffers::new();
{
// Compute the tessellation.
@@ -110,7 +111,8 @@ impl LyonPathBuilderForStrokeAndFill {
.unwrap();
}
- extract_vertex_buffer(geometry, &mut result, color);
+ let cur_glyph_id = *self.glyph_id_map.get(&cur_path_id).unwrap_or(&0) as i32;
+ extract_vertex_buffer(geometry, &mut result, color, cur_glyph_id);
}
result
}
@@ -120,6 +122,7 @@ fn extract_vertex_buffer(
geometry: VertexBuffers,
dst: &mut PathTibble,
paint_color: Option,
+ glyph_id: i32,
) {
let offset = dst.triangle_id.as_ref().map_or(0, |v| match v.last() {
Some(last_triangle_id) => last_triangle_id + 1,
@@ -127,10 +130,9 @@ fn extract_vertex_buffer(
});
for (n, &i) in geometry.indices.iter().enumerate() {
if let Some(v) = geometry.vertices.get(i) {
- dst.x.push(v.position.x as _);
- dst.y.push(v.position.y as _);
- dst.glyph_id.push(v.glyph_id as _);
- dst.path_id.push(v.path_id as _);
+ dst.x.push(v.0.x as _);
+ dst.y.push(v.0.y as _);
+ dst.glyph_id.push(glyph_id);
if let Some(triangle_id) = &mut dst.triangle_id {
triangle_id.push(n as i32 / 3 + offset);
}
diff --git a/src/rust/src/into_path.rs b/src/rust/src/into_path.rs
index 6edfe4a..bd9072a 100644
--- a/src/rust/src/into_path.rs
+++ b/src/rust/src/into_path.rs
@@ -6,19 +6,18 @@ use crate::result::PathTibble;
impl LyonPathBuilderForPath {
pub fn into_path(mut self) -> PathTibble {
let paths = self.build();
- let color = if self.layer_color.is_empty() {
+
+ let mut x = Vec::new();
+ let mut y = Vec::new();
+ let mut glyph_id = Vec::new();
+ let mut path_id = Vec::new();
+ let mut color = if self.layer_color.is_empty() {
None
} else {
Some(Vec::new())
};
- let mut result = PathTibble {
- x: Vec::new(),
- y: Vec::new(),
- glyph_id: Vec::new(),
- path_id: Vec::new(),
- triangle_id: None,
- color,
- };
+
+ let mut cur_path_id: u32 = 0;
for (path, paint_color) in paths {
let paint_color = match paint_color {
Some(RgbaColor {
@@ -29,7 +28,6 @@ impl LyonPathBuilderForPath {
}) => format!("#{red:02x}{green:02x}{blue:02x}{alpha:02x}",),
None => "#00000000".to_string(),
};
- let mut cur_path_id: u32 = 0;
for p in path.iter() {
let point = match p {
lyon::path::Event::Begin { at } => {
@@ -50,19 +48,27 @@ impl LyonPathBuilderForPath {
};
if let Some(pos) = point {
- result.x.push(pos.x as _);
- result.y.push(pos.y as _);
- result
- .glyph_id
- .push(*self.glyph_id_map.get(&cur_path_id).unwrap_or(&0) as _);
- result.path_id.push(cur_path_id as _);
+ x.push(pos.x as _);
+ y.push(pos.y as _);
- if let Some(v) = result.color.as_mut() {
+ let cur_glyph_id = *self.glyph_id_map.get(&cur_path_id).unwrap_or(&0) as _;
+ glyph_id.push(cur_glyph_id);
+ path_id.push(cur_path_id as _);
+
+ if let Some(v) = color.as_mut() {
v.push(paint_color.clone())
}
}
}
}
- result
+
+ PathTibble {
+ x,
+ y,
+ glyph_id,
+ path_id: Some(path_id),
+ triangle_id: None,
+ color,
+ }
}
}
diff --git a/src/rust/src/result.rs b/src/rust/src/result.rs
index e5e1edc..847bf9e 100644
--- a/src/rust/src/result.rs
+++ b/src/rust/src/result.rs
@@ -9,25 +9,34 @@ pub struct PathTibble {
// IDs to distinguish the glyphs. Note that this is a different ID than [ttf_parser::GlyphId].
pub glyph_id: Vec,
// IDs to distinguish the groups of paths (i.e., `Begin` path event to `End` path event).
- pub path_id: Vec,
+ pub path_id: Option>,
// IDs to distinguish the triangles. This field is `None` for `ConversionType::Path`.
pub triangle_id: Option>,
// Color of color emoji font.
pub color: Option>,
}
-impl TryFrom for savvy::Sexp {
- type Error = savvy::Error;
-
- fn try_from(value: PathTibble) -> savvy::Result {
- let mut len = 4;
- if value.triangle_id.is_some() {
+impl PathTibble {
+ fn len(&self) -> usize {
+ let mut len = 3;
+ if self.path_id.is_some() {
+ len += 1
+ };
+ if self.triangle_id.is_some() {
len += 1
};
- if value.color.is_some() {
+ if self.color.is_some() {
len += 1
};
- let mut out = savvy::OwnedListSexp::new(len, true)?;
+ len
+ }
+}
+
+impl TryFrom for savvy::Sexp {
+ type Error = savvy::Error;
+
+ fn try_from(value: PathTibble) -> savvy::Result {
+ let mut out = savvy::OwnedListSexp::new(value.len(), true)?;
out.set_name_and_value(0, "x", ::try_from(value.x.as_slice())?)?;
out.set_name_and_value(1, "y", ::try_from(value.y.as_slice())?)?;
@@ -36,24 +45,24 @@ impl TryFrom for savvy::Sexp {
"glyph_id",
::try_from(value.glyph_id.as_slice())?,
)?;
- out.set_name_and_value(
- 3,
- "path_id",
- ::try_from(value.path_id.as_slice())?,
- )?;
- let mut idx = 3;
+ // optional columns
+ let mut idx = 2;
+
+ if let Some(path_id) = value.path_id {
+ idx += 1;
+ let v = ::try_from(path_id.as_slice())?;
+ out.set_name_and_value(idx, "path_id", v)?;
+ }
if let Some(triangle_id) = value.triangle_id {
idx += 1;
- out.set_name_and_value(
- idx,
- "triangle_id",
- ::try_from(triangle_id.as_slice())?,
- )?;
+ let v = ::try_from(triangle_id.as_slice())?;
+ out.set_name_and_value(idx, "triangle_id", v)?;
}
if let Some(color) = value.color {
idx += 1;
- out.set_name_and_value(idx, "color", ::try_from(color.as_slice())?)?;
+ let v = ::try_from(color.as_slice())?;
+ out.set_name_and_value(idx, "color", v)?;
}
out.into()
diff --git a/tests/testthat/_snaps/snapshot.md b/tests/testthat/_snaps/snapshot.md
index 650216c..dea8003 100644
--- a/tests/testthat/_snaps/snapshot.md
+++ b/tests/testthat/_snaps/snapshot.md
@@ -6,49 +6,49 @@
# A tibble: 4 x 4
x y glyph_id path_id
- 1 0 0 0 1
- 2 0.800 0.800 0 1
- 3 0 0.800 0 1
- 4 0 0 0 1
+ 1 0 0 1 1
+ 2 0.800 0.800 1 1
+ 3 0 0.800 1 1
+ 4 0 0 1 1
---
Code
string2stroke("A", "./font/test.ttf")
Output
- # A tibble: 18 x 5
- x y glyph_id path_id triangle_id
-
- 1 0.836 0.815 0 1 0
- 2 0.764 0.785 0 1 0
- 3 0.0150 0.785 0 1 0
- 4 0.836 0.815 0 1 1
- 5 0.0150 0.785 0 1 1
- 6 -0.0150 0.815 0 1 1
- 7 -0.0150 0.815 0 1 2
- 8 0.0150 0.785 0 1 2
- 9 0.0150 0.0362 0 1 2
- 10 -0.0150 0.815 0 1 3
- 11 0.0150 0.0362 0 1 3
- 12 -0.0150 -0.0362 0 1 3
- 13 -0.0150 -0.0362 0 1 4
- 14 0.0150 0.0362 0 1 4
- 15 0.764 0.785 0 1 4
- 16 -0.0150 -0.0362 0 1 5
- 17 0.764 0.785 0 1 5
- 18 0.836 0.815 0 1 5
+ # A tibble: 18 x 4
+ x y glyph_id triangle_id
+
+ 1 0.836 0.815 1 0
+ 2 0.764 0.785 1 0
+ 3 0.0150 0.785 1 0
+ 4 0.836 0.815 1 1
+ 5 0.0150 0.785 1 1
+ 6 -0.0150 0.815 1 1
+ 7 -0.0150 0.815 1 2
+ 8 0.0150 0.785 1 2
+ 9 0.0150 0.0362 1 2
+ 10 -0.0150 0.815 1 3
+ 11 0.0150 0.0362 1 3
+ 12 -0.0150 -0.0362 1 3
+ 13 -0.0150 -0.0362 1 4
+ 14 0.0150 0.0362 1 4
+ 15 0.764 0.785 1 4
+ 16 -0.0150 -0.0362 1 5
+ 17 0.764 0.785 1 5
+ 18 0.836 0.815 1 5
---
Code
string2fill("A", "./font/test.ttf")
Output
- # A tibble: 3 x 5
- x y glyph_id path_id triangle_id
-
- 1 0 0 0 1 0
- 2 0 0.800 0 1 0
- 3 0.800 0.800 0 1 0
+ # A tibble: 3 x 4
+ x y glyph_id triangle_id
+
+ 1 0 0 1 0
+ 2 0 0.800 1 0
+ 3 0.800 0.800 1 0
# the data extracted from installed font are as expected
@@ -58,16 +58,16 @@
# A tibble: 27 x 4
x y glyph_id path_id
- 1 -0.00131 0 0 1
- 2 0.245 0.641 0 1
- 3 0.336 0.641 0 1
- 4 0.598 0 0 1
- 5 0.502 0 0 1
- 6 0.427 0.194 0 1
- 7 0.159 0.194 0 1
- 8 0.0887 0 0 1
- 9 -0.00131 0 0 1
- 10 0.184 0.263 0 2
+ 1 -0.00131 0 1 1
+ 2 0.245 0.641 1 1
+ 3 0.336 0.641 1 1
+ 4 0.598 0 1 1
+ 5 0.502 0 1 1
+ 6 0.427 0.194 1 1
+ 7 0.159 0.194 1 1
+ 8 0.0887 0 1 1
+ 9 -0.00131 0 1 1
+ 10 0.184 0.263 1 2
# i 17 more rows
---
@@ -75,19 +75,19 @@
Code
string2stroke("A", "Arial")
Output
- # A tibble: 150 x 5
- x y glyph_id path_id triangle_id
-
- 1 0.255 0.626 0 1 0
- 2 0.234 0.656 0 1 0
- 3 0.346 0.656 0 1 0
- 4 0.255 0.626 0 1 1
- 5 0.346 0.656 0 1 1
- 6 0.326 0.626 0 1 1
- 7 0.326 0.626 0 1 2
- 8 0.346 0.656 0 1 2
- 9 0.621 -0.0150 0 1 2
- 10 0.326 0.626 0 1 3
+ # A tibble: 150 x 4
+ x y glyph_id triangle_id
+
+ 1 0.255 0.626 1 0
+ 2 0.234 0.656 1 0
+ 3 0.346 0.656 1 0
+ 4 0.255 0.626 1 1
+ 5 0.346 0.656 1 1
+ 6 0.326 0.626 1 1
+ 7 0.326 0.626 1 2
+ 8 0.346 0.656 1 2
+ 9 0.621 -0.0150 1 2
+ 10 0.326 0.626 1 3
# i 140 more rows
---
@@ -95,18 +95,18 @@
Code
string2fill("A", "Arial")
Output
- # A tibble: 75 x 5
- x y glyph_id path_id triangle_id
-
- 1 0.0887 0 0 1 0
- 2 -0.00131 0 0 1 0
- 3 0.159 0.194 0 1 0
- 4 0.427 0.194 0 1 1
- 5 0.159 0.194 0 1 1
- 6 0.184 0.263 0 2 1
- 7 0.159 0.194 0 1 2
- 8 -0.00131 0 0 1 2
- 9 0.184 0.263 0 2 2
- 10 0.184 0.263 0 2 3
+ # A tibble: 75 x 4
+ x y glyph_id triangle_id
+
+ 1 0.0887 0 1 0
+ 2 -0.00131 0 1 0
+ 3 0.159 0.194 1 0
+ 4 0.427 0.194 1 1
+ 5 0.159 0.194 1 1
+ 6 0.184 0.263 1 1
+ 7 0.159 0.194 1 2
+ 8 -0.00131 0 1 2
+ 9 0.184 0.263 1 2
+ 10 0.184 0.263 1 3
# i 65 more rows