# -*- coding: utf-8 -*- ## @package importAirfoilDAT # \ingroup DRAFT # \brief Airfoil (.dat) file importer # # This module provides support for importing airfoil .dat files '''@package importAirfoilDAT Airfoil (.dat) file importer This module provides support for importing airfoil .dat files. ''' # Check code with # flake8 --ignore=E226,E266,E401,W503 # *************************************************************************** # * Copyright (c) 2010 Heiko Jakob * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * # * as published by the Free Software Foundation; either version 2 of * # * the License, or (at your option) any later version. * # * for detail see the LICENCE text file. * # * * # * This program is distributed in the hope that it will be useful, * # * but WITHOUT ANY WARRANTY; without even the implied warranty of * # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # * GNU Library General Public License for more details. * # * * # * You should have received a copy of the GNU Library General Public * # * License along with this program; if not, write to the Free Software * # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * # * USA * # * * # *************************************************************************** __title__ = "FreeCAD Draft Workbench - Airfoil data importer" __author__ = "Heiko Jakob " __url__ = "https://www.freecad.org" import re import os import FreeCAD import Draft import Part from FreeCAD import Vector from FreeCAD import Console as FCC from draftutils.utils import pyopen if FreeCAD.GuiUp: from draftutils.translate import translate else: def translate(context, txt): return txt useDraftWire = True def open(filename): """Open filename and parse. Parameters ---------- filename : str The path to the filename to be opened. Returns ------- App::Document The new FreeCAD document object created, with the parsed information. """ docname = os.path.splitext(os.path.basename(filename))[0] doc = FreeCAD.newDocument(docname) doc.Label = docname process(filename) doc.recompute() def insert(filename, docname): """Get an active document and parse. If no document exists, it is created. Parameters ---------- filename : str The path to the filename to be opened. docname : str The name of the active App::Document if one exists, or of the new one created. Returns ------- App::Document The active FreeCAD document, or the document created if none exists, with the parsed information. """ groupname = os.path.splitext(os.path.basename(filename))[0] try: doc = FreeCAD.getDocument(docname) except NameError: doc = FreeCAD.newDocument(docname) obj = process(filename) if obj is not None: importgroup = doc.addObject("App::DocumentObjectGroup", groupname) importgroup.Group = [obj] doc.recompute() def process(filename): """Process the filename and create a Draft Wire from the data. The common airfoil dat format has many flavors. This code should work with almost every dialect. Parameters ---------- filename : str The path to the filename to be opened. Returns ------- Part::Part2DObject or None. The created Draft Wire object or None if the file contains less than 3 points. """ # Regex to identify data rows and throw away unused metadata xval = r'(?P\-?\s*\d*\.*\d*([Ee]\-?\d+)?)' yval = r'(?P\-?\s*\d*\.*\d*([Ee]\-?\d+)?)' _regex = r'^\s*' + xval + r'\,?\s*' + yval + r'\s*$' regex = re.compile(_regex) afile = pyopen(filename, 'r') # read the airfoil name which is always at the first line airfoilname = afile.readline().strip() coords = [] # upside = True # last_x = None # Collect the data for lin in afile: curdat = regex.match(lin) if (curdat is not None and curdat.group("xval") and curdat.group("yval")): x = float(curdat.group("xval")) y = float(curdat.group("yval")) # Some files specify the number of upper and lower points on the 2nd line: # " 67. 72." # See: http://airfoiltools.com/airfoil # This line must be skipped: if x < 2 and y < 2: # the normal processing coords.append(Vector(x, y, 0)) afile.close() if len(coords) < 3: FCC.PrintError(translate("ImportAirfoilDAT", "Did not find enough coordinates") + "\n") return None # sometimes coords are divided in upper an lower side # so that x-coordinate begin new from leading or trailing edge # check for start coordinates in the middle of list if coords[0:-1].count(coords[0]) > 1: flippoint = coords.index(coords[0], 1) upper = coords[0:flippoint] lower = coords[flippoint+1:] lower.reverse() for i in lower: upper.append(i) coords = upper # do we use the parametric Draft Wire? if useDraftWire: obj = Draft.make_wire(coords, True) # obj.label = airfoilname else: # alternate solution, uses common Part Faces lines = [] first_v = None last_v = None for v in coords: if first_v is None: first_v = v # Line between v and last_v if they're not equal if (last_v is not None) and (last_v != v): lines.append(Part.makeLine(last_v, v)) # The new last_v last_v = v # close the wire if needed if last_v != first_v: lines.append(Part.makeLine(last_v, first_v)) wire = Part.Wire(lines) face = Part.Face(wire) obj = FreeCAD.ActiveDocument.addObject('Part::Feature', airfoilname) obj.Shape = face return obj