IGH_QucikCast in Goo-Casting

hey hey,

In some custom components I hacked into the internal ExpressionParser to add additional methods and similar…

Due the fact that GH_Variant is used there, I tought is a good idea to wrap it up in a custom Goo/Parameter.

The casting works just fine if I plug them into a container of a supported type.
But, If I plug the Goo into a standard-Component like ‘Addition’ the casting wants to have an object of type ‘IGH_QuickCast’.
Can anybody tell me what should be done here?

Addition doesn’t use the expression logic, instead it is defined for a limited number of known types and their interrelationships. You can add integers to booleans, vectors to colours, but not complex numbers to colours.

A bunch of IGH_Goo types in the core assembly implement the additional IGH_QuickCast interface and make it more efficient for the arithmetic operator components to figure out which addition/multiplication/subtraction method to use and what the type of the result is.

You cannot add a new IGH_QuickCast type, as there is an enumeration listing all known types which cannot be extended.

If your goo however contains data that is just like one of the quick cast types, then I guess you can implement the interface and return whatever is inside your GH_Variant as a boolean, integer, double, complex, vector, colour, …

Here’s the interface as defined in the Grasshopper source:

  ''' <summary>
  ''' IGH_QuickCast allows certain components to speed up type detection for inputs. 
  ''' This interface is a Grasshopper core interface only, do not implement it. Do not use it.
  ''' </summary>
  ''' <exclude/>
  Public Interface IGH_QuickCast
    ''' <summary>
    ''' Gets the QuickCast type for this instance.
    ''' </summary>
    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    ReadOnly Property QC_Type As GH_QuickCastType

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Distance(other As IGH_QuickCast) As Double

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Hash() As Int32

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Bool() As Boolean

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Int() As Int32

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Num() As Double

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Text() As String

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Col() As Color

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Pt() As Point3d

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Vec() As Vector3d

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Complex() As Complex

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Matrix() As Matrix

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_Interval() As Interval

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Function QC_CompareTo(other As IGH_QuickCast) As Int32
  End Interface

The problem lies in that case where your GH_Variant doesn’t represent one of the quick cast predefined types. There’s no lee-way build into the mechanism for ‘unknown’ or ‘other’ types. I guess you can always say it’s text in that case and just return a string.

1 Like

Sure, this one I was aware of.

Thanks for this tip. I don’t know why I haven’t thought in this direction my own :smiley:

Allright. I did so, implementing the interface within my Goo. It looks like it changes some behaviour but does not solve the issue. Doing so, at least I get a warning-ballon on the ‘Addition’-Component telling me that the Goo can not be casted to GH_Number (the GH_Variant is a double). The thing I don’t get, is the fact that it works with a container… Can’t track the issue :frowning:

Furthermore, I do not know how to implement some of the requested methods or for what is asked exactly:

  • QC_Distance (what?)
  • QC_Compare (how?) for numbers, ok, but, points/strings/…?

Do u mind to have a look at the code?
Would be amazing.

VariantGoo.cs (8.9 KB)

Yeah looking deeper into the new addition code it indeed performs a cast to GH_Number if the IGH_QuickCast type claims it’s a number.

Looks like you won’t be able to make this work.

I still don’t fully understand why you want to work with GH_Variant to begin with, it’s just a class used in the not-strongly-typed environment of expressions.

Does this mean that the ongoing casting-operation does not call the ‘CastTo’-Method after asking for the type of IGH_QuickCast?

Hm, as I said, I use the expression-environment to evaluate some stuff against custom types of objects. This objects are always kind of a database. Variables detected in an expression is collect from this database. To keep the system as flexible as the expression-environment I decided to go on with GH_Variant. So Its not just about outputting different kind of data, is also about inputting it to set the database during the workflow. I thougt is a good idea to pass GH_Variant around, so I do not need to cast the data at all between my components. If I go an other way I would need to cast on each in- and output. So, kind of an overhead not needed if the casting back to primitives would work in a native way…

As an example for such a system is a CellularAutomat which can perform on numbers, boolean or either points and planes without changing any code, and even on a mixed database .

Its such a pitty that this is not working :frowning:
But, ya, I guess I have to accept it.

I guess I have to set a generic parameter on in/outputs and cast the GH_Variant all the time?
Or do u have an other idea?

I know this is an old post, but I was able to get this working after playing with it for a while. Here are my findings for other’s trying to get their custom implementations of GH_Goo<T>, where T is a variant type, automatically casting to the appropriate data types.

  • Implementing CastTo<T> is mostly what you need to do, something like this works great:
public override bool CastTo<Q>(ref Q target)
{
    object val = Value.GetValue(); //Get the value out of the variant as an object
    if(val is Q qObj)
    {
        target = qObj; // if the direct cast works, then great
        return true;
    }
    if(target is IGH_Goo targetGoo)
    {
        return targetGoo.CastFrom(val) // Let the other object do the work
    }
    base.CastTo(ref target)
}
  • As noted above this doesn’t fix things like the basic mathematically operators. They use the IGH_QuickCast interface to determine if they’ll work with the passed in datatypes. Unfortunately just implementing this interface as you think it should be for the values that can be stored in your variant doesn’t work because of what David noted about the way the addition code is implemented.
  • I also tried implemented explicit cast operators for my GH_Goo<T> implementation, but they weren’t getting called. At this point I was really confused so I did some digging. The implementation of the math components in the *gha doesn’t appear to cast to GH_Number, but instead to GH_Goo<double>. My guess is that this is invalidating implementing a casting operators because that’s not a fully defined type and GH_Goo<double> in my assembly is not the same type as the one defined in MathComponents.gha. While digging I noted that Booleans are also cast to GH_Goo<bool>.
  • To get around this, my implementation of IGH_QuickCast returns “Text”, per David’s suggestion above, as the type for anything that’s a bool or a double. Every other data type appears to behave correctly. This work-around works because the operator tries to parse text into it’s appropriate type via the expression parser.
public GH_QuickCastType QC_Type {
            get
            {
                var typeId = Value.Definition.TypeId;
                if (typeId == AttributeTypeGuids.EnumGuid || typeId == AttributeTypeGuids.IntegerGuid)
                {
                    return GH_QuickCastType.@int;
                }
                else if (typeId == AttributeTypeGuids.ColorGuid)
                {
                    return GH_QuickCastType.col;
                }
                return GH_QuickCastType.text; 
            }
        }

@DavidRutten, thanks for pointing towards IGH_QuickCast. I know you didn’t intend for it to be used outside of Grasshopper’s core, but other than the math operator’s quirky behavior, are there other components that leverage that interface instead of the GH_Goo.CastTo<Q>? Additionally, any chance of you going in and changing the internal implementation to use IGH_QuickCast.QC_Num() instead?

Thanks,
Cullen