Handle keyboard in OnDynamicDraw without the command line receiving input

I’m trying to make a script/plugin for dynamically sectioning a mesh.

I do this by overriding the GetPoint class and using its OnDynamicDraw.

Then, I intersect the mesh with MeshPlane, to which I pass the plane, the origin of which is based on GetPoint’s point.

I’d like to rotate the said plane around its X and Z axes. My first idea was to use the mouse wheel + SHIFT or CTRL, but I couldn’t find a convenient method to do it outside of eto forms.

Now I want to use Z, X, and SHIFT buttons for rotation control.

But I can’t figure out how to handle button presses within OnDynamicDraw and how to not pass the pressed button to the command line / make the command line out of the input’s focus.

Any help or suggestions will be appreciated.

Hi @Ex_John,

You can’t stop the command line from seeing the keystroke — while a Get is running it owns keyboard input by design. RhinoApp.KeyboardEvent is a passive Windows hook, so the key still lands in the command line after your handler runs. There’s no mouse-wheel hook either.

The Rhino way to do what you want is to make X and Z hidden command line options. They don’t show up in the prompt, they’re activated by typing the name, and because the name is one character it’s a single key plus Enter.

GetSectionPoint gp = new GetSectionPoint(mesh);
double step = RhinoMath.ToRadians(15.0);

while (true)
{
  gp.SetCommandPrompt("Section location. Type X or Z to tilt 15 degrees");
  gp.ClearCommandOptions();
  int x_option = gp.AddOption("X", null, true);   // true == hidden
  int z_option = gp.AddOption("Z", null, true);
  gp.AcceptNumber(true, false);                   // type an angle if you like

  GetResult res = gp.Get();

  if (res == GetResult.Option)
  {
    int index = gp.OptionIndex();
    if (index == x_option)
      gp.TiltX += step;
    else if (index == z_option)
      gp.TiltZ += step;
    continue;                                     // back into the drag
  }

  if (res == GetResult.Number)
  {
    step = RhinoMath.ToRadians(gp.Number());
    continue;
  }

  if (res != GetResult.Point)
    return Result.Cancel;

  break;
}

Plane section_plane = gp.SectionPlane;

Two things to know. Enter is still needed — command line options are matched by text, so there’s no way to fire one from a bare keypress. And each option ends the Get, so you re-enter it in the loop; keep the tilt on your GetPoint subclass as fields so it survives, and expect the preview to reappear on the next mouse move.

If you want truly modifier-style control with no Enter at all, use Shift and Ctrl instead — those never reach the command line. Cache them in OnMouseMove and use them in OnDynamicDraw, and turn off the built-ins that already claim them:

PermitOrthoSnap(false);   // Shift
PermitElevatorMode(0);    // Ctrl
PermitTabMode(false);
protected override void OnMouseMove(GetPointMouseEventArgs e)
{
  bool tilt_x = e.ShiftKeyDown;
  bool tilt_z = e.ControlKeyDown;

  if (tilt_x || tilt_z)
  {
    if (!m_tilting)   // modifier just went down - anchor here
    {
      m_tilting = true;
      m_anchor = e.WindowPoint;
      m_tilt_start = tilt_x ? m_tilt_x : m_tilt_z;
    }
    double delta = (e.WindowPoint.X - m_anchor.X) * 0.01;   // radians per pixel
    if (tilt_x)
      m_tilt_x = m_tilt_start + delta;
    else
      m_tilt_z = m_tilt_start + delta;
  }
  else
  {
    m_tilting = false;
    m_origin = e.Point;
  }

  // do the work here, not in OnDynamicDraw - this runs once per mouse
  // move, OnDynamicDraw runs once per frame
  m_sections = Intersection.MeshPlane(m_mesh, m_cache, SectionPlane, RhinoMath.ZeroTolerance);

  base.OnMouseMove(e);
}

protected override void OnDynamicDraw(GetPointDrawEventArgs e)
{
  if (null != m_sections)
  {
    foreach (Polyline section in m_sections)
      e.Display.DrawPolyline(section, System.Drawing.Color.Yellow, 2);
  }
  base.OnDynamicDraw(e);
}

Note the MeshIntersectionCache overload of Intersection.MeshPlane — worth using when you re-intersect the same mesh every mouse move.

– Dale

Thank you very much for your response and accept my apologies for taking too long to reply. It seems to have worked fine for my case.

The only issue is that even if I use caching, the section gets slower the moment you start rotating the plane away from its original orientation. This isn’t caused by the area of the section, because even a smaller section that was tilted results in a slow-down. Please forgive my million-triangle cube.

I tried MeshMeshFast intersection instead, and the resulting slow-down is still present.