typemytype / booleanOperations

Boolean operations on paths
MIT License
39 stars 18 forks source link

Glyph distortion after union #32

Open khaledhosny opened 8 years ago

khaledhosny commented 8 years ago

Using this script on ReemKufi-Regular.ufo.zip:

import sys
from booleanOperations import BooleanOperationManager
from defcon import Font

ufo = Font(sys.argv[1])
manager = BooleanOperationManager()
for glyph in ufo:
    contours = list(glyph)
    glyph.clearContours()
    manager.union(contours, glyph.getPointPen())
ufo.save(sys.argv[1].replace(".ufo", "-new.ufo"))

Results in several distorted glyphs: arMeem.isol, arHeh.fina and arHeh.fina1: 2016-07-08 18-13-20

anthrotype commented 8 years ago

hm.. that's not good. Does re-locating the contours' starting point make any difference?

khaledhosny commented 8 years ago

Changing the start points makes no difference, but moving the horizontal contour so that its first point is not directly above the point in the other contour β€œfixes” the distortion.

jamesgk commented 8 years ago

I dug into this a bit and have a theory on why this is happening. If I'm correct, the code here: https://github.com/typemytype/booleanOperations/blob/ebeaf6ba940dbfeda25b232a065f0cabf8ec9a8f/Lib/booleanOperations/flatten.py#L877

is trying to match an input curve (from the original glyph) with an output curve (from the union-ed polygon) by their first points. It searches for t values in the input curve for the last point on the output curve, and if it doesn't find one, as it might not in the example here, it just drops an entire segment because it assumes at least one would be found: https://github.com/typemytype/booleanOperations/blob/ebeaf6ba940dbfeda25b232a065f0cabf8ec9a8f/Lib/booleanOperations/flatten.py#L924

I'm not sure if I'm right on the mark in diagnosing the actual issue, and I certainly don't know what the solution would be at this point. But maybe this is a start.

jamesgk commented 8 years ago

cc @typemytype

This is almost certainly a duplicate of https://github.com/typemytype/booleanOperations/issues/27, and of https://github.com/googlei18n/nototools/issues/268#issuecomment-238014664. Basically it's a pretty big issue with BooleanOperations. The code is pretty complex though; I'm hesitant to make any changes for fear of breaking something else.

marekjez86 commented 8 years ago

@jamesgk @anthrotype @typemytype @behdad : I will be at AtypI, jamesgk will be at AtypI. @anthrotype I think you will be there also (and I think you own this code).

Could we meet on Wednesday, 14/Sep evening over some food and beer (water or any non-alcoholic drink of your choice if you don't drink alcohol) and fix it?

@khaledhosny : would be be able to help with testing it (will you be around during AtypI)? I would test for any regressions over the next 24 hours after the fix is checked in (ending on Friday, 16/Sep) using our pipeline (as of today we produce 639 different OTF files including one that causes the problem). If there were any issues most likely they'd appear early, but if there were none I'd know on Friday. If there were any issues we could fix them on Thursday or Friday...

What do you think? I want it fixed soon :-)

behdad commented 8 years ago

Code is owned by @typemytype . But indeed, all of us (including Khaled and Cosimo) will be at ATypI, so we should get together and tackle this one there. Looks like Wednesday after dinner might work after all. Let's aim for that.

khaledhosny commented 8 years ago

Wednesday sounds fine.

anthrotype commented 8 years ago

sounds like a plan! πŸ‘

typemytype commented 8 years ago

date set!

gr Frederik www.typemytype.com

On 06 Sep 2016, at 12:42, Cosimo Lupo notifications@github.com wrote:

sounds like a plan! πŸ‘

β€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/typemytype/booleanOperations/issues/32#issuecomment-244913895, or mute the thread https://github.com/notifications/unsubscribe-auth/ABIp1usM5civmLuPBcK8ckU5hf8a_4Frks5qnUN7gaJpZM4JIH_o.

anthrotype commented 8 years ago

I managed somehow to fix the issue, by simply using the updated pyclipper extension!

Also see the other screenshots at https://github.com/typemytype/booleanOperations/issues/27#issuecomment-246126715

screenshot 2016-09-10 18 57 32

pyclipper is also available on PyPI https://pypi.org/project/pyclipper/ with pre-compiled wheels for all Mac, Win and Linux!

I'll send a PR soon πŸ’ƒ

behdad commented 8 years ago

Awesome! Now we need something else to do on Wednesday night!

typemytype commented 8 years ago

Warsaw nightlife ????? with T-shirts showing those tricky unions :)

gr Frederik www.typemytype.com

