Skip to content

Instantly share code, notes, and snippets.

@mdunschen
Last active April 12, 2016 19:14
Show Gist options
  • Save mdunschen/8cb1b6ef71cd9d9d0305 to your computer and use it in GitHub Desktop.
Save mdunschen/8cb1b6ef71cd9d9d0305 to your computer and use it in GitHub Desktop.
# 60 pieces for one lampshade, ratio of short arm to long neck: 16/25
# needs more spacing in y direction, fewer elements in x direction
import math
s60 = math.sqrt(3.0) / 2.0
c60 = 0.5
rad = 10 # rad of all arcs
l0 = 90 # neck
l1 = (16.0 / 25.0) * l0 # legs and arms
mrad = 1.5
# tiling, number of pieces in x an y direction
nnx = 10
nny = 13
totalpieces = 60 # for one lampshade we only need 60 pieces
# the spacing
xstep = l1 + 2 * rad + 1.0
ystep = l0 + l1 * 0.5
xphase = l1 * 0.95
yphase = 2 * rad + 1.0
class SVGWriter:
def __init__(self, fn):
self.f = open(fn, "w")
self.f.write(
'''<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="297mm" height="420mm" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<!-- The dimensions for this rectange are given in real
world units. -->
<rect x="0mm" y="0mm" width="297mm" height="420mm" fill="none" stroke="blue" stroke-width="1mm"/>\n''')
self.pathopened = False
self.lastP = None
self.trans = False
def openPath(self):
assert not self.pathopened
self.p = ['<path d="']
self.pathopened = True
def moveTo(self, p):
assert self.pathopened
self.p.append("M %f,%f" % (p.u, p.v))
self.lastP = p
def lineTo(self, p):
assert self.pathopened
self.p.append("L %f,%f" % (p.u, p.v))
self.lastP = p
def arcTo(self, p, c):
assert self.pathopened
rad = (p - c).Len()
assert abs(rad - (self.lastP - c).Len()) < 1.e-5, (rad, (self.lastP - c).Len())
xaxisrot = math.acos((p - c).Norm().Dot(P(1,0)))
large_flag, sweep_flag = 0, 0
self.p.append("A %f,%f %f %d,%d %f,%f" % (rad, rad, xaxisrot, large_flag, sweep_flag, p.u, p.v))
self.lastP = p
def closePath(self):
self.p.append('" fill="none" stroke="blue" stroke-width="0.01mm" />')
self.f.write('%s\n' % ' '.join(self.p))
self.p = None
self.pathopened = False
def popTransform(self):
self.f.write('</g>\n')
self.f.write('</g>\n')
self.trans = False
def pushTransform(self, trans, rot):
assert not self.trans
self.trans = True
self.f.write('<g transform="translate(%f, %f)">\n' % trans)
self.f.write('<g transform="rotate(%f)">\n' % rot)
def close(self):
assert self.p == None
self.f.write('</svg>\n')
self.f.close()
class P:
def __init__(self, u, v):
self.u = u
self.v = v
def __add__(self, pp):
return P(self.u + pp.u, self.v + pp.v)
def __sub__(self, pp):
return P(self.u - pp.u, self.v - pp.v)
def Len(self):
return math.sqrt(self.u * self.u + self.v * self.v)
def Dot(self, p):
return self.u * p.u + self.v * p.v
def Norm(self):
l = self.Len()
return P(self.u / l, self.v / l)
def APerp(self):
return P(-self.v, self.u)
def CPerp(self):
return P(self.v, -self.u)
def draw_base(w, c):
p0 = c + P(rad, rad)
p1 = p0 + P(l1 - rad, 0)
# arc connecting p1, p2, p3
p2 = p1 + P(rad, -rad)
p3 = p2 + P(-rad, -rad)
p4 = p3 - P(l1 - rad/c60, 0)
p5 = p4 + P((l1 - 2 * rad) * c60, -(l1 - 2 * rad) * s60)
p5_c = p5 + P(rad * c60, -rad * s60).CPerp()
p6 = p5 + P(rad * c60, -rad * s60) + P(rad * c60, -rad * s60).CPerp()
p7 = p5 + P(2 * rad * c60, -2 * rad * s60).CPerp()
p8 = c - P(0, 2 * rad)
p9 = P(c.u - p7.u, p7.v)# mirror of p7
p9_c = P(c.u - p5_c.u, p5_c.v)
p10 = P(c.u - p6.u, p6.v)# mirror of p6
p11 = P(c.u - p5.u, p5.v)# mirror of p5
p12 = P(c.u - p4.u, p4.v)
p13 = P(c.u - p3.u, p3.v)
p14 = P(c.u - p2.u, p2.v)
p15 = P(c.u - p1.u, p1.v)
p16 = P(c.u - p0.u, p0.v)
p17 = p16 + P(0, l0 - 2 * rad)
p18 = c + P(0, l0)
p19 = P(c.u - p17.u, p17.v)
p20 = p0
w.openPath()
w.moveTo(p0)
w.lineTo(p1)
w.arcTo(p2, P(p1.u, p1.v - rad))
w.arcTo(p3, P(p1.u, p1.v - rad))
w.lineTo(p4)
w.lineTo(p5)
w.arcTo(p6, p5_c)
w.arcTo(p7, p5_c)
w.lineTo(p8)
w.lineTo(p9)
w.arcTo(p10, p9_c)
w.arcTo(p11, p9_c)
w.lineTo(p12)
w.lineTo(p13)
w.arcTo(p14, p13 + P(0, rad))
w.arcTo(p15, p14 + P(0, rad))
w.lineTo(p16)
w.lineTo(p17)
w.arcTo(p18, c + P(0, l0 - rad))
w.arcTo(p19, c + P(0, l0 - rad))
w.lineTo(p20)
w.closePath()
# 5 circles for mounting
for cc in [c + P(l1, 0), c + P(-l1, 0), p5_c, p9_c, c + P(0, l0 - rad)]:
w.openPath()
w.moveTo(cc + P(mrad, 0))
w.arcTo(cc + P(0, -mrad), cc)
w.arcTo(cc + P(-mrad, 0), cc)
w.arcTo(cc + P(0, mrad), cc)
w.arcTo(cc + P(mrad, 0), cc)
w.closePath()
if __name__ == "__main__":
w = SVGWriter("lamp_base.svg")
c0 = P(0, 0)
xmargin = l1 + rad + 5.0
ymargin = 1.5 * l1 + 1.0
yp = 0
for j in range(nny):
if totalpieces <= 0:
break
xp = 0
if j % 2:
xp += xphase
for i in range((j%2 and nnx) or (nnx + 1)):
if i % 2:
w.pushTransform((xp + xmargin, yp + yphase + ymargin), 180)
else:
w.pushTransform((xp + xmargin, yp + ymargin), 0)
draw_base(w, c0)
totalpieces -= 1
w.popTransform()
if totalpieces <= 0:
break
xp += xstep
yp += ystep
w.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment