from graphics import *
from math import cos,acos,sin,tan,pi
n=10 # Anzahl Zacken
alpha = 45 # Winkel bei A
win = GraphWin("Faltsterne", 800, 800)
win.setCoords(-1.2,-1.2,1.2,1.2) # Koordinatensystem setzen
# Umrechnung Grad -> rad
def rad(grad):
return grad/180*pi
# Umrechung rad -> Grad
def deg(r):
return r/pi*180
# Punkt auf dem Einheitskreis, evtl. mit anderem Radius und anderem Zentrum
def palpha(grad, r=1.0, c=Point(0,0)):
x = r*cos(rad(grad))+c.getX()
y = r*sin(rad(grad))+c.getY()
return Point(x,y) # Resultat zurückgeben
# Summe zweier Punkte (Vektoraddition)
def add(a,b):
return Point(a.getX()+b.getX(), a.getY()+b.getY())
# Punkt Strecken am Ursprung mit Faktor l
def mul(a,l):
return Point(a.getX()*l, a.getY()*l)
# Vektordifferenz
def sub(a,b):
return add(a,mul(b,-1))
# Lineare inerpolation zweier Punkte: a + t*(b-a)
def interp(a,b,t):
return add(mul(a,1.0-t), mul(b,t))
# Rechtwinkliger Vektor (Drehung um +90 Grad)
def perp(a):
return Point(-a.getY(), a.getX())
# Länge eines Vektors (bzw. Abstand vom Ursprung)
def norm(a):
return (a.getX()**2+a.getY()**2)**0.5
# Vektor zwischen zwei Punkten
def vec(a,b):
return sub(b,a)
# Skalarprodukt
def dot(a,b):
return a.getX()*b.getX()+a.getY()*b.getY()
# Winkel zwischen zwei Vektoren
def vangle(a,b):
return deg(acos(dot(a,b)/norm(a)/norm(b)))
# Rotation des Punktes p um Zentrum z mit Winkel alpha
def rotate(p,z,alpha):
print(p,z,alpha)
pp = sub(p,z)
print(pp)
pp = add(mul(palpha(alpha), pp.getX()), mul(palpha(alpha+90), pp.getY()))
return add(pp, z)
# Schnittpunkt der beiden Geraden l1, l2 (definiert als Line(p1, p2))
def intersect(l1, l2):
n1 = perp(vec(l1.getP1(), l1.getP2()))
d1 = -dot(n1, l1.getP1())
v2 = vec(l2.getP1(), l2.getP2())
t = (-d1-dot(l2.getP1(),n1))/dot(v2,n1)
return add(l2.getP1(), mul(v2,t))
# Header einer SVG-Datei, A4-Format
def svgHeader():
return """
"""
# Skalieren des Einheitskreises auf Grösse A4, zum Drucken
def toa4(points):
return [add(mul(p,95), Point(105,148.5)) for p in points]
# Pfad in SVG
def svgPath(points, style=0, closed=False):
points = toa4(points)
style = ['fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1',\
'fill:none;stroke:#000000;stroke-width:0.20974073;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.20974073,0.62922219;stroke-dashoffset:0;stroke-opacity:1'][style]
res = f"M {points[0].getX()} {points[0].getY()}"
for p in points[1:]:
res += f" L {p.getX()} {p.getY()}"
if closed:
res += " z"
res = f''
return res
# Gestrichelter Pfad (effiktive Striche, für Laserschnittvorlage)
def dottedPath(points, closed=False):
res = ""
if closed:
points = points+[points[0]]
for i in range(len(points)-1):
l = norm(vec(points[i], points[i+1]))*95
num = int(l/2)
for j in range(num):
res += svgPath([interp(points[i], points[i+1], j/num), interp(points[i], points[i+1], (j+0.5)/num)])
return res
##############
# Berechnung #
##############
beta = 180/n
a = Point(-1,0)
z = Point(0,0)
# Punkt c = l*cis(gamma), (l sin(beta))/(1-l cos(beta)) = tan(alpha)
l = tan(rad(alpha))/(sin(rad(beta))+tan(rad(alpha))*cos(rad(beta)))
c = palpha(180-beta, l)
# Umkreismittelpunkt o = (0.5, oy), ac dot (oy-(0.5(a+c)) = 0, bzw. oy = acm+t*acperp
acm = interp(a,c,0.5)
acperp = mul(perp(vec(a,c)),-1)
t = (-0.5-acm.getX())/acperp.getX()
o = add(acm, mul(acperp,t))
# Schnittpunkt mit z+t*acperp: |o - (z+t*acperp)| = orad
t = 2*(o.getX()*acperp.getX()+o.getY()*acperp.getY())/(norm(acperp)**2)
zz = mul(acperp,t)
faltwinkel = 2*vangle(vec(a,c), vec(a,zz))
faltlinie = Line(z, palpha(180-beta))
faltdir = rotate(z,a,alpha+faltwinkel)
d = intersect(faltlinie, Line(a, faltdir))
schnittlinie = Line(a, d)
# Konstruktion anzeigen
schnittlinie.draw(win)
faltlinie.draw(win)
Circle(faltdir,0.01).draw(win)
Line(mul(acperp, -1.0),acperp).draw(win)
Circle(o,norm(o)).draw(win) # Ortsbogen
Circle(zz,0.01).draw(win)
Circle(d,0.01).draw(win)
Polygon([a,z,c]).draw(win)
Polygon([a,zz,c]).draw(win)
rcout = norm(d) # Entfernung des Punktes C' (Blattrand, wo der Einschnitt ist)
rcin = norm(c) # Entfernung von C (Bis wohin eingeschnitten werden soll)
svg = svgHeader()
pts = []
pts2 = []
for i in range(n):
pts.append(palpha(i*360/n+180))
pts.append(mul(palpha((i+0.5)*360/n+180),rcout))
#svg+=dottedPath([z,pts[-2]])
svg+=dottedPath([z,pts[-2]])
l = Line(palpha(i*360/n+180), mul(palpha((i+0.5)*360/n+180),rcout))
l.setWidth(2)
l.setOutline("red")
l.draw(win)
l = Line(palpha((i+1)*360/n+180), mul(palpha((i+0.5)*360/n+180),rcout))
l.setWidth(2)
l.setOutline("red")
l.draw(win)
strich = [mul(palpha((i+0.5)*360/n+180),rcin), mul(palpha((i+0.5)*360/n+180),rcout)]
pts2+= [pts[-2],strich[0]]
svg += svgPath(strich)
svg += dottedPath([z,strich[0]])
l = Line(strich[0], strich[1])
l.setWidth(2)
l.setOutline("red")
l.draw(win)
svg += svgPath(pts, 0, True)
svg += dottedPath(pts2, True)
azr = norm(vec(a,zz))
czr = norm(vec(c,zz))
pts = []
for i in range(n):
pts += [mul(palpha(i*360/n+180),azr), mul(palpha((i+0.5)*360/n+180),czr)]
l = Line(pts[-2], pts[-1])
l.setWidth(2)
l.setOutline("green")
l.draw(win)
l = Line(mul(palpha((i+1)*360/n+180),azr), mul(palpha((i+0.5)*360/n+180),czr))
l.setWidth(2)
l.setOutline("green")
l.draw(win)
svg += svgFooter()
with open(f"n{n}-a{alpha}.svg", "w") as f:
f.write(svg)
win.getMouse() # Pause to view result
win.close() # Close window when done