Hi All,
Quick question that is likely deeper than I yet know…
I’m working on a little command which simulates divide while I learn Rhino Common and C# - since I know how the command is supposed to work it’s been helpful. My little twist is id like to give the user the ability to specify a division which starts at a point and radiates outward to the ends of the curve (a pretty common action in curtainwall layouts). The goal is to allow the user to specify a startpoint and then specify length of the divisions (for now).
However, studying the divide command closely I noticed that when entering length the user can see a real time preview of where the points are going to be without having to press the enter key at all! This is a really nice quality of life feature and I definitely want to add it but I’ve been struggling to figure out how to pass that value dynamically.
Best I’ve gotten so far is this -
public class DivideOut : Command
{
public override string EnglishName => "DivideOut"; // Command name
protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
// Filter for selecting a curve
const ObjectType filter = ObjectType.Curve;
ObjRef objRef;
// Ask the user to select a curve
var rc = RhinoGet.GetOneObject("Select a curve to divide", false, filter, out objRef);
if (rc != Result.Success || objRef == null)
return rc;
var curve = objRef.Curve();
if (curve == null || curve.IsShort(RhinoMath.ZeroTolerance))
return Result.Failure; // If curve is invalid, return Result.Failure
// Ask the user to click on a point for the division start
Point3d startPoint;
rc = RhinoGet.GetPoint("Select the start point for the division", false, out startPoint);
if (rc != Result.Success)
return rc;
// Find the parameter corresponding to the start point on the curve
double startParam;
var success = curve.ClosestPoint(startPoint, out startParam);
if (!success)
return Result.Failure;
// Initial placeholder for the segment length
double segmentLength = 0;
double totalLength = curve.GetLength();
// Check that the total length of the curve is sufficient for division
if (totalLength < 0.001)
{
RhinoApp.WriteLine("The curve is too short to divide.");
return Result.Failure;
}
// Store the preview points as Rhino objects for later deletion
List<RhinoObject> previewObjects = new List<RhinoObject>();
// We will use a GetNumber instance and loop continuously to emulate real-time input
var getNumber = new GetNumber();
getNumber.SetCommandPrompt("Enter the length of each segment");
getNumber.SetLowerLimit(0.001, true); // Set a lower limit for the distance
getNumber.AcceptNumber(true, false); // Enable dynamic input (without pressing Enter)
// Initial dynamic input loop (waits for user input continuously)
bool loopRunning = true;
while (loopRunning)
{
// Get the real-time input from the user
GetResult result = getNumber.Get();
if (result == GetResult.Cancel)
{
loopRunning = false; // Exit loop if the user cancels
continue; // Move on to finish
}
// Ensure the number is valid
if (getNumber.Number() <= 0)
{
RhinoApp.WriteLine("Segment length must be greater than zero.");
continue; // Keep asking until valid input is provided
}
// Update the segment length dynamically as user types
segmentLength = getNumber.Number();
// Delete previously drawn preview points
foreach (var obj in previewObjects)
{
doc.Objects.Delete(obj, true); // Delete each preview point
}
previewObjects.Clear(); // Clear the list of preview points
// Calculate how many segments can fit along the curve
int segmentCount = (int)(totalLength / segmentLength);
previewObjects.Clear(); // Reset preview points
// Divide the curve in the forward direction (from start point)
for (int i = 0; i <= segmentCount; i++)
{
double t = startParam + i * segmentLength / totalLength * curve.GetLength();
if (t > curve.GetLength()) break; // Ensure we do not exceed the curve length
Point3d point = curve.PointAt(t);
var pointObj = doc.Objects.AddPoint(point); // Add point to the document
previewObjects.Add(doc.Objects.Find(pointObj)); // Store the RhinoObject
}
// Divide the curve in the backward direction (from start point)
for (int i = 1; i <= segmentCount; i++) // Start from 1 to avoid duplicating the start point
{
double t = startParam - i * segmentLength / totalLength * curve.GetLength();
if (t < 0) break;
Point3d point = curve.PointAt(t);
var pointObj = doc.Objects.AddPoint(point);
previewObjects.Add(doc.Objects.Find(pointObj)); // Store the RhinoObject
}
// Redraw the view to show the points dynamically
doc.Views.Redraw();
}
// Finalize the division and stop
return Result.Success; // Command completed successfully
}
}
But I have to hit enter once to preview and again to confirm the selection. It gives the user the same control but is not nearly as intuitive. I’d like to keep the user experience as close as possible to the vanilla divide command rather than expecting someone to learn a different logic for the sake of this practice command!
https://developer.rhino3d.com/api/rhinocommon/rhino.input.custom.getbaseclass/acceptnumber this seems like a step in the right direction but I’m not sure how to implement beyond as I have.
Apologies if this has been answered before and bear with me!