Community
Fusion API and Scripts
Got a new add-in to share? Need something specialized to be scripted? Ask questions or share what you’ve discovered with the community.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Determining JointInput.angle to use for Joint creation

6 REPLIES 6
SOLVED
Reply
Message 1 of 7
balunist
801 Views, 6 Replies

Determining JointInput.angle to use for Joint creation

I need a way to determine the appropriate angle to use when creating a face to sketch profile CenterKeyPoint joint using a face from a body

that is not aligned with x, y, or z axis. 

 

Run the script below and step through the history to see the problem I need to solve or view the screencast.

 

Thanks in advance!  

 

import adsk.core, adsk.fusion, traceback

 

#==============================================================================

# class used to pass position and size of desired rectangle taking default units as input

#==============================================================================

class rect:

def __init__(self, insert=(0, 0), size=(0, 0)):

app = adsk.core.Application.get()

design = adsk.fusion.Design.cast(app.activeProduct)

self.unitsManager = design.unitsManager

x = self.convertToInternal(insert[0])

y = self.convertToInternal(insert[1])

self.defaultLength = size[0]

self.defaultWidth = size[1]

self.length = self.convertToInternal(size[0])

self.width = self.convertToInternal(size[1])

self.startPoint = adsk.core.Point3D.create(x, -y, 0)

self.endPoint = adsk.core.Point3D.create(x + self.length, -(y + self.width), 0)

 

def convertToInternal(self, valueIn):

return self.unitsManager.convert(valueIn, self.unitsManager.defaultLengthUnits, self.unitsManager.internalUnits)

 

#==============================================================================

# find the largest face and it's length & width edges for a body

#==============================================================================

class findMatchFace:

def __init__(self, body):

faceAreas = []

for face in body.faces:

faceAreas.append(face.area)

idx = faceAreas.index(max(faceAreas))

self.face = body.faces.item(idx)

 

edgeLengths = []

for edge in self.face.edges:

edgeLengths.append(edge.length)

idx = edgeLengths.index(max(edgeLengths))

self.edge = self.face.edges.item(idx)

self.lengthVector = self.edgeToVector(self.edge)

self.widthVector = None

for edge in self.face.edges:

vector = self.edgeToVector(edge)

if vector.isPerpendicularTo(self.lengthVector):

self.widthVector = vector

break

 

def edgeToVector(self, edge):

v1x = edge.startVertex.geometry.x

v1y = edge.startVertex.geometry.y

v1z = edge.startVertex.geometry.z

v2x = edge.endVertex.geometry.x

v2y = edge.endVertex.geometry.y

v2z = edge.endVertex.geometry.z

return adsk.core.Vector3D.create(v2x-v1x, v2y-v1y, v2z-v1z)

 

#==============================================================================

# class used to map component bodies to a board on xy plane

#==============================================================================

class mapper:

def __init__(self):

self.sketch = None

self.boardNum = 0

self.app = adsk.core.Application.get()

self.ui = self.app.userInterface

self.design = adsk.fusion.Design.cast(self.app.activeProduct)

self.rootComp = self.design.rootComponent

self.mapOcc = self.rootComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())

self.mapComp = self.mapOcc.component

self.mapComp.name = 'map'

self.xYPlane = self.mapComp.xYConstructionPlane

lib = self.app.materialLibraries.itemByName('Fusion 360 Material Library')

self.paperMaterial = lib.materials.itemByName('Paper')

 

def _newSketch(self, compOcc=None, offset=0):

try:

if offset == 0:

self.sketch = compOcc.component.sketches.add(self.xYPlane)

else:

offsetValue = adsk.core.ValueInput.createByReal(offset)

planes = self.mapOcc.component.constructionPlanes

planeInput = planes.createInput()

planeInput.setByOffset(self.xYPlane, offsetValue)

plane = planes.add(planeInput)

self.sketch = compOcc.component.sketches.add(plane)

self.sketchLines = self.sketch.sketchCurves.sketchLines

self.sketchTexts = self.sketch.sketchTexts

except:

self.ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

def _add(self, rect):

sketchLines = self.sketchLines.addTwoPointRectangle(rect.startPoint, rect.endPoint)

return sketchLines

 

def addBoard(self, rect):

self.boardOcc = self.mapComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())

self.boardComp = self.boardOcc.component

self.boardNum += 1

self.boardName = 'Board {}'.format(self.boardNum)

self.boardComp.name = self.boardName

 

self._newSketch(compOcc=self.boardOcc)

self.sketch.name = self.boardName

self._add(rect)

 

idx = len(self.sketch.profiles) - 1

prof = self.sketch.profiles.item(idx)

# Create the patch feature

patches = self.boardComp.features.patchFeatures

patchInput = patches.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)

patchFeature = patches.add(patchInput)

self.boardBody = patchFeature.bodies.item(0)

self.boardBody.name = self.boardName

self.boardBody.material = self.paperMaterial

 

def addComponent(self, source, targetRect, name):

self.body = source.body

self.compOcc = self.boardComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())

self.compComp = self.compOcc.component

self.compComp.name = name

self._newSketch(self.boardOcc)

self.sketch.name = '{}{}'.format(name, "_target")

self._add(targetRect)

idx = len(self.sketch.profiles) - 1

targetProf = self.sketch.profiles.item(idx)

self.mapJoint(self.compOcc, targetProf, source)

 

def mapJoint(self, compOcc, targetProf, source):

self.body.copyToComponent(compOcc)

self.bodyOcc = compOcc.bRepBodies.item(0)

matchFace = findMatchFace(self.bodyOcc)

# Create the first joint geometry with the end face

geo0 = adsk.fusion.JointGeometry.createByPlanarFace(matchFace.face, None, adsk.fusion.JointKeyPointTypes.CenterKeyPoint)

# Create the second joint geometry with the sketch profile

geo1 = adsk.fusion.JointGeometry.createByProfile(targetProf, None, adsk.fusion.JointKeyPointTypes.CenterKeyPoint)

