[x3d-public] Building an X3D Object Model - step 2

Roy Walmsley roy.walmsley at ntlworld.com
Fri Oct 20 07:12:54 PDT 2017


Hi,

 

Last time we defined X3DField and X3DArrayField objects based on the
abstract standard ISO/IEC 19775-1:2013. Today, we'll extend to a specific
field type, and we'll also add SAI information from ISO/IEC 19775-2:2015.

 

Let's start with a simple field, and build definitions for SFInt32 and
MFInt32.

Reference: ISO/IEC 19775-1:2013, 5.3.7 SFInt32 and MFInt32 -
http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/fieldsDef.
html#SFInt32AndMFInt32

 

What additional characteristics are required for each of these field types,
in addition to those that will be inherited from the base classes? Only one.
The value. In  order to add these we are going to need a new data type.
We'll call it "int32", to represent a 32-bit integer. We will have,
therefore:

 

SFInt32 : X3DField {

    int32 value;

};

 

MFInt32 : X3DArrayField {

    int32 value[];

};

 

Because MFInt32 requires storage for multiple values, which can be none, we
specify an array type by using square brackets. Furthermore, we leave the
size of the array unspecified by not including a number within the brackets.
However, there is one final thing we have to handle. And that is the default
value. The text tells us that the default value for an SFInt32 field is 0,
while that for an MFInt32 field is the empty list. For the SFInt32 field we
will represent the default value by adding the expression "=0" to the value
definition. For the MFInt32 field we do not need any additional text, as we
will assume that when any array is created it is, by default, an empty list
unless otherwise specified. Therefore, our SFIn32 definition becomes:

 

SFInt32 : X3DField {

    int32 value = 0;

};

 

Now, let's turn to the SAI. And start with the ISO/IEC 19775-2:2016 clause 5
Data type reference.

Reference:
http://www.web3d.org/documents/specifications/19775-2/V3.3/Part02/dataRef.ht
ml

 

Clause 5.2 specifies thirty seven data type definitions. For our immediate
requirement to add SAI capabilities to the object definitions we have made
so far, we are going to need the following:

1.	5.2.11 SAIFieldAccess. This ". defines the type of access that is
permitted to a field."
2.	5.2.13 SAIField. This ". represents an identifier for a particular
field of a node."
3.	5.2.14 SAIFieldName. This ". represents a name for a field."
4.	5.2.15 SAIFieldType. This ". specifies the type of data a field
represents."
5.	5.2.16 SAIFieldValue. This ". represents the value to be set or to
be returned of an SAIFieldType in language specific terms."

 

What about primitive data types? We can see that there is an  SAIBoolean
(clause 5.2.2) and an SAIString (clause 5.2.34). There is no data type
corresponding to integers, floats or doubles. Let's flag that.

 

Problem 3: The SAI does not define data types for primitives such as
integers, floats, or doubles.

 

To use these in our object model we either a) need to map the SAI data types
to the types we use, or b) just use these types directly. Let's try to do
the latter, as we want the object model to be language independent, and we
don't want to introduce another mapping if we don't need to.

 

Now we can look at the field service requirements, described in ISO/IEC
19775-2:2015 clause 6.7 Field services.

Reference:
http://www.web3d.org/documents/specifications/19775-2/V3.3/Part02/servRef.ht
ml#FieldServices

 

We'll work through these one at a time. The first is at clause 6.7.2
getAccessType. Because this introduces some new concepts, we'll go through
each line of the definition.

 

The first line is parameters, and for this function SAIField and SAINode are
specified. We need to remember that we are defining an interface for fields.
These are not permitted to exist outside of nodes. And how is a field
reference obtained? By querying a node. So having a field reference implies
the node reference, and we don't need to add it into our interface.

 

The second line is the return type, SAIFieldAccess. But looking at our
previous definition for X3DField, we defined an enumeration called
AccessType. Now we can see that we should rename this enumeration to
SAIFieldAccess. Therefore, this enumeration becomes:

 

enum SAIFieldAccess {

    initializeOnly,

    inputOnly,

    outputOnly,

    inputOutput

};

 

And our definition of X3DField is updated to:

 

X3DField {

    enum Type type;

    enum SAIFieldAccess accessType;

    string name;

};

 

The third line of the service definition specifies errors. It lists two, in
addition to the one specified in the second paragraph in clause 6.7.1
Introduction. Therefore there are three errors, namely SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, and SAI_DISPOSED.

 

