Merge two fixtures

Let us know if you have a nice R.U.B.E script to share!
Post Reply
iforce2d
Site Admin
Posts: 860
Joined: Sat Dec 22, 2012 7:20 pm

Merge two fixtures

Post by iforce2d » Fri Aug 02, 2013 8:39 am

The script at the bottom of this post takes two fixtures and joins them together. You need to select the 'destination' fixture which will remain, and the other fixture will be deleted. You also need to select one vertex from each fixture to specify where the join should be made. If the body of the deleted fixture is left with no fixtures, the body will also be deleted (comment out the last part of the script if you don't want this).

If the necessary items are not selected, the script uses the queryYesNo dialog to remind the user that they need to select them. The dialog has a Yes/No choice, but the result is not used so you can click either of the buttons.

Here is a simple example, using two line fixtures. The fixture on the left is selected to remain:
Selection_060.png
Selection_060.png (3.95 KiB) Viewed 20174 times
...and the two closest vertices are selected to be the join position. The order of the vertices (shown in red) is important!
Selection_061.png
Selection_061.png (8.36 KiB) Viewed 20174 times
This is the result:
Selection_062.png
Selection_062.png (6.42 KiB) Viewed 20174 times
The script works by adding all the vertices from the to-be-deleted fixture, to the fixture that will remain, starting from the selected points, so the vertex winding of the two fixtures is important. For example, if we had selected the fixture on the right, we would get this result:
Selection_067.png
Selection_067.png (5.04 KiB) Viewed 20174 times
It can be a bit confusing when this happens, but you can usually reverse the winding of one of the fixtures to get the result you intended.

This script does not care what type of shape the fixture has, so you can also use it to merge fixtures of different types. Keep in mind that the fixture which remains will dictate the shape type of the result. For example, we can merge the two shapes here into one polygon fixture by selecting the polygon to be the fixture that remains:
Selection_070.png
Selection_070.png (10.43 KiB) Viewed 20174 times
Result:
Selection_071.png
Selection_071.png (8.29 KiB) Viewed 20174 times
Hey... that's not what we wanted :?

In this case, we need to reverse the winding on one of the fixtures (best to keep polygons wound counter-clockwise so I will change the line fixture):
Selection_073.png
Selection_073.png (10.75 KiB) Viewed 20174 times
... and also select the vertices that give a clean line sequence that does not self-intersect:
Selection_074.png
Selection_074.png (10.63 KiB) Viewed 20174 times
Result:
Selection_075.png
Selection_075.png (10.79 KiB) Viewed 20174 times
Unfortunately, it's not very intuitive to know how to get the arrangement you want, but this is about as good as the script can do. I humbly suggest a little trial and error, since there are only four possibilities in total. In particular I think the most typical cases where this will be used are:

1) to connect lines end-to-end as in the first example above, for which you should select the last vertex of the fixture to remain, and the first vertex of the fixture to be deleted. Because the vertices to choose are not optional, you will need to reverse the windings of the fixtures as necessary.

2) to connect two polygons. Both polygons will typically be wound counter-clockwise, so the problem shown above will not occur. It's best to leave the windings alone, and select the pair of vertices as necessary. There are usually only two possibilities that make sense, for example to join the two sections of ground below, you would choose either the top pair, or the bottom pair, depending on which of the fixtures was selected to remain:
Selection_076.png
Selection_076.png (11.7 KiB) Viewed 20174 times
Here is the script:

Code: Select all

void main() {
	//check that one fixture is selected
	if ( sf().length != 1 ) {
		queryYesNo("Please select a single fixture to be the destination for the merged result");
		return;
	}
	fixture destinationFixture = sf()[0];

	//check that two vertices are selected
	if ( sv().length != 2 ) {
		queryYesNo("Please select one vertex from each of the fixtures to merge");
		return;
	}
	vertex[] vs = sv();
	vertex v0 = vs[0];
	vertex v1 = vs[1];
	fixture f0 = v0.getFixture();
	fixture f1 = v1.getFixture();

	//check that one (and only one) of the vertices belongs to the selected fixture
	if ( ! (f0 != f1 && (f0 == destinationFixture || f1 == destinationFixture)) ) {
		queryYesNo("Please select one vertex from each of the fixtures to merge");
		return;
	}

	//make sure 0 is the destination, and 1 is the source
	if ( f1 == destinationFixture ) {
		vertex tmpv = v1;
		v1 = v0;
		v0 = tmpv;
		fixture tmpf = f1;
		f1 = f0;
		f0 = tmpf;
	}

	//add all vertices from f1 to f0
	uint dstIndex = v0.index+1;
	uint srcIndex = v1.index;
	for (int i = 0; i < f1.getNumVertices(); i++) {
		f0.addVertex( dstIndex++, f0.getBody().getLocalPoint(f1.getVertex(srcIndex).wpos) );
		srcIndex = (srcIndex + 1) % f1.getNumVertices();
	}

	body b1 = f1.getBody();

	//delete the source fixture (the body will remain)
	f1.delete();

	//delete body if it now has no fixtures
	if ( b1.getFixtures().length == 0 )
		b1.delete();

}

