Open ffd8 opened 5 years ago
A couple of questions:
How does the 2d return array handle multiple paths for letters? Is there a way for the caller to know which paths correspond to which letters? For example, in the string "%#!"
Does the proposed code still work for all values of the other two options: sampleFactor and simplifyThreshold?
Do we have other functions in p5.js that change return type based on options? If not, we might also consider a new function called 'textToPaths'.
1 – At the moment, each piece of a letter/glyph is simply handled as its own path in the first dimension of array, followed by their collection of points in the 2nd dimension. This is how it's handled in Geomerative (Processing library, where there's an example called getPointPaths
). The example "%#!" would result in 6 paths (3 for the %), so one uses a loop within loop to avoid the connecting line. Of course with something like a 'B' or 'p' where there's a path over another one.. it gets tricky for which one should be filled and which one empty.. (also an issue in Geomerative). Usually best to just use strokes and avoid the issue. Here it could be possible to use a 3D array – just like how the function breaks things down [glyphs][paths][points] – but that might get too loopy?! 🤪
2 – Yup, it's using the exact same point extraction, so both of those options work smoothly.
3 – That was my question.. of course it's probably not ideal to have a different type of return... so I wondered if there was another function in the library that does so for documentation style guiding. At first I thought of having another function, but it would be 99% copy + paste code to then maintain... OR it's another function that passes a request to the textToPoints()
function with this extra option that's not documented on textToPoints()
(but is available) and listed as it's own function ie. textToPaths()
? Hard to say which is easier or most compatible for adjusting ones code.. changing an option and knowing you get something else out, or having to write a whole different function name? perhaps the later is more clear? Then if making a 3rd option to get the [glyphs][paths][points] = textToGlyphs()
? (that's not so clear... also seems like something to embed as an option perhaps even on original function)
Hope there's another function with multiple returns, as that might be smoothest implementation IMHO. Then having two additional options to textToPoints()
, separatePaths
(returns 2d array [paths][points]
), separateGlyphs
(returns 2d array [glyphs][points]
.. still connected line within letter), activating them both returns 3d array [glyphs][paths][points]
.
@lmccart any thoughts on the API implications here (and on whether any other API functions change return types based on options) ?
@ffd8 have you tested the current implementation with the full set of unusual glyph path types: open, closed, intersecting, etc. ( %, !, g, &, ...)
@dhowe Yup, it's working fine with all special characters, since it's the same textToPoints()
function, just now returns more detail/depth than currently done.
Here's an example for testing it out: https://editor.p5js.org/ffd8/sketches/jQANRzdQ2
Right - I guess I was thinking about how to do filled paths... but this is still not possible given the data returned (part 3 of your example). I suppose another option in terms of the API would be to keep the 1D array but separate each path by some constant value, maybe Number.NEGATIVE_INFINITY. This would allow a user to realize your example sketch, but not change the return type for the function (and not introduce a new function to the API).
Yeah filled paths are tricky.. Just added a function to export glyphs too and learned that of course this works great for letters like B
with inner being opposite/different than outer, however it's tricky for something like %
, where the o
has two paths.. but the /
not... anyways, those are just small details that aren't an issue if one uses outlines.
Here's an example with a custom build for exporting glyphs too: https://editor.p5js.org/ffd8/sketches/eCNcHelzT
I'd propose to have the two options:
separatePaths
, returns 2D array [paths][points]
separateGlyphs
, returns 3D array [glyphs][paths][points]
(separateGlyphs
overrides separatePaths
, so it's no problem if one writes both.. just mentioning separateGlyphs
forces the return of 3D array).Regarding the idea of sending a special flag whenever the path changes, might be smooth for just outlining the paths, though would still require special if()
to catch when an extra begin/endShape()
are needed – but could make it more difficult once using rotate
or translate
of the paths.
could make it more difficult once using rotate or translate of the paths how so?
Here's a demo sketch: https://editor.p5js.org/ffd8/sketches/A_1KGFkac
The demo hopefully shows how one more flexible per level of separation. Tried to simulate end of path 'flags' (faked it with dist
) in top example, but then in order correctly close each shape, one probably has to refer back to the first vertex in the path (storing var outside loop)... and to specifically talk to a certain path, one would also have to add a counter within the end/beginShape
moment.
For type experiments, this separation is really helpful... maybe want to only distort in 2nd path of a glyph.. or fade out letters per path in a glyph/string?
Regarding the return
in documentation, adding a few words could make it clear, together with 3 examples showing the difference and brief description in optional params description?
from:
an array of points, each with x, y, alpha (the path angle)
to:
a single or multi-dimensional array of points, each with x, y, alpha (the path angle)
@lmccart could we get your insight for this toTextPoints()
feature enhancement?
How best to implement the return of points isolated within paths + glyphs for more precise typo experiments (request came from students):
[paths][points]
= points in separate paths[glyphs][paths][points]
= points in separate paths in separate glyphsExtend the optional inputs:
let pointsInPaths = font.textToPoints(txt, 0, 0, fSize, {
sampleFactor: 5,
simplifyThreshold: 0,
separatePaths: true // returns: [paths][points]
separateGlyphs: true // returns: [glyphs][paths][points] (supersedes separatePaths)
});
So many semantic options... happy for better suggestions:
paths
or separatePaths
or pointsInPaths
glyphs
or separateGlyphs
or pointsInGlyphs
or pointsInPathsInGlyphs
Add 2 new functions that simply call the original function with additional options:
-textToPaths()
or textToPointsInPaths()
-textToGlyphs()
or textToPointsInGlyphs()
or looong, textToPointsInPathsInGlyphs()
😬
// p5 coder types:
let pointsInPaths = font.textToPaths(txt, 0, 0, fSize, {
sampleFactor: 5,
simplifyThreshold: 0,
});
// behind the scenes, wraps above function with added option (pseudo code):
function textToPaths(inText, inX, inY, inSize, inOptions){
inOptions.separatePaths = true;
return textToPoints(inText, inX, inY, inSize, inOptions);
}
Concern is this might produce repeated documentation (since options are the same and require same description as the normal textToPoints()
.
Personally I'm for option 1. Curious if there are other ideas? In any case, all about adding examples to demonstrate usage of returned 2D and 3D arrays.
Hi all, interesting discussion here. We're focused on the priorities for the 1.0 release right now so I don't think I'll have the bandwidth to weigh in on this until after that's done.
After a few recent messages asking about the status, here's an example, where the linked p5_textToPoints.js
file can be snagged and used to temp override textToPoints()
:
https://editor.p5js.org/ffd8/sketches/TaPWHTaH
(fixed a bug I had in previous versions above that killed function w/o options)
With 1.0 released 🎉– I'd like to bring this up again. To keep it a really modest modification, I'd suggest option 1 above, simply adding optional params to the existing function based on the level of detail/separation one wants from the type.
Basic (current):
let points = font.textToPaths('hello', 0, 0, 50);
// returns points[pts]
Options (current):
let points = font.textToPaths('hello', 0, 0, 50, {
sampleFactor: 5,
simplifyThreshold: 0
});
// returns points[pts]
Separate Paths Option (proposed):
let points = font.textToPaths('hello', 0, 0, 50, {
sampleFactor: 5,
simplifyThreshold: 0,
separatePaths: true
});
// returns points[paths][pts]
Separate Glyphs Option (proposed):
let points = font.textToPaths('hello', 0, 0, 50, {
sampleFactor: 5,
simplifyThreshold: 0,
separateGlyphs: true
});
// returns points[glyphs][paths][pts]
Examples would be given in the docs for each optional usage.
Hi, I'm moving some discussion here from another issue!
As someone who uses Typescript bindings, option 2 would have the benefit of being more easily typed (there are type overloads that one can use, but it means typing of the options argument would be required.) That said, TS support shouldn't be the priority, so I'll defer to someone else for picking between the two. @ffd8 thanks for writing your implementation, it works great!
The one other thing I'd want to bring up is that, in 2D mode, if you want to fill the shapes, you'd have to rely on the even-odd fill rule in order to fill shapes with holes correctly. begin/endContour
lets you do that, but it doesn't work in p5 unless there are non-contour vertices too. Here's a version of your older sketch showing how you'd have to use this right now: https://editor.p5js.org/davepagurek/sketches/cUjihInOf Basically, the first path is treated as vertices, and all subsequent paths as contours. Ideally, I'd also want to let users just use contours for all paths. If this blows up the scope too much it can be a follow-up PR, but it should maybe be bundled with this change to avoid having an example that has to explain the workaround.
Nature of issue?
Most appropriate sub-area of p5.js?
Which platform were you using when you encountered this?
Feature enhancement details:
Preparing my first p5.js pull request, so making an issue first.
This will extend
textToPoints()
to allow the return of a 2D Array,[paths][points]
, since as a single array of points, there will always be a line drawn between the letters if placed within abegin/endShape()
.Figured the smoothest way to implement this, is adding it as an option when calling the function, and imagined that option to be
separatePaths : true
.Code is already done.. just have to read up on Docs for contributing and happy for feedback on PR.