# Create joint input

joints = self.rootComp.joints

jointInput = joints.createInput(geo0, geo1)

jointInput.angle = source.angle

jointInput.isFlipped = True

# Create the joint

joints.add(jointInput)

self.bodyOcc.material = self.paperMaterial

 

 

#==============================================================================

# This class will create a body which will be moved to serveral alignments with

# respect to x,y,z axis, copied, then mapped to a sketch profile with a joint.

#==============================================================================

class createSourceComponent:

def __init__(self):

app = adsk.core.Application.get()

actDoc = app.activeDocument.dataFile

if actDoc != None:

app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)

 

design = adsk.fusion.Design.cast(app.activeProduct)

self.rootComp = design.rootComponent

occs = self.rootComp.occurrences

for occ in occs:

if occ.component.name.startswith('map'):

occ.deleteMe()

break

for occ in occs:

if occ.component.name.startswith('map'):

occ.deleteMe()

break

 

rootComp = design.rootComponent

mapSourceOcc = rootComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())

self.mapSourceComp = mapSourceOcc.component

self.mapSourceComp.name = 'mapSource'

 

sketches = self.mapSourceComp.sketches

sketch = sketches.add(self.rootComp.xYConstructionPlane)

sketchLines = sketch.sketchCurves.sketchLines

 

compRect = rect(insert=(0, -4), size=(18, 4))

self.length = compRect.defaultLength

self.width = compRect.defaultWidth

self.thick = .75 * 2.54

 

sketchLines.addTwoPointRectangle(compRect.startPoint, compRect.endPoint)

self.targetProf1 = sketch.profiles.item(0)

 

profiles = adsk.core.ObjectCollection.create()

profiles.add(self.targetProf1)

 

extrudeFeats = self.mapSourceComp.features.extrudeFeatures

extrudeFeature = extrudeFeats.addSimple(profiles,

adsk.core.ValueInput.createByReal(self.thick),

adsk.fusion.FeatureOperations.NewBodyFeatureOperation)

self.body = extrudeFeature.bodies.item(0)

self.bodies = adsk.core.ObjectCollection.create()

self.bodies.add(extrudeFeature.bodies.item(0))

 

chkRotate = checkRotate(self.body)

self.isRotate = chkRotate.isRotate

self.angle = chkRotate.angle

self.odd = chkRotate.odd

 

def move(self, vector):

transform = adsk.core.Matrix3D.create()

transform.translation = vector

moveFeats = self.mapSourceComp.features.moveFeatures

moveFeatureInput = moveFeats.createInput(self.bodies, transform)

moveFeats.add(moveFeatureInput)

chkRotate = checkRotate(self.body)

self.isRotate = chkRotate.isRotate

self.angle = chkRotate.angle

self.odd = chkRotate.odd

 

def rotate(self, angle, axis, origin):

transform = adsk.core.Matrix3D.create()

transform.setToRotation(angle, axis, origin)

moveFeats = self.mapSourceComp.features.moveFeatures

moveFeatureInput = moveFeats.createInput(self.bodies, transform)

moveFeats.add(moveFeatureInput)

chkRotate = checkRotate(self.body)

self.isRotate = chkRotate.isRotate

self.angle = chkRotate.angle

self.odd = chkRotate.odd

 

#==============================================================================

# check body orientation with respect to X,Y,Z axis & determine if rotation is

# required when assemble joint is created with sketch profile in XY plane

#==============================================================================

RadFor90 = round(1.5707963267948966, 3) # 90 deg in radians

class checkRotate:

def __init__(self, body):

source = findMatchFace(body)

lengthVector = source.lengthVector

widthVector = source.widthVector

 

app = adsk.core.Application.get()

design = adsk.fusion.Design.cast(app.activeProduct)

xVector = design.rootComponent.xConstructionAxis.geometry.direction

yVector = design.rootComponent.yConstructionAxis.geometry.direction

zVector = design.rootComponent.zConstructionAxis.geometry.direction

 

aList = []

aList.append(round(lengthVector.angleTo(xVector), 3)) # 0 - lengthToX

aList.append(round(widthVector.angleTo(xVector), 3)) # 1 - widthToX

aList.append(round(lengthVector.angleTo(yVector), 3)) # 2 - lengthToY

aList.append(round(widthVector.angleTo(yVector), 3)) # 3 - widthToY

aList.append(round(lengthVector.angleTo(zVector), 3)) # 4 - lengthToZ

 

self.odd = self.chkOriented(aList)

 

aList.append(round(widthVector.angleTo(zVector), 3)) # 5 - widthToZ

 

self.isRotate = False

self.angle = adsk.core.ValueInput.createByReal(0)

 

t1 = (aList[0] == RadFor90 and aList[1] == 0)

t2 = (aList[1] == RadFor90 and aList[0] == 0 and aList[4] != RadFor90)

t3 = ((aList[0] == RadFor90) and (aList[1] == RadFor90)) and ((aList[4] != 0) and (aList[2] != RadFor90))

t4 = (aList[0] == RadFor90 and aList[3] == RadFor90)

# print('t1 {} t2 {} t3 {} t4 {}'.format(t1, t2, t3, t4))

if (t1 or t2 or t3 or t4):

self.angle = adsk.core.ValueInput.createByReal(-RadFor90)

self.isRotate = True

print('>*>*>*>* -90 angle rotate required!')

 

if self.odd:

print('>*>*>*>* special angle required!')

 

print('>>>>> {} - rotate: {} odd: {} lengthToX: {} widthToX: {} lengthToY: {} widthToY: {} lengthToZ: {} widthToZ: {}'.format(

body.name,self.isRotate,self.odd, aList[0], aList[1], aList[2], aList[3], aList[4], aList[5]))

 

def chkOriented(self, aList):

hasOdd = False

hasNormal = False

for angle in aList:

if angle != RadFor90 and angle != 0:

