Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extrapolate stroke caps for overlapping strokes #28

Closed
chanind opened this issue Feb 4, 2018 · 13 comments
Closed

Extrapolate stroke caps for overlapping strokes #28

chanind opened this issue Feb 4, 2018 · 13 comments

Comments

@chanind
Copy link
Contributor

chanind commented Feb 4, 2018

Thank you for making this incredible library and open-sourcing it! One possible point of improvement:

Make Me a Hanzi currently clips the end of strokes if it's obscured by another overlapping stroke. It makes sense because this data was extracted from a real font, but this isn't ideal for a few reasons:

  • Since the SVG path points are all rounded to the nearest integer this can result in a really thin gap between strokes, especially visible when the character is drawn at a larger size. For example: screen shot 2018-02-04 at 4 23 20 pm
  • When showing stroke animations, it looks more natural if the full stroke can be drawn first and then a subsequent stroke can be animated on top of the end of a stroke since this is how strokes would be drawn in reality.
  • If stroke ends can be added, it makes it possible for some interesting interactive effects like breaking apart a character in 3d (http://wenlincdl.com/) or moving parts of the stroke on click/mouseover (http://wenlincdl.com/demos/explorer). These effects would look strange on strokes with clipped ends.

I think it should be possible to add a realistic stroke end to clipped strokes that won't change the way the character looks after all strokes are drawn, but should look natural when the stroke is viewed in isolation and fix the issues brought up above. Maybe it could work by trying to fit a stroke end from other similar-looking strokes such that the fitted end is fully obscured by the stroke drawn on top.

@hugolpz
Copy link

hugolpz commented Feb 7, 2018

@chanind, do you have a script/programmatical approach in mind or do you consider SVG editing by hand ? Just to know.

@chanind
Copy link
Contributor Author

chanind commented Feb 7, 2018

I think it should be possible to do programmatically. Maybe something like for every stroke that's clipped, search through all the other characters in the dataset for the closest looking stroke and try to use the end from that stroke? I'll experiment with this if I have some time.

@hugolpz
Copy link

hugolpz commented Feb 7, 2018

CDL has a cascading system between files. It maybe the way to go somedays. The transformation bounding-box's of the component is the current bounding box of its strokes.

@skishore
Copy link
Owner

This would be a nice enhancement, and I thought about algorithms for it a while back.

One approach here is to use more data from the "bridges" data structure that is the key to breaking the original glyph down into stroke components. I've drawn some of the bridges for the example character from above in this diagram:

image

Using some geometry, every time a stroke boundary hits a bridge, we could automatically create two quadratic Bezier curve that smoothly interpolates between the stroke's angle on the two sides of that bridge. For simple bridges which are collinear with the stroke itself, this interpolation would just be a line, but for the bridge up near the top-left of that character, it would come close to a point as you'd expect. There is a third case for the diagonal strokes in 木, where the two angles actually spread apart, but I think the same math would cover all three.

The geometry here is a bit finicky to get right, which is why I never got around to doing it. But the point is that I think this piece is doable without manually drawing any curves and without needing to use decompositions.

@parsimonhi
Copy link

There are some special cases such as the 6th stroke of 者.

@parsimonhi
Copy link

parsimonhi commented Feb 18, 2018

I made a try with a simple algorithm that replaces every straight line by a cubic Bezier curve.

  • The start point of the Bezier curve is the start point of the straight line.
  • The end point of the Bezier curve is the end point of the straight line.
    Assuming d = (length of the straight line)/3 (or /2):
  • The first handle of the Bezier curve is placed at the distance of d from the start point of the straight line on the tangent of the curve that ends at the start point of the straight line.
  • The second handle of the Bezier curve is placed at the distance of d from the end point of the straight line on the tangent of the curve that starts at the end point of the straight line.
    Note: computing the tangent of a bezier curve is trivial

Below is the result for 我:
25105brush

I put online a demo at http://gooo.free.fr/animCJK/all.php. Select the "brush" checkbox, 512 or 1024px radio input and Hsk hanzi (China) radio input. Then enter a hanzi in the data field or select a hanzi in the list on the bottom of the page.

The result is not always perfect (mostly because the svg data have some defects such as very short curves) but seems acceptable most of the time.

@parsimonhi
Copy link

If you want to test the algorithm using the makeMeAHanzi data, just get the javascript strokeBrushing() function from the code of http://gooo.free.fr/animCJK/all.php and apply it to makeMeAHanzi stroke paths just before displaying them. This function adds to a makeMeAHanzi stroke path brush-like start and end.

@chanind chanind mentioned this issue Feb 18, 2018
@chanind
Copy link
Contributor Author

chanind commented Feb 18, 2018

It looks like you beat me to it! I made a similar attempt here: #32. I suspect we're doing almost the same thing

@parsimonhi
Copy link

parsimonhi commented Feb 18, 2018

chanind: It looks like you beat me to it! I made a similar attempt here: #32. I suspect we're doing almost the same thing

:-)

Yes, it's similar. But my solution is not perfect as is, and requires some improvements (especially when there are "bridges" with disturbing points around it). So any other solution may help!

@chanind
Copy link
Contributor Author

chanind commented Feb 18, 2018

Yes, it's similar. But my solution is not perfect as is, and requires some improvements (especially when there are "bridges" with disturbing points around it). So any other solution may help!

I noticed the same thing - whenever there's a bridge there's likely to be a tiny distorted curve, typically less than 2px in length, that throws off all the tangent calculations. I tried to get around that by calculating the tangent using getPointAtLength() and looking a few pixels back from the clip points. That way it doesn't matter if there's a tiny distorted path curve in the pathstring.

@skishore
Copy link
Owner

I've just about gotten your work integrated into the tool, @chanind - once it's in, we'll have the corrections applied to the SVGs too, and future runs will be incremental and fast. The server data migration is taking an hour on my machine though!

@skishore
Copy link
Owner

And done as of e0089f7!

I'll probably run the stroke-caps logic one more time to get those last few stragglers, and then from now on, the tool will run it twice (heh) on every character whenever it's updated.

@chanind
Copy link
Contributor Author

chanind commented Jun 20, 2018

Thanks for your work getting this merged into the tools branch! It looks great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants