Skip to content

Commit

Permalink
Initial version.
Browse files Browse the repository at this point in the history
  • Loading branch information
alanwatsonforster committed Sep 20, 2023
1 parent a1783ff commit 4c4fc66
Show file tree
Hide file tree
Showing 3 changed files with 392 additions and 0 deletions.
Binary file added .DS_Store
Binary file not shown.
251 changes: 251 additions & 0 deletions airpower/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
print("airpower")

from airpower.draw import *

import numpy as np

import matplotlib
import matplotlib.pyplot as plt
matplotlib.rcParams['figure.figsize'] = [7.5, 10]
plt.rcParams.update({'font.size': 10})

def altitudeband(altitude):
if altitude <= 7:
return "LO"
elif altitude <= 16:
return "ML"
elif altitude <= 25:
return "MH"
elif altitude <= 35:
return "HI"
elif altitude <= 45:
return "VH"
elif altitude <= 60:
return "EH"
else:
return "UH"

class Aircraft:

def __init__(self, name, x, y, facing, altitude):
self.turn = 0
self.name = name
self.x = x
self.y = y
self.facing = facing
self.altitude = altitude
self.saved = []
self._save(0)
self.drawatend()

def __str__(self):
return "%s: (%.2f,%.2f) %03d %02d" % (self.name, self.x, self.y, self.facing, self.altitude)

def _restore(self, i):
self.x, self.y, self.facing, self.altitude = self.saved[i]

def _save(self, i):
if len(self.saved) == i:
self.saved.append(None)
self.saved[i] = (self.x, self.y, self.facing, self.altitude)

def _maxprevturn(self):
return len(self.saved) - 1

def drawflightpath(self, lastx, lasty):
drawlineinhex(lastx, lasty, self.x, self.y, color="lightgrey", linestyle="dashed", zorder=0.5)

def drawbeforeend(self):
drawdartinhex(self.x, self.y, self.facing, dy=-0.02, size=0.5, color="grey")
drawtextinhex(self.x, self.y, self.facing, self.name, dx=-0.3, dy=0.0, size=7, color="grey")
drawtextinhex(self.x, self.y, self.facing, "%2d" % self.altitude, dx=+0.3, dy=0.0, size=7, color="grey")

def drawatend(self):
drawdartinhex(self.x, self.y, self.facing, dy=-0.02, size=0.5)
drawtextinhex(self.x, self.y, self.facing, self.name, dx=-0.3, dy=0.0, size=7)
drawtextinhex(self.x, self.y, self.facing, "%2d" % self.altitude, dx=+0.3, dy=0.0, size=7)

def _H(self):
dx = {
0: +1.00,
30: +1.00,
60: +0.50,
90: +0.00,
120: -0.50,
150: -1.00,
180: -1.00,
210: -1.00,
240: -0.50,
270: -0.00,
300: +0.50,
330: +1.00
}
dy = {
0: +0.00,
30: +0.50,
60: +0.75,
90: +1.00,
120: +0.75,
150: +0.50,
180: +0.00,
210: -0.50,
240: -0.75,
270: -1.00,
300: -0.75,
330: -0.50
}
self.x += dx[self.facing]
self.y += dy[self.facing]

def onedge(self):
if self.x % 1 != 0:
return True
elif self.x % 2 == 0 and self.y % 1 == 0.5:
return True
elif self.x % 2 == 1 and self.y % 1 == 0.0:
return True
else:
return False

def _R(self, pointingchange):
if self.onedge():
if self.facing == 0:
self.y -= 0.5
elif self.facing == 60:
self.x += 0.50
self.y -= 0.25
elif self.facing == 120:
self.x += 0.50
self.y += 0.25
elif self.facing == 180:
self.y += 0.5
elif self.facing == 240:
self.x -= 0.50
self.y += 0.25
elif self.facing == 300:
self.x -= 0.50
self.y -= 0.25
self.facing = (self.facing + 360 - pointingchange) % 360

def _L(self, pointingchange):
if self.onedge():
if self.facing == 0:
self.y += 0.5
elif self.facing == 60:
self.x -= 0.50
self.y += 0.25
elif self.facing == 120:
self.x -= 0.50
self.y -= 0.25
elif self.facing == 180:
self.y -= 0.5
elif self.facing == 240:
self.x += 0.50
self.y -= 0.25
elif self.facing == 300:
self.x += 0.50
self.y += 0.25
self.facing = (self.facing + pointingchange) % 360

