Quantcast
Channel: Manufacturing DevBlog
Viewing all 519 articles
Browse latest View live

Edit an Item BOM Row in Vault – VB example

$
0
0

By Wayne Brill

Here is a VB example that changes the Quantity of a BOM Row. To test this code you can add a button to this SDK sample:

C:\Program Files (x86)\Autodesk\Autodesk Vault 2016 SDK\vs12\VB\ItemEditor

 

After the code runs you can see the Quantity being changed in Vault Explorer. The ItemBOM and ItemAssoc are retrieved from the Item being edited. 

PrivateSub Button1_Click_WB(sender AsObject, e AsEventArgs) Handles Button1.Click
    Dim parentItem AsItem = m_selectedItem
    'Change this to use an Item Number in your vault
    Dim ChildItem AsItem = m_connection.WebServiceManager.ItemService.GetLatestItemByItemNumber("100001")
    Dim newQty AsInteger = 2
    updateBOMQuantity_WB(parentItem, ChildItem, newQty)
EndSub

PrivateSub updateBOMQuantity_WB(parentItem AsItem, childItem AsItem, newQty AsInteger)
    Dim item2 AsItem = Nothing
    Try
        item2 = m_connection.WebServiceManager.ItemService.EditItems(NewLong() {m_selectedItem.RevId})(0)

        Dim options AsBOMViewEditOptions = BOMViewEditOptions.Defaults OrBOMViewEditOptions.ReturnOccurrences _
                     OrBOMViewEditOptions.ReturnExcluded OrBOMViewEditOptions.ReturnUnassignedComponents

        ' Get the ItemBOM from the Item being edited
        Dim bom AsItemBOM = m_connection.WebServiceManager.ItemService.GetItemBOMByItemIdAndDate _
                                (item2.Id, DateTime.MinValue, BOMTyp.Latest, options)

        ' Get the ItemAssoc from the ItemBOM being edited
        Dim itemAssoc2 AsItemAssoc =
            bom.ItemAssocArray.FirstOrDefault(Function(x) x.CldItemMasterID = childItem.MasterId)

        Dim newBomItemAssocParam2 AsNewItemAssocParam
        newBomItemAssocParam2.Id = itemAssoc2.Id
        newBomItemAssocParam2.EditAct = BOMEditAction.Update
        newBomItemAssocParam2.Quant = newQty

        Dim bom2 AsItemBOM = m_connection.WebServiceManager.ItemService.UpdateItemBOMAssociations _
                  (item2.Id, {newBomItemAssocParam2}, BOMViewEditOptions.ReturnBOMFragmentsOnEdits)

        m_connection.WebServiceManager.ItemService.UpdateAndCommitItems(NewItem() {item2})

    Catch ex AsException
        MsgBox(ex.Message)
        m_connection.WebServiceManager.ItemService.UndoEditItems(NewLong() {item2.Id})
    EndTry
EndSub

Map family's table column to Inventor property

$
0
0

By Adam Nagy

When trying to figure out how to do something in the Inventor API, then almost always the best way to go is: do it in the UI, investigate in the API.

So I registered in the Content Center my own part, created a SIZE property then mapped it to Project.Description (Design Tracking Properties:Description):

PropertyMapping

Now using the API I can find out how the SIZE property got mapped to Project.Description property:

Public Sub CCtest()
  Dim cc As ContentCenter
  Set cc = ThisApplication.ContentCenter
  Dim ctvn As ContentTreeViewNode
  Set ctvn = cc.TreeViewTopNode. _
    ChildNodes("Features"). _
    ChildNodes("English"). _
    ChildNodes("Block")
  Dim cf As ContentFamily
  Set cf = ctvn.Families(1)
  Dim ctc As ContentTableColumn
  Set ctc = cf.TableColumns("Size")
  If ctc.HasPropertyMap Then
    Dim psid As String
    Dim pid As String
    Call ctc.GetPropertyMap(psid, pid)
    Debug.Print "PropertySetId = " & psid
    Debug.Print "PropertyId = " & pid
  End If
End Sub

Result:

PropertySetId = {32853F0F-3444-11d1-9E93-0060B03C1CA6}
PropertyId = 29

So basically you just have to look for the given property set in Document.PropertySets and use its InternalName property, then look for the property inside it and use the Property's PropId.

Overlapped Areas of Mated Bodies in Assembly

$
0
0


Question:

I have as assembly with 2 plate parts, they are constrained to each other face to face by mate constraint. How to get their overlapped area and boundary.

Solution:
From API help: TransientBRep.ImprintBodies finds regions of faces on two bodies which overlap and creates new bodies where the faces are split at the edges of the overlaps. This does not modify the original bodies but creates new transient bodies that contain the imprints.

The code below illustrates how to get the overlapped areas of two bodies of one mate constraint. To display the area, the code creates the relevant client graphics and moves them aside of the original bodies. The area and edges are selectable.

 




    Private Sub GetOverlappedArea()

        'get active Inventor
        Dim ThisApplication As Inventor.Application =
            System.Runtime.InteropServices.Marshal.GetActiveObject("Inventor.Application")' Get the active assembly document and its definition.
        Dim doc As AssemblyDocument
        doc = ThisApplication.ActiveDocument

        Dim def As AssemblyComponentDefinition
        def = doc.ComponentDefinition

        'assume one constraint is Mate Constraint
        Dim AConstraint As AssemblyConstraint
        AConstraint = def.Constraints(1)

        Dim oOccurrenceOne As ComponentOccurrence
        oOccurrenceOne = AConstraint.OccurrenceOne

        Dim oOccurrenceTwo As ComponentOccurrence
        oOccurrenceTwo = AConstraint.OccurrenceTwo



        ' Get the bodies in part space as transient bodies.
        Dim transBrep As TransientBRep
        transBrep = invApp.TransientBRep
        Dim body1 As SurfaceBody
        Dim body2 As SurfaceBody'assume the geometries in both occurrence are within the fist surface body'of the native model

        body1 = transBrep.Copy(oOccurrenceOne.Definition.SurfaceBodies(1))
        body2 = transBrep.Copy(oOccurrenceTwo.Definition.SurfaceBodies(1))

        ' Transform the bodies to be in the location represented in the assembly.
        Call transBrep.Transform(body1, oOccurrenceOne.Transformation)
        Call transBrep.Transform(body2, oOccurrenceTwo.Transformation)' Imprint the bodies.
        Dim newBody1 As SurfaceBody
        Dim newBody2 As SurfaceBody
        Dim body1OverlapFaces As Faces
        Dim body2OverlapFaces As Faces
        Dim body1OverlapEdges As Edges
        Dim body2OverlapEdges As Edges
        Call ThisApplication.TransientBRep.ImprintBodies(body1, body2, True, newBody1, newBody2, _
                                                         body1OverlapFaces, body2OverlapFaces, _
                                                         body1OverlapEdges, body2OverlapEdges)
        Dim matchingIndexList1(body1OverlapFaces.Count - 1) As Integer
        Dim matchingIndexList2(body2OverlapFaces.Count - 1) As Integer
        Dim i As Integer
        For i = 1 To body1OverlapFaces.Count' Find the indices of the overlapping face in the Faces collection.
            Dim j As Integer
            For j = 1 To newBody1.Faces.Count
                If body1OverlapFaces.Item(i) Is newBody1.Faces.Item(j) Then
                    matchingIndexList1(i - 1) = j
                    Exit For
                End If
            Next

            Dim body2Index As Integer
            For j = 1 To newBody2.Faces.Count
                If body2OverlapFaces.Item(i) Is newBody2.Faces.Item(j) Then
                    matchingIndexList2(i - 1) = j
                    Exit For
                End If
            Next
        Next

     ' The code below creates new non-parametric base features using the new imprinted bodies' so that the results can be visualized.  The new bodies are created offset from the' original so that they don't overlap and are more easily seen.' Define a matrix to use in transforming the bodies.
        Dim trans As Matrix
        trans = ThisApplication.TransientGeometry.CreateMatrix' Move the first imprinted body over based on the range so it doesn't lie on the original.
        trans.Cell(1, 4) = (body1.RangeBox.MaxPoint.X - body1.RangeBox.MinPoint.X) * 1.1
        Call ThisApplication.TransientBRep.Transform(newBody1, trans)' Move the second imprinted body over based on the rangeso it doesn't lie on the original.
        trans.Cell(1, 4) = trans.Cell(1, 4) + (body1.RangeBox.MaxPoint.X - body1.RangeBox.MinPoint.X) * 1.1
        Call ThisApplication.TransientBRep.Transform(newBody2, trans)'draw the point graphics
        Dim oCoordSet As GraphicsCoordinateSet = Nothing
        Dim oGraphicsNode As GraphicsNode = Nothing
        Dim oDataSets As GraphicsDataSets = Nothing'get datasets, dataset, graphics node for client graphics
        getCG(oGraphicsNode, oCoordSet, oDataSets)

        oGraphicsNode.Selectable = True

        'add surface Graphics
        Dim oBody1Graphics As SurfaceGraphics
        oBody1Graphics = oGraphicsNode.AddSurfaceGraphics(newBody1)
        oBody1Graphics.ChildrenAreSelectable = True
        For i = 1 To oBody1Graphics.DisplayedFaces.Count
            oBody1Graphics.DisplayedFaces.Item(i).Selectable = True
        Next

        Dim oBody2Graphics As SurfaceGraphics
        oBody2Graphics = oGraphicsNode.AddSurfaceGraphics(newBody2)

        oBody2Graphics.ChildrenAreSelectable = True
        For i = 1 To oBody2Graphics.DisplayedFaces.Count
            oBody2Graphics.DisplayedFaces.Item(i).Selectable = True
        Next

 
        ThisApplication.ActiveView.Update()

        'Now the faces of client graphics are selectable. The overlapped faces can be selected.'help function
Private Sub getCG(ByRef oGraphicsNode As Object, _
                     Optional ByRef oCoordSet As Object = Nothing, _
                     Optional ByRef oOutDataSets As Object = Nothing)

        Dim oDoc As Document
        oDoc = invApp.ActiveDocument

        Dim oDataOwner As Object = Nothing
        Dim oGraphicsOwner As Object = Nothing

        'check the document type and get the owner of the datasets and graphics
        If oDoc.DocumentType = DocumentTypeEnum.kPartDocumentObject Or oDoc.DocumentType = DocumentTypeEnum.kAssemblyDocumentObject Then
            oDataOwner = oDoc
            oGraphicsOwner = oDoc.ComponentDefinition
        ElseIf oDoc.DocumentType = DocumentTypeEnum.kDrawingDocumentObject Then
            If oDoc.ActiveSheet Is Nothing Then
                MsgBox("The current document is a drawing. The command is supposed to draw client graphics on active sheet! But active sheet is null!")
                Exit Sub
            Else
                oDataOwner = oDoc.ActiveSheet
                oGraphicsOwner = oDoc.ActiveSheet
            End If
        End If'delete the data sets and graphics if they exist
        Try
            oDataOwner.GraphicsDataSetsCollection("TestCG").Delete()
        Catch ex As Exception
        End Try

        Try
            oGraphicsOwner.ClientGraphicsCollection("TestCG").Delete()
        Catch ex As Exception
        End Try'create DataSets 
        Dim oDataSets As GraphicsDataSets = oDataOwner.GraphicsDataSetsCollection.Add("TestCG")
        oOutDataSets = oDataSets'create one coordinate data set
        oCoordSet = oDataSets.CreateCoordinateSet(oDataSets.Count + 1)'create graphics node
        Dim oClientGraphics As Inventor.ClientGraphics = oGraphicsOwner.ClientGraphicsCollection.Add("TestCG")
        oGraphicsNode = oClientGraphics.AddNode(oClientGraphics.Count + 1)

    End Sub

    End Sub





Imprint

Add the sketch using DrawingView Sketches when creating a section view

$
0
0

By Wayne Brill

In the SDK help-->Inventor API User's Manual----> Drawings--->Working With Drawing Views---> “Creating a section drawing view” it includes this code:

Dim oDrawingSketch As DrawingSketch
Set oDrawingSketch = oSheet.Sketches.Add

AddSectionView() fails when the Drawing Sketch is added using the Sheet Sketches.

If the DrawingSketch is added using the View Sketches the error goes away and the Section View is created.

Dim oDrawingSketch As DrawingSketch
’ Error goes away on AddSectionView() using the base view sketches collection
Set oDrawingSketch = oView1.Sketches.Add

This issue with the sample in the SDK help has been reported to Inventor Engineering.

Cable & Harness component names

$
0
0

By Adam Nagy

When iterating through the components of a Cable & Harness assembly you'll find VirtualComponentDefinition's named like "HA_VC_xxx":

...
Hard Drive:2
CD Ribbon Cable
  CD Ribbon Cable
  Ribbon Cable Connector:1
  Ribbon Cable Connector:2
  Ribbon Cable Connector:3HA_VC_100:1
Power Supply Harness
  Power Supply Connector:1
...

This does not line up with what you see in the Browser Tree. It's because what's listed there are not actually these components but just placeholders. If you select them and run a code like this then you can see that those parts are just ClientBrowserNodeDefinitionObject's:

Selectset

The Part Number of the VirtualComponentDefinition's resemble more how those components are represented in the Browser Tree, so you could use that instead:

Sub PrintHierarchyTreeRecursive(occs, indent)
  Dim occ As ComponentOccurrence
  For Each occ In occs
    Dim pss As PropertySets
    If TypeOf occ.Definition Is VirtualComponentDefinition Then
      Set pss = occ.Definition.PropertySets
    Else
      Set pss = occ.Definition.Document.PropertySets
    End If
    Dim partNumber As String
    partNumber = pss("Design Tracking Properties")("Part Number").value
    Write #1, Spc(indent); occ.Name + ", " + partNumber
    Call PrintHierarchyTreeRecursive(occ.SubOccurrences, indent + 2)
  Next
End Sub

Sub PrintHierarchyTree()
  Dim asm As AssemblyDocument
  Set asm = ThisApplication.ActiveDocument
  
  Open "C:\temp\assemblyparts.txt" For Output As #1
  Call PrintHierarchyTreeRecursive(asm.ComponentDefinition.Occurrences, 0)
  Close #1
End Sub

PartNumber

 

Delete Objects with Fusion 360 API

$
0
0

By Xiaodong Liang

All objects that can be deleted support the the deleteMe method. The usage is  very straightforward. Get the object and call deleteMe.

I found a forum post that one customer failed to delete the object which is within the generic object collection.

http://forums.autodesk.com/t5/api-and-scripts/how-do-i-delete-a-sketch-curve-line-arc-etc/m-p/6394526#U6394526

but, it looks working well at my side. The following code demo three scenarios:

1. delete selected objects

2. delete the specific object in the collection created by UI

3. delete the specific object in the generic collection (e.g. in this code, the collection is the offset curves collection)


def deleteObjectsOfSelection():

    app = adsk.core.Application.get()
    try:      
        #assume some entities are selected
        for eachObj in app.userInterface.activeSelections:
            if eachObj.isValid:
                #get the selected entity of the selection.                
                #call deleteMe
                eachObj.entity.deleteMe()
    except:
        ui = app.userInterface
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))  
            

def deleteItemOfObjCollectionItem():

    app = adsk.core.Application.get()
    
    try:
        #current product            
        product = app.activeProduct
        #current component
        rootComp = product.rootComponent 
        #get sketches collection
        sketches = rootComp.sketches
        #get one sketch 
        if sketches.count > 0:
            oneSketch = sketches.item(0)
            oneSketch.deleteMe()
       
    except:
        ui = app.userInterface
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))             


def deleteItemOfGenericCollection():
    app = adsk.core.Application.get()
    ui = app.userInterface
    
    try:
      
        
        #select a sketch        
        selectedObjs = app.userInterface.activeSelections
        if selectedObjs.count == 0:
           ui.messageBox('no object is selected in the document!')
           return
        
        oneObj = selectedObjs.item(0)
        if oneObj.entity.objectType != 'adsk::fusion::Sketch':
             print( oneObj.entity.objectType)
             ui.messageBox('not a sketch!')
             return        
        sketchIn = oneObj.entity 
        
        sketchIn.isComputeDeferred = True
        
        curvesIn = sketchIn.sketchCurves
        if curvesIn.count == 0:
             ui.messageBox('no curve in the sketch!')
             return
             
        curvesForOffset = adsk.core.ObjectCollection.create() 
        offsetCurves = adsk.core.ObjectCollection.create()
        
        curvesForOffset = sketchIn.findConnectedCurves(curvesIn.item(0));
        offsetCurves = sketchIn.offset(curvesForOffset, adsk.core.Point3D.create(100,100,100), 2.0);
        
        if offsetCurves == None or offsetCurves.count ==0:
             ui.messageBox('no offset curves are created!')
             return
        
        transform = adsk.core.Matrix3D.create();
        
        #current product            
        product = app.activeProduct
        #current component
        rootComp = product.rootComponent 
        #get sketches collection
        sketches = rootComp.sketches
        
        #create a new sketch 
        sketchOut = sketches.add(rootComp.xYConstructionPlane)
        sketchIn.copy(offsetCurves, transform, sketchOut);
        
        #delete the offseted curves in sketchIn
        for eachCurve in offsetCurves:
            eachCurve.deleteMe()
        
        sketchIn.isComputeDeferred =False 
       
    except:
        ui = app.userInterface
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))  
     

Check or UnCheck iProperties “Date Created” and “Date Checked” example

$
0
0

By Wayne Brill

It is not obvious how to add or remove a check to an iProperty such as Creation Date:

image

 

In the VBA watch window you will see that when the "Date Created" iProperty is not checked the Expression property of the iProperty is not a valid date. When it is checked it has a valid date. If you give the iProperty Expression a valid date value it will be checked. Using a date value of "1601, 1, 1" removes the checkmark.  

Here is a VBA example:

Public Sub iProperties_Test()
    ' Get the active document.
    Dim invDoc As Document
    Set invDoc = ThisApplication.ActiveDocument
   
    ' Get the design tracking property set.
    Dim invDesignInfo As PropertySet
    Set invDesignInfo = invDoc.PropertySets.Item("Design Tracking Properties")
       
    Dim iProp As Property
    For Each iProp In invDesignInfo
        'If iProp.Name = "Date Checked" Then
        If iProp.DisplayName = "Date Created" Then
            'Property is unchecked with this
           ' iProp.Expression = "1601, 1, 1"
            'Property is checked with this
            iProp.Expression = #7/22/2016#
           
            Exit For
        End If
    Next iProp
   
End Sub

Fusion API: Toggle [Capture Design History]

$
0
0

By Xiaodong Liang

There are two primary ways to work in the "Model" environment; parametrically, which focuses on the relationships between features, or directly, which deals only with selected model faces. The main difference is that "Direct Modeling" functions by manipulating faces without regard for previously established relationships.

There are quite a lot of articles on such topic on internet. I just picked two of them:

http://www.engineering.com/tutorials/turning-off-caption-design-history-in-autodesk-fusion-360/

https://www.youtube.com/watch?v=YJU4avXux2s

There is a switch in [Capture Design History] of the context menu of root node of the browser pane.

clip_image001

With API, the corresponding object is adsk.fusion.DesignTypes.


def toogleCaptureHistory(isEnabled):
     app = adsk.core.Application.get()
     des = adsk.fusion.Design.cast(app.activeProduct)
     
     if isEnabled:
         des.designType = adsk.fusion.DesignTypes.ParametricDesignType 
     else:
         des.designType = adsk.fusion.DesignTypes.DirectDesignType
    

Autodesk App Store Forge and Fusion 360 Hackathon September 1 - October 31

$
0
0

By Virupaksha Aithal

If you’ve been thinking about developing on the Autodesk® Forge Platform, Autodesk’s new cloud platform, or on Autodesk® Fusion 360™—or, if you’re just curious about learning more about these technologies and what they could do to help your business or your career—join us for our upcoming free online hackathon.  This is our fourth online hackathon and you are welcome to participate in all or any part of it.


The Autodesk® App Store Forge and Fusion 360® Hackathon is an online event which consists of two segments:

  1. A series of web training and open Q&A sessions related to the Autodesk Forge platform and Fusion 360 - open to anyone interested in learning about these APIs.
  2. An online hackathon where developers can develop new apps on the Forge platform or on Fusion 360 for submission to Autodesk App Store.

We know that learning and implementing something new takes time and often gets put off so we’ve designed this event to give you the support you need - as well as a financial incentive - to motivate you to learn the APIs and publish your app on the Forge platform or on Fusion 360 in the Autodesk App Store. It will make taking action to publish apps simple and feasible. 


The training webinars will help familiarize you with the Forge platform and in the open Q&A sessions you’ll have the opportunity to receive help in real-time from the Autodesk team answering your questions to help you develop your app(s). For all such new apps submitted to the Autodesk App Store by midnight on October 31, 2016 and accepted for publication in the store by November 30, 2016, each entrant of the Hackathon will receive US$ 500 for one eligible app per entrant.

 
You may attend the educational webinars without publishing an app. Click here for Webinar only registration or get full details and register for the Hackathon at http://autodeskforge.devpost.com. If you register for the Hackathon you will automatically be registered for the webinars.  They will be recorded so that you can view them at your convenience.

Visit the pages on the Hackathon website to learn more about the opportunities and resources for developing on Forge, Fusion 360 and publishing in the Autodesk App Store and if you have questions send us an email.

Hackathon promo image_3

Making your add-in callable from VBA

$
0
0

By Adam Nagy

If you want to create your add-in in a way that its functionality can also be accessed by other programs (e.g. from VBA) then there are two ways to do it:

a) Use ControlDefinition object
Most add-ins implement ControlDefinition objects, which are hooked up to controls in the Inventor User Interface. When the user clicks that control (mainly buttons) then your code can run to do its thing. These ControlDefintion objects can also be accessed by other programs, just like the ones Inventor exposes. Once you found the ControlDefinition object you need you can call its Execute or Execute2 method in order to run it:
Running Commands Using the API
&
Execute vs Execute2 of ControlDefinition

You can also send parameters to a command in the form of a string. Your add-in then could call PeekPrivateEvent to access that data: 
Provide command parameters using PostPrivateEvent

b) Implement Automation property
Your add-in could also implement the Automation property of the ApplicationAddInServer class as described in this blog post:
Connect to an Inventor add-in an external application

Through the Automation property you can expose an object that can have any functions and properties that you want.

Fusion API: Usage of Mouse Event

$
0
0

By Xiaodong Liang

Custom actions with mouse are widely used in an add-in. Fusion provides MouseEvents that monitors the events. The custom Command object contains the corresponding functions such as Command.mouseClick(), Command.mouseMove() and Command.mouseWheel() etc. By this methods, the add-in will hook the mouse behaviors and grab the data for custom workflow.

The typical steps to use MouseEvent are (take Python as an example)

1. Define the custom class (say MyMouseClickHandler) of the corresponding event which derives from adsk.core.MouseEventHandle


