Ok, this is a long one, so bear with me!
My command basically takes two vertices of two different meshes, calculates their average and set these values back in the meshes. The problem I found is in the Undo support for this when using a CustomMeshObject.
If I run the command on normal meshes, the behavior is what you expect: mesh vertices get averaged and undo/redo works as they should.
If I use CustomMeshObject, however, if I undo the command and perform it again on a different location, the first change (which was undone) gets redone. Or, maybe, the mesh representations between what is in memory and what is in the document/on screen is out of sync. I don’t know.
To reproduce, see http://youtu.be/2Hzx_lbDiJw and
- run the command below once to put a mesh in the document
- run the command again and select two vertices. They get averaged
- Undo
- run the command again and select two different vertices. They get averaged and the first two get averaged too. I expected the first two not to get averaged.
private bool _hasRunOnce = false;
protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
// if the command is run for the first time: put
// two simple meshes in the document - slightly offset to each other
if (!_hasRunOnce)
{
AddMeshes(doc);
_hasRunOnce = true;
return Result.Success;
}
// if the meshes are in the document perform this command,
// undo it and perform it again (at a different position).
// The undone action
// is performed again. But only if a CustomMeshObject is used.
GetObject go = new GetObject();
go.SetCommandPrompt("Select two mesh vertices to average");
go.GeometryFilter = ObjectType.MeshVertex;
ObjRef[] vRefs;
while (true)
{
GetResult res = go.GetMultiple(2, 2);
if (res == GetResult.Cancel) return Result.Cancel;
if (res == GetResult.Object)
{
vRefs = go.Objects();
break;
}
}
if (null == vRefs || vRefs.Length != 2)
return Result.Failure;
Guid id0 = vRefs[0].ObjectId;
Guid id1 = vRefs[1].ObjectId;
if (id0 == id1)
return Result.Failure;
Mesh m0 = doc.Objects.Find(id0).Geometry as Mesh;
Mesh m1 = doc.Objects.Find(id1).Geometry as Mesh;
if (null == m0 || null == m1)
return Result.Failure;
m0.EnsurePrivateCopy();
m1.EnsurePrivateCopy();
ComponentIndex c0 = vRefs[0].GeometryComponentIndex;
ComponentIndex c1 = vRefs[1].GeometryComponentIndex;
if (c0.ComponentIndexType != ComponentIndexType.MeshTopologyVertex ||
c1.ComponentIndexType != ComponentIndexType.MeshTopologyVertex)
return Result.Failure;
Point3f p0 = m0.TopologyVertices[c0.Index];
Point3f p1 = m1.TopologyVertices[c1.Index];
m0.TopologyVertices[c0.Index] = new Point3f(0.5f * (p0.X + p1.X), 0.5f * (p0.Y + p1.Y), 0.5f * (p0.Z + p1.Z));
m1.TopologyVertices[c1.Index] = new Point3f(0.5f * (p0.X + p1.X), 0.5f * (p0.Y + p1.Y), 0.5f * (p0.Z + p1.Z));
doc.Objects.Replace(id0, m0);
doc.Objects.Replace(id1, m1);
doc.Views.Redraw();
return Result.Success;
}
private void AddMeshes(RhinoDoc doc)
{
Mesh m = new Mesh();
Point3d[] pts = new[]
{
new Point3d(0, 0, 0),
new Point3d(1, 0, 0),
new Point3d(2, 0, 0),
new Point3d(0, 1, 0),
new Point3d(1, 1, 0),
new Point3d(2, 1, 0),
new Point3d(0, 2, 0),
new Point3d(1, 2, 0),
new Point3d(2, 2, 0)
};
m.Vertices.AddVertices(pts);
MeshFace[] faces = new[]
{
new MeshFace(0, 1, 4, 3),
new MeshFace(1, 2, 5, 4),
new MeshFace(3, 4, 7, 6),
new MeshFace(4, 5, 8, 7)
};
m.Faces.AddFaces(faces);
// no bug if added as normal mesh
//doc.Objects.Add(m);
// bug if added as CustomMeshObject
doc.Objects.AddRhinoObject(new MyMeshObject(m));
Mesh m2 = new Mesh();
m2.Vertices.AddVertices(pts);
m2.Faces.AddFaces(faces);
Transform translate = Transform.Translation(0.2, 0.2, 0.0);
m2.Transform(translate);
// no bug if added as normal mesh
//doc.Objects.Add(m2);
// bug if added as CustomMeshObject
doc.Objects.AddRhinoObject(new MyMeshObject(m2));
doc.Views.Redraw();
}
}
public class MyMeshObject : CustomMeshObject
{
public MyMeshObject()
{
}
private BoundingBox _bb;
public MyMeshObject(Mesh m) : base(m)
{
_bb = m.GetBoundingBox(true);
}
protected override void OnDuplicate(RhinoObject source)
{
MyMeshObject obj = source as MyMeshObject;
if (null != obj)
_bb = obj._bb;
base.OnDuplicate(source);
}
protected override void OnTransform(Transform transform)
{
_bb.Transform(transform);
base.OnTransform(transform);
}
protected override void OnDraw(DrawEventArgs e)
{
e.Display.DrawPoint(_bb.Center, Color.Red);
base.OnDraw(e);
}
}