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: 

A problem accessing occurrences of SLIDER joints within occurrences of Component

11 REPLIES 11
SOLVED
Reply
Message 1 of 12
RogerInHawaii
754 Views, 11 Replies

A problem accessing occurrences of SLIDER joints within occurrences of Component

I previously asked for some help in properly accessing joint occurrences within component occurrences. Several people, including @jeff_strater provided excellent help and helped me to not only access the full set of allJoints and allAsBuiltJoints via the root, but also to distinguish one joint occurrence from another (which all have the same NAME), by accessing the joint's fullPath property, since each one (should!) have a unique fullPath.

That worked just fine for the REVOLUTE joints that I was then working on, and I can indeed now manipulate individual occurrences of my revolute joints. For example, as I scanned through the  allJoints and allAsBuiltJoints arrays I found three separate joints each with the exact same name, as I had expected to find, at each one I displayed its fullPath, and each one had a distinct, unique path...

I'm simplifying the names here just for clarity...

The first Rev Joint named "RJ" had path "LTV:1 + HPLG:1"
The second Rev Joint named "RJ" had path "LTV:1 + HPLG:2"

The third Rev Joint named "RJ" had path "LTV:1 + HPLG:3"

In other words they are distinguishable by the HPLG version numbers, HPLG standing for "Hexagon Panel - Landing Gear". So, I succeeded in accessing all three of the "RJ" joints and distinguishing among them so that I could subsequently, individually, manipulate each one separately.

So far so good.

But my project ALSO includes SLIDER joints. Here's a pic of the ONE of the Hexagon Panels and the locations of the Revolute and the Slider joints that I'm trying to access...

Slider Joint Problem 1.jpg

 You can see that this panel contains both the Revolute joint AND the Slider joint that I want to access.

But I have THREE of these panels, as seem here.

Slider Joint Problem 2.jpg

 

When I go through the root's allJoints and allAsBuilt joints to locate the REVOLUTE joints I get each one, and each one has a UNIQUE fullPath, as I showed above.

BUT when I try to do the same thing, going through the root's allJoints and allAsBuilt joints, to locate the SLIDER joints, here's what I get...

The first Slider Joint named "SJ" has path "LTV:1 + HPLG:3"
The second Slider Joint named "SJ" has path "LTV:1 + HPLG:3"

The third Slider Joint named "SJ" has path "LTV:1 + HPLG:3"

 

They all have the SAME fullPath. And, weirdly, they all reference HPLG:3!!

When I actually pick up the jointMotion object for each of them (one for each) and use those jointMotion objects to attempt to control the  three individual slider joints, each one actually moves the SAME slider joint. And even weirder, they don't move the joint that resides in HPLG:3, they instead move the joint that resides in HPLG:1.

So, something is really weird with occurrences of Slider Joints. It sure seems that it's not actually distinguishing between multiple occurrences of the same slider joint, but rather thinks that they're all the same thing.

Oh, and I guess I should mention that I'm using C++ for my script and therefore accessing the C++ API.

I hope this has been clear as to what's happening and I'm not just suffering from a stroke.

 

ALSO, on a side note, it APPEARS that, in spite of what the API documentation indicates, the generic Joint object does not actually HAVE an assemblyContext property. If you go through the root->allJoints list and pick up each joint individually and then access its assemblyContext() method, what it returns is a NULL. If you caste the returned joint to, say, a revoluteJoint, then its assemblyContext()method does indeed return a pointer to the assemblyContext and you can then get, from that, things like its fullPath property.


11 REPLIES 11
Message 2 of 12
BrianEkins
in reply to: RogerInHawaii

All motion types for joints should behave the same.  I wrote a little test script to report on all joints in the model and I'm not seeing the problem you described.  The script is below.  It reports each joint with the type of motion it controls and the proxy path to the joint.

 

def runJointReport(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        design = adsk.fusion.Design.cast(app.activeProduct)
        root = design.rootComponent

        textPalette = ui.palettes.itemById('TextCommands')
        if not textPalette.isVisible:
            textPalette.isVisible = True
        textPalette.writeText("== Joint Results ==")

        cnt = 0
        for joint in root.allJoints:
            cnt += 1
            textPalette.writeText('  Joint ' + str(cnt) + ', ' + joint.jointMotion.classType())
            textPalette.writeText('     ' + joint.name + ', ' + getPath(joint))

        for joint in root.allAsBuiltJoints:
            cnt += 1
            textPalette.writeText('  As-Built Joint ' + str(cnt) + ', ' + joint.jointMotion.classType())
            textPalette.writeText('     ' + joint.name + ', ' + getPath(joint))
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


# Builds up the string showing the proxy path by stepping up the path from
# the proxy entity itself to each occurrence that defines its context.
def getPath(ent):
    path = ''
    if ent.assemblyContext:

        occ = ent.assemblyContext
        while occ:
            if path == '':
                path = occ.name
            else:
                path = occ.name + '/' + path
occ = occ.assemblyContext path = 'Root/' + path else: path = 'Root' return path
---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
Message 3 of 12
RogerInHawaii
in reply to: BrianEkins

It appears that your code is in Python. Mine is in C++. My code does essentially the same thing, but when it attempts to pick up the assemblyContext of the Joint object :

 

Ptr<Occurrence> propertyValue = joint_var->assemblyContext();


the returned pointer is NULL, so there's no way for it to access the fullPath property of the assemblyContext, which is what my code is attempting to acquire, OR for it to wend its way back up the hierarchy, which is what your code does. Without access to the assemblyContext of the joint it simply cannot determine the full path for the joint.

I'll keep trying various approaches. Perhaps the C++ implementation of the IDE has some errors.

Message 4 of 12
BrianEkins
in reply to: RogerInHawaii

The Python API is simply a wrapper that calls the C++ API that you're using so the behavior will be the same or at least nothing should ever work in Python but fail in C++. I know you're using C++ but writing the Python script was a simpler, faster way to create a basic test to see if there is a problem with Fusion. If you create a new Python script you can paste my code into it and run it with your assembly to verify there isn't something weird going on with your model.
 
The assemblyContext property will return null but it should only be when you're at the root level and can't go any higher. 
---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
Message 5 of 12
RogerInHawaii
in reply to: BrianEkins

The actual problem is not really that I can't seem to access the assemblyContext, and through that the fullPath.

The real problem is this:

1) I have multiple occurrences (instances) of a given Slider joint in the design. Each one, of course, has the exact same name.

2) I scan through the root's allAsBuiltJoints, finding those instances one-by-one.

 

3) For each one that I find I store its respective JointMotion individually into separate entries in a SliderJointMotion array. That SHOULD let me keep them all separate and subsequently address and manipulate them separately.

 

4) I then go through that SliderJointMotion array and, one by one pass the SliderJointMotion to a method that manipulates the joint.

BUT, when I do that, no matter which one of the SliderJointMotion objects that I pass to my manipulation method, it ALWAYS manipulates the FIRST joint, never any of the other ones.

I've created a very simple test design. I first created a components consisting of a cyclinder and a rods, and defining a Slider Joint between the cylinder and rod, so that manipulating it causes the rod to move out and then back in to the cylinder.

I then did a Copy/Paste twice of the entire component, creating a total of three occurrences of it. The Slider Joint in each one has the exact same name. I then wrote a C++ script that gathers the individual SliderJointMotion objects and then attempts to manipulate each one individually. But when I run that script the only one that moves is the joint associated with the FIRST instance of the overall component. 

test.jpg

 

Now, when I did something similar in my actual project and ALSO accessed the root's allJoints array in order to pick up a number of instances of an REVOLUTE JOINT MOTIONS everything works just fine. Manipulating each one of those Revolute Joint Motions actually move the desired joints.

It SEEMS that picking up instances of SliderJointMotions always accesses the very same joint motion, instead of accessing the individual occurrences of those joints.

I'm attaching the design file and the C++ file to this posting.

Message 6 of 12

For reasons I cannot fathom it failed to upload the C++ file. SO, here it is...

#include <Core/CoreAll.h>
#include <Fusion/FusionAll.h>
#include <CAM/CAMAll.h>

using namespace adsk::core;
using namespace adsk::fusion;
using namespace adsk::cam;

#include <Core/Application/Application.h>
#include <Core/Application/Documents.h>
#include <Core/Application/Document.h>
#include <Core/Application/Product.h>
#include <Core/Geometry/Matrix3D.h>
#include <Core/UserInterface/UserInterface.h>
#include <Fusion/Components/Component.h>
#include <Fusion/Components/Occurrence.h>
#include <Fusion/Components/Occurrences.h>
#include <Fusion/Components/OccurrenceList.h>
#include <Fusion/Fusion/Design.h>

#include <windows.h>
#include <stdio.h> 

using namespace adsk::core;
using namespace adsk::fusion;
using namespace adsk::cam;

