-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a1783ff
commit 4c4fc66
Showing
3 changed files
with
392 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |