ReplayHistoryResult for Blocks?

Blocks are supported by History, but I don’t see a way to update it through ReplayHistoryResult.

So even if you give doc.Objects.AddInstanceObject a history record, it’s not being written to the history table? So neither RhinoCommon nor GH support blocks?

What do people do instead? I absolutely must find a way to translate a block with history.

Hi @EricM,

You are correct - there isn’t any kind of “UpdateToInstanceReferenceGeometry”. This is probably due to core Rhino not having anything like this.

What are you doing where you need something like this?

Thanks,

– Dale

I wrote quite a few commands over the past 6 months, but I haven’t been testing with blocks. I think they all boil down to:

doc.Objects.TransformWithHistory(someBlock, someXFormThatChanges)

^ that works, but I need to supply a different xform during the replay.

Can I use PInvoke to insert the history record that AddInstanceObject() is ignoring? Or use TransformWithHistory() for the initial xform and overwrite its record?

Hi @EricM,

Can you provide code to a simple history-participating command that you want to work with history?

– Dale

I mean, I can, but my code base is in FSharp. Do you need me to translate to c#?

Some of this was in helper functions that I copy pasta’d to show relevant bits, but here is adding geometry to the doc:

        let doCommand () =
            getSurfaceN ()
            |> Result.bind getGeoToArray
            |> Result.bind showPreview
            |> function
            | Ok inputs ->
                let histParam = ArrayPolarSrfN.inputsToHistory inputs
                let iterAddToDoc (h: HistoryRecord) i (g: GeometryBase) =
                    match g with
                    | :? InstanceReferenceGeometry as a ->
                        let id = a.ParentIdefId |> doc.InstanceDefinitions.FindId
                        doc.Objects.AddInstanceObject(id.Index, a.Xform, null, h, id.IsReference) |> ignore
                    | _ -> 
                        doc.Objects.Add(g, null, h, false) |> ignore
                let iterWriteHist (param: ArrayPolarSrfN.History) =
                    let histRecord = WriteHistory param
                    ArrayPolarSrfN.transformParent param |> Array.iteri (iterAddToDoc histRecord)
                
                histParam |> Array.iter iterWriteHist 
                Result.Success
            | Error r -> 
                r
        try
            try
                doCommand ()
            with 
            | ex -> 
                RhinoApp.WriteLine(ex.Message)
                RhinoApp.WriteLine(ex.InnerException.Message)
                Result.Failure
        finally 
            turnOffDisplayConduit ()

Doing the history replay:

    override this.ReplayHistory (e: ReplayHistoryData) =
        let update (y: ReplayHistoryResult) (x: GeometryBase): bool =
            let attrib = y.ExistingObject.Attributes
            match x with
            | :? AngularDimension as a -> y.UpdateToAngularDimension(a, attrib)
            | :? Brep as a -> y.UpdateToBrep(a, attrib)
            | :? Curve as a -> y.UpdateToCurve (a, attrib)
            | :? Extrusion as a -> y.UpdateToExtrusion (a, attrib)
            | :? LinearDimension as a -> y.UpdateToLinearDimension (a, attrib)
            | :? Mesh as a -> y.UpdateToMesh (a, attrib)
            | :? Point as a -> y.UpdateToPoint (a.Location, attrib)
            | :? RadialDimension as a -> y.UpdateToRadialDimension (a, attrib)
            | :? SubD as a -> y.UpdateToSubD (a, attrib)
            | :? Surface as a -> y.UpdateToSurface(a, attrib)
            | :? TextEntity as a -> y.UpdateToText (a, attrib)
            | :? TextDot as a -> y.UpdateToTextDot (a, attrib)
            //| :? InstanceReferenceGeometry as a -> ?????
            | _ -> false
        
        match ReadHistory e with
            | Ok histRecord ->
                let geo = ArrayPolarSrfN.transformParent histRecord
                if e.Results.Length = geo.Length then
                    let mutable i = -1
                    let rec loop (last: bool): bool =
                        i <- i + 1
                        if last && i < geo.Length then 
                            update e.Results[i] geo[i]
                            |> loop 
                        else last
                    loop true
                else false 
            | Error _ -> false

@dale I’m traveling today, but if you need me to translate this to c# I can on Friday. This component does an array polar using some surface normal as the pole. The surface normal has history.

