Hello All,
I am working on a plugin that involves moving points on a mesh. Is it possible for a plugin to support a flow such as:
- The user selects a point (1 point selected)
- The user moves selected point
- The user selects another point (2 points selected now)
- The user moves both points.
-
User Confirms Moves(Edit: Not necessary)
I would also like for a user to be able to deselect any selected points as well. Ultimately I want to do this with no prompts to the user in between the steps laid out above. Also, how would one make the vertices of the mesh visible from within a plugin (as opposed to PointsOn in the command line)?
Here is an example of the operation I want to perform in Rhino (outside of the plugin):
This is a video of what I currently do inside of my plugin. It is based on the Gumball examples in the Rhino github (rhino-developer-samples/cpp/SampleCommands at 6 · mcneel/rhino-developer-samples · GitHub):
Here is the relevant code related to how the plugin has the user select points and then move them:
CRhinoCommand::result move_vertices(MeshSurfaces* a_MeshSurfaces, const CRhinoCommandContext& a_Context)
{
//Grab mesh vertex to move
CRhinoGetObject t_GetVerts;
t_GetVerts.SetCommandPrompt(L"Select Mesh Vertex/Vertices to Move");
t_GetVerts.SetGeometryFilter(CRhinoObject::meshvertex_filter);
t_GetVerts.GetObjects(1, 0);
if (t_GetVerts.CommandResult() != CRhinoCommand::success)
return t_GetVerts.CommandResult();
int t_NumThreads = 1;
VertexGumballGetXForm t_VG(a_MeshSurfaces, t_GetVerts, t_NumThreads);
while (true)
{
t_VG.setBaseGumball();
t_VG.EnableGumballDraw(true);
t_VG.Enable();
a_Context.Document()->Redraw();
if (t_VG.PreTransform().IsIdentity())
{
t_VG.SetCommandPrompt(L"Drag gumball");
t_VG.AcceptNothing(FALSE);
}
else
{
t_VG.SetCommandPrompt(L"Drag gumball. Press Enter when done");
t_VG.AcceptNothing(TRUE);
}
t_VG.moveGumball();
t_VG.EnableGumballDraw(false);
t_VG.Disable();
if (t_VG.CommandResult() != CRhinoCommand::success)
break;
CRhinoGet::result t_Result = t_VG.Result();
if (t_Result == CRhinoGet::point)
{
t_VG.updateGumball();
continue;
}
break;
}
t_VG.EnableGumballDraw(false);
t_VG.Disable();
if (!t_VG.PreTransform().IsIdentity())
{
t_VG.updateMeshSurfaces();
}
a_Context.Document()->Redraw();
return CRhinoCommand::success;
}
VertexGumballGetXForm.cpp
CRhinoGet::result VertexGumballGetXForm::moveGumball()
{
if (m_GumballDC == 0)
return CRhinoGet::cancel;
if (!m_GumballDC->PreTransform().IsIdentity())
{
m_bHaveXform = TRUE;
m_xform = m_GumballDC->PreTransform();
}
SetBasePoint(m_GumballDC->BaseGumball().m_frame.m_plane.Origin(), false);
const_cast<CRhinoXformObjectList&>(ObjectList()).EnableDisplayFeedback(true);
if (!m_xform.IsIdentity())
const_cast<CRhinoXformObjectList&>(ObjectList()).UpdateDisplayFeedbackXform(m_xform);
SetGetPointCursor(RhinoApp().m_default_cursor);
CRhinoGet::result t_Result = GetPoint(0, true);
const_cast<CRhinoXformObjectList&>(ObjectList()).EnableDisplayFeedback(false);
return t_Result;
}
void VertexGumballGetXForm::Disable()
{
m_GumballDC->Disable();
}
void VertexGumballGetXForm::Enable()
{
m_GumballDC->Enable(m_Context.Document()->RuntimeSerialNumber());
}
void VertexGumballGetXForm::EnableGumballDraw(bool a_Enable)
{
m_GumballDC->EnableGumballDraw(a_Enable);
}
void VertexGumballGetXForm::updateGumball()
{
if (!m_GumballDC->m_drag_settings.m_bRelocateGumball)
{
ON_Xform xform = m_GumballDC->TotalTransform();
m_GumballDC->SetPreTransform(xform);
}
// update location of base gumball
CRhinoGumballFrame t_GumballDCFrame = m_GumballDC->Gumball().m_frame;
CRhinoGumballFrame t_GumballFrame = m_Gumball->m_frame;
t_GumballFrame.m_plane = t_GumballDCFrame.m_plane;
t_GumballFrame.m_scale_grip_distance = t_GumballDCFrame.m_scale_grip_distance;
m_Gumball->m_frame = t_GumballFrame;
}
/*
* Input: CRhinoViewport: Rhino Viewport
* ON_3dPoint: The 3d location of the mouse pointer
* ON_Xform: The resulting Xform is stored here
*
* Output: BOOL: TRUE for Xform != IdentityTransform
*/
BOOL VertexGumballGetXForm::CalculateTransform(CRhinoViewport& vp, const ON_3dPoint& pt, ON_Xform& xform)
{
if (m_GumballDC == 0)
return FALSE;
if (m_GumballDC->m_drag_settings.m_bRelocateGumball)
xform = m_GumballDC->PreTransform();
else
xform = m_GumballDC->TotalTransform();
m_TranslationDirection = ON_3dVector(xform.m_xform[0][3], xform.m_xform[1][3], xform.m_xform[2][3]);
return xform.IsValid() ? TRUE : FALSE;
}
void VertexGumballGetXForm::OnMouseDown(CRhinoViewport& vp, UINT flags, const ON_3dPoint& pt, const ON_2iPoint* p)
{
if (p == 0 || m_GumballDC == 0 || m_GumballDC->m_pick_result.m_gumball_mode != gb_mode_nothing)
return;
m_GumballDC->m_pick_result.SetToDefaultPickResult();
CRhinoPickContext t_PickContext;
t_PickContext.m_view = vp.ParentView();
t_PickContext.m_pick_style = CRhinoPickContext::point_pick;
if (vp.SetClippingRegionTransformation(p->x, p->y, t_PickContext.m_pick_region))
{
vp.VP().GetFrustumLine(p->x, p->y, t_PickContext.m_pick_line);
t_PickContext.UpdateClippingPlanes();
m_GumballDC->PickGumball(t_PickContext, this);
}
}
/*
* Create Nurbs Surfaces each time the mouse pointer is moved
*/
void VertexGumballGetXForm::OnMouseMove(CRhinoViewport& vp, UINT flags, const ON_3dPoint& pt, const ON_2iPoint* p)
{
if (p == 0 || m_GumballDC == 0 || m_GumballDC->m_pick_result.m_gumball_mode == gb_mode_nothing)
return;
m_GumballDC->CheckShiftAndControlKeys();
ON_Line t_WorldLine;
if (!vp.VP().GetFrustumLine(p->x, p->y, t_WorldLine))
t_WorldLine = ON_Line(ON_UNSET_POINT, ON_UNSET_POINT);
bool t_Success = m_GumballDC->UpdateGumball(pt, t_WorldLine);
if (t_Success)
{
ON_Xform t_Xform;
m_Draw = false;
if (CalculateTransform(vp, pt, t_Xform))
{
updateUpdatedNurbs();
m_Draw = true;
}
CRhinoGetXform::OnMouseMove(vp, flags, pt, p);
}
vp.SetClippingPlanes(ON_BoundingBox(ON_3dPoint(-1000, -1000, -1000), ON_3dPoint(1000, 1000, 1000)));
}
/*
* Draw the Mesh and Nurbs Surfaces (with Zebras) to the viewport
*/
void VertexGumballGetXForm::DynamicDraw(CRhinoDisplayPipeline& dp, const ON_3dPoint& pt)
{
if (m_Draw)
{
if (!m_Hidden)
{
hide();
}
//TODO: Draw only modified portion of Mesh
// Draw Mesh
dp.DrawMesh(m_OnMesh, true, false, nullptr);
// Draw Surfaces
const CRhinoZebraAnalysisSettings& t_ZebraSettings = RhinoApp().AppSettings().ZebraAnalysisSettings();
const ON_MeshParameters& t_MeshParameters = m_Context.Document()->Properties().AnalysisMeshSettings();
const ON_Color& t_Color = RhinoApp().AppSettings().SelectedObjectColor();
for (NurbsSurfaceToHandlePair t_Current : m_UpdatedNurbs)
{
ON_Brep* t_Brep = t_Current.m_NurbsSurface.BrepForm();
dp.DrawZebraPreview(t_Brep, t_ZebraSettings, t_MeshParameters, t_Color, nullptr);
delete t_Brep;
}
}
CRhinoGetXform::DynamicDraw(dp, pt);
}
The reason I want to figure out how this can be done is because it will make the user experience more fluid. Any help is appreciated.
Thanks