hasOdd = True

else:

hasNormal = True

return hasOdd and not hasNormal

 

#==============================================================================

# Create a demo showing the issue I am having with a face to profile CenterKeyPoint

# joint assembly. I need a way to determine the appropriate angle to use

# when creating a face to profile CenterKeyPoint joint using face from a body

# that is not aligned with x,y, or z.

#==============================================================================

def run(context):

app = adsk.core.Application.get()

ui = app.userInterface

try:

# length 0 deg to X, width 0 deg to Y, length & width 90 deg to Z

source = createSourceComponent()

 

# rotate on Y 45 deg to X and Z axis

angle = -0.785398 # -45 degrees

axis = adsk.core.Vector3D.create(0.0, 10.0, 0.0)

origin = adsk.core.Point3D.create(0.0, 0.0, 0.0)

source.rotate(angle, axis, origin)

 

mapBody = mapper()

boardRect = rect(insert=(0,0), size=(96,12))

mapBody.addBoard(boardRect)

xOffset = 0

yOffset = 0

 

compRect = rect(insert=(xOffset, yOffset), size=(source.length, source.width))

mapBody.addComponent(source, compRect, 'Y45ToXandZ')

 

# rotate on X 45 deg to Y and Z axis

angle = -0.785398 # -45 degrees

axis = adsk.core.Vector3D.create(10, 0.0, 0.0)

origin = adsk.core.Point3D.create(0.0, 0.0, 0.0)

source.rotate(angle, axis, origin)

 

xOffset += source.length

compRect = rect(insert=(xOffset, yOffset), size=(source.length, source.width))

mapBody.addComponent(source, compRect, 'X45ToYandZ')

 

# rotate on Z 45 deg to X and Y axis

angle = -0.785398 # -45 degrees

axis = adsk.core.Vector3D.create(0.0, 0.0, 10)

origin = adsk.core.Point3D.create(0.0, 0.0, 0.0)

source.rotate(angle, axis, origin)

 

xOffset += source.length

compRect = rect(insert=(xOffset, yOffset), size=(source.length, source.width))

mapBody.addComponent(source, compRect, 'Z45ToXandY')

 

camera_ = app.activeViewport.camera

camera_.isFitView = True

app.activeViewport.camera = camera_

 

except:

if ui:

ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

6 REPLIES 6
Message 2 of 7
balunist
in reply to: balunist

Post did not pick up my screencast.  Here it is ..  

Message 3 of 7
marshaltu
in reply to: balunist

Hello,

 

I tried to reproduce your issue. Unfortunately I cannot get the sample script you posted run because of format issue.

 

It would be great if you can repost your codes by using "Insert Code" command (like as below).

 

Thanks,

Marshal

 

import adsk.core, adsk.fusion, traceback

 

#==============================================================================

# class used to pass position and size of desired rectangle taking default units as input

#==============================================================================

class rect:

    def __init__(self, insert=(0, 0), size=(0, 0)):
    
        app = adsk.core.Application.get()
        
        design = adsk.fusion.Design.cast(app.activeProduct)
        
        self.unitsManager = design.unitsManager
        
        x = self.convertToInternal(insert[0])
        
        y = self.convertToInternal(insert[1])
        
        self.defaultLength = size[0]
        
        self.defaultWidth = size[1]
        
        self.length = self.convertToInternal(size[0])
        
        self.width = self.convertToInternal(size[1])
        
        self.startPoint = adsk.core.Point3D.create(x, -y, 0)
        
        self.endPoint = adsk.core.Point3D.create(x + self.length, -(y + self.width), 0)

 

    def convertToInternal(self, valueIn):

        return self.unitsManager.convert(valueIn, self.unitsManager.defaultLengthUnits, self.unitsManager.internalUnits)

 

#==============================================================================

# find the largest face and it's length & width edges for a body

#==============================================================================

class findMatchFace:

    def __init__(self, body):
    
        faceAreas = []
        
        for face in body.faces:
        
            faceAreas.append(face.area)
        
        idx = faceAreas.index(max(faceAreas))
        
        self.face = body.faces.item(idx)

 

        edgeLengths = []
        
        for edge in self.face.edges:
        
            edgeLengths.append(edge.length)
        
        idx = edgeLengths.index(max(edgeLengths))
        
        self.edge = self.face.edges.item(idx)
        
        self.lengthVector = self.edgeToVector(self.edge)
        
        self.widthVector = None
        
        for edge in self.face.edges:
        
            vector = self.edgeToVector(edge)
        
            if vector.isPerpendicularTo(self.lengthVector):
        
                self.widthVector = vector
        
                break

 

    def edgeToVector(self, edge):
    
        v1x = edge.startVertex.geometry.x
        
        v1y = edge.startVertex.geometry.y
        
        v1z = edge.startVertex.geometry.z
        
        v2x = edge.endVertex.geometry.x
        
        v2y = edge.endVertex.geometry.y
        
        v2z = edge.endVertex.geometry.z
        
        return adsk.core.Vector3D.create(v2x-v1x, v2y-v1y, v2z-v1z)

 

#==============================================================================

# class used to map component bodies to a board on xy plane

#==============================================================================

