
Not applicable
12-05-2019
12:54 PM
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Hello everyone, I'm creating a script to model WindMill blades aligning the airfoils by the %of the chord length. Have created the script which read the data file and creates the airfoil sketch at the desired radial position (of the blade) by creating a sketch plane and spline.
The thing is I want to move the sketch by the "aerodynamic_center" to the selected (TwistAxis) edge and then rotate it by the (TwistAngle). I have tried different ways to do it, but it always rotates around the origin of the sketch.
Also, I have to check if the selected Edge (TwistAxis) is normal to the sketch plane.
import adsk.core, adsk.fusion, adsk.cam, traceback from math import ceil,floor,radians import os # global set of event handlers to keep them referenced for the duration of the command ui = None app = adsk.core.Application.get() if app: ui = app.userInterface handlers = [] product = app.activeProduct design = adsk.fusion.Design.cast(product) class Airfoil(): def __init__(self, file): """ :param name: :param kwargs: :raise Exception: """ self.coordinates = [] self.camber_line = [] self.__read_file(file) self.__get_camberline() self.aerodynamic_center = [0.25, 0.0] def __extremePoint(self): max_x = self.coordinates.index(max(self.coordinates,key=lambda pt: pt[0])) max_y = self.coordinates.index(max(self.coordinates,key=lambda pt: pt[1])) min_x = self.coordinates.index(min(self.coordinates,key=lambda pt: pt[0])) min_y = self.coordinates.index(min(self.coordinates,key=lambda pt: pt[1])) return [min_x, max_x], [min_y, max_y] def __get_camberline(self, points=50): n_points=len(self.coordinates) top = self.coordinates[:int(n_points/2)] bottom = self.coordinates[int(n_points/2):] #bottom[0]=top[-1] for i,point in enumerate(reversed(bottom)): mid_point_x = (point[0]+top[i][0])/2 mid_point_y = (point[1]+top[i][1])/2 self.camber_line.append([mid_point_x,mid_point_y]) def chord_size(self, chord): self.coordinates=[[p[0]*chord,p[1]*chord] for p in self.coordinates] self.camber_line=[[p[0]*chord,p[1]*chord] for p in self.camber_line] self.aerodynamic_center=[self.aerodynamic_center[0]*chord,self.aerodynamic_center[1]*chord] def set_aerodynamic_center(self, chord_percent=25): chord_position = chord_percent / 100 self.aerodynamic_center = (chord_position,0) def __read_file(self, file): """ :rtype : object :param name: :return: :raise Exception: """ with open(file, 'r') as f: for line in f.readlines(): line = line.strip(' \t') line = line.replace('\t', ' ') line = line.replace(',', ' ') p1 = line.find(" ") p2 = line.rfind(" ") x = float(line[0:p1]) y = float(line[p2+1:]) self.coordinates.append([x,y]) self.coordinates[-1]=self.coordinates[0] class CreateAirfoil(): def Execute(self,Plane,Offset,Edge,Chord,TwistAxis,TwistAngle): dlg = ui.createFileDialog() dlg.title = 'Open DAT File' dlg.filter = 'Airfoil DAT files (*.dat);;Txt Files (*.txt);;All Files (*.*)' if dlg.showOpen() != adsk.core.DialogResults.DialogOK: return filename = dlg.filename airfoil = Airfoil(filename) airfoilName = filename.split("/")[-1].split(".")[0] if TwistAxis: airfoil.set_aerodynamic_center(TwistAxis) if Chord: airfoil.chord_size(Chord) root = design.rootComponent # Create a construction plane by offsetting the end face if Offset: planes = root.constructionPlanes airfoilPlaneInput = planes.createInput() offsetVal = adsk.core.ValueInput.createByReal(Offset) airfoilPlaneInput.setByOffset(Plane, offsetVal) sketchPlane = planes.add(airfoilPlaneInput) sketchPlane.name = f'{airfoilName}_{Offset}' else: sketchPlane = Plane sketch = root.sketches.add(sketchPlane) aerodynamic_center = adsk.core.Point3D.create(*airfoil.aerodynamic_center,0) points = adsk.core.ObjectCollection.create() for p in airfoil.coordinates[1:-1]: x = p[0] y = -p[1] point = adsk.core.Point3D.create(x, y, 0.0) points.add(point) sketch.sketchCurves.sketchFittedSplines.add(points) sketch.sketchCurves.sketchLines.addByTwoPoints(points[0], points[-1]) sketch.sketchPoints.add(aerodynamic_center) sketch.name=f'{airfoilName}_{Offset}' class AirfoilCommandExecuteHandler(adsk.core.CommandEventHandler): def __init__(self): super().__init__() def notify(self, args): try: command = args.firingEvent.sender inputs = command.commandInputs Plane = inputs[0].selection(0).entity Offset = inputs[1].value Edge = inputs[2].selection(0).entity Chord = inputs[3].value TwistAxis = inputs[4].value TwistAngle = inputs[5].value airfoil = CreateAirfoil() airfoil.Execute(Plane,Offset,Edge,Chord,TwistAxis,TwistAngle) except: if ui: ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class AirfoilCommandCreatedHandler(adsk.core.CommandCreatedEventHandler): def __init__(self): super().__init__() def notify(self, args): try: cmd = args.command onExecute = AirfoilCommandExecuteHandler() cmd.execute.add(onExecute) onDestroy = AirfoilCommandDestroyHandler() cmd.destroy.add(onDestroy) onValidateInput = AirfoilValidateInputHandler() cmd.validateInputs.add(onValidateInput) onInputChanged = AirfoilCommandInputChangedHandler() cmd.inputChanged.add(onInputChanged) # keep the handler referenced beyond this function handlers.append(onExecute) handlers.append(onDestroy) handlers.append(onValidateInput) handlers.append(onInputChanged) #define the inputs inputs = cmd.commandInputs i1 = inputs.addSelectionInput('BaseConstPlane', 'Reference Plane', 'Please select a construction plane') i1.addSelectionFilter(adsk.core.SelectionCommandInput.ConstructionPlanes) i1.addSelectionFilter(adsk.core.SelectionCommandInput.PlanarFaces) i2 = inputs.addDistanceValueCommandInput('AirfoilOffsetPlane', 'Airfoil distance from reference', adsk.core.ValueInput.createByReal(10)) i2.isEnabled = False i3 = inputs.addSelectionInput('AirfoilAlignAxis', 'Center Edge', 'Please select a edge') i3.addSelectionFilter(adsk.core.SelectionCommandInput.Edges) i3.addSelectionFilter(adsk.core.SelectionCommandInput.ConstructionLines) i4 = inputs.addValueInput('AirfoilChord', 'ChordLength', '', adsk.core.ValueInput.createByReal(1)) i4.minimumValue = 0 i4.isMinimumValueInclusive = False i5 = inputs.addValueInput('TwistChord', "Twist Axis (% chord)", '', adsk.core.ValueInput.createByReal(25)) i6 = inputs.addValueInput('Twist', 'TwistAngle', '', adsk.core.ValueInput.createByReal(0)) except: if ui: ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class AirfoilCommandDestroyHandler(adsk.core.CommandEventHandler): def __init__(self): super().__init__() def notify(self, args): try: # when the command is done, terminate the script # this will release all globals which will remove all event handlers adsk.terminate() except: if ui: ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class AirfoilValidateInputHandler(adsk.core.ValidateInputsEventHandler): def __init__(self): super().__init__() def notify(self, args): try: sels = ui.activeSelections if len(sels) == 2: args.areInputsValid = True else: args.areInputsValid = False except: if ui: ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class AirfoilCommandInputChangedHandler(adsk.core.InputChangedEventHandler): def __init__(self): super().__init__() def notify(self, args): try: command = args.firingEvent.sender cmdInput = args.input if cmdInput.id == 'BaseConstPlane': inputs = cmdInput.commandInputs planeInput = inputs.itemById('BaseConstPlane') distanceInput = inputs.itemById('AirfoilOffsetPlane') if cmdInput.selectionCount > 0: sel = cmdInput.selection(0) selPt = sel.point ent = sel.entity plane = ent.geometry distanceInput.setManipulator(selPt, plane.normal) distanceInput.isEnabled = True distanceInput.isVisible = True else: distanceInput.isEnabled = False distanceInput.isVisible = False except: if ui: ui.messageBox(_('Input changed event failed: {}').format(traceback.format_exc())) def run(context): try: title = 'Select Construction Plane' if not design: ui.messageBox('No active Fusion design', title) return commandDefinitions = ui.commandDefinitions # check the command exists or not cmdDef = commandDefinitions.itemById('BladeAirfoil') if not cmdDef: resourceDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'Resources') # absolute resource file path is specified cmdDef = commandDefinitions.addButtonDefinition('BladeAirfoil', 'Airfoil Parameters', 'Creates airfoil spline on selected construction plane', resourceDir) onCommandCreated = AirfoilCommandCreatedHandler() cmdDef.commandCreated.add(onCommandCreated) # keep the handler referenced beyond this function handlers.append(onCommandCreated) inputs = adsk.core.NamedValues.create() cmdDef.execute(inputs) # prevent this module from being terminate when the script returns, because we are waiting for event handlers to fire adsk.autoTerminate(False) except: if ui: ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
Solved! Go to Solution.