Ptr<Application> theFusion360Application;
Ptr<UserInterface> ui;
Ptr<Documents> currentOpenDocuments;
Ptr<Product> theActiveProduct;
Ptr<Design> theActiveDesign;
Ptr<Component> theRootComponent;
Ptr<Occurrences> allRootOccurrences;
Ptr<Joints> theRootComponentJointsList;
Ptr<AsBuiltJoints> theRootComponentAsBuiltJointsList;
Ptr<Components> allComponents;
std::vector<Ptr<Joint>> AllJointsFromTheRoot;
std::vector<Ptr<AsBuiltJoint>> allAsBuiltJointsFromTheRoot;


#define UpdateImage(); adsk::doEvents();

//
// SHOW MESSAGE
//
void Show(std::string msg)
{
	ui->messageBox(msg);
}

//
// MANIPULATE ONE SLIDER JOINT
//
BOOL ManipulateOneSliderJoint(Ptr<SliderJointMotion> TheSliderJointMotion)
{
	if (!TheSliderJointMotion)
	{
		Show("Called ManipulateOneSliderJoint with NULL TheSliderJointMotion");
		return FALSE;
	}
	double Minimum, Maximum;
	Minimum = TheSliderJointMotion->slideLimits()->minimumValue();
	Maximum = TheSliderJointMotion->slideLimits()->maximumValue();

	int const NumberOfMotionIncrements = 200;
	double MotionIncrement = (Maximum - Minimum) / NumberOfMotionIncrements;

	// Move out
	for (double Position = Minimum; Position <= Maximum; Position += MotionIncrement)
	{
		TheSliderJointMotion->slideValue(Position);
		UpdateImage();
	}
	// Move back in
	for (double Position = Maximum; Position >= Minimum; Position -= MotionIncrement)
	{
		TheSliderJointMotion->slideValue(Position);
		UpdateImage();
	}

	return TRUE;
}

extern "C" XI_EXPORT bool run(const char* context)
{
	// Acquire all the necessary parts of the application, aborting (return false) if it fails to acquire any necessary part.

	theFusion360Application = Application::get();
	if (!theFusion360Application)
	{
		// Can't do this because we don't have the ui object, which is acquired from this application object. !!! Show("FAILED to get theFusion360Application");
		return false;
	}

	ui = theFusion360Application->userInterface();
	if (!ui)
		return false;

	currentOpenDocuments = theFusion360Application->documents();
	if (!currentOpenDocuments)
	{
		Show("FAILED to get currentOpenDocuments");
		return false;
	}

	theActiveProduct = theFusion360Application->activeProduct();
	if (!theActiveProduct)
	{
		Show("FAILED to get theActiveProduct");
		return false;
	}

	theActiveDesign = theActiveProduct;
	if (!theActiveDesign)
	{
		Show("FAILED to get theActiveDesign");
		return false;
	}

	theRootComponent = theActiveDesign->rootComponent();
	if (!theRootComponent)
	{
		Show("FAILED to get theRootComponent");
		return false;
	}

	allRootOccurrences = theRootComponent->occurrences();
	if (!allRootOccurrences)
	{
		Show("FAILED to get allRootOccurrences");
		return false;
	}

	allComponents = theActiveDesign->allComponents();
	if (!allComponents)
	{
		Show("FAILED to get allComponents");
		return false;
	}

	//
	// HERE IS WHERE THE TESTING BEGINS
	//
	int const TheNumberOfSliderJoints = 3;
	// Create an array that will hold all of the slider joint's jointMotion objects individually.
	Ptr<SliderJointMotion> TheSetOfSliderMotions[TheNumberOfSliderJoints];

	std::vector<Ptr<AsBuiltJoint>> vectorOfAllAsBuiltJointsFromRoot;
	vectorOfAllAsBuiltJointsFromRoot = theRootComponent->allAsBuiltJoints();
	Ptr<AsBuiltJoint> ThisAsBuiltRootJoint;

	// All of the slider joints were created as AsBuilt joints, so go through the vectorOfAllAsBuiltJointsFromRoot, find each one and
	// put each joint's jointMotion into a separate element of TheSetOfSliderMotions 
	if (vectorOfAllAsBuiltJointsFromRoot.size() > 0)
	{
		int IndexIntoTheSetOfSliderMotions = 0;
		for (int IndexIntoAllAsBuiltJointsFromRoot = 0; IndexIntoAllAsBuiltJointsFromRoot < vectorOfAllAsBuiltJointsFromRoot.size(); ++IndexIntoAllAsBuiltJointsFromRoot)
		{
			ThisAsBuiltRootJoint = vectorOfAllAsBuiltJointsFromRoot[IndexIntoAllAsBuiltJointsFromRoot];
			// SLIDER JOINTS : Each one has the exact same name because that are each within a separate Occurrence (instance) of the SlidingRodInsideCyclinder component.
			if (ThisAsBuiltRootJoint->name() == "Slider1")
			{
				// Save the joint's jointMotion to the array of TheSetOfSliderMotions 
				TheSetOfSliderMotions[IndexIntoTheSetOfSliderMotions] = ThisAsBuiltRootJoint->jointMotion();

				++IndexIntoTheSetOfSliderMotions;
			}
		}
		// We SHOULD end up with IndexIntoTheSetOfSliderMotions equal to TheNumberOfSliderJoints
		if (IndexIntoTheSetOfSliderMotions != TheNumberOfSliderJoints)
		{
			Show("Failed to find all " + std::to_string(TheNumberOfSliderJoints) + " jointMotions");
			return false;
		}
	}
	else
	{
		Show("OOOPS, we don't seem to have any AsBuiltJointsFromRoot");
		return false;
	}

	// We should now have all of the individual (instances of) the various slider jointMotions in the array, TheSetOfSliderMotions,
	// and be able to manipulate each one individually.
	// Do them one at a time.
	for (int IndexIntoTheSetOfSliderMotions = 0; IndexIntoTheSetOfSliderMotions < TheNumberOfSliderJoints ; ++IndexIntoTheSetOfSliderMotions)
	{
		Show("About to manipulate slider # " + std::to_string(IndexIntoTheSetOfSliderMotions));
		ManipulateOneSliderJoint(TheSetOfSliderMotions[IndexIntoTheSetOfSliderMotions]);
	}

	return true;
}

