ianmackenzie / elm-iso-10303 / Step.Encode

This module allows you to encode data in ISO 10303-21 (STEP file) format.

Example

Here is a sample STEP file (adapted from Wikipedia):

ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('A minimal AP214 example with a single part'),'2;1');
FILE_NAME('demo','2003-12-27T11:57:53',('Lothar Klein'),('LKSoft'),' ','IDA-STEP',' ');
FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 2 1 1}'));
ENDSEC;
DATA;
#10=ORGANIZATION('O0001','LKSoft','company');
#11=PRODUCT_DEFINITION_CONTEXT('part definition',#12,'manufacturing');
#12=APPLICATION_CONTEXT('mechanical design');
#13=APPLICATION_PROTOCOL_DEFINITION('','automotive_design',2003,#12);
#14=PRODUCT_DEFINITION('0',$,#15,#11);
#15=PRODUCT_DEFINITION_FORMATION('1',$,#16);
#16=PRODUCT('A0001','Test Part 1','',(#18));
#17=PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#16));
#18=PRODUCT_CONTEXT('',#12,'');
#19=APPLIED_ORGANIZATION_ASSIGNMENT(#10,#20,(#16));
#20=ORGANIZATION_ROLE('id owner');
ENDSEC;
END-ISO-10303-21;

To create this file using this package, you could write

module Example exposing (..)

import Step.Encode as Encode

stepFile : String
stepFile =
    let
        header =
            { description = [ "A minimal AP214 example with a single part" ]
            , implementationLevel = "2;1"
            , fileName = "demo"
            , timeStamp = "2003-12-27T11:57:53"
            , author = [ "Lothar Klein" ]
            , organization = [ "LKSoft" ]
            , preprocessorVersion = " "
            , originatingSystem = "IDA-STEP"
            , authorization = " "
            , schemaIdentifiers = [ "AUTOMOTIVE_DESIGN { 1 0 10303 214 2 1 1}" ]
            }

        applicationContext =
            Encode.entity "APPLICATION_CONTEXT"
                [ Encode.string "mechanical design"
                ]

        applicationProtocolDefinition =
            Encode.entity "APPLICATION_PROTOCOL_DEFINITION"
                [ Encode.string ""
                , Encode.string "automotive_design"
                , Encode.int 2003
                , Encode.referenceTo applicationContext
                ]

        product =
            Encode.entity "PRODUCT"
                [ Encode.string "A0001"
                , Encode.string "Test Part 1"
                , Encode.string ""
                , Encode.list Encode.referenceTo
                    [ Encode.entity "PRODUCT_CONTEXT"
                        [ Encode.string ""
                        , Encode.referenceTo applicationContext
                        , Encode.string ""
                        ]
                    ]
                ]

        productDefinition =
            Encode.entity "PRODUCT_DEFINITION"
                [ Encode.string "0"
                , Encode.null
                , Encode.referenceTo <|
                    Encode.entity "PRODUCT_DEFINITION_FORMATION"
                        [ Encode.string "1"
                        , Encode.null
                        , Encode.referenceTo product
                        ]
                , Encode.referenceTo <|
                    Encode.entity "PRODUCT_DEFINITION_CONTEXT"
                        [ Encode.string "part definition"
                        , Encode.referenceTo applicationContext
                        , Encode.string "manufacturing"
                        ]
                ]

        productRelatedProductCategory =
            Encode.entity "PRODUCT_RELATED_PRODUCT_CATEGORY"
                [ Encode.string "part"
                , Encode.null
                , Encode.list Encode.referenceTo [ product ]
                ]

        appliedOrganizationAssignment =
            Encode.entity "APPLIED_ORGANIZATION_ASSIGNMENT"
                [ Encode.referenceTo <|
                    Encode.entity "ORGANIZATION"
                        [ Encode.string "O0001"
                        , Encode.string "LKSoft"
                        , Encode.string "company"
                        ]
                , Encode.referenceTo <|
                    Encode.entity "ORGANIZATION_ROLE"
                        [ Encode.string "id owner"
                        ]
                , Encode.list Encode.referenceTo [ product ]
                ]
    in
    Encode.file header
        [ applicationContext
        , applicationProtocolDefinition
        , productDefinition
        , productRelatedProductCategory
        , appliedOrganizationAssignment
        ]

Note that entities can be declared directly 'inside' other entities where this makes sense, and not all entities have to be explicitly listed in the Encode.file call, only top-level ones. Any entities directly or indirectly referenced by the listed entities will also be included in the output. Entity IDs are automatically generated and entities may be written out in arbitrary order.

file : Step.Types.Header -> List Step.Types.Entity -> String

Build a string representing a complete STEP file from a header and a list of entities. Entities will be assigned integer IDs automatically, and nested entities (entities that reference other entities) will be 'flattened' into separate entities referring to each other by their automatically-generated IDs.

Note that it is not actually necessary to list all entities explicitly, only top-level ones; any entities that are referenced by entities in the given list will also get included in the output.

Entities

entity : String -> List Step.Types.Attribute -> Step.Types.Entity

Construct a single simple entity with the given type and attributes. The type name will be capitalized if necessary. An IfcDirection representing the positive Y direction in 3D could be created using

direction =
    Encode.entity "IFCDIRECTION"
        [ Encode.list Encode.float [ 0, 1, 0 ]
        ]

which might get encoded as #1=IFCDIRECTION((0.,1.,0.));.

If a given entity is only referred to by a single other entity, you can create it directly inside the definition of the parent entity. For example, to create entity #121 from this AP214 example, you could use

Encode.entity "AXIS2_PLACEMENT_3D"
    [ Encode.string ""
    , Encode.referenceTo <|
        Encode.entity "CARTESIAN_POINT"
            [ Encode.string ""
            , Encode.list Encode.float [ 20, 7.5, 0 ]
            ]
    , Encode.referenceTo <|
        Encode.entity "DIRECTION"
            [ Encode.string ""
            , Encode.list Encode.float [ 1, 0, 0 ]
            ]
    , Encode.referenceTo <|
        Encode.entity "DIRECTION"
            [ Encode.string ""
            , Encode.list Encode.float [ 0, 0, -1 ]
            ]
    ]

When actually encoded to a STEP file, this will get converted into four separate entities, with the top-level entity referring to the other three by their automatically-generated IDs, something like:

#1=AXIS2_PLACEMENT_3D('',#2,#3,#4);
#2=CARTESIAN_POINT('',(20.,7.5,0.));
#3=DIRECTION('',(1.,0.,0.));
#4=DIRECTION('',(0.,0.,-1.));

complexEntity : List Step.Types.SubEntity -> Step.Types.Entity

Construct a single 'complex entity'; for example

Encode.complexEntity
    [ Encode.subEntity "A" [ Encode.int 1 ]
    , Encode.subEntity "B"
        [ Encode.int 2
        , Encode.string "three"
        ]
    , Encode.subEntity "C" [ Encode.enum "FOUR" ]
    ]

will be encoded as

#1=(A(1)B(2,'three')C(.FOUR.));

subEntity : String -> List Step.Types.Attribute -> Step.Types.SubEntity

Encode a sub-entity that will become part of a complex entity, with the given type name and attributes.

Attributes

derivedValue : Step.Types.Attribute

The special 'derived value' attribute.

null : Step.Types.Attribute

The special 'null value' attribute.

optional : (a -> Step.Types.Attribute) -> Maybe a -> Step.Types.Attribute

Encode a Maybe either using the given decoder if it is a Just value, or as null if it is Nothing.

bool : Basics.Bool -> Step.Types.Attribute

Construct a Boolean-valued attribute.

int : Basics.Int -> Step.Types.Attribute

Construct an integer-valued attribute.

float : Basics.Float -> Step.Types.Attribute

Construct a real-valued attribute.

string : String -> Step.Types.Attribute

Construct a string-valued attribute.

referenceTo : Step.Types.Entity -> Step.Types.Attribute

Construct a reference to another STEP entity (will end up being encoded using an integer ID in the resulting STEP file, e.g. #123).

enum : String -> Step.Types.Attribute

Construct an attribute that refers to an enumeration value defined in an EXPRESS schema. Enumeration values are always encoded as all-caps with leading and trailing periods, like .STEEL..

This function will capitalize and add periods if necessary, so both Encode.enum "steel" and Encode.enum ".STEEL." will be encoded as .STEEL..

binaryData : Bytes.Encode.Encoder -> Step.Types.Attribute

Construct an attribute from a blob of binary data given as an encoder.

Note that since Elm only supports byte-aligned binary data, there is no way to create (for example) a blob of binary data exactly 5 bits in size even though that is supported by the STEP standard.

list : (a -> Step.Types.Attribute) -> List a -> Step.Types.Attribute

Construct an attribute which is itself a list of other attributes. You provide a list of values and a function to convert each of those values to an attribute (which will usually be one of the attribute construction functions in this module!). For example, to construct an attribute which is a list of floats:

Encode.list Encode.float [ 0, 1, 0 ]

To construct a list of references to various entities:

Encode.list Encode.referenceTo
    [ firstEntity
    , secondEntity
    , thirdEntity
    ]

In the odd case where you already have a List Attribute, you can use Elm's built-in identity function as the first argument:

Encode.list identity
    [ firstAttribute
    , secondAttribute
    , thirdAttribute
    ]

tuple2 : (a -> Step.Types.Attribute) -> ( a, a ) -> Step.Types.Attribute

Encode a tuple of two values as a list using the given encoding function.

tuple3 : (a -> Step.Types.Attribute) -> ( a, a, a ) -> Step.Types.Attribute

Encode a tuple of three values as a list using the given encoding function.

Typed attributes

Typed attributes are sometimes needed when dealing with SELECT types.

boolAs : String -> Basics.Bool -> Step.Types.Attribute

Construct a type-tagged Boolean-valued attribute.

intAs : String -> Basics.Int -> Step.Types.Attribute

Construct a type-tagged integer-valued attribute.

floatAs : String -> Basics.Float -> Step.Types.Attribute

Construct a type-tagged float-valued attribute.

stringAs : String -> String -> Step.Types.Attribute

Construct a type-tagged string-valued attribute.

enumAs : String -> String -> Step.Types.Attribute

Construct a type-tagged enumeration attribute.

binaryDataAs : String -> Bytes.Encode.Encoder -> Step.Types.Attribute

Construct a type-tagged binary-valued attribute.

listAs : String -> (a -> Step.Types.Attribute) -> List a -> Step.Types.Attribute

Construct a type-tagged list attribute.

typedAttribute : String -> Step.Types.Attribute -> Step.Types.Attribute

Construct a generic type-tagged attribute. In most cases it will be simpler to use one of the specific functions such as floatAs or enumAs.