Mesh disappears after input change

Hello everyone,
I am trying to create a custom goal based on the K2Goals (GitHub - Dan-Piker/K2Goals: Example Goals for use with the Kangaroo2 solver) examples. I started by just copying one of them (Hinge) and creating the following:

The SolveInstance is simply this:

    protected override void SolveInstance(IGH_DataAccess DA)
    {
        Point3d P0 = Point3d.Origin;
        Point3d P1 = Point3d.Origin;
        Point3d P2 = Point3d.Origin;
        Point3d P3 = Point3d.Origin;

        double RestAngle = 0, Strength=0;

        // Then we need to access the input parameters individually. 
        // When data cannot be extracted from a parameter, we should abort this method.
        if (!DA.GetData(0, ref P0)) return;
        if (!DA.GetData(1, ref P1)) return;
        if (!DA.GetData(2, ref P2)) return;
        if (!DA.GetData(3, ref P3)) return;
        if (!DA.GetData(4, ref RestAngle)) return;
        if (!DA.GetData(5, ref Strength)) return;
        
        SHinge A = new SHinge(P0, P1, P2, P3, RestAngle, Strength);
        DA.SetData(0, A);
    }

When I hit the button, it all works ok, but as soon as I change the angle or strength, the mesh disappears. Probably a noob mistake, but I can’t figure it out… Any ideas?

Hi Roi, As I have experienced the same Problem many times I just tried to reset the engine with pushing the button after any change because it doesn’t recognize the changes in a live manner!

Thanks, but it would be nice to have a solution. This issue doesn’t come up with Kangaroo’s predefined goals, so there must be something wrong…

Hi @roi.poranne,

Is your SHinge goal different from the Hinge goal, or does this happen with exactly the same code?

Can you check what the V output of the solver is after the mesh disappears?
If there are some points in there with extremely high values like 1e+50, it suggests there is some instability in the goal or the setup, and the simulation is exploding (Grasshopper completely stops displaying geometry if any points are a very large distance from the origin). Also note that hinge goals do usually need to be combined with Length goals on the edges to be stable, so if you are testing it in isolation you will probably get some strange results.

If that’s not the case it could be related to the indexing, though from the code you show that shouldn’t be a problem here. Since the Goal is getting created new whenever the input data updates, it gets indexed each time, so I would expect that to work.

Hi @DanielPiker,
Thanks for the suggestions. This is indeed exactly the same code as Hinge, and in fact, when I replace SHinge by Hinge it works exactly as expected.
I checked the V output and saw that it just outputs null, so maybe it is related to indexing, but I’m using points as input and not indices. Another hint that I found now is that it appears to disappear starting from one corner. I switched to the step solver and set it to run only one subiteration. Initially it looks like this:
image

If I change the strength once I get this:
image

After few more:
image

Until it disappears. You get the idea… I’ll continue debugging this, but more hints are very welcome :slight_smile:

Very strange. If it works when calling the Hinge goal, but not another goal containing identical code, on the same input geometry, I’m at a loss. Could you post the full code of the component class?

There. Thanks a lot.

SHinge.cs (2.9 KB) SBendingComponent.cs (4.0 KB) SBendingInfo.cs (1.3 KB)

I see that when the goal is recreated, the indices in PIndex are all zero, which causes some of the values to become NaN. In the first run they are set to some numbers, but subsequent runs are all zero. How are these number even generated? Is there an example with indices instead of points?

I circumvented the problem by using indices instead of positions. To get the indices I used this:


Which is not so nice. It would be great to have an understanding of what went wrong though, and why it works for the predefined goals.

1 Like

Ah, I think I spotted the issue! Sorry it took me a while.
In the SHinge goal, at line 38 you have:
PIndex = new int[4];
If you remove this line, I think it will work and allow live updates of the input parameters.

To explain about the PIndex-

When it runs, the solver builds a list of particles without duplicates, and the PIndex array of each goal contains the integer indices identifying the particles that goal acts on.

Normally when a goal is input into the solver from a goal component, this PIndex array is null.
For each goal in turn, the solver creates a new PIndex array then looks at the PPos array of positions, and for each one, searches for a particle at this location and assigns the index of that particle to the corresponding item of the goal’s PIndex array (adding a new particle if one doesn’t already exist there).

However… some goals also have the option to assign the PIndex values directly, before they go into the solver (this isn’t used much, but is potentially useful if you have something like a very large mesh where you know in advance the indexing, and will be updating the parameters frequently during iteration, so want to avoid the speed cost of the searching and indexing in the solver).

(This is all to allow the adding and removal of goals even between iterations, which wasn’t possible in the earliest versions of Kangaroo, which required a reset whenever the set of forces or particles was changed.)

If the PIndex array is not null, the solver assumes it has already been filled deliberately, and doesn’t try to assign it itself. Because your goal had the line initialising the array, it was filled with zeros, so the indexing wasn’t getting applied properly.

Now this is my fault, as I can see now that some of the example goals can be misleading by having that PIndex initialiser there.
The reason is that the components of some of the predefined goals work a bit differently - some actually do store their indexing so that it doesn’t get reassigned whenever, say, the strength parameter is updated. To make this work also when the inputs to the goal are data trees ends up making things a bit more complicated, since the SolveInstance gets called multiple times, so the indexing needs to be stored outside this. I’ve never been entirely happy with the solution I came up with for this.

I’ve found since though that just creating goals new each time SolveInstance is called is actually very rarely a problem, and it keeps the component code much simpler. It does mean that whenever you adjust a parameter like the strength or angle, the indexing gets assigned again, but unless you have tens of thousands of points, the time taken for this is negligible.

I hope this makes things a little clearer.
I’ll try and add some more examples not just of goals, but also of actual goal components.

(by the way, I recognised the name from some of your nice papers. The RoboCut work is very cool!)

That Solved it! Thank you so much for the explanation!
So in general, for better performance I should stick to indices, since otherwise they will be determined via closest point queries?
(Thanks for the complement! I’ve been learning how to use Kangaroo for the last couple of days and it has been an inspiration!)

1 Like

Yes, assigning indices if you know them in advance will generally be marginally quicker than just setting positions and letting the solver assign the indexing.
This indexing only takes a tiny amount of time though, since it doesn’t actually do a full closest point query.
(It also only affects the speed of the first iteration and of changing the input parameters)
For example - here for a mesh of over 4k springs, the indexing is being reassigned whenever you change the length slider, but it is still fairly responsive.
customgoalexample.gh (7.5 KB)

For comparison I made another example of a goal which does the same thing, but where the indexing is stored between iterations, instead of reassigned every time the input slider updates:
customgoalexample_persistent.gh (14.6 KB)

It is noticeably smoother when adjusting the length slider.
Note that the indexing is still not assigned directly by the goal here - it lets the solver handle that, but it stores the goal and its indexing between calls to RunScript.

This approach does not work when the input is a list or tree though, because then RunScript gets called for each item of the input, while the component is only storing one goal.