IdreesInc / Monocraft

A monospaced programming font inspired by the Minecraft typeface
https://idreesinc.com
SIL Open Font License 1.1
8.02k stars 115 forks source link

Combine Pixels Into Polygons #98

Closed Dheatly23 closed 1 year ago

Dheatly23 commented 1 year ago

This PR fixes #88 by combining pixels into polygons.

Ciubix8513 commented 1 year ago

There seems to be an issue with some glyphs: image image While I don't think it will cause any issues with the font rendering, it's still a bug

Dheatly23 commented 1 year ago

Yeah, to handle holes, i basically put a tiny cut in the outer polygon, then splice the hole in. There may be better solution though. The hole polygonizer algorithm is still there, just need to rework how to put it back in.

EDIT: Meanwhile i'll fix colinear line in the splicing algorithm.

Dheatly23 commented 1 year ago

Okay i fixed it. Turns out, i just need to emit the inner polygon to be recognized as holes. Fingers crossed that font renderers can handle that.

Ciubix8513 commented 1 year ago

Now there's another issue, for some reason the glyphs Ф ф broke: image image I don't think I have enough python knowledge to help fix it

Dheatly23 commented 1 year ago

Huh, i tested it and it seems like some weird interaction with mutating iterator variable. I'll push the fix soon.

Ciubix8513 commented 1 year ago

I think you should add a license notice similar to other files, but otherwise it looks good to me.

dantaeusb commented 1 year ago

My apologies for being a little off-topic, but I am super fascinated by this. Is there a common name for the algorithm you've used?

My question is how exactly the length is figured out, as the output of doMove is a vector with origin. I can't really wrap my head around where this matrix turns to points because from the first glance, there's no recursive calls or some traversing when those directional flags are set.

I would be super happy to know more about this if you don't mind. You can use email in my profile if you would rather not explain here.

Thanks!

Dheatly23 commented 1 year ago

As far as my knowledge goes, there seem to be no one else that do it the same way. I'll explain what the algorithm does:

  1. Split the image into connected parts (the segmentize function). Two pixels are connected if they share an edge or corner.
  2. Now the segmented parts only contains 2 bundaries, outer and possibly inner. So it will make the calculation easier.
  3. To trace the boundary, i use a "turtle" that hugs the boundary from right (right side is filled, left side is empty). The turtle is initally on the top-leftmost pixel facing right.
  4. To make the turtle movement more efficient, in the doMove function i check on neighboring pixels where the turtle at to plan it's movement. There are 9 cases to be considered.
  5. While the turtle is moving, i record every corner that the turtle made.
  6. The turtle is done travelling whenever it goes back to it's original point (this took so long to tweak). Remove the extra path beyond that point.
  7. And for the internal boundary do the same, making sure the turtle is moving opposite of outer loop.
  8. (Optionally) i tried to simplify even further by splicing the inner loop to outer loop, but apparently this may cause issues. Maybe in the future it may be implemented better.

The cell data is a bitflag with values as follows:

The filled bit is obvious. The wall bits are used to find inner boundaries that has not been found (if it should have wall but isn't set). I don't know what you mean by length, i don't use length in the algorithm, only direction of the turtle is considered.

And that problem with ф is apparently Python does not like mutating iterator variable while it is iterating. I never experienced this issue until now, but currently i restore the inner value every time the inner polygon generation is done. Maybe it's my bug, but so far that fixes it.

IdreesInc commented 1 year ago

Apologies for the delay in reviewing this, had the flu, not fun. Thanks @Ciubix8513 for reviewing it in the meantime and discovering those bugs! But wow this is brilliant. Thank you @Dheatly23 for putting the time and effort into solving this problem, I didn't realize how nontrivial it would turn out to be. I especially appreciate the comments that illustrate how the outlines are being built. Going to merge this into a branch first so that I can update the version number, then will get a release out later today!

dantaeusb commented 1 year ago

FYI I noticed a detail when installing new version of the font, I believe these margin alignments are (leftMargin from 44034119d788630c41590cf549fc6dfd8f16d200) are no longer applied.

Screenshot 2023-02-05 at 23 25 08

P.S. I also extended symbols with european-nonlatin set: https://github.com/dantaeusb-mc/Minefont/blob/main/src/characters.json, but it's auto-generated, and I'm still working on it. I want to get non-monospaced version too.

Dheatly23 commented 1 year ago

Oh yeah, i didn't see that left margin can be non-integer. I just assumed it is and shift the entire image. I think i did the same with descent and diacritic space too. Oh well, that could be fixed later. Thanks anyway for accepting my PR.