def _D(self, altitudechange):
self.altitude -= altitudechange

def _C(self, altitudechange):
self.altitude += altitudechange

def _report(self, s):
print("%s: turn %d: %s" % (self.name, self.turn, s))

def _reportfp(self, s):
print("%s: turn %d: FP %d: %s" % (self.name, self.turn, self.ifp, s))

def start(self, turn, nfp, s):

if turn > self._maxprevturn() + 1:
raise ValueError("turn %d is out of sequence." % turn)

self.turn = turn
self.nfp = nfp
self.ifp = 0
self.ihfp = 0
self.ivfp = 0
self._restore(turn - 1)

self.initialaltitude = self.altitude

self._report("--- start of turn ---")
self._report("%d FPs available." % self.nfp)
self._report("initial altitude = %5.2f (%s)" % (self.altitude, altitudeband(self.altitude)))

if s != "":
self.next(s)

def next(self, s):

lastx = self.x
lasty = self.y
altitudechange = 1

for t in s.split(","):

self.ifp = self.ifp + 1

self._reportfp("movement code is %s." % t)

if t[0] == 'H':
self.ihfp = self.ihfp + 1
elif t[0] == 'D' or t[0] == 'C':
self.ivfp = self.ivfp + 1
else:
raise ValueError("movement code must begin with H, D, or C.")

for c in t:
if c == 'H':
self._H()
elif c == 'C':
self._C(altitudechange)
altitudechange = 1
elif c == 'D':
self._D(altitudechange)
altitudechange = 1
elif c == '¼':
altitudechange = 1/4
elif c == '½':
altitudechange = 1/2
elif c == '¾':
altitudechange = 3/4
elif c == 'L':
self._L(30)
elif c == 'R':
self._R(30)
else:
raise ValueError("unknown movement code %s" % c)

self.drawflightpath(lastx, lasty)
lastx = self.x
lasty = self.y

self._reportfp("%d HFPs and %d VFPs used." % (self.ihfp, self.ivfp))

if self.ifp < self.nfp:

self._reportfp("%d FPs remaining." % (self.nfp - self.ifp))

self.drawbeforeend()

elif self.ifp == self.nfp:

self._report("all %d FPs used." % (self.nfp))

self._report("final altitude = %5.2f (%s)" % (self.altitude, altitudeband(self.altitude)))
if altitudeband(self.initialaltitude) != altitudeband(self.altitude):
self._report("altitude band changed from %s to %s." % (altitudeband(self.initialaltitude), altitudeband(self.altitude)))
self._report("--- end of turn ---")

self._save(self.turn)

self.drawatend()

else:

raise ValueError("only %d FPs are available." % self.nfp)
141 changes: 141 additions & 0 deletions airpower/draw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
print("airpower.draw")

import numpy as np

import matplotlib
import matplotlib.pyplot as plt
matplotlib.rcParams['figure.figsize'] = [7.5, 10]
plt.rcParams.update({'font.size': 10})

def cosd(x):
return np.cos(np.radians(x))
def sind(x):
return np.sin(np.radians(x))

def hextophysical(x,y):
return x * np.sqrt(3/4), y
def physicaltohex(x,y):
return x / np.sqrt(3/4), y

def drawhex(x, y, size=1, color="lightgrey"):
# size is inscribed diameter
azimuths = np.array((0, 60, 120, 180, 240, 300, 0))
xvertices = x + 0.5 * size * cosd(azimuths) / cosd(30)
yvertices = y + 0.5 * size * sind(azimuths) / cosd(30)
plt.plot(xvertices, yvertices, color=color, zorder=0)

def drawdot(x, y, size=1, facing=0, dx=0, dy=0, color="black"):
x = x + dx * sind(facing) + dy * cosd(facing)
y = y - dx * cosd(facing) + dy * sind(facing)
plt.plot(x, y, marker=".", color=color, zorder=1)

def drawsquare(x, y, facing, size=1, dx=0, dy=0, color="black"):
# size is diagonal
x = x + dx * sind(facing) + dy * cosd(facing)
y = y - dx * cosd(facing) + dy * sind(facing)
azimuths = np.array((0, 90, 180, 270, 0))
if facing == None:
facing = 45
xvertices = x + 0.5 * size * cosd(azimuths + facing)
yvertices = y + 0.5 * size * sind(azimuths + facing)
plt.plot(xvertices, yvertices, color=color, zorder=1)

