def _round_path(
    path: pathops.Path, round: Callable[[float], float] = otRound
) -> pathops.Path:
    rounded_path = pathops.Path()
    for verb, points in path:
        rounded_path.add(verb, *((round(p[0]), round(p[1])) for p in points))
    return rounded_path
Example #2
0
 def __init__(self, subject: VMobject, clip: VMobject, **kwargs):
     super().__init__(**kwargs)
     outpen = pathops.Path()
     pathops.difference(
         [_convert_vmobject_to_skia_path(subject)],
         [_convert_vmobject_to_skia_path(clip)],
         outpen.getPen(),
     )
     _convert_skia_path_to_vmobject(outpen, self)
Example #3
0
 def __init__(self, *vmobjects: VMobject, **kwargs):
     if len(vmobjects) < 2:
         raise ValueError("At least 2 mobjects needed for Union.")
     super().__init__(**kwargs)
     outpen = pathops.Path()
     paths = [
         _convert_vmobject_to_skia_path(vmobject) for vmobject in vmobjects
     ]
     pathops.union(paths, outpen.getPen())
     _convert_skia_path_to_vmobject(outpen, self)
Example #4
0
 def __init__(self, *vmobjects: VMobject, **kwargs):
     if len(vmobjects) < 2:
         raise ValueError("At least 2 mobjects needed for Exclusion.")
     super().__init__(**kwargs)
     outpen = pathops.Path()
     pathops.xor(
         [_convert_vmobject_to_skia_path(vmobjects[0])],
         [_convert_vmobject_to_skia_path(vmobjects[1])],
         outpen.getPen(),
     )
     new_outpen = outpen
     for _i in range(2, len(vmobjects)):
         new_outpen = pathops.Path()
         pathops.xor(
             [outpen],
             [_convert_vmobject_to_skia_path(vmobjects[_i])],
             new_outpen.getPen(),
         )
         outpen = new_outpen
     _convert_skia_path_to_vmobject(outpen, self)
Example #5
0
def skia_path(svg_cmds: SVGCommandSeq, fill_rule: str) -> pathops.Path:
    try:
        fill_type = _SVG_FILL_RULE_TO_SKIA_FILL_TYPE[fill_rule]
    except KeyError:
        raise ValueError(f"Invalid fill rule: {fill_rule!r}")
    sk_path = pathops.Path(fillType=fill_type)
    for cmd, args in svg_cmds:
        if cmd not in _SVG_CMD_TO_SKIA_FN:
            raise ValueError(f'No mapping to Skia for "{cmd} {args}"')
        _SVG_CMD_TO_SKIA_FN[cmd](sk_path, *args)
    return sk_path
Example #6
0
def _convert_vmobject_to_skia_path(vmobject: VMobject) -> pathops.Path:
    path = pathops.Path()
    subpaths = vmobject.get_subpaths_from_points(vmobject.get_all_points())
    for subpath in subpaths:
        quads = vmobject.get_bezier_tuples_from_points(subpath)
        start = subpath[0]
        path.moveTo(*start[:2])
        for p0, p1, p2 in quads:
            path.quadTo(*p1[:2], *p2[:2])
        if vmobject.consider_points_equals(subpath[0], subpath[-1]):
            path.close()
    return path
Example #7
0
def skia_path(shape: SVGShape):
    path = (
        shape.as_path().explicit_lines()  # hHvV => lL
        .absolute(inplace=True).arcs_to_cubics(inplace=True))

    sk_path = pathops.Path()
    for cmd, args in path:
        if cmd not in _SVG_CMD_TO_SKIA_FN:
            raise ValueError(f'No mapping to Skia for "{cmd}"')
        _SVG_CMD_TO_SKIA_FN[cmd](sk_path, *args)

    return sk_path
Example #8
0
def ttfont_glyph_to_skia_path(glyph_name: str, tt_font: ttFont.TTFont) -> pathops.Path:
    """
    Converts fontTools.ttLib.TTFont glyph to a pathops.Path object
    by glyph name.  During this conversion, all composite paths are
    decomposed.
    """
    glyf_table = tt_font["glyf"]
    glyph_set: ttFont._TTGlyphSet = tt_font.getGlyphSet()
    tt_glyph = glyf_table[glyph_name]
    skia_path = pathops.Path()
    skia_path_pen = skia_path.getPen()

    if tt_glyph.isComposite():
        decompose_pen = DecomposingRecordingPen(glyph_set)
        glyph_set[glyph_name].draw(decompose_pen)
        decompose_pen.replay(skia_path_pen)
        return skia_path
    else:
        glyph_set[glyph_name].draw(skia_path_pen)
        return skia_path
Example #9
0
def decomposeAndRemoveOverlap(font, glyphName):

    glyfTable = font["glyf"]
    glyphSet = font.getGlyphSet()

    # record TTGlyph outlines without components
    dcPen = DecomposingRecordingPen(glyphSet)
    glyphSet[glyphName].draw(dcPen)

    # replay recording onto a skia-pathops Path
    path = pathops.Path()
    pathPen = path.getPen()
    dcPen.replay(pathPen)

    # remove overlaps
    path.simplify()

    # create new TTGlyph from Path
    ttPen = TTGlyphPen(None)
    path.draw(ttPen)
    glyfTable[glyphName] = ttPen.glyph()
Example #10
0
src = sys.argv[1]
dst = sys.argv[2]

with TTFont(src) as f:
    glyfTable = f["glyf"]
    glyphSet = f.getGlyphSet()

    for glyphName in glyphSet.keys():
        if not glyfTable[glyphName].isComposite():
            continue

        # record TTGlyph outlines without components
        dcPen = DecomposingRecordingPen(glyphSet)
        glyphSet[glyphName].draw(dcPen)

        # replay recording onto a skia-pathops Path
        path = pathops.Path()
        pathPen = path.getPen()
        dcPen.replay(pathPen)

        # remove overlaps
        path.simplify()

        # create new TTGlyph from Path
        ttPen = TTGlyphPen(None)
        path.draw(ttPen)
        glyfTable[glyphName] = ttPen.glyph()

    f.save(dst)
def skPathFromGlyph(glyphName: str, glyphSet: _TTGlyphMapping) -> pathops.Path:
    path = pathops.Path()
    pathPen = path.getPen(glyphSet=glyphSet)
    glyphSet[glyphName].draw(pathPen)
    return path