<<< Further Implementation Decisions | Chapters |
The PK interface is a collection of C declarations for tokens, structures and functions. The complete set of definitions are listed in the file 'parasolid_kernel.h' on the Parasolid Release CD. This appendix explains some of the conventions used for naming and calling functions in the PK.
All PK functions have names of the form PK_ <CLASS>_ <operation>, where
Parasolid entities are organized in an object-oriented class hierarchy such that a function that is called on <CLASS> can also be called on all the subclasses of <CLASS>. For example, PK_CURVE_ask_fin can be called on any subclass of CURVE, such as CIRCLE, ELLIPSE, or LINE.
Each function has a fixed set of arguments: some are used to supply data, and others are used to return information. Argument types are simple values, arrays, and structures.
You must set each argument explicitly when calling PK functions, rather than omitting them completely. This is made easier by the use of option structures and initialization macros.
When a PK class has an object belonging to it (which it nearly always has) the name of the C type for that object is of the form PK_ <CLASS>_t.
A PK class may have other types associated with it - structures or token enumerations - and these have names of the form PK_ <CLASS>_ <text>_t. An example of this is PK_SESSION_frustrum_t, which is the type you use when declaring a frustrum. When these types are more directly associated with a particular function their names reflect this, as there are naming conventions for options structures, type classifications, etc.
A PK function argument is used either to receive information from the application, or to return information to your application, but never both.
For example, given a PK function with the following declaration:
PK_ERROR_code_t PK_THING_do_something ( /* received arguments */ const PK_THING_t *in_thing /* returned arguments */ PK_THING_t *const out_thing ) |
You should never call this function as follows, since the results are undefined:
PK_THING_do_something(&my_thing, &my_thing);
Much of the information passed in PK function code is collected together in related groups, or structures. There are three basic structures that you need to be aware of:
Optional arguments and option switches passed to functions are generally collected together in a single structure and passed as one argument, known as an options structure. Option structures are named by adding the string "_o_t" to the function name.
Warning: The first field of any options structure is the version number (
o_t_version
). Your application must never set or alter this value. |
Before you can use an options structure, every field in the structure must be given a value. To make this easier, a macro is available for each option structure that sets every field in the structure to a default value.
After calling the macro, you just need to set any fields that you want to use a different value for. Functions themselves do not have a default action; they always reference the given option structure.
To get the macro name for a given option structure replace the "_o_t" string at the end of the structure name with "_o_m".
An example call to a PK function that uses an options structure is shown below:
/* Declare entities */ PK_FACE_t face; PK_TOPOL_t topol; PK_VECTOR_t point; point.coord[0] = 5.0; point.coord[1] = 5.0; point.coord[2] = 0.0; /* Declare options structure */ PK_FACE_contains_vectors_o_t option; /* Initialize options structure fields */ PK_FACE_contains_vectors_o_m(option); option.vectors = &point; /* Override default values for two fields*/ option.n_vectors = 1; /* Make the function call */ pk_ifail = PK_FACE_contains_vectors (face, &option, &topol); /* Test for pk_ifail */ |
Many classes have a special structure known as a "standard form", that needs to be used when creating instances of that class. You can think of a standard form as a template for given class instance. Standard forms always have names of the form PK_ <CLASS>_sf_t.
Most entities have associated standard forms that contain the definitions of curves, surfaces, and so on required for those entities (though some, such as bodies and vertices, do not). Some other classes also have an associated standard form (such as PK_AXIS1_sf_t), even though there is no equivalent entity.
The same standard form for a class is used whether the calling function is an input function or an output function. For example, PK_CYL_sf_t is used by both PK_CYL_create and PK_CYL_ask.
The following example shows how a cylinder is created using the standard form for a cylinder:
PK_CYL_t my_cylinder; PK_CYL_sf_t my_cyl_sf; my_cyl_sf.basis_set.location.coord[0] = 1.0; my_cyl_sf.basis_set.location.coord[1] = 2.0; my_cyl_sf.basis_set.location.coord[2] = 3.0; my_cyl_sf.basis_set.axis.coord[0] = 1.0; my_cyl_sf.basis_set.axis.coord[1] = 0.0; my_cyl_sf.basis_set.axis.coord[2] = 0.0; my_cyl_sf.basis_set.ref_direction.coord[0] = 0.0; my_cyl_sf.basis_set.ref_direction.coord[1] = 1.0; my_cyl_sf.basis_set.ref_direction.coord[2] = 0.0; my_cyl_sf.radius = 5; PK_CYL_create(&my_cyl_sf, &my_cylinder); |
Some PK functions pass information back to your application using a return structure. Return structures are used when a lot of related information needs to be handed back to your application, such as tracking information describing the changes made to a part as the result of a function call. The names of return structures end in "r_t" - for example, the return structure that returns information about which topology was split, deleted, or created, is called PK_TOPOL_track_r_t.
Returned arguments to PK functions consume variable amounts of data that are returned from the PK as C arrays. The extent to which you need to control memory allocation for these arguments depends on how easy it is to determine how much memory is required for them.
Your application declares the space at compile time and passes a pointer to it to Parasolid. Arguments like this are shown in the PK function headers as, for example, |
|
If the size of the argument can be determined by your application at run time before making the function call. |
Your application allocates space at run-time and passes a pointer to it to Parasolid. Arguments like this are shown in the PK function headers as, for example, |
In all other cases Parasolid dynamically allocates space to return the array, using PK_MEMORY_alloc. |
Your application declares a pointer to the returned type, and passes a pointer to this pointer to Parasolid. Parasolid sets this pointer to point to the returned information. Arguments like this are shown in the PK function headers as, for example, |
Your application allocates and frees memory for variable length return arguments using PK_MEMORY functions. These functions ensure that your application can free memory when it has been allocated by Parasolid, and that space is freed consistently regardless of where it was originally allocated. If the functions are not registered, then the default memory allocation and freeing functions for the operating system are used.
If a PK function fails, and has already allocated space for variable length returns, then Parasolid frees the space by calling the FMFREE function directly.
The example below demonstrates how to use PK_MEMORY_free to free space used by a return argument once it is no longer needed.
PK_BODY_t my_body; int n_faces; PK_FACE_t *my_faces; PK_BODY_ask_faces(my_body, &n_faces, &my_faces); ... ... ... PK_MEMORY_free(my_faces); |
In general, whenever the returned argument is a pointer to a pointer, Parasolid allocates space for whatever is pointed to at the end of the line, and your application frees that space when it is finished with, using PK_MEMORY_free. You can spot two levels of indirection like this easily by looking for a declaration of the form:
type **const name
Sometimes, a PK function returns a pointer to a structure that itself contains a pointer. In this case, it can be harder to spot the second level of indirection. In particular, standard forms (structures that represent the data encapsulated by an object of a particular class) have a fixed size, but they may point to variable length arrays.
For example, PK_BCURVE_ask returns a standard form PK_BCURVE_sf_t which, because it is a fixed size, is declared as:
PK_BCURVE_sf_t *const bcurve_sf
The structure PK_BCURVE_sf_t contains the field:
double *knot
and space for the knot vector is allocated by Parasolid.
Most PK functions that return information using return structures have equivalent memory destructing functions to free up all the memory allocated to the structure in one call. The names of these functions end in "_r_f" - for example, the freeing function for the return structure PK_blend_rib_r_t is PK_blend_rib_r_f.
You can set some return arguments declared in the form
type **const name
to NULL in the function call to indicate that this information is not to be returned and no space is to be allocated for it. Such arguments are indicated by the word 'optional' in the function header.
An example of an optional return argument is shown below:
PK_ERROR_code_t PK_BODY_ask_faces ( --- received arguments --- PK_BODY_t body, --- a body --- returned arguments --- int *const n_faces, --- number of faces (>= 0) PK_FACE_t **const faces --- faces (optional) ) |
In order to just return the number of faces, rather than the faces themselves, set the faces argument to NULL in the function call as follows:
PK_ERROR_code_t status; PK_BODY_t body = ... int n_faces; status = PK_BODY_ask_faces (body, &n_faces, NULL); |
This use of NULL is only allowed where it is explicitly documented. Functions which have option structures never have optional return arguments.
The following simple example demonstrates how the PK interface is used. This example sets an attribute on a face, but ignores the possibility of errors.
/* Assume a face in a valid model has been selected */ PK_FACE_t face = ... ; /* Local variables for attribute code */ PK_ERROR_code_t status; PK_ATTDEF_t colour_defn; PK_ATTRIB_t colour_attrib; double rgb[3]; /* Locate the definition of the */ /* system defined color attribute */ status = PK_ATTDEF_find("SDL/TYSA_COLOUR", &colour_defn); /* Create a new attribute of type color */ /* attached to the given face */ status = PK_ATTRIB_create_empty ( face, colour_defn, &colour_attribute ); /* Fill in the fields of the attribute */ /* with the desired values */ rgb[0] = 0.25; rgb[1] = 0.25; rgb[2] = 0.5; status = PK_ATTRIB_set_doubles(colour_attrib, 0, 3, rgb); |
If you are using Microsoft Visual Studio to develop your Parasolid-powered application, you need to integrate Parasolid's DLL, LIB, and header files correctly into your project environment. This is done as follows:
pskernel.dll
in either the same folder as your application executable, or somewhere on your PATH.
#include <parasolid_kernel.h>
to any source code files that call PK functionality.
pskernel.lib
object library to the MS Visual C++ environment, as described in Section B.6.1.
To add
pskernel.lib
into the MS Visual C++ environment, choose
Project > Settings to display the Project Settings dialog, and follow the instructions in Figure B-1.
Figure B-1 Adding pskernel.lib to the MS Visual Studio environment
You can add one or more directories to the list of directories that are searched for include files. Choose Project > Settings to display the Project Settings dialog, and follow the instructions in Figure B-2.
Figure B-2 Adding an additional include directory to MS Visual Studio
<<< Further Implementation Decisions | Chapters |