View Full Version : Mirroring Point Postions
Scott Ayers 07-06-2010, 10:57 PM I'd like to make a script that will mirror the X positions of points on a mesh. But before I attempt to write the code. I'm not even sure how to approach the whole process.
I was thinking that the first thing I will need to do is use a for() loop to get the selected vertice.
Then store it in a variable.
Then use another for() loop to find the -x vector of the selected vertice.
But I'm not sure if this is the correct approach.
From there things get even more unclear.
Once I have the selected vert and it's mirrored companion loaded into memory. How do I handle the mirroring behavior?
Should I use some sort of position comparison to do the updating? Or should I maybe scale them to produce the mirroring effect?
Can anyone offer any advice about the best way to do this?
-ScottA
|
|
Cairyn
07-06-2010, 11:24 PM
I'd like to make a script that will mirror the X positions of points on a mesh. But before I attempt to write the code. I'm not even sure how to approach the whole process.
I was thinking that the first thing I will need to do is use a for() loop to get the selected vertice.
Then store it in a variable.
Then use another for() loop to find the -x vector of the selected vertice.
But I'm not sure if this is the correct approach.
From there things get even more unclear.
Once I have the selected vert and it's mirrored companion loaded into memory. How do I handle the mirroring behavior?
Should I use some sort of position comparison to do the updating? Or should I maybe scale them to produce the mirroring effect?
Can anyone offer any advice about the best way to do this?
-ScottA
uh... this description confuses the hell out of me.
You want to mirror positions? That would be a simple negation of the x value of the point's position. But that does not seem to be what you are doing in the following paragraph. You seem to want to find the already mirrored points for a selection.
And then what? "Mirroring behaviour", what behaviour? You already have the mirrored points, so why do you want to mirror them again? Updating? Scaling? *scritches head*
I would understand a lot better what you want to do if you could give me a proper technical description: What is the situation? What kind of data do you have? What do you want to do with the data for what purpose? And what is the desired end situation?
Scott Ayers
07-07-2010, 12:03 AM
I'd like to emulate what the symmetry tag does. Only on a mesh that has both sides to it.
I figured I would start off with the X axis first just to keep it simple. Then add the other two after I get the X axis working.
I just found an old Plugin that does this with coffee: MirrorPointPosition (http://studio-fabian.de/downloads_en/cinema-4d-plug-ins_en)
So I think can use that to learn how to do this using vector negations.
But the mirroring behavior doesn't happen live. It only happens after the point is moved. And I was wondering how to get that live interaction behavior you get from one side to the other like when using the symmetry tool.
*Edit--I Just found the "Real Time" option for this plugin. This is exactly what I wanted to learn how to do, only without all of those fancy options. So I guess I'll try to use the code from his plugin code and see if I can do this myself. There's a lot of junk in there so I don't know if I'll be able to do it. But at least I found a working example to learn from.
Wish me luck.
-ScottA
Cairyn
07-07-2010, 11:23 AM
I'd like to emulate what the symmetry tag does. Only on a mesh that has both sides to it.
I figured I would start off with the X axis first just to keep it simple. Then add the other two after I get the X axis working.
Ah, so you want to mirror the tool behavior for arbitrary tools? Quite an undertaking if the tools do not support symmetry themselves.
I do not think that this is possible with mere scripting, since it would require listening to the message system. Here's what I would do with a C++ plugin:
1. On creation, build an array that maps point indices: for each point in the model, find the corresponding point on the "other side" (with the reverted x coordinate). This array will tell you what "mirrored" point to modify. (I assume that's what you wanted to build with the loops in your original post.)
2. Get a callback in the plugin for the "points changed" message of the object the tag belongs to. This callback will go through all changed points when invoked, use the array map to find the corresponding point, and set its coordinates to the ones of the original, with the x value inverted.
This is only the most primitive solution, since it doesn't take care of added or removed points. Of course you can handle structure changes as well by listening to the appropriate messages, but you need to add and remove points yourself then (the mirrored versions) and update your mirror index array map.
An even more challenging approach would involve non-symmetrical meshes where the new point positions are interpolated between positions of the closest points. In that case the array map would need to map to several (closest) points instead of just to one.
Without a C++ plugin, you may not be able to hook into the internal messaging system. Of course a COFFEE tag will be evaluated at every change, but this happens far too often to perform such a costly operation as "loop through all points".
mazab
07-07-2010, 12:49 PM
Here's a mirroring XPresso. It's got a refresh problem but it shows the principle.
Scott Ayers
07-07-2010, 02:53 PM
That's not the result I was looking for mazab. But it's still pretty cool and it might come in handy for something else someday. Thanks.
Robert,
I'm working in C4D but I'm trying to learn scripting principles that don't just apply to C4D. So I've been staying away from plugins. So if it can't be done live, then I'll accept that as a limitation. But I still want to learn how to do this even if the mirroring doesn't happen until the point is moved.
I know how to create an array and use a for() loop to get the selected vertice.
But where I'm unclear is how to go about getting it's mirrored partner on the other side of the object.
Do I use another array and for() loop or what?
-ScottA
Cairyn
07-07-2010, 03:28 PM
I'm working in C4D but I'm trying to learn scripting principles that don't just apply to C4D. So I've been staying away from plugins. So if it can't be done live, then I'll accept that as a limitation. But I still want to learn how to do this even if the mirroring doesn't happen until the point is moved.
I know how to create an array and use a for() loop to get the selected vertice.
But where I'm unclear is how to go about getting it's mirrored partner on the other side of the object.
Do I use another array and for() loop or what?
To create the map of mirror-partners:
Create one array that is as big as the number of points in the object, with integers.
Write one for() loop over all points, index i.
Write one for() loop over all points inside this loop, index j.
If, for i != j, the original points i and j are at the same coordinates (watch out for numerical imprecision and allow for an epsilon difference (1e-7 or something), then set array = j. (Also, after finishing both loops, you will get array[j] = i.)
Now you have your map. As you see, one array is enough.
During execution now (when C4D messages you that points have been changed, or when the user triggers the symmetry by hand), you go through the array with a loop. For each point, you look if it has a coordinate x>0. If so, determine its mirrored counterpoint index by looking at array[i], yielding the index j. Take this point j, and give it the mirrored coordinates of the original point i.
So far, this is easy. But apart from the obvious shortcomings of this algorithm - it doesn't take inserted or deleted points into account, and it will not work properly if a point is moved over to the other side of the mirrored area -, there is a very serious defect in it.
As we have seen in various other examples in this forum, a script doesn't save its variables!
So, when you build up this map, it will get lost as soon as the script ends. And you need to build the map [i]before you execute any tools that change the point positions, and you need to execute the mirroring method after the tools, obviously.
That means the map-builder needs to be one script, and the mirror-maker needs to be another. And you need to store the map data between the calls of these scripts. With a plugin this is easy; with scripts or COFFEE tags it's something I haven't solved yet.
Cairyn
07-07-2010, 03:52 PM
Of course you can write plugins in COFFEE. This might be the best solution, although you have stated that you don't want to do it with plugins...
The plugin would be a tag plugin. It contains two buttons: "Make Map" and "Make Symmetrical". First button creates the mapping, which is then stored in the plugin, the other creates the symmetry after you used a few tools.
Or with a plugin tag, you might also use the Message() function to create the "live" symmetry effect (although I don't know whether dragging points will cause Message() triggering during tool handling).
Scott Ayers
07-07-2010, 04:43 PM
To create the map of mirror-partners:
Create one array that is as big as the number of points in the object, with integers.
Write one for() loop over all points, index i.
Write one for() loop over all points inside this loop, index j.
If, for i != j, the original points i and j are at the same coordinates (watch out for numerical imprecision and allow for an epsilon difference (1e-7 or something), then set array = j. (Also, after finishing both loops, you will get array[j] = i.)
I'm not sure what you mean by this. Especially the "with integers part".
when I think of creating an array for all of an object's points. I think of this: var obj = doc->FindObject("Cube");
var count = obj->GetPointCount();
var arr = new(array,count);
But this makes an array with Nil values. Not integers.
Can you possibly post an example?
-ScottA
Cairyn
07-07-2010, 06:05 PM
I'm not sure what you mean by this. Especially the "with integers part".
when I think of creating an array for all of an object's points. I think of this: var obj = doc->FindObject("Cube");
var count = obj->GetPointCount();
var arr = new(array,count);
But this makes an array with Nil values. Not integers.
Can you possibly post an example?
Sorry, I tend to think in typed languages. Your code is fine. Now fill arr with the indices of the points that mirror the point with the array's index:
for (i = 0; i<count; i++) { // for all array elements...
for (j = 0; j<count; j++) { // ...go through all points to find the mirrored point
if (i!=j) { // exclude same point
if (point[j] has mirrored coordinates from point[i]) {
arr[i] = j;
}
}
}
}
You may want to add checks whether a mirrored point has been found at all, or whether several points have been found, and so on.
Scott Ayers
07-07-2010, 06:51 PM
Hmm.:hmm:
I can visualize how this might be used to tell if an object's point count has changed by testing the point count of one loop against the point count of the other one.
But I still don't understand how creating two copies of the same array's point count can be used to find the mirrored point position?
When a for() loop iterates through all the points like this. Does it also log their vector locations?
If it does. I don't know how to acces that and use it to make position comparisons.
-ScottA
Cairyn
07-08-2010, 08:03 AM
I can visualize how this might be used to tell if an object's point count has changed by testing the point count of one loop against the point count of the other one.
But I still don't understand how creating two copies of the same array's point count can be used to find the mirrored point position?
When a for() loop iterates through all the points like this. Does it also log their vector locations?
If it does. I don't know how to acces that and use it to make position comparisons.
It's not a copy of the point count, it's a mapping of point indices (not the positions). It tells you which point "twin" on the left side to change when a point has changed on the right side. I just wanted to go back to your original looping.
As I said farther up, this method is a very simple one and will only allow you to mirror the point positions for "point moved" events. If the point count changes, the array must be adapted as well, which I didn't cover here.
And even if I did, you can't save the array between calls, so this whole thing is hypothetical unless you use a COFFEE plugin (tag plugin) that can store data between events.
Let's go back a step and look at the situation.
- Something has been changed on the right side of the object, and you want to make the same change on the left.
- You need to know what has changed - you will need to listen to the message system to find out. Otherwise you will only see the result of the change. How do you know point X or point Y has been changed? That can only be determined if the C4D system tells you, or if you have stored the original positions of all points so you can compare previous positions with current positions.
- You need to know what left side points are "twinned" to the right side points. For this, you need either a mapping, as described previously, that was created when the object was still symmetrical, or you need the previous positions of the right side points, so you can find the "twins" even if the current positions have changed.
- If you know the "twin" point index, you can transfer the current point position of the right side point to the twin on the left, with the x axis mirrored.
That's about it. So you need either the mapping, or a copy of the object itself (all points' previous positions) to even know what to change on the left side. And either dataset has to be created before the right-side changes are made, and be stored for access by the actual mirroring component.
Unless someone here knows how to store an array through COFFEE in a simple way (perhaps it is possible through a container), scripting this through simple scripts is not possible. You WILL need to create a plugin tag for this. (Which is also the most simple and elegant way.)
(And yes, I am still ignoring the creation and deletion of points, because this would take us into the realm of polygons which depend on these points, and of merged points.)
Scott Ayers
07-08-2010, 03:07 PM
OK thanks.
Right now I only know how to get the vertices of an object based on their world location, selection state, Or their actual index number. But I really need to learn how to get the mirrored vertice of another vertice. And then maybe do something to it.
But at this point. Just being able to automatically find and select the mirrored vertice with a script would be a huge victory for me.
-ScottA
spedler
07-08-2010, 03:30 PM
I've been following this with some interest, as I developed my own plugin for mirroring (but it doesn't do what you're trying to do - the functionality is very different). However, it seems to me that what you're working on is exactly what the free 'TrueSymmetry' plugin does. Might be worth having a look at that before getting too deeply into it.
Scott Ayers
07-08-2010, 04:38 PM
Thanks spedler.
I have plugins that do a good job of this. But I'm trying to learn more about the process of manipulating an object's vertices. I know some of the basics, but I need to learn more about how to do this stuff.
-ScottA
spedler
07-08-2010, 07:09 PM
Fair enough. As you say, the only way to learn is to do it and see what happens.
Scott Ayers
07-08-2010, 07:25 PM
FYI:
As long as we're talking about this. I brought up the fact that the built in mirror function(the old one in the Structure menu) is broken.
I had a couple of people confirm it's broken here so hope it got reported to Maxon and it will get fixed.
Here's a copy of my test just in case anyone wants to test it themselves: var c = doc->FindObject("Cube");
var data=new(BaseContainer);
data->SetData(MDATA_MIRROR_DUPLICATE,true);// Make a new point copy when mirrored
data->SetData(MDATA_MIRROR_SYSTEM,1);// Mirror in "Object" mode
data->SetData(MDATA_MIRROR_PLANE,2);// Mirror selected point along ZY plane--Broken!!--Stuck in XY
data->SetData(MDATA_MIRROR_TOLERANCE,.1);//The weld tolerance
data->SetData(MDATA_MIRROR_WELD,true);//weld points witin tolerance-->Broken!!--does not weld
data->SetData(MDATA_MIRROR_SELECTIONS,true);//Adds the mirrored point to the selection tag if it exists
SendModelingCommand(MCOMMAND_MIRROR, doc, op, data, MODIFY_POINTSELECTION);
Even if Maxon fixes this.
I'd still like to know how to do this by hand so I can do this in other software that might not have this built in function available.
-ScottA
Cairyn
07-08-2010, 07:29 PM
Right now I only know how to get the vertices of an object based on their world location, selection state, Or their actual index number. But I really need to learn how to get the mirrored vertice of another vertice. And then maybe do something to it.
But at this point. Just being able to automatically find and select the mirrored vertice with a script would be a huge victory for me.
Here, have a simple script:
main(doc,op) {
if (doc == null || op == null) return;
var bs = op->GetPointSelection();
var bs2 = bs->GetClone();
var i = 0;
var j = 0;
var found = 0;
var pt, ptmirr;
for (i = 0; i < op->GetPointCount(); i++) {
if (found >= bs->GetCount()) break;
if (bs->IsSelected(i)) {
pt = op->GetPoint(i);
println(pt);
found++;
// index i is selected. Find the mirrored point!
for (j = 0; j < op->GetPointCount(); j++) {
if (i != j) {
ptmirr = op->GetPoint(j);
println("Test point: ", ptmirr);
if (
ptmirr.x == -pt.x &&
ptmirr.y == pt.y &&
ptmirr.z == pt.z
) { // found mirrored point
println("Mirror found");
bs2->Select(j);
}
}
}
}
}
op->SetPointSelection(bs2);
doc->Message(MSG_UPDATE);
}
What does it do? It takes the current selection and goes through all points of the object. Then for each selected point, it finds the mirrored point by a second loop and a direct comparison of the coordinates. If found, this mirrored point is selected too.
Note: I made a clone of the selection because during the loop, I need to change the selection but must retain the original.
Note: The comparison for coordinates with the == operator may suffer from numerical imprecision. In that case, try an epsilon distance for comparison.
Scott Ayers
07-08-2010, 07:59 PM
Thanks a ton Robert,
It never would have occurred to me to use a clone that way.
Thanks for the help.
-ScottA
CGTalk Moderation
07-08-2010, 07:59 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
vBulletin v3.0.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.