On 10 Sep 2016, at 21:28, Behdad Esfahbod notifications@github.com wrote:

Awesome! Now we need something else to do on Wednesday night!

β€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/typemytype/booleanOperations/issues/32#issuecomment-246131459, or mute the thread https://github.com/notifications/unsubscribe-auth/ABIp1r2nFxxk0BeZpTbAPgetMqMMoD-bks5qowT6gaJpZM4JIH_o.

behdad commented 8 years ago

I'm looking into arranging a dinner. Stay tuned.

anthrotype commented 6 years ago

https://github.com/googlei18n/noto-fonts/issues/1206

typemytype commented 6 years ago

Could you post the glif xml? Thanks

anthrotype commented 6 years ago

here

<?xml version="1.0" encoding="UTF-8"?>
<glyph name="medial_ra_wa.w3" format="2">
  <advance width="229.0"/>
  <note>
    medial_ra_wa.w3
  </note>
  <outline>
    <contour>
      <point x="296.0" y="-467.0" type="curve" smooth="yes"/>
      <point x="1542.0" y="-465.0" type="line"/>
      <point x="1396.0" y="-393.0" type="line"/>
      <point x="292.0" y="-395.0" type="line" smooth="yes"/>
      <point x="214.0" y="-395.0"/>
      <point x="181.0" y="-349.0"/>
      <point x="181.0" y="-269.0" type="curve" smooth="yes"/>
      <point x="181.0" y="806.0" type="line" smooth="yes"/>
      <point x="181.0" y="885.0"/>
      <point x="214.0" y="931.0"/>
      <point x="292.0" y="931.0" type="curve" smooth="yes"/>
      <point x="1531.0" y="931.0" type="line" smooth="yes"/>
      <point x="1607.0" y="931.0"/>
      <point x="1642.0" y="885.0"/>
      <point x="1642.0" y="806.0" type="curve" smooth="yes"/>
      <point x="1642.0" y="760.0" type="line"/>
      <point x="1731.0" y="760.0" type="line"/>
      <point x="1731.0" y="789.0" type="line" smooth="yes"/>
      <point x="1731.0" y="932.0"/>
      <point x="1663.0" y="1003.0"/>
      <point x="1526.0" y="1003.0" type="curve" smooth="yes"/>
      <point x="296.0" y="1003.0" type="line" smooth="yes"/>
      <point x="159.0" y="1003.0"/>
      <point x="91.0" y="932.0"/>
      <point x="91.0" y="789.0" type="curve" smooth="yes"/>
      <point x="91.0" y="-253.0" type="line" smooth="yes"/>
      <point x="91.0" y="-396.0"/>
      <point x="159.0" y="-467.0"/>
    </contour>
    <contour>
      <point x="1542.0" y="-465.0" type="curve" smooth="yes"/>
      <point x="1658.0" y="-465.0"/>
      <point x="1729.0" y="-381.0"/>
      <point x="1729.0" y="-282.0" type="curve" smooth="yes"/>
      <point x="1729.0" y="-177.0"/>
      <point x="1661.0" y="-90.0"/>
      <point x="1545.0" y="-90.0" type="curve" smooth="yes"/>
      <point x="1433.0" y="-90.0"/>
      <point x="1362.0" y="-171.0"/>
      <point x="1362.0" y="-278.0" type="curve" smooth="yes"/>
      <point x="1362.0" y="-385.0"/>
      <point x="1432.0" y="-465.0"/>
    </contour>
    <contour>
      <point x="1546.0" y="-398.0" type="curve" smooth="yes"/>
      <point x="1482.0" y="-398.0"/>
      <point x="1441.0" y="-348.0"/>
      <point x="1441.0" y="-281.0" type="curve" smooth="yes"/>
      <point x="1441.0" y="-208.0"/>
      <point x="1481.0" y="-157.0"/>
      <point x="1546.0" y="-157.0" type="curve" smooth="yes"/>
      <point x="1610.0" y="-157.0"/>
      <point x="1649.0" y="-210.0"/>
      <point x="1649.0" y="-283.0" type="curve" smooth="yes"/>
      <point x="1649.0" y="-350.0"/>
      <point x="1606.0" y="-398.0"/>
    </contour>
  </outline>
  <lib>
    <dict>
      <key>com.schriftgestaltung.Glyphs.glyph.leftMetricsKey</key>
      <string>medial_ra</string>
      <key>com.schriftgestaltung.Glyphs.glyph.widthMetricsKey</key>
      <string>medial_ra</string>
      <key>com.schriftgestaltung.Glyphs.lastChange</key>
      <string>2017/10/10 16:27:16</string>
    </dict>
  </lib>