tescott
Posts: 68
Joined: Wed Feb 06, 2013 6:32 pm

Re: Merge two fixtures

Post by tescott » Fri Aug 02, 2013 12:12 pm

Sweet!

tescott
Posts: 68
Joined: Wed Feb 06, 2013 6:32 pm

Re: Merge two fixtures

Post by tescott » Mon Aug 05, 2013 6:05 pm

Just had the chance to use this script for the first time. It works great! Thanks!

I didn't realize that if you selected a fixture, and then changed over to vertex mode, that the fixture remains selected. Good to know!

Thanks again,
--tim

iforce2d
Site Admin
Posts: 860
Joined: Sat Dec 22, 2012 7:20 pm

Re: Merge two fixtures

Post by iforce2d » Tue Aug 06, 2013 4:32 am

if you selected a fixture, and then changed over to vertex mode, that the fixture remains selected.
Yep. There are separate selection sets for each item type, which are held in the document itself, so they are shared across all views of the document. Scripts have no notion of a 'current edit mode' so the functions getSelectedBodies, getSelectedFixtures etc will all work in any script, at any time, even on the command line. It's only the editor view GUI that has any concept of dealing with one type of item at a time.

qq200600
Posts: 29
Joined: Fri Dec 28, 2012 9:27 am

Re: Merge two fixtures

Post by qq200600 » Sat Apr 01, 2017 9:21 am

17:20:12: EditorView::updateObjectsAfterLoad()
17:20:13: onUndoRedo
17:20:13: EditorView::updateObjectsAfterLoad()
17:20:20: created: getVertex(18,0).select();
17:20:20: created: getVertex(7,16).select();
17:20:20: onUndoRedo
17:20:20: EditorView::updateObjectsAfterLoad()
17:20:23: buildCustomMenu coming from EditorView::doCustomContextMenu
17:20:23: MainWindow::buildCustomMenu
17:20:23: MainWindow::loadCustomMenuActions
17:20:23: Found 2 entries
17:20:23: First pass ignoring action menu: myscripts-
17:20:23: Returning from loadCustomMenuActions because nothing has changed.
17:20:23: After loading custom menu, m_rootCustomMenuActionItem = 0x79444376X
17:20:23: After loading custom menu, m_rootCustomMenuActionItem has 10 children
17:20:27: Custom menu action triggered: config/actionMenu/default/joinFixtures
17:20:27: Null pointer access

iforce2d
Site Admin
Posts: 860
Joined: Sat Dec 22, 2012 7:20 pm

Re: Merge two fixtures

Post by iforce2d » Sat Apr 01, 2017 12:39 pm

Thanks for the mail follow-up qq200600.

Looks like the updated version of Angelscript in v1.7.4 is causing this problem. Since the above post does not explain much, the problem is that using the "Join fixtures" script stops without doing anything and the message "Null pointer access" is shown in the message log panel.

I have found that this can be worked around by changing this line of the joinFixtures.rs script (in the config/actionMenu/default folder):

Code: Select all

f0.addVertex( dstIndex++, f0.getBody().getLocalPoint(f1.getVertex(srcIndex).wpos) );
... to this:

Code: Select all

body tmpBody = f0.getBody();
vertex tmpVertex = f1.getVertex(srcIndex);
vec2 tmpVec = tmpBody.getLocalPoint(tmpVertex.wpos);
f0.addVertex( dstIndex++, tmpVec );
As you can see, all this does is spread the same work into some temporary variables, so I don't know what's going on here...

qq200600
Posts: 29
Joined: Fri Dec 28, 2012 9:27 am

Re: Merge two fixtures

Post by qq200600 » Sat Apr 01, 2017 2:11 pm

iforce2d wrote:Thanks for the mail follow-up qq200600.

Looks like the updated version of Angelscript in v1.7.4 is causing this problem. Since the above post does not explain much, the problem is that using the "Join fixtures" script stops without doing anything and the message "Null pointer access" is shown in the message log panel.

I have found that this can be worked around by changing this line of the joinFixtures.rs script (in the config/actionMenu/default folder):

Code: Select all

f0.addVertex( dstIndex++, f0.getBody().getLocalPoint(f1.getVertex(srcIndex).wpos) );
... to this:

Code: Select all

body tmpBody = f0.getBody();
vertex tmpVertex = f1.getVertex(srcIndex);
vec2 tmpVec = tmpBody.getLocalPoint(tmpVertex.wpos);
f0.addVertex( dstIndex++, tmpVec );
As you can see, all this does is spread the same work into some temporary variables, so I don't know what's going on here...
Thanks. It can work nice with 1.7.4 now.

Post Reply