/** This library allows you to load and format various airfoils. It uses the data available at the UIUC Airfoil Data site: https://m-selig.ae.illinois.edu/ads.html and the extensive UIUC Airfoil Coordinates Database: https://m-selig.ae.illinois.edu/ads/coord_database.html All of the airfoils can be downloaded from: https://m-selig.ae.illinois.edu/ads/archives/coord_seligFmt.zip You can plot airfoil shapes at: http://airfoiltools.com/plotter/index Airfoils in these databases are mostly listed in the "Selig" format named for Michael S. Selig. The coordinates are in an x,y format starting from trailing edge, along the upper surface to the leading edge and back around the lower surface to trailing edge For example, see E205 at https://m-selig.ae.illinois.edu/ads/coord/e205.dat Other airfols are in the "Lednicer" format which are upper surface points leading edge to trailing edge and then lower surface leading edge to trailing edge, separated by an intervening blank line. For example, see Clark Y at https://m-selig.ae.illinois.edu/ads/coord/clarky.dat Some of the airfoils listed do not close at the trailing edge, i.e. the trailing edge has a finite thickness as designed. */ class Airfoil { /** The name of the airfoil */ var name /** An array of dimensionless [x,y] points in the "Selig" format. The coordinates are in an x,y format starting from trailing edge, along the upper surface to the leading edge and back around the lower surface to trailing edge For example, see E205 at https://m-selig.ae.illinois.edu/ads/coord/e205.dat It is expected that the leading edge is at x=0 and the trailing edge is at x=1 but this may not be guaranteed. */ var points /** The upward rotation of the airfoil as an angle. This can be populated when using the rotate method to create a new Airfoil. */ var rotation = 0 deg /** Constructor. Creates a new airfoil. */ new[name, points] := { this.name = name this.points = points } /** This loads an airfoil definition from a Selig-formatted URL and returns a new Airfoil object. */ class loadSelig[url] := { lines = array[lines[url]] name = trim[lines@0] points = new array LINE: for i=1 to length[lines]-1 { // For FrinkTNG this needs to be \p{Space} if lines@i =~ %r/[^0-9eE\.[:space:]-]/ { println["Rejecting line $i in $url: " + lines@i] next LINE } [x,y] = eval[split[%r/\s+/, trim[lines@i]]] if ! (isUnit[x] and isUnit[y]) next LINE if y != undef points.push[[x,y]] } return new Airfoil[name, points] } /** Creates a new (unclosed) polyline object from this airfoil. */ toPolyline[scaleX=1, scaleY=1] := { return new polyline[getPoly[scaleX, scaleY]] } /** Creates a new (closed) polygon object from this airfoil. */ toPolygon[scaleX=1, scaleY=1] := { return getPoly[scaleX, scaleY] } /** Creates a new (closed, filled) polygon object from this airfoil. */ toFilledPolygon[scaleX=1, scaleY=1] := { return new filledPolygon[getPoly[scaleX, scaleY]] } /** Rotate the airfoil upward by the specified angle. The airfoil is rotated around the specified x and y coordinates. If either coordinate is undef, it is replaced by the geometric centroid of the wing. It returns a new Airfoil object. */ rotate[angle, cx=undef, cy=undef] := { if cx == undef || cy == undef { [cx1, cy1] = getPoly[].getCentroid[] if cx == undef cx = cx1 if cy == undef cy = cy1 } p1 = new array for [x,y] = points { x1 = (x - cx) cos[-angle] - (y - cy) sin[-angle] + cx y1 = (x - cx) sin[-angle] + (y - cy) cos[-angle] + cy p1.push[[x1,y1]] } a = new Airfoil[name, p1] a.rotation = this.rotation + angle return a } /** Turns the array of points into a polygon object and returns it. */ getPoly[scaleX=1, scaleY=1] := { poly = new polygon for [x,y] = points poly.addPoint[x scaleX, -y scaleY] return poly } }