Skip to content

Instantly share code, notes, and snippets.

@simoncozens
Last active January 26, 2021 09:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simoncozens/e06e945994153210afba1684c70b2744 to your computer and use it in GitHub Desktop.
Save simoncozens/e06e945994153210afba1684c70b2744 to your computer and use it in GitHub Desktop.
# Dot a Glyph
# Copyright 2019 Simon Cozens
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from beziers.path import BezierPath
from beziers.path.representations.fontparts import FontParts
from fontParts.world import *
from beziers.point import Point
from beziers.path.geometricshapes import Circle
import sys
dotradius = 5
dotspacing = 8
drawarrows = True
arrowvector = Point(15,15)
fontpath = "StrokeTest-Regular.ufo"
glyphname = "A"
height = 800
vmargin = 200
font = OpenFont(fontpath)
paths = FontParts.fromFontpartsGlyph(font[glyphname])
print("""<?xml version="1.0" standalone="no"?>
<svg width="1000px" height="%spx" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="arrowhead" viewBox="0 0 10 10" refX="3" refY="5"
markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
</defs>""" % (height + vmargin))
def pt2svg(pt): return "%i %i " % (pt.x, height-pt.y)
def path2svg(segs):
svgpath = "M " + pt2svg(segs[0].start)
for j in range(0,len(segs)):
if segs[j].order == 2:
svgpath = svgpath + " L "
elif segs[j].order == 3:
svgpath = svgpath + " Q "
elif segs[j].order == 4:
svgpath = svgpath + " C "
svgpath += ", ".join([ pt2svg(x) for x in segs[j][1:] ])
# if paths[i].closed: # It isn't.
# svgpath += "Z"
return svgpath
if drawarrows:
for i in range(0,len(paths)):
arrowP = paths[i].clone()
arrowP.translate(arrowvector)
arrowSeg, _ = (arrowP.asSegments())[0].splitAtTime(0.25)
s = arrowSeg.start
print('<text x="%s" y="%s">%i</text>' % (s.x+5.0,height-(s.y+5.0),1+i))
print('<path d="%s" stroke="black" stroke-width="2" fill="transparent" marker-end="url(#arrowhead)"/>\n' % path2svg([arrowSeg]))
splitlist = []
for i in range(0,len(paths)):
for j in range(i+1,len(paths)):
one = paths[i]
two = paths[j]
for s1 in one.asSegments():
for s2 in two.asSegments():
for inter in s1.intersections(s2):
splitlist.append((inter.seg1,inter.t1))
splitlist.append((inter.seg2,inter.t2))
for path in paths:
path.splitAtPoints(splitlist)
for i in range(0,len(paths)):
segs = paths[i].asSegments()
for s in segs:
print('<path d="%s" fill="red"/>' % path2svg(Circle(dotradius, origin=s.start).asSegments()))
if s.length > dotspacing*dotradius:
closeEnough = int(s.length/(dotspacing*dotradius))
samples = s.regularSample(closeEnough)
for p in samples[1:]:
print('<path d="%s"/>' % path2svg(Circle(dotradius, origin=p).asSegments()))
print('<path d="%s" fill="red"/>' % path2svg(Circle(dotradius, origin=segs[-1].end).asSegments()))
segs = paths[i].asSegments()
print('<path d="%s" stroke="grey" stroke-width="1" fill="transparent"/>\n' % path2svg(segs))
print("</svg>\n")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment