Define output for a function

Hi All,

Just wondering if anyone has experience in defining additional outputs in a function.

For example I created a simple polygon array and used a polygon by function to perform an offset to those initial Polygons. During the function I created an array of points at the centroid of each polygon that I used as the point input in the offset. The result is a polygon. I would however like to also have an output from the node the array of points that were created along the way so they could be reused for anther operation.

Hopefully that makes sense. Below is the function I have, the return as you can see is the Polygon arrary MyPolyArray, I would also like to output the point array MyPointArray.

function (Polygon InputPoly, double ODistance)
{
Polygon MyPolyArray = {};
Point MyPointArray ={};
for (int i = 0; i < InputPoly.Count; ++i)
{   
    Polygon MypolyList = {};
    Point MyPointList = {};
    for (int j = 0; j < InputPoly[i].Count; ++j)
    {
    Polygon MyPoly = {};
    Point MyPoint = {};
    MyPoint = new Point().CentroidOfSet(InputPoly[i][j].Vertices);
    MyPoly = new Polygon().Offset(InputPoly[i][j], OffsetMethod.ByDistance,MyPoint, ODistance);
    MyPointList.Add(MyPoint);
    MypolyList.Add(MyPoly);   
    }
 MyPointArray.Add(MyPointList);
 MyPolyArray.Add(MypolyList);  
    
}

return MyPolyArray;
}

Below is the simple dgn file

Thanks
Wayne

Function-Output.dgn

Parents
  • For whatever reason, I'm not able to open that DGN file. Please re-submit it as a GCT (GC Transaction) file.
     
    If you don't know how to create a GCT file from a DGN file, here's how:
    1. Open the DGN file.
    2. From GC's 'Transactions' dialog, select the rightmost toolbar command: Edit this entire transaction list.
    3. Within the Editor, select the menu command: File -> Save Current Document to Transaction (GCT) File.
    4. A standard file-save dialog will appear. Navigate to the location to which you want to save the GCT file, give the file an appropriate name, and click Save.
    5. Finally, post that resultant GCT file to this forum thread.
    Also, I have no idea whether your model is large and complex, but, if it is: It would be very helpful if you could (firstly) reduce your model to the smallest essence that still demonstrates the problem.
    Thanks!
    Jeff

  • Hi Jeff,

    Apologies I should have sent the gct file originally. It is a very small example file.

    I just changed it to a txt format as the forum didn't like the gct extension.

    Thanks

    Wayne

    // Bentley GenerativeComponents Transaction File -- File structure version 1.41. (Please do not delete or change this line.)
    
    environment
    {
        GCVersion                 = '10.05.00.77';
        MSVersion                 = '10.12.00.41';
        MSProject                 = '';
        MSDesignFile              = 'C:\Users\wayne.dickerson\Desktop\function-output.dgn';
        MSMasterUnit              = {Meter, 'mm', Metric, 1.0, 1000.0};
        MSSubUnit                 = {Meter, 'mm', Metric, 1.0, 1000.0};
        MSStorageUnit             = {Meter, '', Metric, 1.0, 1000.0};
        MSUorsPerStorageUnit      = 1000.0;
    }
    
    transaction 1 stateChange 'Add function MyFunction'
    {
        script
        {
            global redeclare object MyFunction(Polygon InputPoly, double ODistance)
            {
            Polygon MyPolyArray = {};
            Point MyPointArray ={};
            for (int i = 0; i < InputPoly.Count; ++i)
            {   
                Polygon MypolyList = {};
                Point MyPointList = {};
                for (int j = 0; j < InputPoly[i].Count; ++j)
                {
                Polygon MyPoly = {};
                Point MyPoint = {};
                MyPoint = new Point().CentroidOfSet(InputPoly[i][j].Vertices);
                MyPoly = new Polygon().Offset(InputPoly[i][j], OffsetMethod.ByDistance,MyPoint, ODistance);
                MyPointList.Add(MyPoint);
                MypolyList.Add(MyPoly);   
                }
             MyPointArray.Add(MyPointList);
             MyPolyArray.Add(MypolyList);  
            
            }
            
            //return MyPolyArray;
            return{polys=MyPolyArray,points=MyPointArray};
            }
        }
    }
    
    transaction 2 stateChange 'Add baseCS'
    {
        gcModel
        {
            node User.Objects.baseCS Bentley.GC.NodeTypes.CoordinateSystem
            {
                Technique                 = 'AtDGNModelOrigin';
                DGNModelName              = 'Design Model';
                SymbolSize                = 1;
                GraphLocation             = <auto> {40.0, 40.0};
            }
        }
    }
    
    transaction 3 stateChange 'Add point1'
    {
        gcModel
        {
            node User.Objects.point1 Bentley.GC.NodeTypes.Point
            {
                Technique                 = 'ByCartesianCoordinates';
                CoordinateSystem          = baseCS;
                XTranslation              = Series(0,30000,1500);
                YTranslation              = 0;
                ZTranslation              = Series(0,30000,3000);
                Replication               = ReplicationOption.AllCombinations;
                GraphLocation             = <auto> {314.0, 40.0};
            }
        }
    }
    
    transaction 4 stateChange 'Add polygon1, polygon2'
    {
        gcModel
        {
            node User.Objects.polygon1 Bentley.GC.NodeTypes.Polygon
            {
                Technique                 = 'ByPointGrid';
                Points                    = point1;
                FacetOption               = FacetOption.Quads;
                PlaneIndex                = 1;
                UClosed                   = false;
                VClosed                   = false;
                GraphLocation             = {603.279, -87.643, 0.0, 115.76};
            }
            node User.Objects.polygon2 Bentley.GC.NodeTypes.Polygon
            {
                Technique                 = 'ByFunction';
                Function                  = function (Polygon InputPoly, double ODistance)
                                            {
                                            Polygon MyPolyArray = {};
                                            Point MyPointArray ={};
                                            for (int i = 0; i < InputPoly.Count; ++i)
                                            {   
                                                Polygon MypolyList = {};
                                                Point MyPointList = {};
                                                for (int j = 0; j < InputPoly[i].Count; ++j)
                                                {
                                                Polygon MyPoly = {};
                                                Point MyPoint = {};
                                                MyPoint = new Point().CentroidOfSet(InputPoly[i][j].Vertices);
                                                MyPoly = new Polygon().Offset(InputPoly[i][j], OffsetMethod.ByDistance,MyPoint, ODistance);
                                                MyPointList.Add(MyPoint);
                                                MypolyList.Add(MyPoly);   
                                                }
                                             MyPointArray.Add(MyPointList);
                                             MyPolyArray.Add(MypolyList);  
                                                
                                            }
                                             
                                            return MyPolyArray;
                                            };
                DebuggerTrigger           = DebuggerTriggerLevel.Breakpoints;
                InputPoly                 = polygon1;
                ODistance                 = 200;
                GraphLocation             = {1049.101, -31.987, 0.0, 141.393};
            }
        }
    }
    
    transaction 5 stateChange 'Add functionCall1, functionCall2, SpecialFunctionCall'
    {
        gcModel
        {
            node User.Objects.functionCall1 Bentley.GC.NodeTypes.FunctionCall
            {
                Technique                 = 'Default';
                Function                  = function (Polygon InputPoly, double ODistance)
                                            {
                                            Polygon MyPolyArray = {};
                                            Point MyPointArray ={};
                                            for (int i = 0; i < InputPoly.Count; ++i)
                                            { 
                                                Polygon MypolyList = {};
                                                Point MyPointList = {};
                                                for (int j = 0; j < InputPoly[i].Count; ++j)
                                                {
                                                Polygon MyPoly = {};
                                                Point MyPoint = {};
                                                MyPoint = new Point().CentroidOfSet(InputPoly[i][j].Vertices);
                                                MyPoly = new Polygon().Offset(InputPoly[i][j], OffsetMethod.ByDistance,MyPoint, ODistance);
                                                MyPointList.Add(MyPoint);
                                                MypolyList.Add(MyPoly);   
                                                }
                                             MyPointArray.Add(MyPointList);
                                             MyPolyArray.Add(MypolyList);  
                                                
                                            }
                                             
                                            return MyPolyArray;
                                             
                                            };
                InputPoly                 = polygon1;
                ODistance                 = 200;
                GraphLocation             = {837.525, -173.347, 0.0, 141.577};
            }
            node User.Objects.functionCall2 Bentley.GC.NodeTypes.FunctionCall
            {
                Technique                 = 'Default';
                Function                  = MyFunction;
                InputPoly                 = polygon1;
                ODistance                 = 200;
                GraphLocation             = {885.108, 61.114, 174.0, 141.577};
            }
            node User.Objects.SpecialFunctionCall Bentley.GC.NodeTypes.FunctionCall
            {
                Technique                 = 'Default';
                Function                  = MyFunction;
                InputPoly                 = polygon1;
                ODistance                 = 200;
                GraphLocation             = {395.074, 269.335, 174.0, 141.577};
            }
        }
    }
    
    transaction 6 stateChange 'Add polygon3'
    {
        gcModel
        {
            node User.Objects.polygon3 Bentley.GC.NodeTypes.Polygon
            {
                Technique                 = 'ByFunction';
                Function                  = function ()
                                            {
                                            Polygon MyPoly = SpecialFunctionCall.Value.polys;
                                            return MyPoly;
                                            };
                DebuggerTrigger           = DebuggerTriggerLevel.Breakpoints;
                GraphLocation             = {632.848, 269.265, 174.0, 128.577};
            }
        }
    }
    
    transaction 7 stateChange 'Add polygon4'
    {
        gcModel
        {
            node User.Objects.polygon4 Bentley.GC.NodeTypes.Polygon
            {
                Technique                 = 'ByFunction';
                Function                  = function (Polygon InputPoly, double InputDistance)
                                            {
                                            record result = MyFunction(InputPoly, InputDistance);
                                            Polygon MyPoly = result.polys;
                                            return MyPoly;
                                            };
                DebuggerTrigger           = DebuggerTriggerLevel.Breakpoints;
                InputPoly                 = polygon1;
                InputDistance             = 200;
                GraphLocation             = {-257.082, 192.291};
            }
        }
    }
    

  • I'm not sure what you mean by "Scripted Node", but, in any case...

    Apologies, I was refering to the top-level Scripted node here.

    It sounds like you're assuming that every global function is intended to serve as a child-node generator for another node. However, a global function can be used for any purpose, such a performing a complex calculation, or processing data from an XML file.

    No, I think that the expectation is that the Global Function is kind of like a neighbourhood 'car wash' that you set up when you know your Nodes will need to repeat a certain kind of processing. Once set up, there would be no need to initialise etc. Your other nodes pass it stuff and it 'washes' the arguments through and returns the results.

    Kind of like of like the way some of the pre-defined or Script functions in GC are used. Of course, you are going to say that these are all constructed by the overarching node that contain them :-)

    However, if that same global function is assigned directly to a ByFunction technique's Function input, then that global function will be called in context (that is, within the same context that its result will be used).

    How would you directly assign a Global Function to a ByFunction technique's Function input? Bearing in mind multiple nodes could be calling the Global Function. Sounds like there is an alternative to the "CopyTransformGeometricContents" hack?

  • Why set up a carwash and not allow GC to generate one on demand? I suppose it is to keep the mayor happy as he can control how many car washes he has to deal with and budget for them?

  • Ah, now I'm starting to think we're using the same terminology to refer to two different things.

    When I say "global function", I'm referring to an encapsulation of functionality that has a name and can be invoked (called) from any number of contexts any number of times, with each call being able to specify different parameters.

    A global function could be a built-in function, such as "Sqrt" (square root), or a function the user writes, themself, in script code. In the latter case, the user creates and manages their function using GC's Functions panel. (To use your analogy, this is where the user creates their own carwashes.)

    (Additionally, GC's Packager panel allows users to export and import their script functions between themselves and other users.)

    Regardless of how it's been created, a global function isn't intrinsically involved in the GC model/graph. It doesn't appear as a node. It's relevant to the GC model only as much as it's referenced by one or more nodes.

    There are multiple ways that a node can reference a global function, but the two most common are:

    • FunctionCall node: If a global function is assigned (by name) to a FunctionCall node's Function input, then, whenever that node is updated, that global function will be called.
    • Any geometry-type node, using its ByFunction technique: If a global function is assigned (by name) to the node's Function input, then, whenever that node is updated, that global function will be called.

    The consistent rule is that a global function is called when, and only when, a node referencing that function is updated.

    Does that clarify anything?

  • Thanks for taking the time, Jeff. Much appreciated.

    I will need to take a step back and review.

    If I understand correctly, global functions should ideally be generated as Scripted Functions. The carwashes would be created in the Functions Panel and not part of the Node graph. No need to think about parent - child relationships and what is in context or not when they are generated this way. These global functions can be always be called from any Node, using the means you mentioned, but only updates when the Node is updated and calls the 'carwash' global function causing it to process and return its arguments.

    OTOH, the Global Script Functions that uses the 'global redeclare' keyword can be part of a transaction, therefore a Node and subject to the parent:child / context rules. When the GSF is called in a Node, it just passes whatever it has ('this'?) without doing any processing because it will always be upstream of the calling Node and is not required to update.

  • It sounds like you've got it. But, something you should be aware of:

    OTOH, the Global Script Functions that uses the 'global redeclare' keyword can be part of a transaction, therefore a Node and subject to the parent:child / context rules.

    In newer versions of GC, global script functions are no longer defined within transactions. Instead, they're defined and maintained within the Functions panel, with no relationship to which transactions have been played or not played. (Well, except that, if you revise the definition of a script function that's currently referenced by one or more nodes, those nodes will be updated automatically.)

    If you were to load the transaction list shown at "the" into the latest version of GC, GC would automatically remove transaction 1's "script" block, and move that function, BasicLine, into a separate storage location where script functions reside.

Reply
  • It sounds like you've got it. But, something you should be aware of:

    OTOH, the Global Script Functions that uses the 'global redeclare' keyword can be part of a transaction, therefore a Node and subject to the parent:child / context rules.

    In newer versions of GC, global script functions are no longer defined within transactions. Instead, they're defined and maintained within the Functions panel, with no relationship to which transactions have been played or not played. (Well, except that, if you revise the definition of a script function that's currently referenced by one or more nodes, those nodes will be updated automatically.)

    If you were to load the transaction list shown at "the" into the latest version of GC, GC would automatically remove transaction 1's "script" block, and move that function, BasicLine, into a separate storage location where script functions reside.

Children
No Data