class mapper:

    def __init__(self):
    
        self.sketch = None
        
        self.boardNum = 0
        
        self.app = adsk.core.Application.get()
        
        self.ui = self.app.userInterface
        
        self.design = adsk.fusion.Design.cast(self.app.activeProduct)
        
        self.rootComp = self.design.rootComponent
        
        self.mapOcc = self.rootComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())
        
        self.mapComp = self.mapOcc.component
        
        self.mapComp.name = 'map'
        
        self.xYPlane = self.mapComp.xYConstructionPlane
        
        lib = self.app.materialLibraries.itemByName('Fusion 360 Material Library')
        
        self.paperMaterial = lib.materials.itemByName('Paper')

 

    def _newSketch(self, compOcc=None, offset=0):
    
        try:
        
            if offset == 0:
            
                self.sketch = compOcc.component.sketches.add(self.xYPlane)
            
            else:
            
                offsetValue = adsk.core.ValueInput.createByReal(offset)
                
                planes = self.mapOcc.component.constructionPlanes
                
                planeInput = planes.createInput()
                
                planeInput.setByOffset(self.xYPlane, offsetValue)
                
                plane = planes.add(planeInput)
                
                self.sketch = compOcc.component.sketches.add(plane)
                
                self.sketchLines = self.sketch.sketchCurves.sketchLines
                
                self.sketchTexts = self.sketch.sketchTexts
        
        except:
        
            self.ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

    def _add(self, rect):
    
        sketchLines = self.sketchLines.addTwoPointRectangle(rect.startPoint, rect.endPoint)
    
        return sketchLines

 

    def addBoard(self, rect):
    
        self.boardOcc = self.mapComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())
        
        self.boardComp = self.boardOcc.component
        
        self.boardNum += 1
        
        self.boardName = 'Board {}'.format(self.boardNum)
        
        self.boardComp.name = self.boardName

 

        self._newSketch(compOcc=self.boardOcc)
        
        self.sketch.name = self.boardName
        
        self._add(rect)

 

        idx = len(self.sketch.profiles) - 1

        prof = self.sketch.profiles.item(idx)
        
        # Create the patch feature
        
        patches = self.boardComp.features.patchFeatures
        
        patchInput = patches.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        
        patchFeature = patches.add(patchInput)
        
        self.boardBody = patchFeature.bodies.item(0)
        
        self.boardBody.name = self.boardName
        
        self.boardBody.material = self.paperMaterial

 

    def addComponent(self, source, targetRect, name):
    
        self.body = source.body
        
        self.compOcc = self.boardComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())
        
        self.compComp = self.compOcc.component
        
        self.compComp.name = name
        
        self._newSketch(self.boardOcc)
        
        self.sketch.name = '{}{}'.format(name, "_target")
        
        self._add(targetRect)
        
        idx = len(self.sketch.profiles) - 1
        
        targetProf = self.sketch.profiles.item(idx)
        
        self.mapJoint(self.compOcc, targetProf, source)

 

    def mapJoint(self, compOcc, targetProf, source):
    
        self.body.copyToComponent(compOcc)
        
        self.bodyOcc = compOcc.bRepBodies.item(0)
        
        matchFace = findMatchFace(self.bodyOcc)

        # Create the first joint geometry with the end face
        
        geo0 = adsk.fusion.JointGeometry.createByPlanarFace(matchFace.face, None, adsk.fusion.JointKeyPointTypes.CenterKeyPoint)
        
        # Create the second joint geometry with the sketch profile
        
        geo1 = adsk.fusion.JointGeometry.createByProfile(targetProf, None, adsk.fusion.JointKeyPointTypes.CenterKeyPoint)
        
        # Create joint input
        
        joints = self.rootComp.joints
        
        jointInput = joints.createInput(geo0, geo1)
        
        jointInput.angle = source.angle
        
        jointInput.isFlipped = True
        
        # Create the joint
        
        joints.add(jointInput)
        
        self.bodyOcc.material = self.paperMaterial

 

 

#==============================================================================

# This class will create a body which will be moved to serveral alignments with

# respect to x,y,z axis, copied, then mapped to a sketch profile with a joint.

#==============================================================================