def drawline(x0, y0, x1, y1, color="black", linestyle="solid", zorder=1):
plt.plot((x0, x1), (y0, y1), linestyle=linestyle, color=color, zorder=zorder)

def drawarrow(x, y, facing, size=1.0, dx=0, dy=0, color="black"):
# size is length
x = x + dx * sind(facing) + dy * cosd(facing)
y = y - dx * cosd(facing) + dy * sind(facing)
x0 = x - 0.5 * size * cosd(facing)
y0 = y - 0.5 * size * sind(facing)
x1 = x0 + 1.0 * size * cosd(facing)
y1 = y0 + 1.0 * size * sind(facing)
plt.arrow(x0, y0, x1 - x0, y1 - y0, width=0.02*size, head_width=0.1*size, color=color, length_includes_head=True, zorder=1)

def drawdart(x, y, facing, size=1.0, dx=0, dy=0, color="black"):
# size is length
x = x + dx * sind(facing) + dy * cosd(facing)
y = y - dx * cosd(facing) + dy * sind(facing)
x0 = x - 0.5 * size * cosd(facing)
y0 = y - 0.5 * size * sind(facing)
x1 = x0 + 1.0 * size * cosd(facing)
y1 = y0 + 1.0 * size * sind(facing)
plt.arrow(x0, y0, x1 - x0, y1 - y0, width=0.02, head_length=size, head_width=0.5*size, color=color, length_includes_head=True, zorder=1)

def drawtext(x, y, facing, s, size=10, dx=0, dy=0, color="black"):
x = x + dx * sind(facing) + dy * cosd(facing)
y = y - dx * cosd(facing) + dy * sind(facing)
plt.text(x, y, s, size=size, rotation=facing - 90,
color=color,
horizontalalignment='center',
verticalalignment='center_baseline',
rotation_mode="anchor")

def drawhexinhex(x, y, **kwargs):
drawhex(*hextophysical(x, y), **kwargs)

def drawdotinhex(x, y, **kwargs):
drawdot(*hextophysical(x, y), **kwargs)

def drawsquareinhex(x, y, pointing, **kwargs):
drawsquare(*hextophysical(x, y), pointing, **kwargs)

def drawlineinhex(x0, y0, x1, y1, **kwargs):
drawline(*hextophysical(x0, y0), *hextophysical(x1, y1), **kwargs)

def drawarrowinhex(x, y, pointing, **kwargs):
drawarrow(*hextophysical(x, y), pointing, **kwargs)

def drawdartinhex(x, y, pointing, **kwargs):
drawdart(*hextophysical(x, y), pointing, **kwargs)

def drawtextinhex(x, y, pointing, s, **kwargs):
drawtext(*hextophysical(x, y), pointing, s, **kwargs)

def drawhexgrid(sx, sy, nx, ny):
matplotlib.rcParams['figure.figsize'] = [nx, ny * np.sqrt(3/4)]
plt.figure()
plt.axis('equal')
plt.axis('off')
for ix in range(sx, sx + nx):
for iy in range(sy, sy + ny):
drawhexinhex(ix, iy + 0.5 * (ix % 2))

def drawinhex(x, y, facing):
#drawsquareinhex(x, y, facing, size=0.866)
drawdartinhex(x, y, facing, dy=-0.02, size=0.5)
drawtextinhex(x, y, facing, "F1", dx=-0.3, dy=0.0, size=7)
drawtextinhex(x, y, facing, "10", dx=+0.3, dy=0.0, size=7)

def numbertohex(n):

# The hexes in the TSOH maps are numbered as XXYY, where XX is the column number and YY is the row number, in this sense:
#
# 0201
# 0101 0301
# 0202
# 0102 0302
# 0203
# 0103 0303

x = n // 100
y = - (n % 100)
if x % 2 == 1:
y -= 0.5
return x, y

def drawhexnumber(n):
drawtextinhex(*numbertohex(n), 90, "%04d" % n, dy=0.3, size=7, color="grey")

def drawmapA1():
drawhexgrid(11,-16,19,15)
for x in range(11,30):
for y in range(1,16):
drawhexnumber(x * 100 + y)

def drawmapC1():
drawhexgrid(51,-16,19,15)
for x in range(51,70):
for y in range(1,16):
drawhexnumber(x * 100 + y)

0 comments on commit 4c4fc66

Please sign in to comment.