Though this is implied in the AVS/Express documentation it is not explicitly mentioned nor any examples given. The problem is how to map (i,j,k) data to coordinates of (xmin,ymin,zmin)->(xmax, ymax,zmax). This might be because simply the data's origin is not at (0,0,0) or that the spacing differs in each dimension (eg medical data's Z spacing).
This can be done by supplying the coordinates in a separate file or appended immediately after the node data. The layout should be in the form:
xmin xman ymin ymax .....
and can be ascii or binary (where the values are floats).
The attached files are a modified hydrogen.fld (looks at default Express install on Windows for actual data) and a separate ascii coordinates file which changes the origin and the spacing.
hydrogen_coords.v
:
# AVS field file # this is a header file for a field to be # used in conjunction with the build a field module of AVS # ndim = 3 dim1 = 64 dim2 = 64 dim3 = 64 nspace = 3 veclen = 1 data = byte field = uniform coord 1 file=hydrogen.coordxyz filetype=ascii coord 2 file=hydrogen.coordxyz filetype=ascii offset=2 coord 3 file=hydrogen.coordxyz filetype=ascii offset=4 variable 1 file=c:/Express/data/volume/hydrogen.dat filetype=binary skip=3
hydrogen_coords.v
:
-10.0 10 -20.0 20 -30.0 30
Again this is documented but is far from clear as it uses the terms minimum/maximum_ext to mean subtley different things.
The min_ext
and max_ext
keywords are used to define a bounding box that may describe for example the original extents of the data before it was cropped in a preprocessing stage. This bounding box is then used for centering and rotation and hence causes some strange and unintuative behaviour behaviour when inappropiately set, particularly if the bounding box is much greater than the extent of the cropped data. Specifically the values override the Read_Field.out.coordinates.min/max_vec[]
arrays.
Here a just a few tips for compiling projects after I tried doing one myself. There is basically one choice to be made depending on how you've set up your project and applications. Whether to save selected objects or save current application.
save selected objects - this way you end up with an executable which you tell to load an application, the binary containing all the necessary components. This is the most flexible as you can call it with different applications that use the same components. Also you can you it when save current application doesn't work, see below. If you want to select libraries that you've created containing you objects you should do so from the Templates library page as if you select a NE library that maps from the Templates to compilation seems to fail. You can either supply your applications as separate .v/.vo files or it is possible to combine them into the executable (or the appl.vo
file if build v into executable is not selected). Create an flibrary called something like MYAPPS somewhere and then reference your application files:
flibrary MYAPPS { "../apps/my_app1.v" MyApp1; "../apps/my_app2.v" MyApp2; };
You can then run which ever application you wish using a small v file for each application e.g. bin/$MACHINE/express runapp1.v
which looks like:
MYPROJ.MYAPPS.MyApp1 MyApp1;
save current application - this lets you have a executable that fires up with the chosen application if you select instance objects automatically on restore. Also you can use it to reduce the number of objects that are compiled in to the executable since it will only add those used in the application, useful say if your project contains lot of IAC objects but only a few a used in the app (and its too fiddly to go and find and select all the individual objects). Of course though a different app may well require additional objects. Though the project I compiled only had a single app that I wanted to launch straight away, I found a problem since I had used a single gen.cxx
file for all my modules. Not all of which are used in the application and hence weren't linked in, the gen.cxx then had unresolved symbols for these modules. A rough solution would be to copy over the .o
files and modify the express.mk
then relink but I choose the method described above.
Finally I did this under Linux and Win32. Linux was fine though surprise, surprise there were niggles under Win32. Mainly the gen.*
files not referenced from the parent project and neither did it linked external libraries that I'd place in lib/
these all had to be copied to the same locations but under the complied project tree. Grrrrr!
Got a lot of IAC projects but fed a with having to find the the docs for each module? Well no more troubles with this handy script, just run it from the Express project directory:
#!/bin/sh # makeindex # search for html files in iac_proj tree and build a index.html file echo "<html><head><title>IAC Express Projects - Documentation</title></head>" > index.html echo "<body bgcolor=\"#92bfbf\">" >> index.html echo "<h2 align="center">IAC Express Projects - Documentation</h2>" >> index.html echo "<ul>" >> index.html cd iac_proj find -name "*.html" -printf "<li><a href=\"iac_proj/%p\">%p</a></li>\n" >> ../index.html cd .. echo "</ul>" >> index.html echo "<br><a href=\"http://www.iavsc.org\">IAC Homepages</a>" >> index.html echo "</body></html>" >> index.html
If you're using Windoze then you could install Cygwin :).
If using external libraries in a module that need to be closing when the module is deinstanced or have allocated memory that exists for the duration of the module you will want to have a method called when the module is deinstanced so as to free these resources. The notify_deinst
attribute is what you need to set on this "cleanup" method. All well and good except that under C++ there is a bug in AVS/Express which means that the C++ object representing the module is deleted before the "cleanup" method is called. This usually mainfests itself as internal pointers that you have allocated being set to zero or worse being completely overwritten causing memory heap errors when you try and deallocate the pointer. Thankfully the solution is simple; replace the "cleanup" method with and explict C++ destructor for your module's C++ class. This can be done using the cxx_member
property e.g.,
library MYLIB { cxxmodule mymod<...., cxx_member="~MYLIB_mymod();", ....> { .... }; };
You can now close libraries and deallocate memory as you would have done in the Express method.
This object sits in Root and keeps track of whats happening while the Object Manager runs the application. This is what it looks like by default:
scheduler Scheduler { sched_list notifiers; run_list running; int disable = 0; int interrupt = 0; int percent_done = 100; string current_mod = ""; boolean status_check = 1; };
"$XP_PATH<0>/v/templ.v" Templates { Scheduler { status_check = 0; }; };
Anyone who has used AVS/Express will agree that the volume rendering is poor to say the least. This however is not the case the volume renderer is actually pretty good its the appalling user interface and macro that's the problem.
As is the Volume_Render module provides the provide the following functionality. According to the UI you can only have two data ranges delineated by the Range Position, i.e., only a ramped stepped transfer function. Further more the min/max of the data range is not set to be the min/max of your data but calculated to the nearest 8/12/16 bit data range (i.e., 0-255/0-4095/0-65536) this lead to problems if the majority of your data was constrained to only a small part of the range, a common occurrence in medical data. What you would get was some green blob as 95% of your data lay between 200 and 500 but was rendered with a datamap extending from 0-4095.
The problem of the min/max data range I addressed with my volren2 macro available from the IAC which allowed you to set the data range. However with the release of 5.1 and the new ColourMapEditor its painfully obvious that the UI has crippled the use of the volume render as you can edit the colour map to your hearts content, creating also sort of complex transfer functions.
The lousy UI is compounded by the fact that the volume render object uses VolumeRenderDatamap, this is the criminal piece of coding that chooses the inappropriate data range rather than using the data's actual min/max. The only thing to do with it is rip it out, yes I want you to do it now! In its place simply use a DefaultLinear datamap. You can also scrap half the UI and just hook up the ColourMapEditor. Of course the ColourMapEditor needs to be kicked into shape, but don't get me started....
Are you fed up with Express complaining that the object cache size is too small? Have to wait ages for Express to render the uncached object before carefully increasing the cache size? Simply use or modify the following templ.v
file in your project and you can change the the default setting for all DataObject related objects.
NOTE: the cache size is just an upper limit and does not waste memory. In generally you should only not cache objects when they are so big the system does not have enough memory to cache the object (mesh_size->memory_size) but you still want to try and render it.
@AVS: it would be nice if the default cache_size could be set using and environment variable or maybe by adding as define to a project avsenv file(?)
// Change the DataObject default cache size to something // sensible for a high performance system #define DEFAULT_CACHE_SIZE 128 "$XP_PATH<0>/v/templ.v" Templates { GD { DefaultObject { cache_size = DEFAULT_CACHE_SIZE; }; // these other objects derive from DefaultObject but are already // created before this project modifies DefaultObject AltObject { Obj { cache_size = DEFAULT_CACHE_SIZE; }; }; DataObject { Obj { cache_size = DEFAULT_CACHE_SIZE; }; }; GroupObject { Top { cache_size = DEFAULT_CACHE_SIZE; }; }; DataObjectLite { Obj { cache_size = DEFAULT_CACHE_SIZE; }; }; DataObjectNoTexture { Obj { cache_size = DEFAULT_CACHE_SIZE; }; }; DataObjects { dos { Obj { cache_size = DEFAULT_CACHE_SIZE; }; }; }; DataObjectArr { dos { cache_size = DEFAULT_CACHE_SIZE; }; }; DataObjectNoTextureArr { dos { cache_size = DEFAULT_CACHE_SIZE; }; }; DataObjectLightArr { dos { cache_size = DEFAULT_CACHE_SIZE; }; }; }; };
To explicitly set a 2D array in the NE you need to use =>
rather than =
otherwise Express simplfies it to 1D array on pressing Ctrl-Enter. this can cause errors in macros that examine the dimensionality of the array such as the Mapping macros. For example typing into an int arr[][]
:
= {{0,0}, {1,1}, {2,2}} Ctrl-Enter becomes = {0,0,1,1,2,2}
and the object becomes int a[6]
, whereas
=> {{0,0}, {1,1}, {2,2}} Ctrl-Enter
remains unchanged and the object becomes int a[3][2]
.
Obviously you can go and lookup function in the header files (e.g. om.h
) yourself but here I shall draw your attention to the ones that I've found useful and hopeful give correct information on their usage.
OMtype_size(int type)
sizeof()
for OM_TYPE_XXXX
definesOMvalid_obj(OMobj_id obj_id, OMobj_id templ, int mode)
OMnull_obj
. The mode should be set to 0. The OMX version is mentioned in the developer's C++ guide but not listed in the OM referenceThis is a list of undocumented or underdocumented V-code objects.
NOTE: Functions marked » aren't actually V code functions but are func
objects see the Types section below.
is_valid(Obj obj)
obj
is valid or not. However this only works as expected on prims and strings i.e., does the obj
have a value, e.g.,
string text; UIlabel text_label { label => switch(is_valid(<-.text)+1,"Undefined", text); };A group will always return 1 even if its sub-objects are not, a group reference is 1 if its connected to an existing group and only 0 if it doesn't reference anything.
»get_coords_unif(int ndim, int dims[], int nspace, float points[])
»get_coords_rect(int ndim, int dims[], int nspace, float points[])
OMparse_obj_ref(out.coordinates.values.obj_id(), "get_coords_rect(ndim,dims,nspace,points)");
»get_coords_cyl_unifint ndim, int dims[], int nspace, float points[])
»get_coords_cyl_rect(int ndim, int dims[], int nspace, float points[])
»get_coords_spher_unifint ndim, int dims[], int nspace, float points[])
»get_coords_spher_rect(int ndim, int dims[], int nspace, float points[])
sort
library+sort MYMACS { ...
func
func
objects. I hope later to be able to provide a description of how to write your own.animate
init_code
BaseInit
has run i.e. after the Template library has loaded. This is useful if you've added a config group to Templates to store some global information, you can then initialize the config values for your project. So where does Express place your code? Well if you define a library using this attribute the code is place in a function called <library_name>_Init
which will be found in xexpress.cxx
. The very useful thing is that <library_name>_Init
is passed the command-line arguments:
void <library_name>_Init(int *argc,char **argv #ifdef MSDOS , HINSTANCE *hInst, HINSTANCE *hPrevInst #endif )So your initialization code can read its own command-line arguments. Express should ignore arguments it doesn't recognize, though it may silently try (and fail) to load them as an application. In which case you may need to explicitly pass -ne (open Network Editor).
notify_val
pre_init_code
PRE<library_name>_Init
is created in xexpress.cxx
and is called by PREinit
in express.cxx
. As with init_code
its wrapper function is passed the command-line arguments:
void PRE<library_name>_Init(int *argc,char **argv #ifdef MSDOS , HINSTANCE *hInst, HINSTANCE *hPrevInst #endif )
virtual
virtual
is used to add additional subobjects based on the matching of existing subobjects. For example lets define a base group object:
group base { int type; };We can then add additional subobjects based on the value of
type:
base foo {
type = 1;
float+virtual a = 4.1;
}
base bar {
type = 2;
float+virtual a = 1.4;
}
Any object that uses base
as a template will gain float a
with a different default value depending on the value of type:
base b {
type = 2;
};
$float b.a
1.400000
you can use more than one subobject to form the templates. If the templates have similarities the OM will choose the template that has the closest match to any obect you define. Finally the virtual objects don't need to be of the same object type i.e., bar
could have defined a virtual int
. A common use of virtual object is the render and pick methods that are selected according to the Mesh
and Cell_Set
type.
weight
weight
orders execution of methods in a module that share the same input parameter. Firstly to be clear, the method with the least value executes first the method with the greatest last and the default value is 1. So a method with weight=-10
will execute before a method with weight=10
when both have been triggered by a single input parameter changing. Secondly there is no requirement the methods to be in the same module, hence weight
can be used to make two or more separate modules execute in order if they all have the same trigger.$invoke <obj>
invoke of parse_v_relative-<null> returns 1
Described here are the functions of the OMXobj
base class. All objects with a C++ class representation will be derived from this class e.g. OMXgroup
, OMXint
. Developers should also refer to the omx.hxx
include file. Though many are documented under their OM equivalent there is no complete list of them.
virtual const char *class_name()
const char *name()
OMobj_id obj_id(int mode = OM_OBJ_RW)
OMobj_id
of this object, this is called by the (OMobj_id)
overloaded cast.void set_obj_id (OMobj_id obj_id)
OMobj_id obj_val()
OMget_obj_val()
and returns the OMobj_id
of the object at the end of the chain of references. If no valid object found return OMnull_obj
.int set_obj_val(OMobj_id conn_id,int mode = 0)
OMset_obj_val()
.operator OMobj_id ()
(OMobj_id)
overloaded cast and returns this object's OMobj_id
.int operator== (OMobj_id rhs)
OMobj_id
with another object's OMobj_id
.int operator!= (OMobj_id rhs)
OMobj_id
with another object's OMobj_id
.int ret_num_subobjs()
OMobj_id ret_subobj_id(int i, int mode = OM_OBJ_RD)
OMobj_id
of the ith sub-object.unsigned int push_ctx (int state = 0, int event_mode = 0, int child_event_mode = 0)
OMpush_ctx()
.int pop_ctx ()
OMpop_ctx()
.int add_obj_ref(OMobj_id conn_id,int mode = 0)
OMadd_obj_ref()
.int del_obj_ref(OMobj_id conn_id,int mode = 0)
OMdel_obj_ref()
.int set_obj_ref(OMobj_id conn_id,int mode = 0)
OMset_obj_ref()
.int insert_obj_ref(int ind, OMobj_id conn_id, int mode = 0)
OMinsert_obj_ref()
.int valid_obj(OMobj_id templ = OMnull_obj, int mode = 0)
OMvalid_obj()
returns true if object has a valid value or reference. Use the templ for sub-objects to check against, you should usually pass OMnull_obj
. The mode should be set to 0.int changed(int seq_num, OMobj_id templ = OMnull_obj, int mode = OM_SEQ_VAL)
int get_obj_seq(OMobj_id templ = OMnull_obj, int mode = OM_SEQ_VAL)
OMget_obj_seq()
<./dd>
int is_type_of(OMtype_id type)
type
.The following functions should be called only if the object is an array.
int get_array(int *type, void **array, int *ndim, int *dims, int mode)
OMget_array()
.int set_array(int type, void *array, int len, int mode = OM_SET_ARRAY_COPY)
OMset_array()
.void free_array(void *array)
ARRfree()
in the array pointer.void *ret_array_ptr(int mode = OM_GET_ARRAY_RW, int *size = NULL, int *type = NULL)
OMret_array_ptr()
.void *ret_typed_array_ptr(int mode = OM_GET_ARRAY_RW, int type = OM_TYPE_INT, int *size = NULL)
OMret_typed_array_ptr()
.char *ret_str_array_val(int index, char *buffer = NULL, int buffer_size = 0 )
OM*ret_str_array_val()
.int set_str_array_val(int index, const char *value)
OMset_str_array_val()
.int get_array_size(int *size)
OMget_array_size()
.int set_array_size(int size)
OMset_array_size()
.int ret_array_size()
get_array_size()
as it return the size directly.