Update History Record

Is there a way to edit a HistoryRecord? As in retrieving a HistoryRecord by ObjRef or RecordID, clearing a SetInt() value, then set a new one?

I want to enable editing in a plugin command, but I don’t see a way to edit/replace/overwrite a HistoryRecord.

 private bool WriteHistory(Rhino.DocObjects.HistoryRecord history, Rhino.DocObjects.ObjRef objref, int updateFromEto)
    {
      if (!history.SetObjRef(0, objref))
        return false;

      if (!history.SetInt(1, updateFromEto))
        return false;

      return true;
    }

I also thought of maintaining plugin data in an ArchivableDictionary and linking it to the history replay with my own RecordID:

if (!history.SetInt(1, updateFromEto))
        return false;

to

if (!history.SetInt(1, pluginDataRecordID))
        return false;

But then the question is, how do you force a call to the virtual ReplayHistory(...)? I need access to the replay.Results[i].Update___(...) methods to change the children objs.

Hi @EricM,

I don’t know of any way of updating a history record. Why is this needed?

– Dale

It’s the same situation as _BlendCrv Edit. I need to change the parameterization of the command, not the input ObjRefs.

Maybe @lowell can shed light on how he added Edit to BlendCrv?

I’m not following. Can you explain further?

Any deep access to the history record isn’t available in RhinoCommon.

– Dale

I figured out I can store the parameters outside of HistoryRecord. All I need is a way to update a child object without Rhino thinking I broke history.

Sometimes you need to change the parameterization of a command:

Rhino has half a dozen commands that do this: BlendCrv, BlendSrf, MoveExtractedIsocurve, InterpCrvOnSrf, FilletEdge, and ChamferEdge.

Maybe @mikko (MoveExtractedIsocurve/InterpCrvOnSrf) and @lowell (BlendCrv/BlendSrf) can say how they updated a child without breaking history?

They just fixed commands to temporarily suspend History Lock when doing that update.

The Editors for BlendCrv and BlendSrf are part of the BlendCrv and BlendSrf command classess, so they share access to a lot of functionality for doing the edits.
After an object to edit is selected, they get the history record for that object.
That has a bunch of parameters that were set by the command before and used to make the object being edited.
After some of those parameters get changed by the edit, a new object is made using the modified params.
A new history record is made with the changed parameters and any that weren’t changed are copied from the old history record.
That new history record is attached to the new object.
The history manager is checked for state settings and history recording is turned on temporarily if necessary.
Also history locking is turned off temporarily if necessary.
The old object being edited is replaced (Doc.ReplaceObjec()) with the newly changed one that has the new history record attached.
As far as the RhinoDoc and ReplaceObject are concerned, that looks just like a new object being made by a command, which it actually is.
The history states are restored if necessary.
CRhinoHistoryRecord and CRhinoHistoryManager are exposed in the sdk.
I don’t really know if there is something you can’t get at from a plugin. I haven’t tried it, but I doubt there is.

Thanks, Lowell.

Maybe doc.Objects.Replace() is what I was after? I’m trying not to break any of the history chain with something editable in the middle:

grandparent    ->               ->   editable parent  ->              ->    child
ObjRef1        ->  PluginCmd()  ->       ObjRef2      ->  RhinoCmd()  ->   ObjRef3

If I store parameterization outside of the HistoryRecord and use doc.Objects.Replace(), then ObjRef1 will still update ObjRef2, and ObjRef2 will update ObjRef3 as usual:

PluginCmd()
    _params = getParams()
    crv2 = doCmd( _params )
    history = new Rhino.DocObjects.HistoryRecord()
    objRef2 = doc.Objects.AddCurve(curv2, null, history, false)
    //object ref ID used to get _params from plugin dict
    WriteHistory(history, ObjRef1,  objRef2.ID) 
    writePluginHistory(objRef2.ID, _params )

PluginCmdEdit()
    _params = readPluginHistory(objRef2.ID)
    update( _params )
    newCrv2 = doCmd( _params )
    doc.Objects.Replace(objRef2, newCrv2 )
    updatePluginHistory(objRef2.ID, _params )

//doc.Objects.Replace(objRef2) triggers a HistoryReplay and updates ObjRef3
//ObjRef1 is still ObjRef2's parent and will trigger HistoryReplay