Shape scripts

It is possible to call parametric shape scripts from a GDL file to create 3D models for shapes that are not natively supported in GDL.

Shape scripts build 3D models that are either surfaces consisting of planar faces or volumes bounded by planar faces. The script functions that are used to build these models are described in Geometric Modeling Script Functions. These script functions can only be called from shape scripts. Attempting to call these in any other execution environment will result in "call to unbound function ..." error messages at script execution time.

Shape scripts may also contain calls to general utility scripts. However, they should never do any dialog with the user. Error messages should be output via USR_MESSAGE() function.

Shape Script File

When a SHAPE_BSV statement is evaluated the GDL engine calls the specified shape script with parameters given in the statement.

GDL expects shape scripts to be found in a compiled script file $PMS_LIBDIR/xxx.bin/pm/GdlShapes.sl, where xxx stands for the host architecture type. This script file is loaded only once during a session, so all globals will maintain their values between script invocations.

The compiled shape script file is created by running script file %PMS_HOME%/pm/macro/CreateGdlShapeLib.mac. This takes place right after CADMATIC has been installed or whenever new shape scripts are installed.

The shape script file is built by combining system provided shape scripts with shape scripts created by the user organization. System provided shape scripts are stored in directory %PMS_HOME%/pm/gdlshapes in files ending with suffix ".shp". Site specific shape scripts are expected to be stored in files $PMS_LIBDIR/src/pm/gdlshapes/*.shp.

Plant Modeler User's Manual chapter Component Modeler describes the shapes that are provided by the system.

Adding a New Shape

To add a new shape 'NewShape' follow these steps:

  • Place the shape script in file $PMS_LIBDIR/src/pm/gdlshapes/NewShape.shp

  • Create an icon for the new shape into file $PMS_LIBDIR/src/pm/gdlshapes/xicon/NewShape. Size of the icon should be 32x32.

  • Edit the menu %PMS_HOME%/pm/$PMS_LANG/menu/cm_gdlshapes.m to make the new shape accessible. The menu entry should look like this:

    str New Shape; val 10172;

    icn $PMS_LIBDIR/src/pm/gdlshapes/xicon/NewShape;

    dat NewShape;;

  • Rebuild the shape script file by running the script

    %PMS_HOME%/pm/macro/CreateGdlShapeLib.mac.

To make shapes behave consistently you should follow these conventions:

  • make the local x-axis be the symmetry axis (if there is any)

  • make the shape "grow" towards the positive x-axis

  • use utility scripts number_of_segs_to_approximate_circle(radius) and accuracy_to_approximate_curve() when you need to know how accurately to approximate a smooth surface with a set of planar faces

Below is an example of a shape script.

Copy
/* @(#)CircleToRect.shp 1.1 98/01/15 */
/*
**  Builds a shape where the cross-section begins as a circle but ends as a
**  rectangle.
**  Local origin of the shape is at the center of the circle.
**  The rectangular end is at distance 'Length' towards the positive
**  x-axis.  The center of the rectangle end is offset from the x-axis
**  as specified by parameters Y_offset and Z_offset.
**
**  Original coding by Cadmatic/Kari Nylund November 1997.
*/
CircleToRectangle(Radius, Width, Height, Length, Y_offset, Z_offset)
{
    int ns, rem, d, fp;
 
    ns = number_of_segs_to_approximate_circle(Radius);
 
    /* Make the # to be evenly divided by 4 */
    d = ns/4;
    rem = ns - 4*d;
    if (rem) ns = ns + 4 - rem;
 
    /* First generate points at the circle end. */
    da = 360/ns;
    a = 0;
    for (i=0; i<ns; i = i+1;) {
        ix = GM_ENTER_POINT(1, 0.0, Radius*COS(a), Radius*SIN(a));
        if (i == 0) fp = ix;    /* remember id of the first point. */
        a = a - da;
    }
 
    /* Next create corner points for the rectangle. */
    GM_ENTER_POINT(1, Length, Y_offset + Width/2, Z_offset - Height/2);
    GM_ENTER_POINT(1, Length, Y_offset - Width/2, Z_offset - Height/2);
    GM_ENTER_POINT(1, Length, Y_offset - Width/2, Z_offset + Height/2);
    GM_ENTER_POINT(1, Length, Y_offset + Width/2, Z_offset + Height/2);
 
    /*  Build faces. Points must be ordered in ccw order as seen from
        the tip of face normal. */
    GM_START_FACE();                /* Circle end */
    for (i=0; i<ns; i = i+1;)
        GM_EDGE_POINT(fp+i, EDGE_VISIBLE);
    GM_END_FACE(0); /* convex face */
 
    GM_START_FACE();    /* rectangle end */
    for (i=3; i >= 0; i = i-1;)
        GM_EDGE_POINT(fp+ns + i, EDGE_VISIBLE);
    GM_END_FACE(0);
 
    /*  Create "large" triangles with tip at circle 3, 12, 9 and 6
        aclock positions and extending to corners of the rectangle end. */
    ic = fp + ns + 3;
    for (i=0; i<4; i=i+1;) {
        GM_START_FACE();                    /* "large" triangles */
        GM_EDGE_POINT(fp+i*ns/4, EDGE_VISIBLE);
        GM_EDGE_POINT(ic, EDGE_VISIBLE);
        if (i == 0) ic = fp + ns;
        else        ic = ic + 1;
        GM_EDGE_POINT(ic, EDGE_VISIBLE);
        GM_END_FACE(0);
    }
 
    /*  Create "small" triangles that cover the positions 3 -> 12,
        12 -> 9, 9 -> 6 and  6 -> 3 aclock positions. */
 
    spq = ns/4;
    ix = fp;
    for (i=0; i<4; i = i + 1; ) {
        cp = fp + ns + i;
        GM_START_FACE();
        GM_EDGE_POINT(ix, EDGE_VISIBLE); ix = ix + 1;
        GM_EDGE_POINT(cp, EDGE_BETW_PATCHES);
        GM_EDGE_POINT(ix, EDGE_VISIBLE);
        GM_END_FACE(0);
 
        nr = spq - 1;
        for (j=1; j < nr; j = j + 1;) {
            GM_START_FACE();
            GM_EDGE_POINT(ix, EDGE_BETW_PATCHES); ix = ix + 1;
            GM_EDGE_POINT(cp, EDGE_BETW_PATCHES);
            GM_EDGE_POINT(ix, EDGE_VISIBLE);
            GM_END_FACE(0);
        }
 
        GM_START_FACE();
        GM_EDGE_POINT(ix, EDGE_BETW_PATCHES); ix = ix + 1;
        GM_EDGE_POINT(cp, EDGE_VISIBLE);
        if (ix >= fp+ns) ix = fp;
        GM_EDGE_POINT(ix, EDGE_VISIBLE);
        GM_END_FACE(0);
    }
 
    return(0);
}