# Event handler for the mouseClick event.
class MyMouseClickHandler(adsk.core.MouseEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.MouseEventArgs.cast(args)
            
            pstn = eventArgs.position            
            txtBox = eventArgs.firingEvent.sender.commandInputs.itemById('clickResults')
            txtBox.text = str(pstn.x) + ', ' + str(pstn.y)
        except:
            ui = adsk.core.Application.get().userInterface
            if ui:
                ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

2. In the class of ButtonDefinition.commandCreated, new an object of MyMouseClickHandler, and delegate with Command.mouseClick


class SampleCommandCreatedEventHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:        
            eventArgs = adsk.core.CommandCreatedEventArgs.cast(args)
            cmd = eventArgs.command
            inputs = cmd.commandInputs
          
            inputs.addTextBoxCommandInput('clickResults', 'Click', '', 1, True)
            
            # Connect to the execute event.
            onExecute = SampleCommandExecuteHandler()
            cmd.execute.add(onExecute)
            handlers.append(onExecute)
            
            # Connect to mouse click event.
            onMouseClick = MyMouseClickHandler()
            cmd.mouseClick.add(onMouseClick)
            handlers.append(onMouseClick)
           
        except:
            ui = adsk.core.Application.get().userInterface
            if ui:
                ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

Then, in MyMouseClickHandler, you can grab the data of clicking and pass them to the next workflow.

This is a demo project of Python. It delegates all mouse events, and display the data in the dialog. It is written by my colleague Jack Li in engineer team (Thanks Jack).

Download Python MouseSample

I converted the code to C++ project.

Download C++ MouseSample

The steps are similar:

1. Define  the custom class of the event (MyMouseClickHandler)


class MyMouseClickHandler : public MouseEventHandler
{
public:
	void notify(const Ptr& eventArgs) override
	{


		Ptr firingEvent = eventArgs->firingEvent();
		if (!firingEvent)
			return;

		Ptr command = firingEvent->sender();
		if (!command)
			return;

		Ptr inputs = command->commandInputs();
		if (!inputs)
			return;

		 
		//get the data of the clicking position
		std::string str = "{";
		str += std::to_string(eventArgs->position()->x());
		str += ",";
		str += std::to_string(eventArgs->position()->y());
		str += "}";
		
		//display the data in the custom dialog
		Ptr  txtBox = inputs->itemById("clickResults");
		txtBox->text(str);

	}
};

2. Delegate an object of MyMouseClickHandler with Command.mouseClick


// CommandCreated event handler.
class OnCommandCreatedEventHandler : public CommandCreatedEventHandler
{
public:
	void notify(const Ptr& eventArgs) override
	{
		if (eventArgs)
		{
			Ptr cmd = eventArgs->command();
			if (cmd)
			{
				// Connect to the CommandExecuted event.
				Ptr onExec = cmd->execute();
				if (!onExec)
					return;
				bool isOk = onExec->add(&onExecuteHander_);
				if (!isOk)
					return;				 

				// Define the inputs.
				Ptr inputs = cmd->commandInputs();
				if (!inputs)
					return;

				//add inputs 
				inputs->addTextBoxCommandInput("clickResults", "Click", "",1,true);
				 

				//add mouse events

				// Connect to the MouseEvent.
				cmd->mouseClick()->add(&onMouseClickHandler_); 
				 			 

			}
		}
	}
private:
	OnExecuteEventHander onExecuteHander_;
	MyMouseClickHandler onMouseClickHandler_;
	 
 } cmdCreated_;

<

Hope it could help C++ programmer.

Create Sketch Text on Face of Assembly

$
0
0

By Xiaodong Liang

Question:
In a assembly (or part), I want to select a face, on this face I want the Partnumber as a sketh text, (with a big font)

Is there a simple solution?

medium

Answer:

The code below assumes a face of assembly is selected, and a planar sketch will be created on the face, and a text will be added on the location of the center point of the face area.

VBA code:


Sub addTextToFace()

    Dim oAssDoc As AssemblyDocument
    Set oAssDoc = ThisApplication.ActiveDocument
        
    Dim oAssDef As AssemblyComponentDefinition
    Set oAssDef = oAssDoc.ComponentDefinition
    
    'assume a face has been selected in the context of assembly
    Dim oFace As FaceProxy
    Set oFace = oAssDoc.SelectSet(1)
    
    
    
    'make sure it is a planar face
    If oFace.SurfaceType = kPlaneSurface Then
        
        Dim oPlanarSurface As Plane
        Set oPlanarSurface = oFace.Geometry
        
        Dim oSketch As PlanarSketch
        Set oSketch = oAssDef.Sketches.Add(oFace)
        
        
        'trying to choose an appropriate point
        'assume this planar face has one edge loop only
        Dim oEdgeLoop As EdgeLoop
        Set oEdgeLoop = oFace.EdgeLoops(1)
        
        Dim oMinPt As Point
        Set oMinPt = oEdgeLoop.RangeBox.MinPoint
        
        
        Dim oMaxPt As Point
        Set oMaxPt = oEdgeLoop.RangeBox.MaxPoint
        
        Dim oCenterPt As Point
        Set CenterPt = ThisApplication.TransientGeometry.CreatePoint((oMaxPt.X + oMinPt.X) / 2#, (oMaxPt.Y + oMinPt.Y) / 2#, (oMaxPt.Z + oMinPt.Z) / 2#)
         

        
        'get one point on the face and transform to the point2d on the sketch
        Dim oTextPt As Point2d
        'Set oTextPt = oSketch.ModelToSketchSpace(oPlanarSurface.RootPoint)
        Set oTextPt = oSketch.ModelToSketchSpace(CenterPt)
        
        'add the textbox
        Dim oSketchText As TextBox
        Set oSketchText = oSketch.TextBoxes.AddFitted(oTextPt, "MY TEST")
        
        
         
    Else
        MsgBox "please select a planar face!"
    End If
    
    

End Sub

 

iLogic code:


  
oAssDoc = ThisApplication.ActiveDocument        

oAssDef = oAssDoc.ComponentDefinition

If oAssDoc.SelectSet.Count = 0 Then
	MsgBox("no face is selected!")
Else
	'assume a face has been selected in the context of assembly
	oFace = oAssDoc.SelectSet(1)
	
	'make sure it is a planar face
	If oFace.SurfaceType = SurfaceTypeEnum.kPlaneSurface Then
		
		oPlanarSurface = oFace.Geometry
		
			'add a sketch
		oSketch = oAssDef.Sketches.Add(oFace)    
			
		'trying to choose an appropriate point
		'assume this planar face has one edge loop only
		oEdgeLoop = oFace.EdgeLoops(1)
			
		oMinPt = oEdgeLoop.RangeBox.MinPoint 
		oMaxPt = oEdgeLoop.RangeBox.MaxPoint
			
		CenterPt = ThisApplication.TransientGeometry.CreatePoint((oMaxPt.X + oMinPt.X) / 2#, (oMaxPt.Y + oMinPt.Y) / 2#, (oMaxPt.Z + oMinPt.Z) / 2#)    
			
		'get one point on the face and transform to the point2d on the sketch 
		'Set oTextPt = oSketch.ModelToSketchSpace(oPlanarSurface.RootPoint)
		oTextPt = oSketch.ModelToSketchSpace(CenterPt)
			
			'add the textbox
		oSketchText = oSketch.TextBoxes.AddFitted(oTextPt, "MY TEST") 
		
	Else
		MsgBox( "please select a planar face!")
	End If
End If

     

image

Introduction to Fusion 360 API

$
0
0

By Virupaksha Aithal

Here is the recording of the “Introduction to Fusion 360 API” presented on 11th of October 2016

Fusion 360 resources:

Learn about the Fusion 360 API here

Talk to fellow Fusion API enthusiasts on the Fusion 360 API forum

Check out the additional learning resources on Github

See Fusion 360 Fundamentals for learning materials

Fusion API: Usage of Table Control

$
0
0

By Xiaodong Liang

Fusion provides various types of controls (CommandInput) for a command. There is a Python demo in API help.


Table type is available with API. It can even embed sub controls within the table or table cell, by which we can implement more scenarios of customization. In my practice recently, I got some knowledge of Table. I am writing down for reference. The whole demo codes has been posted to

https://github.com/xiaodongliang/Fusion-Table-Control

1. CommandInputs.addTableCommandInput adds a new table to the dialog. It defines the table Id, name, columns number and the width ratio of each column. e.g. the code below specifies the table has 3 columns with same width.
     tableInput = inputs.addTableCommandInput(commandId +'my_table', 'Table', 3, '1:1:1')

2. To add a child control to the cell, you need firstly get tableInput.commandInputs and call
   tableInput.addCommandInput (child_control,rowindex, column index)

Note: The API demo above uses childCommandInputs, but this method has been removed. Now please use tableInput.commandInputs

3. If you want to add a child control that locates within the table, instead of a cell, the method is:
     tableInput.addToolbarCommandInput(child_control)

4. , any input changing of a control will fire InputChangedEventHandler. Typically, we will get out the inputs collection of the dialog, and find out the specific control. But in C++, it will be null if getting inputs collection by eventArgs->inputs(). Fortuently, a workaround is available as below.   


class MyCommandInputChange : public InputChangedEventHandler
{
public:
  void notify(const Ptr& eventArgs) override
    {
         Ptr changedInput = eventArgs->input();

	//One issue: in C++, eventArgs->inputs() is invalid if the event is fired for the embedded control within a table
         //Ptr inputs = eventArgs->inputs(); 
	//workaround:	 Ptr command = eventArgs->firingEvent()->sender();
	 Ptr inputs = command->commandInputs();

2016-10-21 14-51-52

iLogic: ActiveDocument.ComponentDefinition throws exception for specific document

$
0
0

By Xiaodong Liang

Recently, we got a strange issue. With a specific dataset, we opened all files in cascade views, then activated each document and ran an external rule. The rule is quite simple. It just gets the document ComponentDefinition and access parameters.

DimcompDef=ThisApplication.ActiveDocument.ComponentDefinition

For most documents, it works well. but for specific assembly document, it throws an exception that says: Member not found.

We doubted ComponentDefinition / Parameters have been broken when activating the document at his side, so we tested with a VBA code. But the code tells the ComponentDefinition / Parameters are always valid. While the issue seems not always reproducible.

Finally, our expert of iLogic Mike Deck shared the insights:

It is caused by internal .NET behavior to do with COM interop. Here’s a simplified rule that will reproduce it:

- Create a new part

- Run the rule in the part

- Create a new assembly

- Run the rule in the assembly

The order matters: it won’t break if you run it in the assembly first.

Here’s what’s happening: ActiveDocument returns a Document object. There is no ComponentDefinition property on the Document object. So VB.NET tries to do late binding. When you run the rule on a Document that is also a PartDocument, it will find a ComponentDefinition property with a DispId on that object. Then later you run the rule on a Document that is also an AssemblyDocument. It also has a ComponentDefinition property, but with another DispId.

For some reason, VB.NET will fail to find that property. it might probably cache the first DispId (from PartDocument), and associates it with the Document interface. Then it thinks that it can always use that DispId to access ComponentDefinition on a Document. But that DispId will fail if the object is an AssemblyDocument.

It seems like an issue of Microsoft .NET. So in such case, there is a workaround. In VB.NET code, never try to access the ComponentDefinition on a Document object. Instead, always cast it to a PartDocument or AssemblyDocument first. Here’s sample code to get the UserParameters:

Dimdoc=ThisApplication.ActiveDocument

DimparamsAsParameters= Nothing

Ifdoc.DocumentType=DocumentTypeEnum.kAssemblyDocumentObjectThen

  DimassemDocAsAssemblyDocument=doc

  params=assemDoc.ComponentDefinition.Parameters

ElseIfdoc.DocumentType=DocumentTypeEnum.kPartDocumentObjectThen

  DimpartDocAsPartDocument=doc

  params=partDoc.ComponentDefinition.Parameters

EndIf

 

DimuserparamsAsUserParameters=params.UserParameters

It requires a lot more than a single line of code, but it avoids the problem.


Fusion API: Which API I should choose?

$
0
0

By Xiaodong Liang

Fusion API provides 3 types of languages: Python, JavaScript and C++. I found an interesting discussion on Fusion API forum, where our API designer Brian Ekins gave an answer on the specifics of each language. To make it more searchable on internet, I posted it in the blog. And we could polish it with more information in the future.

image

There are several things to consider when choosing a language.

As far as the API for each of the languages being developed; functionality is delivered for all three languages at the same time.  Internally, we implement the C++ interface first but then wrappers are constructed automatically for Python and JavaScript.  So all three have the same capabilities.  However, there's one exception to this in that JavaScript doesn't support all of the events.  We ran into a technical problem with events and JavaScript so we've blocked exporting most events in JavaScript.

For speed of execution, C++ will be the fastest since it is compiled code that runs within the Fusion process.  On Windows and Mac, Python also runs in-process but it's interpreted and Python isn't known as a particularly fast language.  On Windows, JavaScript runs out of process and as a result is much slower.  On Mac, it runs in-process and is similar to Python.

For speed of development, much depends on which language you have the most experience with already.  A new language always has a learning curve at the beginning.  But assuming that you are comfortable with all three, I would say that Python is probably the language someone could develop code the fastest in.  One reason for this is that you do the development and debugging within the same IDE.  You can also do some prototyping using the text window in Fusion.  C++ has an advantage that it is typed so you're able to catch many problems while writing and compiling, while you end up debugging a lot of problems at run time with the other two languages.

Another thing to consider is if you plan to deliver your program commercially.  With JavaScript and Python you're essentially delivering your source code.  With C++ you're delivering a binary which is much harder for someone to crack.

If you don't care about the security of your code, I would recommend Python and if you do need to protect your code then C++.

I agree with Brian’s comments. Just one of my practices of Fusion API: I prefer to Python because it is easy to debug and make a quick demo code.

Add to "Frequently Used Subfolders" collection

$
0
0

By Adam Nagy (@AdamTheNagy)

As mentioned in this forum post, unfortunately, the relevant API does not work :(

Until it gets sorted, you could use the following workaround: edit directly the Project file (*.ipj), which is in fact an xml file.

The <Path> needs to starts with "Workspace\" and then continue with the path to the subfolder you want to add. 

I'm showing how to do it in VBA, but .NET (also accessible from iLogic) has similar support for xml editing.  Just add a reference to the "Microsoft XML, v6.0" library:

MicrosoftXml

Sub AddFrequentlyUsedSubfolder()
    Dim oProjectManager As DesignProjectManager
    Set oProjectManager = ThisApplication.DesignProjectManager
    Dim oProject As DesignProject
    Set oProject = oProjectManager.ActiveDesignProject' Use 'Microsoft XML, v6.0'
    Dim xmlDoc As DOMDocument60
    Set xmlDoc = New DOMDocument60
    xmlDoc.async = False
    Dim strProjectPath As String
    strProjectPath = oProject.FullFileName' Load the project file (*.ipj)
    Call xmlDoc.Load(strProjectPath)' Select the  node
    Dim xmlProjectPaths As IXMLDOMNode
    Call xmlDoc.setProperty("SelectionLanguage", "XPath")
    Set xmlProjectPaths = xmlDoc.selectSingleNode("/InventorProject/ProjectPaths")' Add the new subfolder' We need to add something like this:' <ProjectPath pathtype="FrequentlyUsedFolder">'   <PathName>MyFolder</PathName>'   <Path>Workspace\MySubfolder</Path>' </ProjectPath>
    Dim xmlProjectPath As IXMLDOMNode
    Set xmlProjectPath = xmlDoc.createNode(1, "ProjectPath", "")
    Dim xmlPathType As IXMLDOMAttribute
    Set xmlPathType = xmlDoc.createAttribute("pathtype")
    xmlPathType.value = "FrequentlyUsedFolder"
    Call xmlProjectPath.Attributes.setNamedItem(xmlPathType)
    Dim xmlPathName As IXMLDOMNode
    Set xmlPathName = xmlDoc.createNode(1, "PathName", "")
    xmlPathName.Text = "MyFolder"
    Call xmlProjectPath.appendChild(xmlPathName)
    Dim xmlPath As IXMLDOMNode
    Set xmlPath = xmlDoc.createNode(1, "Path", "")
    xmlPath.Text = "Workspace\" + "MySubfolder"
    Call xmlProjectPath.appendChild(xmlPath)
    Call xmlProjectPaths.appendChild(xmlProjectPath)' Make another project active so that we can save the changes
    Call oProjectManager.DesignProjects(1).Activate(False)' Save the changes
    Call xmlDoc.Save(strProjectPath)' Make our project active again
    Call oProject.Activate(False)
End Sub

MyFolder

 

Forge DevCon - Call For Proposals

$
0
0

By Adam Nagy (@AdamTheNagy)

Have you created something cool using the Autodesk Forge platform and want to share with others what you learnt? :)
Then why not do it at our next Autodesk Forge DevCon taking place at the Fort Mason Center, San FranciscoJune 27th to 28th, 2017?

Call for proposals is now open.

To submit your proposal, visit forge.autodesk.com/AttendDevCon and click on the 'Call for Papers' tab - or go straight to the Call for Papers site, where you'll find more information about the types of sessions we're looking for.

Fusion API: Attribute returns none for the Face that has been attached attribute

$
0
0

By Xiaodong Liang

Question:
I attached the attributes to some selected faces of an assembly. When I iterate the faces of each component and try to find out those faces, they returned none attributes. My code is as below. It assumes a face has been selected in the assembly.
def main():
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        face = adsk.fusion.BRepFace.cast(ui.activeSelections.item(0).entity)
        face.attributes.add('MyGroup', 'MyName', 'MyValue')
        design = adsk.fusion.Design.cast(app.activeProduct)
      
        for occ in design.rootComponent.occurrences:
            comp = occ.component
            body = comp.bRepBodies.item(0)
            for face in body.faces:
            
                attr = face.attributes.itemByName('MyGroup', 'MyName')
               
                #always none
                if attr != None:
                    ui.messageBox('Attribute is {}'.format(attr))
                else:
                    print('Attribute is None')

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

Solution:
The geometries we see in the assembly are actually the references from the native components. In Fusion 360, they are called Proxy objects. So, when you select a face in the context of an assembly, the face is actually a proxy face. That means, the attributes was added to the proxy face. While when you iterate the faces of each component, that will be the native face which does not have such attributed added for proxy face. So if you want to find back those information, you need to get the proxy face from the native one. e.g.


def main():
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        face = adsk.fusion.BRepFace.cast(ui.activeSelections.item(0).entity)
        face.attributes.add('MyGroup', 'MyName', 'MyValue') 
        design = adsk.fusion.Design.cast(app.activeProduct)
       
        for occ in design.rootComponent.occurrences:
            comp = occ.component
            body = comp.bRepBodies.item(0)
            for face in body.faces:
                #get proxy face in the assembly context, corresponding to the
                # native face in this occurence
                faceproxy= face.createForAssemblyContext(occ)
                
                #get attribute from the proxy face
                attr = faceproxy.attributes.itemByName('MyGroup', 'MyName')
                #attr = face.attributes.itemByName('MyGroup', 'MyName')
                
                if attr != None: 
                    ui.messageBox('Attribute is {}'.format(attr))
                else:
                    print('Attribute is None')

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

Fusion API: HTTP Request in Fusion 360 by Python

$
0
0

By Xiaodong Liang

For a session of AU China, I produced a small demo on how to connect designer, customer and vendor by Fusion 360 API and Forge. In the process, I borrowed some codes of HTTP Request from the add-in of MakeTime. This add-in uses a typical Python module: requests . I think it will be useful to extract the core section for reference.

After downloading the module requests, import it to the add-in.  

import requests

GET Request


r = requests.get(url_for_get) 
#status code status_code = r.status_code 
#response message 
res_msg = r.text 

Post Request


#playload_array is the array for the parameters of the post request
#upload_file_stream is the file stream that to be uploaded to the remote server
r = requests.post(url_for_post,data=payload_array,file=upload_file_stream)

#status code
status_code = r.status_code
#response message
res_msg = r.text

for more detail on request module of Python, please refer to http://docs.python-requests.org/en/master/.

The codes below is a simply sample add-in. It posts some parameters to the server, including the base64 string of the snapshot of current view. It also submits current Fusion 360 file to the server as a file. To verify the workflow, the codes takes advantage of a test server: http://www.posttestserver.com/

After execution, if the post succeeded, a message will be displayed in the textbox, in which an URL points to the contents and a few lines describing the post. The whole demo package is

Download MyHTTPRequest  


import adsk.core, adsk.fusion, adsk.cam, traceback
from .HTTPRequest import requests
import base64
import tempfile
import uuid



app = None
ui  = None
commandId = 'CommandInputHTTPRequest'
commandName = 'Command Input HTTP Request'
commandDescription = 'Command Input HTTP Request'

payload_data = dict()

handlers = [] 

class MyCommandDestroyHandler(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 MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:            
            ui.messageBox('fired!')url = 'http://posttestserver.com/post.php'
            payload_data['post body string 1'] =  'post body value 1'
            payload_data['post body string 2'] =  'post body value 2'
            payload_data['post body string 3'] =  'post body value 3'
            payload_data['post body string 4'] =  'post body value 4'
            # Instantiate Export Manager
            current_design_context = app.activeProduct
            export_manager = current_design_context.exportManager
            #temp id for the file name
            transaction_id = str(uuid.uuid1())

            #snapshot of the model
            ui.activeSelections.clear()
            output_snapshot_name = tempfile.mkdtemp()+'//'+ transaction_id +'.jpg'
            app.activeViewport.saveAsImageFile(output_snapshot_name, 300, 300)  
            #base64 string of the image
            encoded_string = ''
            with open(output_snapshot_name, "rb") as image_file:
                encoded_string = base64.b64encode(image_file.read())                
            payload_data['snapshot'] = encoded_string 
            #upload a Fusion 360 file 
            payload_data['uuid'] = transaction_id + '.f3d'
            output_file_name = tempfile.mkdtemp()+'//'+ transaction_id +'.f3d'
            options = export_manager.createFusionArchiveExportOptions(output_file_name)
            export_manager.execute(options)
            fusion_file = {'file': open(output_file_name, 'rb')}
            #upload a step file 
            #payload_data['uuid'] = transaction_id + '.step'
            #output_file_name = tempfile.mkdtemp()+'//'+ transaction_id +'.step'
            #options = export_manager.createSTEPExportOptions(output_file_name)
            #export_manager.execute(options)
            #temp = {'file': open(output_file_name, 'rb')}                           

             # Send to platform
            try:
                message = "Error: "

                # POST response               
                res = requests.post(url, data=payload_data,files=fusion_file)

                # Check status
                if res.status_code == 200:  # success
                    message = "Posting Succeeded! "  + res.text          

                else:  # failure/res.status_code==422           
                    message += str(res.status_code)

            # Connection timed out
            except requests.exceptions.ConnectTimeout:
                message += "Connection timed out."

            # Failed to connect
            except requests.exceptions.ConnectionError:
                message += "Connection erroraa."
            #display the message of post response 
            cmd = args.command             
            inputs = cmd.commandInputs                                  
            txtBox = inputs.addTextBoxCommandInput('postresponse', 'Post Response', '', 5, True)
            txtBox.text = message 
        except:
            if ui:
                ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def run(context):
    ui = None
    try:
        global app
        app = adsk.core.Application.get()
        global ui
        ui = app.userInterface

        global commandId
        global commandName
        global commandDescription

        # Create command defintion
        cmdDef = ui.commandDefinitions.itemById(commandId)
        if not cmdDef:
            cmdDef = ui.commandDefinitions.addButtonDefinition(commandId, commandName, commandDescription)

        # Add command created event
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        # Keep the handler referenced beyond this function
        handlers.append(onCommandCreated)

        # Execute command
        cmdDef.execute()

        # 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())) 
Viewing all 519 articles
Browse latest View live