The overall theme of these commands allow users to build geometry in a comfortable space (WorldXY) before orienting it (and its history) to the part space.

Like here’s crv thru poly with symmetry mirrors calculated at a surface frame that moves.

SymCrvU

OK, well now I know of two people using F#.

– Dale

Yeah, @Goswin has done some amazing stuff.

So you can see why and what I need from a PInvoke solution? I have no problem writing c#, I just really prefer f# for readability and catching corner cases.

1 Like

Hey @dale that’s not true!!! With @nathanletwory we are three. That’s 10% of all worldwide #fsharp users already !!

2 Likes

BTW @EricM I recently published GitHub - goswinr/Rhino.Scripting: A complete reimplementation of the Rhino-Script-Syntax in F# in F#

1 Like

LOL, I stand corrected…

– D

1 Like

What is there not to like about F#. Additionally with a great font that has good ligatures it also looks great

1 Like

Hi @EricM,

I’ve added your request to the pile.

https://mcneel.myjetbrains.com/youtrack/issue/RH-74822

Thanks,

– Dale

Awesome! How close is the code editor? I recently met the guy behind Sharp Cells. He’s going to show me how he setup VSCode for run and debug in Excel. Interested? I’d love to have it in Rhino and GH.

@dale, I’ll use it when it get’s here, but I’m at a full stop with too much invested. Are you saying it can’t be done through interop? Do I need to drop into c and look at Moose for this?

Looking at feasibility, I did not see this coming. I wanted to have editability (like Lowell is doing with BlendCrv), but I quickly kicked that down the road to v2 because RhinoCommon doesn’t let you edit a history record when you replace an object.

Hi @EricM,

If you are writing your own “C” wrappers, you can probably work around this. RhinoCommon just calls into to the Rhino C++ SDK for this. I’m not sure how you’re going to add an additional method to ReplayHistoryResult - this could end up being a lot of work.

Or you could just wait. Having a useful sample that I can use for testing could speed up this process, which is why I asked for one.

Also, I am very familiar with the BlendSrf command and how it uses history. Honestly, it doesn’t do anything special - it just saves a lot of parameters so it can allow for editing. I don’t know if RhinoCommon provides what’s needed to do something similar. I’d need to review the code to provide a precise answer.

— Dale

I’m happy to make a working c# example showing what I need: to set and update history for a block.

  1. Manually add the managed HistoryRecord to the history table just like AddInstanceObject should be doing (ON_HistoryRecord → CRhinoHistoryRecord → CRhinoHistoryRecordTable). I’m hoping this is a few PInvoke statements.

  2. For history replay, there isn’t an UpdateTo method for instance refs, but I’m hoping I can make it work with RhinoObject.SetCopyHistoryOnReplace(true).


The stuff I kicked down the road:

I wanted to do add editing support now, but decided to build a minimally functional prototype and determine the financial viability of the whole concept first.

RhinoCommon doesn’t expose CRhinoHistoryRecord’s anywhere. Right off the bat, to build Edit into a command you don’t have access to Command, GetAntecedents, or GetDescendants.

And if you figure a way around that, RhinoCommon splits ON_HistoryRecord into two classes. The setter side of ON_HistoryRecord is only available when you add an obj to the doc, so there’s no way to update an antecedent objref to trigger replay calls.

Okay @dale, I’m back in the office and made a repo you can quickly clone.

There is an ExampleMirror.cs file and ExampleMirrorTestFile.3dm. I added some points to make sure the command works.

ExampleMirror

Search for TODOs to find the parts I need help with.

 doc.Objects.AddInstanceObject(blockDef.Index, block.Xform, null, histRecord, blockDef.IsReference); 
 continue;

 // TODO This is what I need help getting around.  
 // Call _What on the mirrored block.  There is no history record.
// TODO I have a bad feeling Replace is not going to support an instance ref 
// also, there's no documentation for ignore modes
// https://mcneel.github.io/rhinocommon-api-docs/api/RhinoCommon/html/M_Rhino_DocObjects_Tables_ObjectTable_Replace_26.htm
existing.SetCopyHistoryOnReplace(true);
RhinoDoc.ActiveDoc.Objects.Replace(existing.Id, block, true);
return true;

@dale Any updates on this? I’m very much stuck in the mud until I figure this out.