class createSourceComponent:

    def __init__(self):
    
        app = adsk.core.Application.get()
        
        actDoc = app.activeDocument.dataFile
        
        if actDoc != None:
        
            app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
    
     
    
        design = adsk.fusion.Design.cast(app.activeProduct)
        
        self.rootComp = design.rootComponent
        
        occs = self.rootComp.occurrences
        
        for occ in occs:
        
            if occ.component.name.startswith('map'):
        
                occ.deleteMe()
        
                break
        
        for occ in occs:
        
            if occ.component.name.startswith('map'):
        
                occ.deleteMe()
        
                break
    
     
    
        rootComp = design.rootComponent
        
        mapSourceOcc = rootComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())
        
        self.mapSourceComp = mapSourceOcc.component
        
        self.mapSourceComp.name = 'mapSource'
    
     
    
        sketches = self.mapSourceComp.sketches
        
        sketch = sketches.add(self.rootComp.xYConstructionPlane)
        
        sketchLines = sketch.sketchCurves.sketchLines
    
     
    
        compRect = rect(insert=(0, -4), size=(18, 4))
        
        self.length = compRect.defaultLength
        
        self.width = compRect.defaultWidth
        
        self.thick = .75 * 2.54
    
     
    
        sketchLines.addTwoPointRectangle(compRect.startPoint, compRect.endPoint)
        
        self.targetProf1 = sketch.profiles.item(0)
        
         
        
        profiles = adsk.core.ObjectCollection.create()
        
        profiles.add(self.targetProf1)
        
         
        
        extrudeFeats = self.mapSourceComp.features.extrudeFeatures
        
        extrudeFeature = extrudeFeats.addSimple(profiles,
        
        adsk.core.ValueInput.createByReal(self.thick),
        
        adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        
        self.body = extrudeFeature.bodies.item(0)
        
        self.bodies = adsk.core.ObjectCollection.create()
        
        self.bodies.add(extrudeFeature.bodies.item(0))
        
         
        
        chkRotate = checkRotate(self.body)
        
        self.isRotate = chkRotate.isRotate
        
        self.angle = chkRotate.angle
        
        self.odd = chkRotate.odd
    
     
    
    def move(self, vector):
    
        transform = adsk.core.Matrix3D.create()
        
        transform.translation = vector
        
        moveFeats = self.mapSourceComp.features.moveFeatures
        
        moveFeatureInput = moveFeats.createInput(self.bodies, transform)
        
        moveFeats.add(moveFeatureInput)
        
        chkRotate = checkRotate(self.body)
        
        self.isRotate = chkRotate.isRotate
        
        self.angle = chkRotate.angle
        
        self.odd = chkRotate.odd
    
     
    
    def rotate(self, angle, axis, origin):
    
        transform = adsk.core.Matrix3D.create()
        
        transform.setToRotation(angle, axis, origin)
        
        moveFeats = self.mapSourceComp.features.moveFeatures
        
        moveFeatureInput = moveFeats.createInput(self.bodies, transform)
        
        moveFeats.add(moveFeatureInput)
        
        chkRotate = checkRotate(self.body)
        
        self.isRotate = chkRotate.isRotate
        
        self.angle = chkRotate.angle
        
        self.odd = chkRotate.odd

 

#==============================================================================

# check body orientation with respect to X,Y,Z axis & determine if rotation is

# required when assemble joint is created with sketch profile in XY plane

#==============================================================================

RadFor90 = round(1.5707963267948966, 3) # 90 deg in radians

class checkRotate:

    def __init__(self, body):
    
        source = findMatchFace(body)
        
        lengthVector = source.lengthVector
        
        widthVector = source.widthVector
        
         
        
        app = adsk.core.Application.get()
        
        design = adsk.fusion.Design.cast(app.activeProduct)
        
        xVector = design.rootComponent.xConstructionAxis.geometry.direction
        
        yVector = design.rootComponent.yConstructionAxis.geometry.direction
        
        zVector = design.rootComponent.zConstructionAxis.geometry.direction
        
         
        
        aList = []
        
        aList.append(round(lengthVector.angleTo(xVector), 3)) # 0 - lengthToX
        
        aList.append(round(widthVector.angleTo(xVector), 3)) # 1 - widthToX
        
        aList.append(round(lengthVector.angleTo(yVector), 3)) # 2 - lengthToY
        
        aList.append(round(widthVector.angleTo(yVector), 3)) # 3 - widthToY
        
        aList.append(round(lengthVector.angleTo(zVector), 3)) # 4 - lengthToZ
        
         
        
        self.odd = self.chkOriented(aList)
        
         
        
        aList.append(round(widthVector.angleTo(zVector), 3)) # 5 - widthToZ
        
         
        
        self.isRotate = False
        
        self.angle = adsk.core.ValueInput.createByReal(0)
        
         
        
        t1 = (aList[0] == RadFor90 and aList[1] == 0)
        
        t2 = (aList[1] == RadFor90 and aList[0] == 0 and aList[4] != RadFor90)
        
        t3 = ((aList[0] == RadFor90) and (aList[1] == RadFor90)) and ((aList[4] != 0) and (aList[2] != RadFor90))
        
        t4 = (aList[0] == RadFor90 and aList[3] == RadFor90)
        
        # print('t1 {} t2 {} t3 {} t4 {}'.format(t1, t2, t3, t4))
        
        if (t1 or t2 or t3 or t4):
        
            self.angle = adsk.core.ValueInput.createByReal(-RadFor90)
        
            self.isRotate = True
        
            print('>*>*>*>* -90 angle rotate required!')
        
         
        
        if self.odd:
        
            print('>*>*>*>* special angle required!')
        
         
        
            print('>>>>> {} - rotate: {} odd: {} lengthToX: {} widthToX: {} lengthToY: {} widthToY: {} lengthToZ: {} widthToZ: {}'.format(
        
            body.name,self.isRotate,self.odd, aList[0], aList[1], aList[2], aList[3], aList[4], aList[5]))
    
     
    
    def chkOriented(self, aList):
    
        hasOdd = False
        
        hasNormal = False
        
        for angle in aList:
        
            if angle != RadFor90 and angle != 0:
        
                hasOdd = True
        
            else:
        
                hasNormal = True
        
        return hasOdd and not hasNormal

 

#==============================================================================

# Create a demo showing the issue I am having with a face to profile CenterKeyPoint

# joint assembly. I need a way to determine the appropriate angle to use

# when creating a face to profile CenterKeyPoint joint using face from a body

# that is not aligned with x,y, or z.

#==============================================================================

def run(context):

    app = adsk.core.Application.get()
    
    ui = app.userInterface
    
    try:
    
        # length 0 deg to X, width 0 deg to Y, length & width 90 deg to Z
        
        source = createSourceComponent()
        
         
        
        # rotate on Y 45 deg to X and Z axis
        
        angle = -0.785398 # -45 degrees
        
        axis = adsk.core.Vector3D.create(0.0, 10.0, 0.0)
        
        origin = adsk.core.Point3D.create(0.0, 0.0, 0.0)
        
        source.rotate(angle, axis, origin)
        
         
        
        mapBody = mapper()
        
        boardRect = rect(insert=(0,0), size=(96,12))
        
        mapBody.addBoard(boardRect)
        
        xOffset = 0
        
        yOffset = 0
        
         
        
        compRect = rect(insert=(xOffset, yOffset), size=(source.length, source.width))
        
        mapBody.addComponent(source, compRect, 'Y45ToXandZ')
        
         
        
        # rotate on X 45 deg to Y and Z axis
        
        angle = -0.785398 # -45 degrees
        
        axis = adsk.core.Vector3D.create(10, 0.0, 0.0)
        
        origin = adsk.core.Point3D.create(0.0, 0.0, 0.0)
        
        source.rotate(angle, axis, origin)
        
         
        
        xOffset += source.length
        
        compRect = rect(insert=(xOffset, yOffset), size=(source.length, source.width))
        
        mapBody.addComponent(source, compRect, 'X45ToYandZ')
        
         
        
        # rotate on Z 45 deg to X and Y axis
        
        angle = -0.785398 # -45 degrees
        
        axis = adsk.core.Vector3D.create(0.0, 0.0, 10)
        
        origin = adsk.core.Point3D.create(0.0, 0.0, 0.0)
        
        source.rotate(angle, axis, origin)
        
         
        
        xOffset += source.length
        
        compRect = rect(insert=(xOffset, yOffset), size=(source.length, source.width))
        
        mapBody.addComponent(source, compRect, 'Z45ToXandY')
        
         
        
        camera_ = app.activeViewport.camera
        
        camera_.isFitView = True
        
        app.activeViewport.camera = camera_
    
     
    
    except:
    
        if ui:
        
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


Marshal Tu
Fusion 360 Developer
Autodesk, Inc.

Message 4 of 7
balunist
in reply to: marshaltu

"""
Created on Mon Jan 29 16:42:20 2018

@author: icarus
"""
import adsk.core, adsk.fusion, traceback

#==============================================================================
# class used to pass position and size of desired rectangle taking default units as input
#==============================================================================
class rect:
    def __init__(self, insert=(0, 0), size=(0, 0)): 
        app = adsk.core.Application.get()
        design = adsk.fusion.Design.cast(app.activeProduct)
        self.unitsManager = design.unitsManager
        x = self.convertToInternal(insert[0])
        y = self.convertToInternal(insert[1])
        self.defaultLength = size[0]
        self.defaultWidth = size[1]
        self.length = self.convertToInternal(size[0])
        self.width = self.convertToInternal(size[1])
        self.startPoint = adsk.core.Point3D.create(x, -y, 0)
        self.endPoint = adsk.core.Point3D.create(x + self.length, -(y + self.width), 0)

    def convertToInternal(self, valueIn):
        return self.unitsManager.convert(valueIn, self.unitsManager.defaultLengthUnits, self.unitsManager.internalUnits)

#==============================================================================
# find the largest face and it's length & width edges for a body
#==============================================================================
class findMatchFace:
    def __init__(self, body):
        faceAreas = []
        for face in body.faces:
            faceAreas.append(face.area)
        idx = faceAreas.index(max(faceAreas))
        self.face = body.faces.item(idx)
        
        edgeLengths = []
        for edge in self.face.edges:
            edgeLengths.append(edge.length)
        idx = edgeLengths.index(max(edgeLengths))
        self.edge = self.face.edges.item(idx)
        self.lengthVector = self.edgeToVector(self.edge)
        self.widthVector = None
        for edge in self.face.edges:
            vector = self.edgeToVector(edge)
            if vector.isPerpendicularTo(self.lengthVector):
                self.widthVector = vector
                break
            
    def edgeToVector(self, edge):
        v1x = edge.startVertex.geometry.x
        v1y = edge.startVertex.geometry.y
        v1z = edge.startVertex.geometry.z
        v2x = edge.endVertex.geometry.x
        v2y = edge.endVertex.geometry.y
        v2z = edge.endVertex.geometry.z
        return adsk.core.Vector3D.create(v2x-v1x, v2y-v1y, v2z-v1z)       

#==============================================================================
# class used to map component bodies to a board on xy plane
#==============================================================================
class mapper:
    def __init__(self):
        self.sketch = None
        self.boardNum = 0
        self.app = adsk.core.Application.get()
        self.ui = self.app.userInterface
        self.design = adsk.fusion.Design.cast(self.app.activeProduct)
        self.rootComp = self.design.rootComponent
        self.mapOcc = self.rootComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())
        self.mapComp = self.mapOcc.component  
        self.mapComp.name = 'map'
        self.xYPlane = self.mapComp.xYConstructionPlane
        lib = self.app.materialLibraries.itemByName('Fusion 360 Material Library')
        self.paperMaterial = lib.materials.itemByName('Paper')
        
    def _newSketch(self, compOcc=None, offset=0):
        try:
            if offset == 0:
                self.sketch = compOcc.component.sketches.add(self.xYPlane)
            else:
                offsetValue = adsk.core.ValueInput.createByReal(offset)
                planes = self.mapOcc.component.constructionPlanes        
                planeInput = planes.createInput()
                planeInput.setByOffset(self.xYPlane, offsetValue)
                plane = planes.add(planeInput)
                self.sketch = compOcc.component.sketches.add(plane)
            self.sketchLines = self.sketch.sketchCurves.sketchLines
            self.sketchTexts = self.sketch.sketchTexts
        except:
            self.ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
                  
    def _add(self, rect):
        sketchLines = self.sketchLines.addTwoPointRectangle(rect.startPoint, rect.endPoint)
        return sketchLines
        
    def addBoard(self, rect):
        self.boardOcc = self.mapComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())
        self.boardComp = self.boardOcc.component  
        self.boardNum += 1
        self.boardName = 'Board {}'.format(self.boardNum)
        self.boardComp.name = self.boardName

        self._newSketch(compOcc=self.boardOcc)
        self.sketch.name = self.boardName
        self._add(rect)
        
        idx = len(self.sketch.profiles) - 1
        prof = self.sketch.profiles.item(idx)
        # Create the patch feature
        patches = self.boardComp.features.patchFeatures
        patchInput = patches.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        patchFeature = patches.add(patchInput)
        self.boardBody = patchFeature.bodies.item(0)
        self.boardBody.name = self.boardName
        self.boardBody.material = self.paperMaterial
        
    def addComponent(self, source, targetRect, name):
        self.body = source.body
        self.compOcc = self.boardComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())
        self.compComp = self.compOcc.component  
        self.compComp.name = name
        self._newSketch(self.boardOcc)
        self.sketch.name = '{}{}'.format(name, "_target")
        self._add(targetRect)
        idx = len(self.sketch.profiles) - 1
        targetProf = self.sketch.profiles.item(idx)
        self.mapJoint(self.compOcc, targetProf, source) 
                
    def mapJoint(self, compOcc, targetProf, source): 
        self.body.copyToComponent(compOcc)
        self.bodyOcc = compOcc.bRepBodies.item(0)
        matchFace = findMatchFace(self.bodyOcc)
        
        # Create the first joint geometry with the end face
        geo0 = adsk.fusion.JointGeometry.createByPlanarFace(matchFace.face, None, adsk.fusion.JointKeyPointTypes.CenterKeyPoint)
         # Create the second joint geometry with the sketch profile
        geo1 = adsk.fusion.JointGeometry.createByProfile(targetProf, None, adsk.fusion.JointKeyPointTypes.CenterKeyPoint)
        # Create joint input
        joints = self.rootComp.joints
        jointInput = joints.createInput(geo0, geo1)
        jointInput.angle = source.angle
        jointInput.isFlipped = True
        # Create the joint
        joints.add(jointInput)
        self.bodyOcc.material = self.paperMaterial
        
   
