Module MAPLEAF.Rocket.boatTail
Expand source code
import math
from . import AeroFunctions
from MAPLEAF.Motion import AeroParameters, ForceMomentSystem, Vector
from MAPLEAF.Rocket import BodyComponent, FixedMass
from MAPLEAF.Rocket.noseCone import (computeSubsonicPolyCoeffs,
computeTransonicPolyCoeffs,
getSupersonicPressureDragCoeff_Hoerner)
__all__ = [ "Transition", "BoatTail" ]
class Transition(FixedMass, BodyComponent):
''' Models a conical diameter transition (growing or shrinking) '''
def __init__(self, *args):
'''
Two possible sets of inputs:
1. Initialization as a regular, dictionary-defined rocket component:
* args = (componentDictReader, rocket, stage)
* Expected classes: (`MAPLEAF.IO.SubDictReader`, `MAPLEAF.Rocket.Rocket`, `MAPLEAF.Rocket.Stage`)
2. Manual initialization:
* args = (startDiameter, endDiameter, length, position, inertia, rocket, stage, name, surfaceRoughness)
* Expected classes: (float, float, float, `MAPLEAF.Motion.Vector`, `MAPLEAF.Motion.Inertia`, `MAPLEAF.Rocket.Rocket`, `MAPLEAF.Rocket.Stage`, str, float)
'''
if len(args) == 3:
# Classic initialization from componentDictReader
componentDictReader, rocket, stage = args
FixedMass.__init__(self, componentDictReader, rocket, stage)
self.length = componentDictReader.getFloat("length")
self.startDiameter = componentDictReader.getFloat("startDiameter")
self.endDiameter = componentDictReader.getFloat("endDiameter")
self.surfaceRoughness = componentDictReader.tryGetFloat("surfaceRoughness", defaultValue=self.rocket.surfaceRoughness)
else:
# Initialization from parameters passed in
self.startDiameter, self.endDiameter, self.length, self.position, self.inertia, self.rocket, self.stage, self.name, self.surfaceRoughness = args
self._precomputeGeometry()
def _precomputeGeometry(self):
# Calculate basic areas
self.topArea = self.startDiameter**2 * math.pi/4
self.bottomArea = self.endDiameter**2 * math.pi/4
self.frontalArea = abs(self.topArea - self.bottomArea)
# Calculate surface area, volume, CP
if self.length == 0:
self.wettedArea = 0.0
self.volume = 0.0
self.CPLocation = self.position
else:
maxDiameter = max(self.startDiameter, self.endDiameter)
minDiameter = min(self.startDiameter, self.endDiameter)
hypotenuseSlope = ((maxDiameter - minDiameter) / self.length)
# Boat tail is a truncated cone
# Compute surface area of non-truncated cone starting at top of boattail
baseConeHeight = maxDiameter / hypotenuseSlope
baseHypotenuse = math.sqrt((maxDiameter/2)**2 + baseConeHeight**2)
fullConeSurfaceArea = math.pi * (maxDiameter/2) * baseHypotenuse
# Compute surface area of non-truncated cone starting at bottom of boattail
tipConeHeight = minDiameter / hypotenuseSlope
tipHypotenuse = math.sqrt((minDiameter/2)**2 + tipConeHeight**2)
tipConeSurfaceArea = math.pi * (minDiameter/2) * tipHypotenuse
# Surface area is the difference b/w the two
self.wettedArea = fullConeSurfaceArea - tipConeSurfaceArea
# Volume calculation uses same method as surface area calculation above
fullConeVolume = self.topArea * baseConeHeight / 3
tipConeVolume = self.bottomArea * tipConeHeight / 3
self.volume = fullConeVolume - tipConeVolume
# Calculate CP
self.CPLocation = AeroFunctions.Barrowman_GetCPLocation(self.length, self.topArea, self.bottomArea, self.volume, self.position)
# Calc aspect ratio
if self.startDiameter == self.endDiameter:
aspectRatio = 100 # Avoid division by zero
else:
aspectRatio = self.length / abs(self.startDiameter - self.endDiameter)
# Precompute drag properties
if self.endDiameter <= self.startDiameter:
# Calculate Cd multiplier based on aspect ratio - Niskanen Eqn 3.88
if aspectRatio < 1:
self.CdAdjustmentFactor = 1
elif aspectRatio < 3:
self.CdAdjustmentFactor = (3 - aspectRatio) / 2
else:
self.CdAdjustmentFactor = 0
else:
if self.length == 0:
coneHalfAngle = math.pi/2
else:
coneHalfAngle = math.atan(abs(self.startDiameter - self.endDiameter)/2 / self.length)
self.coneHalfAngle = coneHalfAngle
self.SubsonicCdPolyCoeffs = computeSubsonicPolyCoeffs(coneHalfAngle)
self.TransonicCdPolyCoeffs = computeTransonicPolyCoeffs(coneHalfAngle)
def plotShape(self):
import matplotlib.pyplot as plt
Xvals = []
Yvals = []
forePos = self.position.Z
aftPos = forePos - self.length
foreRadius = self.startDiameter/2
aftRadius = self.endDiameter/2
Xvals.append(forePos)
Yvals.append(foreRadius) # top right
Xvals.append(aftPos)
Yvals.append(aftRadius) # top left
Xvals.append(aftPos)
Yvals.append(-aftRadius) # bottom left
Xvals.append(forePos)
Yvals.append(-foreRadius) # bottom right
Xvals.append(forePos)
Yvals.append(foreRadius) # close in the shape
plt.plot(Xvals, Yvals, color = 'k')
def getAppliedForce(self, rocketState, time, environment, CG) -> ForceMomentSystem:
Mach = AeroParameters.getMachNumber(rocketState, environment)
Aref = self.rocket.Aref
#### Normal Force ####
AOA = AeroParameters.getTotalAOA(rocketState, environment)
CN = AeroFunctions.Barrowman_GetCN(AOA, Aref, self.topArea, self.bottomArea)
#### Pressure Drag ####
if self.startDiameter > self.endDiameter:
# Pressure base drag
Cd_base = AeroFunctions.getBaseDragCoefficient(Mach)
Cd_pressure = Cd_base * self.CdAdjustmentFactor
else:
# Pressure drag calculated like a nose cone
if Mach < 1:
# Niskanen pg. 48 eq. 3.87 - Power law interpolation
Cd_pressure = self.SubsonicCdPolyCoeffs[0] * Mach**self.SubsonicCdPolyCoeffs[1]
elif Mach > 1 and Mach < 1.3:
# Interpolate in transonic region - derived from Niskanen Appendix B, Eqns B.3 - B.6
Cd_pressure = self.TransonicCdPolyCoeffs[0] + self.TransonicCdPolyCoeffs[1]*Mach + \
self.TransonicCdPolyCoeffs[2]*Mach**2 + self.TransonicCdPolyCoeffs[3]*Mach**3
else:
Cd_pressure = getSupersonicPressureDragCoeff_Hoerner(self.coneHalfAngle, Mach)
# Make reference are the rocket's, not this objects
Cd_pressure *= self.frontalArea / Aref
#### Skin Friction Drag ####
if self.wettedArea == 0:
skinFrictionDragCoefficient = 0
rollDampingMoment = Vector(0,0,0)
else:
skinFrictionDragCoefficient, rollDampingMoment = AeroFunctions.getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(rocketState, environment, \
self.length, Mach, self.surfaceRoughness, self.wettedArea, Aref, self.rocket.finenessRatio, self.rocket.fullyTurbulentBL)
#### Total Drag ####
Cd = Cd_pressure + skinFrictionDragCoefficient
#### Assemble & return final force object ####
return AeroFunctions.forceFromCdCN(rocketState, environment, Cd, CN, self.CPLocation, Aref, moment=rollDampingMoment)
def getMaxDiameter(self):
return max(self.startDiameter, self.endDiameter)
def getRadius(self, distanceFromTip):
return (distanceFromTip/self.length * (self.endDiameter - self.startDiameter) + self.startDiameter) / 2
class BoatTail(Transition):
'''
Defines a conical boattail (aerodynamic properties quite similar to curved boattails, especially in supersonic flight)
Always assumes it's at the bottom of a rocket.
Modelled like a Transition object, but accounts for base drag.
'''
canConnectToComponentBelow = False
''' Overrides attribute inherited from BodyComponent (through Transition), to indicate that this component must exist at the very bottom of a rocket '''
def getAppliedForce(self, rocketState, time, environment, CG) -> ForceMomentSystem:
Mach = AeroParameters.getMachNumber(rocketState, environment)
Aref = self.rocket.Aref
#### Normal Force ####
AOA = AeroParameters.getTotalAOA(rocketState, environment)
CN = AeroFunctions.Barrowman_GetCN(AOA, Aref, self.topArea, self.bottomArea)
#### Pressure Drag ####
Cd_base = AeroFunctions.getBaseDragCoefficient(Mach)
Cd_pressure = Cd_base * self.CdAdjustmentFactor
Cd_pressure *= self.frontalArea / self.rocket.Aref
noEngine = (self.stage.engineShutOffTime == None)
if noEngine or time > self.stage.engineShutOffTime:
# Add base drag if engine is off
Cd_pressure += Cd_base * self.bottomArea / Aref
#### Skin Friction Drag ####
if self.wettedArea == 0:
skinFrictionDragCoefficient = 0
rollDampingMoment = Vector(0,0,0)
else:
skinFrictionDragCoefficient, rollDampingMoment = AeroFunctions.getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(rocketState, environment, \
self.length, Mach, self.surfaceRoughness, self.wettedArea, Aref, self.rocket.finenessRatio, self.rocket.fullyTurbulentBL)
#### Total Drag ####
Cd = Cd_pressure + skinFrictionDragCoefficient
#### Assemble & return final force object ####
return AeroFunctions.forceFromCdCN(rocketState, environment, Cd, CN, self.CPLocation, Aref, moment=rollDampingMoment)
Classes
class BoatTail (*args)
-
Defines a conical boattail (aerodynamic properties quite similar to curved boattails, especially in supersonic flight) Always assumes it's at the bottom of a rocket. Modelled like a Transition object, but accounts for base drag.
Two possible sets of inputs:
1. Initialization as a regular, dictionary-defined rocket component:
* args = (componentDictReader, rocket, stage)
* Expected classes: (SubDictReader
,Rocket
,Stage
)
2. Manual initialization:
* args = (startDiameter, endDiameter, length, position, inertia, rocket, stage, name, surfaceRoughness)
* Expected classes: (float, float, float,Vector
,Inertia
,Rocket
,Stage
, str, float)Expand source code
class BoatTail(Transition): ''' Defines a conical boattail (aerodynamic properties quite similar to curved boattails, especially in supersonic flight) Always assumes it's at the bottom of a rocket. Modelled like a Transition object, but accounts for base drag. ''' canConnectToComponentBelow = False ''' Overrides attribute inherited from BodyComponent (through Transition), to indicate that this component must exist at the very bottom of a rocket ''' def getAppliedForce(self, rocketState, time, environment, CG) -> ForceMomentSystem: Mach = AeroParameters.getMachNumber(rocketState, environment) Aref = self.rocket.Aref #### Normal Force #### AOA = AeroParameters.getTotalAOA(rocketState, environment) CN = AeroFunctions.Barrowman_GetCN(AOA, Aref, self.topArea, self.bottomArea) #### Pressure Drag #### Cd_base = AeroFunctions.getBaseDragCoefficient(Mach) Cd_pressure = Cd_base * self.CdAdjustmentFactor Cd_pressure *= self.frontalArea / self.rocket.Aref noEngine = (self.stage.engineShutOffTime == None) if noEngine or time > self.stage.engineShutOffTime: # Add base drag if engine is off Cd_pressure += Cd_base * self.bottomArea / Aref #### Skin Friction Drag #### if self.wettedArea == 0: skinFrictionDragCoefficient = 0 rollDampingMoment = Vector(0,0,0) else: skinFrictionDragCoefficient, rollDampingMoment = AeroFunctions.getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(rocketState, environment, \ self.length, Mach, self.surfaceRoughness, self.wettedArea, Aref, self.rocket.finenessRatio, self.rocket.fullyTurbulentBL) #### Total Drag #### Cd = Cd_pressure + skinFrictionDragCoefficient #### Assemble & return final force object #### return AeroFunctions.forceFromCdCN(rocketState, environment, Cd, CN, self.CPLocation, Aref, moment=rollDampingMoment)
Ancestors
Class variables
var canConnectToComponentBelow
-
Overrides attribute inherited from BodyComponent (through Transition), to indicate that this component must exist at the very bottom of a rocket
Methods
def getAppliedForce(self, rocketState, time, environment, CG) ‑> ForceMomentSystem
-
Expand source code
def getAppliedForce(self, rocketState, time, environment, CG) -> ForceMomentSystem: Mach = AeroParameters.getMachNumber(rocketState, environment) Aref = self.rocket.Aref #### Normal Force #### AOA = AeroParameters.getTotalAOA(rocketState, environment) CN = AeroFunctions.Barrowman_GetCN(AOA, Aref, self.topArea, self.bottomArea) #### Pressure Drag #### Cd_base = AeroFunctions.getBaseDragCoefficient(Mach) Cd_pressure = Cd_base * self.CdAdjustmentFactor Cd_pressure *= self.frontalArea / self.rocket.Aref noEngine = (self.stage.engineShutOffTime == None) if noEngine or time > self.stage.engineShutOffTime: # Add base drag if engine is off Cd_pressure += Cd_base * self.bottomArea / Aref #### Skin Friction Drag #### if self.wettedArea == 0: skinFrictionDragCoefficient = 0 rollDampingMoment = Vector(0,0,0) else: skinFrictionDragCoefficient, rollDampingMoment = AeroFunctions.getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(rocketState, environment, \ self.length, Mach, self.surfaceRoughness, self.wettedArea, Aref, self.rocket.finenessRatio, self.rocket.fullyTurbulentBL) #### Total Drag #### Cd = Cd_pressure + skinFrictionDragCoefficient #### Assemble & return final force object #### return AeroFunctions.forceFromCdCN(rocketState, environment, Cd, CN, self.CPLocation, Aref, moment=rollDampingMoment)
Inherited members
class Transition (*args)
-
Models a conical diameter transition (growing or shrinking)
Two possible sets of inputs:
1. Initialization as a regular, dictionary-defined rocket component:
* args = (componentDictReader, rocket, stage)
* Expected classes: (SubDictReader
,Rocket
,Stage
)
2. Manual initialization:
* args = (startDiameter, endDiameter, length, position, inertia, rocket, stage, name, surfaceRoughness)
* Expected classes: (float, float, float,Vector
,Inertia
,Rocket
,Stage
, str, float)Expand source code
class Transition(FixedMass, BodyComponent): ''' Models a conical diameter transition (growing or shrinking) ''' def __init__(self, *args): ''' Two possible sets of inputs: 1. Initialization as a regular, dictionary-defined rocket component: * args = (componentDictReader, rocket, stage) * Expected classes: (`MAPLEAF.IO.SubDictReader`, `MAPLEAF.Rocket.Rocket`, `MAPLEAF.Rocket.Stage`) 2. Manual initialization: * args = (startDiameter, endDiameter, length, position, inertia, rocket, stage, name, surfaceRoughness) * Expected classes: (float, float, float, `MAPLEAF.Motion.Vector`, `MAPLEAF.Motion.Inertia`, `MAPLEAF.Rocket.Rocket`, `MAPLEAF.Rocket.Stage`, str, float) ''' if len(args) == 3: # Classic initialization from componentDictReader componentDictReader, rocket, stage = args FixedMass.__init__(self, componentDictReader, rocket, stage) self.length = componentDictReader.getFloat("length") self.startDiameter = componentDictReader.getFloat("startDiameter") self.endDiameter = componentDictReader.getFloat("endDiameter") self.surfaceRoughness = componentDictReader.tryGetFloat("surfaceRoughness", defaultValue=self.rocket.surfaceRoughness) else: # Initialization from parameters passed in self.startDiameter, self.endDiameter, self.length, self.position, self.inertia, self.rocket, self.stage, self.name, self.surfaceRoughness = args self._precomputeGeometry() def _precomputeGeometry(self): # Calculate basic areas self.topArea = self.startDiameter**2 * math.pi/4 self.bottomArea = self.endDiameter**2 * math.pi/4 self.frontalArea = abs(self.topArea - self.bottomArea) # Calculate surface area, volume, CP if self.length == 0: self.wettedArea = 0.0 self.volume = 0.0 self.CPLocation = self.position else: maxDiameter = max(self.startDiameter, self.endDiameter) minDiameter = min(self.startDiameter, self.endDiameter) hypotenuseSlope = ((maxDiameter - minDiameter) / self.length) # Boat tail is a truncated cone # Compute surface area of non-truncated cone starting at top of boattail baseConeHeight = maxDiameter / hypotenuseSlope baseHypotenuse = math.sqrt((maxDiameter/2)**2 + baseConeHeight**2) fullConeSurfaceArea = math.pi * (maxDiameter/2) * baseHypotenuse # Compute surface area of non-truncated cone starting at bottom of boattail tipConeHeight = minDiameter / hypotenuseSlope tipHypotenuse = math.sqrt((minDiameter/2)**2 + tipConeHeight**2) tipConeSurfaceArea = math.pi * (minDiameter/2) * tipHypotenuse # Surface area is the difference b/w the two self.wettedArea = fullConeSurfaceArea - tipConeSurfaceArea # Volume calculation uses same method as surface area calculation above fullConeVolume = self.topArea * baseConeHeight / 3 tipConeVolume = self.bottomArea * tipConeHeight / 3 self.volume = fullConeVolume - tipConeVolume # Calculate CP self.CPLocation = AeroFunctions.Barrowman_GetCPLocation(self.length, self.topArea, self.bottomArea, self.volume, self.position) # Calc aspect ratio if self.startDiameter == self.endDiameter: aspectRatio = 100 # Avoid division by zero else: aspectRatio = self.length / abs(self.startDiameter - self.endDiameter) # Precompute drag properties if self.endDiameter <= self.startDiameter: # Calculate Cd multiplier based on aspect ratio - Niskanen Eqn 3.88 if aspectRatio < 1: self.CdAdjustmentFactor = 1 elif aspectRatio < 3: self.CdAdjustmentFactor = (3 - aspectRatio) / 2 else: self.CdAdjustmentFactor = 0 else: if self.length == 0: coneHalfAngle = math.pi/2 else: coneHalfAngle = math.atan(abs(self.startDiameter - self.endDiameter)/2 / self.length) self.coneHalfAngle = coneHalfAngle self.SubsonicCdPolyCoeffs = computeSubsonicPolyCoeffs(coneHalfAngle) self.TransonicCdPolyCoeffs = computeTransonicPolyCoeffs(coneHalfAngle) def plotShape(self): import matplotlib.pyplot as plt Xvals = [] Yvals = [] forePos = self.position.Z aftPos = forePos - self.length foreRadius = self.startDiameter/2 aftRadius = self.endDiameter/2 Xvals.append(forePos) Yvals.append(foreRadius) # top right Xvals.append(aftPos) Yvals.append(aftRadius) # top left Xvals.append(aftPos) Yvals.append(-aftRadius) # bottom left Xvals.append(forePos) Yvals.append(-foreRadius) # bottom right Xvals.append(forePos) Yvals.append(foreRadius) # close in the shape plt.plot(Xvals, Yvals, color = 'k') def getAppliedForce(self, rocketState, time, environment, CG) -> ForceMomentSystem: Mach = AeroParameters.getMachNumber(rocketState, environment) Aref = self.rocket.Aref #### Normal Force #### AOA = AeroParameters.getTotalAOA(rocketState, environment) CN = AeroFunctions.Barrowman_GetCN(AOA, Aref, self.topArea, self.bottomArea) #### Pressure Drag #### if self.startDiameter > self.endDiameter: # Pressure base drag Cd_base = AeroFunctions.getBaseDragCoefficient(Mach) Cd_pressure = Cd_base * self.CdAdjustmentFactor else: # Pressure drag calculated like a nose cone if Mach < 1: # Niskanen pg. 48 eq. 3.87 - Power law interpolation Cd_pressure = self.SubsonicCdPolyCoeffs[0] * Mach**self.SubsonicCdPolyCoeffs[1] elif Mach > 1 and Mach < 1.3: # Interpolate in transonic region - derived from Niskanen Appendix B, Eqns B.3 - B.6 Cd_pressure = self.TransonicCdPolyCoeffs[0] + self.TransonicCdPolyCoeffs[1]*Mach + \ self.TransonicCdPolyCoeffs[2]*Mach**2 + self.TransonicCdPolyCoeffs[3]*Mach**3 else: Cd_pressure = getSupersonicPressureDragCoeff_Hoerner(self.coneHalfAngle, Mach) # Make reference are the rocket's, not this objects Cd_pressure *= self.frontalArea / Aref #### Skin Friction Drag #### if self.wettedArea == 0: skinFrictionDragCoefficient = 0 rollDampingMoment = Vector(0,0,0) else: skinFrictionDragCoefficient, rollDampingMoment = AeroFunctions.getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(rocketState, environment, \ self.length, Mach, self.surfaceRoughness, self.wettedArea, Aref, self.rocket.finenessRatio, self.rocket.fullyTurbulentBL) #### Total Drag #### Cd = Cd_pressure + skinFrictionDragCoefficient #### Assemble & return final force object #### return AeroFunctions.forceFromCdCN(rocketState, environment, Cd, CN, self.CPLocation, Aref, moment=rollDampingMoment) def getMaxDiameter(self): return max(self.startDiameter, self.endDiameter) def getRadius(self, distanceFromTip): return (distanceFromTip/self.length * (self.endDiameter - self.startDiameter) + self.startDiameter) / 2
Ancestors
- FixedMass
- RocketComponent
- BodyComponent
- abc.ABC
Subclasses
Methods
def getAppliedForce(self, rocketState, time, environment, CG) ‑> ForceMomentSystem
-
Expand source code
def getAppliedForce(self, rocketState, time, environment, CG) -> ForceMomentSystem: Mach = AeroParameters.getMachNumber(rocketState, environment) Aref = self.rocket.Aref #### Normal Force #### AOA = AeroParameters.getTotalAOA(rocketState, environment) CN = AeroFunctions.Barrowman_GetCN(AOA, Aref, self.topArea, self.bottomArea) #### Pressure Drag #### if self.startDiameter > self.endDiameter: # Pressure base drag Cd_base = AeroFunctions.getBaseDragCoefficient(Mach) Cd_pressure = Cd_base * self.CdAdjustmentFactor else: # Pressure drag calculated like a nose cone if Mach < 1: # Niskanen pg. 48 eq. 3.87 - Power law interpolation Cd_pressure = self.SubsonicCdPolyCoeffs[0] * Mach**self.SubsonicCdPolyCoeffs[1] elif Mach > 1 and Mach < 1.3: # Interpolate in transonic region - derived from Niskanen Appendix B, Eqns B.3 - B.6 Cd_pressure = self.TransonicCdPolyCoeffs[0] + self.TransonicCdPolyCoeffs[1]*Mach + \ self.TransonicCdPolyCoeffs[2]*Mach**2 + self.TransonicCdPolyCoeffs[3]*Mach**3 else: Cd_pressure = getSupersonicPressureDragCoeff_Hoerner(self.coneHalfAngle, Mach) # Make reference are the rocket's, not this objects Cd_pressure *= self.frontalArea / Aref #### Skin Friction Drag #### if self.wettedArea == 0: skinFrictionDragCoefficient = 0 rollDampingMoment = Vector(0,0,0) else: skinFrictionDragCoefficient, rollDampingMoment = AeroFunctions.getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(rocketState, environment, \ self.length, Mach, self.surfaceRoughness, self.wettedArea, Aref, self.rocket.finenessRatio, self.rocket.fullyTurbulentBL) #### Total Drag #### Cd = Cd_pressure + skinFrictionDragCoefficient #### Assemble & return final force object #### return AeroFunctions.forceFromCdCN(rocketState, environment, Cd, CN, self.CPLocation, Aref, moment=rollDampingMoment)
def plotShape(self)
-
Expand source code
def plotShape(self): import matplotlib.pyplot as plt Xvals = [] Yvals = [] forePos = self.position.Z aftPos = forePos - self.length foreRadius = self.startDiameter/2 aftRadius = self.endDiameter/2 Xvals.append(forePos) Yvals.append(foreRadius) # top right Xvals.append(aftPos) Yvals.append(aftRadius) # top left Xvals.append(aftPos) Yvals.append(-aftRadius) # bottom left Xvals.append(forePos) Yvals.append(-foreRadius) # bottom right Xvals.append(forePos) Yvals.append(foreRadius) # close in the shape plt.plot(Xvals, Yvals, color = 'k')
Inherited members