The remaining three lines deal with events, whether it is buffered, and
whether it is only available to an external interface. None of these are of
immediate concern.

 

Now that we have reviewed the service definition, we are in a position to
define a method for getAccessType. Where shall we put it? Since this service
is one that applies to every field, irrespective of type.  Therefore, it can
be added to the X3DField definition, where it will be inherited by all
fields. Thus, the X3DField object definition becomes as follows:

 

X3DField {

    enum Type type;

    enum SAIFieldAccess accessType;

    string name;

 

    enum SAIFieldAccess getAccessType (void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_DISPOSED;

};

 

This definition introduces the "void" argument, which we define to mean that
there are no arguments. It also introduces the term "throws" followed by an
listing of all the permitted errors that may arise.

 

Now that we have the principles established we can repeat the same steps for
the getType service, defined at 6.7.3 getType. We see that there is the data
type SAIFieldType, that we need to use to replace our previous enumeration
Type. This enumeration therefore becomes:

 

enum SAIFieldType {

    SFBool,

    MFBool,

    SFColor,

   .

};

 

And, again, we need to add this service to the X3DField definition,
modifying the "type" characteristic too. The updated X3DField definition
becomes:

 

X3DField {

    enum SAIFieldType type;

    enum SAIFieldAccess accessType;

    string name;

 

    enum SAIFieldAccess getAccessType (void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_DISPOSED;

    enum SAIFieldType getType (void) throws SAI_CONNECTION_ERROR,
SAI_DISPOSED;

};

 

Now we look at the getName service, specified at 6.7.4 getName. We note that
there is a data type SAIFieldName. So we can use that to replace the
previous "string" data type. The X3DField definition therefore becomes:

 

X3DField {

    enum SAIFieldType type;

    enum SAIFieldAccess accessType;

    SAIFieldName name;

 

    enum SAIFieldAccess getAccessType (void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_DISPOSED;

    enum SAIFieldType getType (void) throws SAI_CONNECTION_ERROR,
SAI_DISPOSED;

    SAIFieldName getName (void) throws SAI_CONNECTION_ERROR, SAI_DISPOSED;

};

 

Moving on, we get to the getValue service, defined at 6.7.5 getValue. The
data type of the value, or values, that are stored is dependent on the type
of field. Therefore, this service will need to be defined in the individual
field types, and not in the abstract X3DField. We'll do it first for the
SFInt32 field. Note that we can't simply use SAIFieldValue. As explained in
clause 5.2.15 this needs be mapped to different types, depending on the
field type. We also need to do this mapping. For us, the data type for
SAIFieldValue when the field type is SFInt32 or MFInt32 is int32. Therefore
we need to define this mapping table, as we have it so far:

 

SAIFieldValue mapping:

SFInt32/MFInt32 int32

 

We now add this service to our SFInt32 definition, and get:

 

SFInt32 : X3DField {

    int32 value = 0;

 

    int32 getValue (void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_DISPOSED;

};

 

And what about our MFInt32 definition. The text in the last paragraph states
that "All field types shall support the option to return a single value from
multi-valued arrays.". So we need a separate function for that. We'll define
a function get1Value. It will have a single argument, representing the index
into the array of the value to be returned. Furthermore, we need an
additional error, in case the index is not within the bounds of the array
size. When we look at the list of error types in clause 5.3, there isn't a
suitable one. We'll flag that in a moment. Let's define an additional error
type SAI_INVALID_ARRAY_INDEX. In addition, we would also need a way to find
out how many values there are in a multivalued field. So let's define an
additional method getNum which returns the number of elements in an array
for a specific multivalued field. This last function should be added to the
X3DFieldArray object, because it applies to all multivalued (MFxxxx) fields.

 

Problem 4: The SAI does not define an error type for an array index out of
bounds.

Problem 5: The SAI does not define specific services names for handling
multivalued fields.

 

Therefore, our definitions for X3DArrayField and MFInt32 become:

 

X3DArrayField : X3DField {

    int getNum (void) throws throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_DISPOSED;

};

 

MFInt32 : X3DArrayField {

    int32 value[];

 

    int32[] getValue (void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_DISPOSED;

    int32 get1Value (int index) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE,
SAI_INVALID_ARRAY_INDEX, SAI_DISPOSED;

};

 

Note that we have defined a new primitive type "int" to represent an
integer, without making any reference to particular storage requirements.

 

Wow! That was a big one. But now we have the setValue service, defined at
6.7.6 setValue. Like getValue, this needs to be added to the specific field
types, as well as setting individual values for multivalued fields. And for
multivalued fields, there are additional requirements, specified in the last
paragraph, to provide services to ". append and remove items from the
existing field.". In addition, we might also want a service to insert a new
item, or remove all of the items in a multivalued field. Applying these to
our existing definitions, we now get:

 

SFInt32 : X3DField {

    int32 value = 0;

 

    int32 getValue (void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_DISPOSED;

    void setValue (int32 newValue) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_IMPORTED_NODE,
SAI_DISPOSED;

};

 

X3DArrayField : X3DField {

    int getNum (void) throws throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_DISPOSED;

    void removeAllValues (void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_IMPORTED_NODE,
SAI_DISPOSED;

    void removeValue (int index) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_IMPORTED_NODE,
SAI_INVALID_ARRAY_INDEX, SAI_DISPOSED;

};

 

MFInt32 : X3DArrayField {

    int32 value[];

 

    int32[] getValue (void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_DISPOSED;

    int32 get1Value (int index) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE,
SAI_INVALID_ARRAY_INDEX, SAI_DISPOSED;

    void appendValue (int32 newValue) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_IMPORTED_NODE,
SAI_DISPOSED;

    void insertValue (int index, int32 newValue) throws
SAI_CONNECTION_ERROR, SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE,
SAI_INVALID_ARRAY_INDEX, SAI_DISPOSED;

    void setValue (int32 newValue[]) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE, SAI_IMPORTED_NODE,
SAI_DISPOSED;

    void set1Value (int index, int32 newValue) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE,
SAI_INVALID_ARRAY_INDEX, SAI_DISPOSED;

};

 

Two more to go. The next service is registerFieldInterest, defined at 6.7.7
registerFieldInterest. We can see from Table 6.14 that there are two action
types, namely AddInterest, and RemoveInterest. So we will define two methods
in our interface for these. Furthermore, for the time being, we will use the
parameter SAIRequester without further definition, and get back to that
later in this series of steps. Both of these actions apply to every field,
so need to be added to the X3DField class. So this becomes:

 

X3DField {

    enum SAIFieldType type;

    enum SAIFieldAccess accessType;

    SAIFieldName name;

 

    enum SAIFieldAccess getAccessType (void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_DISPOSED;

    enum SAIFieldType getType (void) throws SAI_CONNECTION_ERROR,
SAI_DISPOSED;

    SAIFieldName getName (void) throws SAI_CONNECTION_ERROR, SAI_DISPOSED;

    void addInterest (SAIRequester requester) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE,
SAI_INSUFFICIENT_CAPABILITIES, SAI_NODE_IN_USE, SAI_DISPOSED;

    void removeInterest (SAIRequester requester) throws
SAI_CONNECTION_ERROR, SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE,
SAI_INSUFFICIENT_CAPABILITIES, SAI_NODE_IN_USE, SAI_DISPOSED;

};

 

And finally, the last service is dispose, specified at clause 6.7.8 dispose.
Again, it applies to every field, and needs to go into X3DField. So this
finally becomes:

 

X3DField {

    enum SAIFieldType type;

    enum SAIFieldAccess accessType;

    SAIFieldName name;

 

    enum SAIFieldAccess getAccessType (void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_DISPOSED;

    enum SAIFieldType getType (void) throws SAI_CONNECTION_ERROR,
SAI_DISPOSED;

    SAIFieldName getName (void) throws SAI_CONNECTION_ERROR, SAI_DISPOSED;

    void addInterest (SAIRequester requester) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE,
SAI_INSUFFICIENT_CAPABILITIES, SAI_NODE_IN_USE, SAI_DISPOSED;

    void removeInterest (SAIRequester requester) throws
SAI_CONNECTION_ERROR, SAI_INVALID_OPERATION_TIMING, SAI_INVALID_ACCESS_TYPE,
SAI_INSUFFICIENT_CAPABILITIES, SAI_NODE_IN_USE, SAI_DISPOSED;

    void dispose void) throws SAI_CONNECTION_ERROR,
SAI_INVALID_OPERATION_TIMING;

};

 

And were done for this step. It might have been long, but we've done enough
now to be able to move on to look at X3DNode next time.

 

All the best,

 

Roy

 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://web3d.org/pipermail/x3d-public_web3d.org/attachments/20171020/c970d363/attachment-0001.html>


More information about the x3d-public mailing list