Hi @mica-92 and @pije76 - sorry for the late reply, only just seen both of your posts!
Here’s my code:
def getangle(deg, sign):
absdeg=signs.find(sign)/2*30+deg
return int(absdeg)
def drawline(inner, outer, absdeg):
# the line will always be from sdist pixels from the origin to edist from the origin at an angle of angle
# angle is: 0=0Li, 330=0Sc, 300=0Sg etc
# conversion rule is mod(540-absdeg,360) to get the angle we want
angle=(540-absdeg)%360
sdist=1024-circles[inner][0]
edist=1024-circles[outer][0]
sl=cmath.rect(sdist,radians(angle))
el=cmath.rect(edist,radians(angle))
draw.line([(1024*rf+sl.real*rf,1024*rf+sl.imag*rf),(1024*rf+el.real*rf,1024*rf+el.imag*rf)], fill='black', width=2*rf)
def getpos(ringo, ringi, sdeg, edeg):
# get the position of the centre of the segment
# eg 3,5,getangle(12,"Pi"),getangle(16,"Pi") will return the middle of the 12-16 segment in the terms ring
sdist=1024-circles[ringi][0]
edist=1024-circles[ringo][0]
midpoint=int((edist-sdist)/2)+sdist
angle=(sdeg+edeg)/2
angle=(540-angle)%360
coor=cmath.rect(midpoint,radians(angle))
return [int((coor.real+1024)*3), int((coor.imag+1024)*3)]
def drawglyph(x, y, glyph, colour, sfont):
draw.text((x,y), glyph, fill=colour, anchor="mm", font=sfont)
def parse_terms(terms):
parsed=[]
for i in range(12):
start=i*30
chunk = terms[i*21:i*21+20]
for j in range(5):
thisterm=chunk[j*4:j*4+4]
end=int(thisterm[0:2])+i*30
plglyph=planet_glyphs[int(planets.find(thisterm[2:4])/2)]
parsed.append([start, end, plglyph])
start=end
return parsed
def placerot(glyph, deg, colour):
boxsize=240
halfbox=int(boxsize/2)
im2=Image.new('RGB', (boxsize,boxsize), (255,255,255))
draw2=ImageDraw.Draw(im2)
draw2.text((halfbox,halfbox), glyph, colour, anchor='mm', font=fontrot2)
# Now work out rotation
# 135 = 45deg, 90=0 deg, 45=-45deg, 5=-85, 175=85, 185=same as 5, 270=0, 315=same as 135
rot=deg
if rot>180:
rot=rot-180
rot=rot-90
im2=im2.rotate(rot, expand=0, fillcolor='white')
# We now have a rotated glyph, so now work out where this goes in the trip segment
segpos=getpos(7, 6, deg, deg)
tup=[segpos[0]-halfbox, segpos[1]-halfbox, segpos[0]+halfbox, segpos[1]+halfbox]
Image.Image.paste(im, im2, tup)
# Initialise
from PIL import Image, ImageDraw, ImageFont
from math import sin, cos, tan, pi, radians, degrees
import cmath
import glob
circles=[[24,2024,True], [128,1920,True], [142,1906,False], [226,1822,True], [298,1750,False], [312,1736,False], [326,1722,True], [462,1586,True], [676,1372,True], [856,1192,True], [1024,1024,False]]
# circles is a list as follows
# Ring numbers:
# 0 Outer circle with signs
# 1 Face ring radius
# 2 Tick mark edge
# 3 Term ring radius
# 4 Long tick mark edge
# 5 Tick mark edge
# 6 Trip ring radius
# 7 Exalt radius
# 8 Rulership radius
# 9 Inner ring
signs="ArTaGeCnLeViLiScSgCpAqPi"
planets="SaJuMaSuVeMeMo"
font36=ImageFont.truetype('C:/Users/Chris/AppData/Local/Microsoft/Windows/Fonts/StarFont_Sans__True_Type.TTF', 36*3, encoding="symb")
font48=ImageFont.truetype('C:/Users/Chris/AppData/Local/Microsoft/Windows/Fonts/StarFont_Sans__True_Type.TTF', 48*3, encoding="symb")
font72=ImageFont.truetype('C:/Users/Chris/AppData/Local/Microsoft/Windows/Fonts/StarFont_Sans__True_Type.TTF', 72*3, encoding="symb")
fontrot=ImageFont.truetype('C:/Users/Chris/AppData/Local/Microsoft/Windows/Fonts/StarFont_Sans__True_Type.TTF', 48*3, encoding="symb")
fontrot2=ImageFont.truetype('C:/Users/Chris/AppData/Local/Microsoft/Windows/Fonts/StarFont_Sans__True_Type.TTF', 60*3, encoding="symb")
planet_glyphs="Sjhsgfd" # Starfont representations of Saturn, Jupiter, Mars, Sun, Venus, Mercury, Moon
sign_glyphs="xcvbnmXCVBNM" # Starfont representations of Aries through to Pisces
# Terms: two types, Egyptian and Ptolemy
# We'll parse each string into a list, but the string format is ddppddppddppddppddpp/ddppddppddppddppddpp/...
# The slash is to separate signs just for readability. The quintuplets are degrees and planet
# eg Aries is 0-6 Jupiter, 6-12 Venus, 12-20 Mercury, 20-25 Mars, 25-30 Saturn, represented as 06Ju12Ve20Me25Ma30Sa
termse="06Ju12Ve20Me25Ma30Sa/08Ve14Me22Ju27Sa30Ma/06Me12Ju17Ve24Ma30Sa/07Ma13Ve19Me26Ju30Sa/06Ju11Ve18Sa24Me30Ma/07Me17Ve21Ju28Ma30Sa/06Sa14Me21Ju28Ve30Ma/07Ma11Ve19Me24Ju30Sa/12Ju17Ve21Me26Sa30Ma/07Me14Ju22Ve26Sa30Ma/07Me12Ve20Ju25Ma30Sa/12Ve16Ju19Me28Ma30Sa"
termsp="06Ju14Ve21Me26Ma30Sa/08Ve15Me22Ju26Sa30Ma/07Me14Ju21Ve25Sa30Ma/06Ma13Ju20Me27Ve30Sa/06Sa13Me19Ve25Ju30Ma/07Me13Ve18Ju24Sa30Ma/06Sa11Ve19Ju24Me30Ma/06Ma14Ju21Ve27Me30Sa/08Ju14Ve19Me25Sa30Ma/06Ve12Me19Ju25Ma30Sa/06Sa12Me20Ve25Ju30Ma/08Ve14Ju20Me26Ma30Sa"
# We need to convert the list of terms into a list of triples of the form [startdeg, enddeg, glyph] so for example, the 3rd term of Gemini for Ptolemaic terms is 14-21 Gemini and is Venus, so this would be [74, 81, "j"]
terms=[parse_terms(termse), parse_terms(termsp)]
# Set up canvas
rf=3 # resize factor for antialiasing
termtype=int(input("Egyptian (0) Ptolemaic (1)? "))
im=Image.new('RGB', (2048*rf, 2048*rf), (255, 255, 255))
draw=ImageDraw.Draw(im)
draw.ellipse((24*rf, 24*rf, 2024*rf, 2024*rf), fill=(255, 255, 255), outline=(0, 0, 0))
# draw the main circles
for coords in circles:
if coords[2]:
draw.ellipse((coords[0]*rf, coords[0]*rf, coords[1]*rf, coords[1]*rf), fill=(255,255,255), outline=(0,0,0), width=2*rf)
# now draw the 12 lines from outer wheel to inner circle
for i in range(12):
drawline(9, 0, i*30)
# now draw tick marks on term ring
for i in range(360):
if i%30 != 0:
oring=5
if i%10 == 0:
oring=4 # long tick
drawline(6, oring, i)
# now draw term segments
chosenterm=terms[termtype] # 0 will give us the Egyptian terms, 1 the Ptolemaic ones
for pos in chosenterm:
drawline(6, 3, pos[1])
# now draw face segments
for i in range(36):
if i%30 !=0:
drawline(3, 1, i*10)
# finally, draw tick marks for face
for i in range(360):
if i%10:
drawline(2, 1, i)
# The lines and circles are done, so now place glyphs
# All glyphs are upright EXCEPT for triplicities - if the angle would be more than 90 deg anticlockwise from the vertical (which happens for Aries to Virgo), subtract from 90
# Trips:
# Font size is 72, and needs to be cusp+6, cusp+13, cusp+20
# eg placerot('j',186,'red'), placerot2('h',193,'red'), placerot2('S',200,'red') using these glyphs as an (incorrectim.re) example as they're the biggest
# So the degree position is signno*30+6, signno*30+13, signno*30+20
if termtype==0:
# Egyptian terms, so we're doing Dorothean triplicities
tripparams=[['red','s','j','S'], ['green', 'g','d','h'], ['darkorange', 'S','f','j'], ['blue', 'g','h','d']] # by element - so fire is red, Sun, Jupiter, Saturn
for i in range(12):
element=i%4
param=tripparams[element]
for j in range(3):
deg=i*30+8+(j*8) # so 7th sign (i=6) maps to 186+0, 186+7, 186+14
if i<6:
index=j+1
else:
index=3-j # Because of the way placerot works, the orientation needs to be reversed for the top half of the chart
placerot(param[index], deg, param[0])
else:
# Ptolemaic terms, so we're using just day and night ruler and a different colour scheme
tripparams=[['red','s','darkred','j'], ['mediumseagreen','g','darkgreen','d'], ['darkorange','S','saddlebrown','f'], ['cornflowerblue','h','darkblue','h']]
for i in range(12):
element=i%4
param=tripparams[element]
for j in range(2):
deg=i*30+11+(j*8) # so 7th sign maps to 190+0, 190+7
if i<6:
index=j*2
else:
index=(1-j)*2
placerot(param[index+1], deg, param[index])
# Inner ring: rulerships
rulers='hgfdsfghjSSj'
for i in range(12):
xy=getpos(9, 8, i*30, (i+1)*30)
colour='red'
if i>9:
colour='blue'
if i<4:
colour='blue'
drawglyph(xy[0], xy[1], rulers[i], colour, font72)
# Exaltations
# No pattern to these so just place directly
exalts=[[0,'s'], [1,'d'], [3,'j'], [5,'f'], [6,'S'], [9,'h'], [11,'g']]
for ex in exalts:
xy=getpos(8, 7, ex[0]*30, (ex[0]+1)*30)
drawglyph(xy[0], xy[1], ex[1], 'black', font72)
# Terms
chosenterm=terms[termtype] # 0 will give us the Egyptian terms, 1 the Ptolemaic ones
for pos in chosenterm:
xy=getpos(6, 3, pos[0], pos[1])
drawglyph(xy[0], xy[1], pos[2], 'black', font36)
# Faces
facelist='hsgfdSj'
for i in range(36):
glyph=facelist[i%7]
xy=getpos(3, 1, i*10, i*10+10)
drawglyph(xy[0], xy[1], glyph, 'black', font72)
# Signs
elcolours=['red', 'green', 'darkorange', 'blue']
for i in range(12):
glyph=sign_glyphs[i]
colour=elcolours[i%4]
xy=getpos(1, 0, i*30, i*30+30)
drawglyph(xy[0], xy[1], glyph, colour, font72)
#Finally, a little dot in the middle
draw.ellipse((1020*rf, 1020*rf, 1028*rf, 1028*rf), fill=(0,0,0), outline=(0,0,0), width=2*rf)
im.resize((2048,2048), Image.LANCZOS)
im.show()
if termtype==0:
im.save('d:/zz/egyptian.png', 'PNG')
else:
im.save('d:/zz/ptolemaic.png', 'PNG')