#ifdef XI_WIN

#include <windows.h>

BOOL APIENTRY DllMain(HMODULE hmodule, DWORD reason, LPVOID reserved)
{
	switch (reason)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

#endif // XI_WIN
Message 7 of 12

@jeff_strater  Could you please take a look at this and see if you can make some recommendation? It sure does seem that you can't reference individual occurrences of SLIDER joints, although you can for revolute joints.

Message 8 of 12
BrianEkins
in reply to: RogerInHawaii

I was curious about this and played around for a bit.  There appears to be a bug in what's returned by the API.  I had a similar model I had built a few years ago and it still works correctly.  I found that it works as expected with joints but has the weird behavior you're seeing with as-built joints.  You might want to try recreating the joints in your model as regular joints and see if that changes the behavior.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
Message 9 of 12
RogerInHawaii
in reply to: BrianEkins

Thank you for continuing to look into this. And I'm glad that at least someone else is indeed seeing the same issue, so I'm not totally crazy!

I'll see if I can change those As-Built joints to simple joints.

I've found in other designs I've worked on that this distinction between joints and as-built joints has often caused problems. I sure wish there were just "joints", with maybe a flag to indicate how they're initially positioned. Accessing them via the API always causes a problem when you have to somehow know before hand whether its a joint or an as-built joint that you're accessing, and sometimes need to scan through two different joint arrays to find the one you want.

Well, on to some more testing...

Message 10 of 12

@RogerInHawaii , sorry, been swamped, just getting to this.  Thanks, @BrianEkins for finding the root cause.  TBH, I didn't believe it at first, so had to try for myself.  But, yes, this does seem to be an API bug with as-built joints.  It's a bit surprising, since, below a certain point, a joint is a joint is a joint, but there must be enough differences there that the assemblyContext does not work correctly for as-built joints.  Nice detective work!

 


Jeff Strater
Engineering Director
Message 11 of 12
goyals
in reply to: jeff_strater

Thanks @RogerInHawaii  for posting the issue. Thanks @BrianEkins and @jeff_strater for validating it. I created UP-45236 in our backlog to track this.



Shyam Goyal
Sr. Software Dev. Manager
Message 12 of 12
RogerInHawaii
in reply to: goyals

@goyals @jeff_strater @BrianEkins 

I changed that joint that was causing all the problems from an asBuilt joint to a regular joint and now my script works. I can indeed pick up each of those joints and manipulate them individually, as opposed to the initial symptom of addressing any one of them but always have the same (original) joint move. My initial thoughts were that it was a difference between slider joints and revolute joints but advice and testing by others (thank you jeff and brian) narrowed it down to some strange difference between normal joints and asBuilt joints and that apparently there actually is a bug in the Fusion code.

I am able to proceed now with my design, using the problem-avoidance approach of making sure I use regular joints and not asBuilt joints. Here's hoping there will actually be a fix the bug.

And, again, thank you to everyone who helped out.

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