</glyph>
anthrotype commented 6 years ago

it does not happen to this glyph, which is very similar in shape:

<?xml version="1.0" encoding="UTF-8"?>
<glyph name="medial_ra_wa.w2" format="2">
  <advance width="229.0"/>
  <note>
    medial_ra_wa.w2
  </note>
  <outline>
    <contour>
      <point x="296.0" y="-467.0" type="curve" smooth="yes"/>
      <point x="1089.0" y="-467.0" type="line"/>
      <point x="959.0" y="-395.0" type="line"/>
      <point x="292.0" y="-395.0" type="line" smooth="yes"/>
      <point x="214.0" y="-395.0"/>
      <point x="181.0" y="-349.0"/>
      <point x="181.0" y="-269.0" type="curve" smooth="yes"/>
      <point x="181.0" y="806.0" type="line" smooth="yes"/>
      <point x="181.0" y="885.0"/>
      <point x="214.0" y="931.0"/>
      <point x="292.0" y="931.0" type="curve" smooth="yes"/>
      <point x="1077.0" y="931.0" type="line" smooth="yes"/>
      <point x="1153.0" y="931.0"/>
      <point x="1188.0" y="885.0"/>
      <point x="1188.0" y="806.0" type="curve" smooth="yes"/>
      <point x="1188.0" y="760.0" type="line"/>
      <point x="1277.0" y="760.0" type="line"/>
      <point x="1277.0" y="789.0" type="line" smooth="yes"/>
      <point x="1277.0" y="932.0"/>
      <point x="1209.0" y="1003.0"/>
      <point x="1072.0" y="1003.0" type="curve" smooth="yes"/>
      <point x="296.0" y="1003.0" type="line" smooth="yes"/>
      <point x="159.0" y="1003.0"/>
      <point x="91.0" y="932.0"/>
      <point x="91.0" y="789.0" type="curve" smooth="yes"/>
      <point x="91.0" y="-253.0" type="line" smooth="yes"/>
      <point x="91.0" y="-396.0"/>
      <point x="159.0" y="-467.0"/>
    </contour>
    <contour>
      <point x="1089.0" y="-467.0" type="curve" smooth="yes"/>
      <point x="1204.0" y="-467.0"/>
      <point x="1275.0" y="-384.0"/>
      <point x="1275.0" y="-283.0" type="curve" smooth="yes"/>
      <point x="1275.0" y="-180.0"/>
      <point x="1206.0" y="-91.0"/>
      <point x="1091.0" y="-91.0" type="curve" smooth="yes"/>
      <point x="976.0" y="-91.0"/>
      <point x="908.0" y="-180.0"/>
      <point x="908.0" y="-283.0" type="curve" smooth="yes"/>
      <point x="908.0" y="-384.0"/>
      <point x="978.0" y="-467.0"/>
    </contour>
    <contour>
      <point x="1092.0" y="-400.0" type="curve" smooth="yes"/>
      <point x="1028.0" y="-400.0"/>
      <point x="988.0" y="-349.0"/>
      <point x="988.0" y="-281.0" type="curve" smooth="yes"/>
      <point x="988.0" y="-210.0"/>
      <point x="1026.0" y="-158.0"/>
      <point x="1092.0" y="-158.0" type="curve" smooth="yes"/>
      <point x="1156.0" y="-158.0"/>
      <point x="1195.0" y="-213.0"/>
      <point x="1195.0" y="-285.0" type="curve" smooth="yes"/>
      <point x="1195.0" y="-350.0"/>
      <point x="1153.0" y="-400.0"/>
    </contour>
  </outline>
  <lib>
    <dict>
      <key>com.schriftgestaltung.Glyphs.glyph.leftMetricsKey</key>
      <string>medial_ra</string>
      <key>com.schriftgestaltung.Glyphs.glyph.widthMetricsKey</key>
      <string>medial_ra</string>
      <key>com.schriftgestaltung.Glyphs.lastChange</key>
      <string>2017/10/10 16:27:16</string>
    </dict>
  </lib>
</glyph>
anthrotype commented 6 years ago

it definitely has something to do with two points from different contours being on top of each other, and one being a tangent point between line/curve, the other being a sharp corner. something like https://github.com/typemytype/booleanOperations/issues/27 -- which we thought was fixed, but apparently not.

typemytype commented 6 years ago

the problem is that the bottom part is not straight, its 2 units of, likely a design mistake...

however it should not happen: but it happens when flatting out that extremely shallow line and points got lost, even when the numbers are bigger.