[RhinoCommon] command history support

There is not much documentation out there, and I can’t figure it out on my own. How can I support history in a RhinoCommon command? A small example would be nice.

What is not working for me:

Draw a plane in the Front view (XZ plane), click “Record History”, run the command below and select an edge of it. The edge gets offset, but subsequent changes to the plane by control-point dragging do not trigger the ReplayHistory method in my command (I don’t get the output nor does it hit a breakpoint there).

(edit for indenting)


protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{

    ObjRef oRef;
    Result res = RhinoGet.GetOneObject("select surface edge", false, ObjectType.EdgeFilter, out oRef);
    if (res != Result.Success) return res;

    BrepEdge b = oRef.Curve() as BrepEdge;
    if (null == b) return Result.Failure;

    int[] adjFaces = b.AdjacentFaces();
    if (adjFaces.Length == 0) return Result.Failure;

    Curve[] offset = b.Offset(Plane.WorldZX, 10, 0.1, CurveOffsetCornerStyle.Smooth);
  
    HistoryRecord h = new HistoryRecord(this, 10);
    h.SetBrep(0, b.Brep);
    h.SetInt(1, b.EdgeIndex);
            

    doc.Objects.AddCurve(offset[0], null, h, false);
    return Result.Success;
}
        
protected override bool ReplayHistory(ReplayHistoryData replayData)
{
    RhinoApp.WriteLine("replay");
    //return base.ReplayHistory(replayData);
    return false;
}

@dale Could you provide me with a short example on this, please?

Sorry for sleeping. Try this:

https://github.com/dalefugier/SampleCsCommands/blob/master/SampleCsHistory.cs

1 Like

Ah, thanks @dale , I’ll try it tomorrow!
Don’t worry about not responding straight away, or ‘sleeping’ as you call it. I’m glad you guys are as responsive as you are, it really helps in nailing down the finer details :smile:

Thanks again, this works very well. My mistake was (probably) to set the Brep rather than the ObjRef. Setting the Brep does not trigger a ReplayHistory - setting ObjRef does.

this is 404 now, is the example somewhere else please?

I’m sure this has been moved to our samples repo:

— Dale

1 Like

Hi Dale

Trying this, but not winning, please have a peek at my code and see what it is I am missing, must be pretty obvious, but im a tad flummoxed. the goal is to make a tapered pipe that updates when you move control points on the origin curve, but is just the beginnings of a longer routine that will end sphere on either end etc and constants will be replaced with rhino command line options

using System;
using System.Collections.Generic;
using Rhino;
using Rhino.Commands;
using Rhino.Geometry;
using Rhino.Input;
using Rhino.Input.Custom;

namespace SupportHistory
{
    public class SupportHistoryCommand : Command
    {
        public SupportHistoryCommand(){Instance = this;}
        static int historyver = 20081237;
        public static SupportHistoryCommand Instance{get; private set; }
        public override string EnglishName{get { return "SupportHistoryCommand"; }}
        

        protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            const Rhino.DocObjects.ObjectType filter = Rhino.DocObjects.ObjectType.Curve;
            Rhino.DocObjects.ObjRef objref;
            Result rc = RhinoGet.GetOneObject("Select curve to process", false, filter, out objref);
            if (rc != Result.Success || objref == null)
                return rc;
            Curve curve = objref.Curve();
            RhinoApp.WriteLine("got here 1");

            double Start = 1.1, End = 0.8;int Break = 47;
            RhinoApp.WriteLine("got here 2");

            List<double> Radii = new List<double>(); List<double> offsets = new List<double>();
            Radii.Add(Start); Radii.Add(End);
            offsets.Add(0); offsets.Add(1);
            var support = Brep.CreatePipe(curve, offsets, Radii, false, PipeCapMode.Flat, false, 0.1, RhinoMath.ToRadians(1));
            RhinoApp.WriteLine("got here 3");

            var aa = doc.Objects.AddBrep(support[0]);
            RhinoApp.WriteLine("got here 4");

            Rhino.DocObjects.HistoryRecord history = new Rhino.DocObjects.HistoryRecord(this, historyver);
            WriteHistory(history, objref,aa, Start,End,Break);
            doc.Views.Redraw();
           
            return Result.Success;
        }

        protected override bool ReplayHistory(Rhino.DocObjects.ReplayHistoryData replay)
        {
            Rhino.DocObjects.ObjRef objref = null;double Start = 0, End = 0;int Break = 0;Guid guid = Guid.Empty;
            if (!ReadHistory(replay, ref objref, ref guid, ref Start, ref End, ref Break))
                return false;

            Curve curve = objref.Curve();if (null == curve) return false;
            List<double> Radii = new List<double>(); List<double> offsets = new List<double>();
            Radii.Add(Start); Radii.Add(End);
            offsets.Add(0); offsets.Add(1);
            var support = Brep.CreatePipe(curve, offsets, Radii, false, PipeCapMode.Flat, false, 0.1, RhinoMath.ToRadians(1));
            RhinoDoc.ActiveDoc.Objects.Replace(guid, support[0]);
            RhinoDoc.ActiveDoc.Views.Redraw();
            return true;
        }

        private bool ReadHistory(Rhino.DocObjects.ReplayHistoryData replay, ref Rhino.DocObjects.ObjRef objref, ref Guid aa, ref double Start, ref double End, ref int Break)
        {
            if (historyver != replay.HistoryVersion)
                return false;

            objref = replay.GetRhinoObjRef(0);if (null == objref)return false;
            if (!replay.TryGetGuid(1, out aa)) return false;
            if(!replay.TryGetDouble(2, out Start))return false;
            if(!replay.TryGetDouble(3, out End)) return false;
            if(!replay.TryGetInt(4, out Break)) return false;
            return true;
        }

        private bool WriteHistory(Rhino.DocObjects.HistoryRecord history, Rhino.DocObjects.ObjRef objref,Guid aa, double Start, double End, int Break)
        {
            if (!history.SetObjRef(0, objref))return false;
            if (!history.SetGuid(1, aa)) return false;
            if (!history.SetDouble(2, Start))return false;
            if (!history.SetDouble(3, End)) return false;
            if (!history.SetInt(4, Break)) return false;
            return true;
        }
    }
}
> var aa = doc.Objects.AddBrep(support[0]);

This is not correct. When adding the object that you want to history-support, add it together with the history record like so:

doc.Objects.AddBrep(support[0], null, history, false);

Also, in WriteHistory you write the ObjRef to the history record. This is sufficient, you do NOT need to write the Guid of the added object as well. To update the resulting BRep, you need to use:

protected override bool ReplayHistory(Rhino.DocObjects.ReplayHistoryData replay)
{
  BRep newBrep; /// new BRep created using your code

  // use this instead of the Replace action!!
  replay.Results[0].UpdateToBrep(newBrep, null); 
}

In short, the approach should be:

  1. WriteHistory to the history record (do not use the BRep GUID, using the ObjRef of the curve is sufficient)
  2. Add the BRep with the history record to the document
  3. when updating the BRep, don’t replace the BRep in the document, but use UpdateToBrep instead.

Good luck!

2 Likes

Many Thanks! Will poke around with that now, owe you a beer!

Solved, thank you again, how can I force history to be on in this command, right now user has to select history to get ti to work?

Put HistorySettings.RecordingEnabled = true; at the start of your command.

1 Like

nm, self resolved, ignore. post deleted.