#==============================================================================
# This class will create a body which will be moved to serveral alignments with  
# respect to x,y,z axis, copied, then mapped to a sketch profile with a joint.
#==============================================================================
class createSourceComponent:
    def __init__(self):    
        app = adsk.core.Application.get()
        actDoc = app.activeDocument.dataFile
        if actDoc != None:
            app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
       
        design = adsk.fusion.Design.cast(app.activeProduct)
        self.rootComp = design.rootComponent
        occs = self.rootComp.occurrences
        for occ in occs:
            if occ.component.name.startswith('map'):
                occ.deleteMe()
                break
        for occ in occs:
            if occ.component.name.startswith('map'):
                occ.deleteMe()
                break

        rootComp = design.rootComponent
        mapSourceOcc = rootComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())
        self.mapSourceComp = mapSourceOcc.component  
        self.mapSourceComp.name = 'mapSource'

        sketches = self.mapSourceComp.sketches
        sketch = sketches.add(self.rootComp.xYConstructionPlane)
        sketchLines = sketch.sketchCurves.sketchLines
        
        compRect = rect(insert=(0, -4), size=(18, 4))
        self.length = compRect.defaultLength
        self.width = compRect.defaultWidth
        self.thick = .75 * 2.54
        self.widthInternal = compRect.width        
        
        sketchLines.addTwoPointRectangle(compRect.startPoint, compRect.endPoint)
        self.targetProf1 = sketch.profiles.item(0)
        
        profiles = adsk.core.ObjectCollection.create()
        profiles.add(self.targetProf1)
       
        extrudeFeats = self.mapSourceComp.features.extrudeFeatures
        extrudeFeature = extrudeFeats.addSimple(profiles, 
                                                adsk.core.ValueInput.createByReal(self.thick),
                                                adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        self.body = extrudeFeature.bodies.item(0)                                        
        self.bodies = adsk.core.ObjectCollection.create()
        self.bodies.add(extrudeFeature.bodies.item(0))
        
        chkRotate = checkRotate(self.body)
        self.isRotate = chkRotate.isRotate
        self.angle = chkRotate.angle
        self.odd = chkRotate.odd
        
    def move(self, vector):        
        transform = adsk.core.Matrix3D.create()
        transform.translation = vector
        moveFeats = self.mapSourceComp.features.moveFeatures
        moveFeatureInput = moveFeats.createInput(self.bodies, transform)
        moveFeats.add(moveFeatureInput)
        chkRotate = checkRotate(self.body)
        self.isRotate = chkRotate.isRotate
        self.angle = chkRotate.angle
        self.odd = chkRotate.odd
        
    def rotate(self, angle, axis, origin):        
        transform = adsk.core.Matrix3D.create()
        transform.setToRotation(angle, axis, origin)
        moveFeats = self.mapSourceComp.features.moveFeatures
        moveFeatureInput = moveFeats.createInput(self.bodies, transform)
        moveFeats.add(moveFeatureInput)
        chkRotate = checkRotate(self.body)        
        self.isRotate = chkRotate.isRotate
        self.angle = chkRotate.angle
        self.odd = chkRotate.odd

#==============================================================================
# check body orientation with respect to X,Y,Z axis & determine if rotation is
# required when assemble joint is created with sketch profile in XY plane
#==============================================================================
RadFor90 = round(1.5707963267948966, 3) # 90 deg in radians
class checkRotate:
    def __init__(self, body):
        source = findMatchFace(body)
        lengthVector = source.lengthVector
        widthVector = source.widthVector
        
        app = adsk.core.Application.get()
        design = adsk.fusion.Design.cast(app.activeProduct)
        xVector = design.rootComponent.xConstructionAxis.geometry.direction
        yVector = design.rootComponent.yConstructionAxis.geometry.direction
        zVector = design.rootComponent.zConstructionAxis.geometry.direction
        
        aList = []
        aList.append(round(lengthVector.angleTo(xVector), 3)) # 0 - lengthToX
        aList.append(round(widthVector.angleTo(xVector), 3))  # 1 - widthToX
        aList.append(round(lengthVector.angleTo(yVector), 3)) # 2 - lengthToY
        aList.append(round(widthVector.angleTo(yVector), 3))  # 3 - widthToY
        aList.append(round(lengthVector.angleTo(zVector), 3)) # 4 - lengthToZ
        
        self.odd = self.chkOriented(aList)  

        aList.append(round(widthVector.angleTo(zVector), 3))  # 5 - widthToZ      
        
        self.isRotate = False
        self.angle = adsk.core.ValueInput.createByReal(0)

        t1 = (aList[0] == RadFor90 and aList[1] == 0)
        t2 = (aList[1] == RadFor90 and aList[0] == 0 and aList[4] != RadFor90)
        t3 = ((aList[0] == RadFor90) and (aList[1] == RadFor90)) and ((aList[4] != 0) and (aList[2] != RadFor90))
        t4 = (aList[0] == RadFor90 and aList[3] == RadFor90)
#        print('t1 {} t2 {} t3 {} t4 {}'.format(t1, t2, t3, t4))    
        if (t1 or t2 or t3 or t4):
            self.angle = adsk.core.ValueInput.createByReal(-RadFor90)
            self.isRotate = True
            print('>*>*>*>* -90 angle rotate required!')
            
        if self.odd:    
            print('>*>*>*>* special angle required!')
         
        print('>>>>> {} - rotate: {} odd: {} lengthToX: {} widthToX: {} lengthToY: {} widthToY: {} lengthToZ: {} widthToZ: {}'.format(
        body.name,self.isRotate,self.odd,    aList[0],     aList[1],    aList[2],     aList[3],    aList[4],     aList[5]))
        
    def chkOriented(self, aList):
        hasOdd = False
        hasNormal = False
        for angle in aList:
            if angle != RadFor90 and angle != 0:
                hasOdd = True
            else:
                hasNormal = True
        return hasOdd and not hasNormal           
      
#==============================================================================
# Create a demo showing the issue I am having with a face to profile CenterKeyPoint
# joint assembly.  I need a way to determine the appropriate angle to use
# when creating a face to profile CenterKeyPoint joint using face from a body 
# that is not aligned with x,y, or z.        
#==============================================================================
def run(context):
    app = adsk.core.Application.get()
    ui = app.userInterface
    try:
        # length 0 deg to X, width 0 deg to Y, length & width 90 deg to Z
        source = createSourceComponent()
        
        # rotate on Y 45 deg to X and Z axis 
        angle = -0.785398  # -45 degrees
        axis = adsk.core.Vector3D.create(0.0, 10.0, 0.0)
        origin = adsk.core.Point3D.create(0.0, 0.0, 0.0)
        source.rotate(angle, axis, origin)
        
        mapBody = mapper()  
        boardRect = rect(insert=(0,0), size=(96,12))        
        mapBody.addBoard(boardRect)
        xOffset = 0
        yOffset = 0        
        
        compRect = rect(insert=(xOffset, yOffset), size=(source.length, source.width))
        mapBody.addComponent(source, compRect, 'Y45ToXandZ')  
        
        # rotate on X 45 deg to Y and Z axis 
        angle = -0.785398  # -45 degrees
        axis = adsk.core.Vector3D.create(10, 0.0, 0.0)
        origin = adsk.core.Point3D.create(0.0, 0.0, 0.0)
        source.rotate(angle, axis, origin)
        
        xOffset += source.length 
        compRect = rect(insert=(xOffset, yOffset), size=(source.length, source.width))
        mapBody.addComponent(source, compRect, 'X45ToYandZ')  
        
        # rotate on Z 45 deg to X and Y axis 
        angle = -0.785398  # -45 degrees
        axis = adsk.core.Vector3D.create(0.0, 0.0, 10)
        origin = adsk.core.Point3D.create(0.0, 0.0, 0.0)
        source.rotate(angle, axis, origin)
        
        xOffset += source.length 
        compRect = rect(insert=(xOffset, yOffset), size=(source.length, source.width))
        mapBody.addComponent(source, compRect, 'Z45ToXandY')  
        
        camera_ = app.activeViewport.camera
        camera_.isFitView = True
        app.activeViewport.camera = camera_        
        
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
Message 5 of 7
marshaltu
in reply to: balunist

Hello,

 

Thank you for the codes. I can run them now. I am not sure if I get your point correctly. If you know the direction of long edge of "Board" body you created and the vector of side face of "mapSource" body, you should be able to use them to calculate the angle and create joint with alignment. I have no better idea.

 

Thanks,

Marshal

 

 JointAngle.png



Marshal Tu
Fusion 360 Developer
Autodesk, Inc.

Message 6 of 7
balunist
in reply to: marshaltu

Marshal,

Thanks for your response.   I was hoping to find a way to calculate the necessary angle to specify on the jointInput.angle.   I measured the resulting angle as 30.4 degrees (0.5305801 radians).  If I specify that angle on in the joinInput.angle prior to the joints.add it is aligned.   Make the following change  at line 271 in the checkRotate class and you will see what I mean.

 

if self.odd:

angleNeeded = -0.5305801 # determine this angle based on body pre-position

print('>*>*>*>* special angle required!')

self.angle = adsk.core.ValueInput.createByReal(angleNeeded)

self.isRotate = True

 

Message 7 of 7
balunist
in reply to: balunist

OK, I believe I have a way to calculate the angle.    Here is the modified mapJoint method to measure the angle between the two occurrences provide by the resultant joint.   

Thanks!

    def mapJoint(self, compOcc, targetProf, source): 
        self.body.copyToComponent(compOcc)
        self.bodyOcc = compOcc.bRepBodies.item(0)
        matchFace = findMatchFace(self.bodyOcc)
        
        # Create the first joint geometry with the end face
        geo0 = adsk.fusion.JointGeometry.createByPlanarFace(matchFace.face, None, adsk.fusion.JointKeyPointTypes.CenterKeyPoint)
         # Create the second joint geometry with the sketch profile
        geo1 = adsk.fusion.JointGeometry.createByProfile(targetProf, None, adsk.fusion.JointKeyPointTypes.CenterKeyPoint)
        # Create joint input
        joints = self.rootComp.joints
        jointInput = joints.createInput(geo0, geo1)
        jointInput.angle = source.angle
        jointInput.isFlipped = True
        # Create the joint
        joint = joints.add(jointInput)
        
        # check angle occurances and if not zero delete joint, combine angle with inital angle, and redo joint
        occOne = joint.occurrenceOne
        bodyOneProxy = occOne.bRepBodies.item(0)
        newSourceOne = findMatchFace(bodyOneProxy)
        occTwo = joint.occurrenceTwo
        bodyTwoProxy = occTwo.bRepBodies.item(0)
        newSourceTwo = findMatchFace(bodyTwoProxy)
        measureResult = self.app.measureManager.measureAngle(newSourceOne.edge, newSourceTwo.edge)
        angle = measureResult.value
        if angle != 0:
            print('>>>>> Angle between source and target {}'.format(angle))
            joint.deleteMe()
            jointInput.angle = adsk.core.ValueInput.createByReal(source.angle.realValue - angle)
            joint = joints.add(jointInput)

 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report