iumio
to read and write the interface file. You can link your
code with this library.
Ipe itself is written in C++, using the PLAGEO library (by
Geert-Jan Giezeman) for the geometric primitives. PLAGEO might
be useful for you as well if you want to write a Ium. (You can get
PLAGEO from ftp.cs.ruu.nl:pub/SGI/GEO). But you can
certainly do without it--the iumio
library will work even if
you only have a C compiler.
This chapter describes how to use iumio
to write your own
Ipe user macros. You are also encouraged to look at the
source code of the standard Iums that come with Ipe. These were
deliberately written in different styles to give you some help in all
possible situations. Look at the
Makefile
to see how to compile the files gridalign.c,
spline.C,
and
goodies.C,
and how to link with iumio.c.
The directory ftp://ftp.cs.ruu.nl/pub/X11/Ipe/IpeUserMacros/ contains these and many other sources.
gridalign.c
, for instance, is written in C and uses the simple-most
version of iumio
. You have to link such a program with
iumio.o.
spline.C
is written in C++ and uses the PLAGEO version of
iumio
. As you can see in the Makefile, you have to link
with pliumio.o and with libplageo.
goodies.C
is also written in C++, uses the PLAGEO
version of iumio
, and also a C++ class Transform
supplied
with Ipe. Transform implements planar affine
transformations, and is useful if you want to handle ellipses (which
are not circles). You have to link with tpliumio.o and
transform.o to use this version.
You will often want to do user interaction from your Ipe user
macro, to display a message or to query for a parameter. You can use
the stdout
and stderr
streams for output, but you will
not be able to use stdin
, since the Ium is run as a background
process. If you need to get some user input, you would need to pop up
a window. Fortunately there is an easier way: Ipe can be told that a
certain Ipe user macro needs a parameter. Before starting the Ium, Ipe
will then pop up a little dialog window, prompt the user for the
necessary information, and pass the string to the Ium as an additional
argument. If the necessary piece of information is a file name (a
typical case for functions that import or export drawings in foreign
formats), Ipe will even show a file selector and allow the user to
browse through the file system.
Another way to pass flags or parameters to a Ium are through an environment variable, or even by passing a text object to the Ium.
Sometimes you will wish that you could have persistent
variables--information that is saved between successive invocations
of a Ium (the rotation angle in precise rotate is a good
example). For the time being, I suggest the following standard: All
persistent parameters are kept in a file .iumrc in the user's
home directory. Every line of this file will contain an alpha-numeric
keyword terminated by a colon. White space after the colon is
ignored, and the rest of the line is a string representation of the
parameter. Ipe user macros can read this file, update parameter
values, and add new parameter lines. I suggest that the keyword used
for a parameter contains the name of the Ium executable, so don't use
alpha, but thales.alpha. Some code to support this
system is included in the iumio
library and described below.
Furthermore, the calling mechanism in Ipe can be told to make the
information that has been obtained from the user persistent.
This version of the iumio
library does not need any supporting
libraries, and compiles with a C compiler. (You should link with
iumio.o, see the Makefile). Your Ium source should
look like this.
/* * my_glorious_ium.c */ #include "ium.h" void main(int argc, char **argv) { int arg; ium_begin(argc, argv); switch (ium_argument) { case 0: /* this is the first function */ ... do something with the objects ... break; case 1: /* this is the second function */ ... do something else with the objects ... break; } ium_mode = ium_select_mode; ium_message = "error_or_success_message"; ium_end(); }
ium_begin
tests the number of parameters, decodes the arguments
and reads in the interface file. It sets the variables
ium_argument
, ipe_environment
, and
ium_input
. Before exiting, you should set the variables
ium_output
, ium_mode
, and ium_message
, and call
ium_end
. (All these variables are initialized to "empty", so
nothing can go wrong if you call ium_end
without setting them
all.)
If you do not need to read the interface file, you can call
ium_init
instead of ium_begin
. Of course, you cannot use
ium_input
or ipe_environment
in that case.
ipe_environment
contains the current state of Ipe. It is a
struct
, defined as follows:
The entries reflect the settings of the various selectors in the Ipe window.typedef struct _IpeEnvironment { IpeColor stroke, fill; /* current stroke and fill colors */ unsigned short linestyle; /* solid, dashed etc. as 16 bits */ float linewidth; /* linewidth of stroke */ short arrow; /* two bits for two arrows */ float arsize; /* size of arrows */ float marksize; /* size of marks */ float gridsize; /* grid size */ float snapangle; /* snap angle */ short marktype; /* type of mark (1 .. 5) */ short font; /* font of text object */ float fontsize; /* fontsize */ bool axisset; /* is an axis system defined ? */ vertex origin; /* if so, this is the origin */ float axisdir; /* and this the base direction */ } IpeEnvironment;
IpeColor
, and vertex
are defined as follows.
To specify the empty color, set thetypedef struct _IpeColor { float red, green, blue; } IpeColor; typedef int bool; typedef struct { float x, y; } vertex;
red
component to
-1
.
There are predefined constants for the possible fonts, IPE_ROMAN
,
IPE_ITALIC
, IPE_BOLD
, and IPE_MATH
.
ium_input
is a pointer to a linked list of Ipe objects. The
list order corresponds to the back to front order in Ipe.
A list element looks like this.
The object type is one of the predefined constantstypedef struct _IpeObject { int type; /* type of this object */ bool primary; /* true if this is the primary sel.*/ IpeColor stroke, fill; /* stroke and fill color of object */ unsigned short linestyle; /* solid, dashed etc. as 16 bits */ float linewidth; /* linewidth of stroke */ struct _IpeObject *next; /* pointer to next object */ union W { Line *line; Circle *circle; Mark *mark; Text *text; Arc *arc; Bitmap *bitmap; } w ; } IpeObject;
IPE_LINE
,
IPE_TEXT
, IPE_CIRCLE
, IPE_MARK
, IPE_ARC
,
IPE_BITMAP
, IPE_SPLINE
, IPE_BEGINGROUP
, and
IPE_ENDGROUP
. The meaning of the w
field of the
struct
depends on the object type.
Here are the types for the different objects. If you have trouble understanding the specifications, refer to the Ipe input file description.
IPE_LINE
and IPE_SPLINE
describe polygonal and spline
objects. Both use the w.line
pointer:
typedef struct _Line { /* also used for splines */ bool closed; /* true if closed curve (polygon) */ short arrow; /* two bits for two arrows */ float arsize; /* size of arrows */ int n; /* number of vertices */ vertex *v; /* pointer to array of vertices */ } Line;
IPE_TEXT
describes text objects and minipages. It uses the
w.text
pointer:
typedef struct _Text { char *str; /* the string */ short font; /* font */ float fontsize; /* LaTeX fontsize */ vertex pos; /* position of text */ bool minipage; /* true if text is minipage */ vertex ll, ur; /* ll and ur vertex of bounding box*/ } Text;
IPE_MARK
is for marks:
typedef struct _Mark { vertex pos; /* position */ short type; /* type of mark (1 .. 5) */ float size; /* size of mark */ } Mark;
IPE_CIRCLE
describes circles and ellipses.
Here,typedef struct _Circle { vertex center; /* center of circle */ float radius; /* radius of circle */ bool ellipse; /* is object an ellipse ? */ float tfm[4]; /* tfm values from file */ } Circle;
ellipse
is TRUE
if the input object is not a
circle. When creating an object, you can always set this to TRUE
,
even if the thing is circular, if you set tfm
correctly. Ipe
will understand what you mean. (You should not set it to
FALSE
if you want to write an ellipse, though!).
IPE_ARC
is for circular arcs.
typedef struct _Arc { short arrow; /* two bits for two arrows */ float arsize; /* size of arrows */ vertex center; /* center of arc */ float radius; /* radius of arc */ float begangle, endangle; /* two angles in radians */ } Arc;
IPE_BITMAP
is for bitmaps.
Here,typedef struct _Bitmap { vertex ll, ur; /* lower left, upper right corner */ short width, height; /* no of bits in bitmap */ unsigned long *words; /* pointer to width*height pixels */ bool in_color; /* color bitmap ? */ } Bitmap;
words
points to width
* height
words
containing the pixels in the format expected by lrectwrite
.
The section on bitmap input
has more details.
Finally, IPE_BEGINGROUP
and IPE_ENDGROUP
indicate the
beginning and end of a group object. They can be nested.
After processing, you should set ium_output
to NULL
or to
a linked list of IpeObject
s. (You need not set the primary
field in the objects). You can set ium_message
to point to a
message string that Ipe will print in the message field:
Furthermore, you should setium_message = "cannot compute weird diagram of bitmap";
ium_mode
to one of the values
IUM_SELECT_OLD
, IUM_SELECT_NEW
, IUM_SELECT_BOTH
,
IUM_REPLACE
, or IUM_DELETE_OLD
. This indicates what you
want Ipe to do with your output. IUM_REPLACE
means that
Ipe should replace the original selection with your
ium_output
. (If you use this, your output must have the same
number of elements as the input! A nice way to do this is to walk
through the list ium_input
, making modifications in place, and,
after successful completion, to simply set ium_output
to
ium_input
. See gridalign.c
for an
example.) In the other cases, the output is added at the front of the
drawing, and ium_mode
determines whether to select the original
or the new objects (or both). If ium_mode
is
IUM_DELETE_OLD
, the selection is deleted befor adding and
selecting the new objects.
If you encounter a really fatal error, you can simply exit
with
non-zero exit status. Ipe will not even try to read your output.
If you encounter a non-fatal error (like, for instance, objects of illegal type in the selection), here is a clean way to transmit an error message, but no objects to Ipe:
As mentioned before, you may sometimes wish to use persistent parameters stored in your .iumrc file. You can callif (error_condition) { ium_output = NULL; ium_message = "error_message"; ium_mode = IUM_SELECT_OLD; ium_end(); }
iumrc_get
and iumrc_put
to access those parameters.
iumrc_get
returns a pointer to the parameter string for the
given keyword if it exists in the .iumrc file, NULL
otherwise. The file is written back by ium_end
if any
parameter has been changed using iumrc_put
. Here is a short
piece of code using these two functions:
For simple situations where you just have to prompt the user for a string it is easier to let Ipe handle this using thechar *iumrc_value, *new_value, buf[64]; iumrc_value = iumrc_get("goodies.rotate"); strcpy(buf, (iumrc_value ? iumrc_value : "30.0")); new_value = fl_show_input("Enter angle of rotation", buf); iumrc_put("goodies.rotate", new_value);
-input
or
-file
flag in connection with the -persist
flag.
iumio
library, you need a
C++ compiler and, of course, the PLAGEO library. Here, we assume
familiarity with PLAGEO. You proceed as for the C version of
iumio
, but before including ium.h, you have to define
IUM_PLAGEO
as follows:
After compiling with C++, you have to link with pliumio.o and with the#include <plageo.h> #define IUM_PLAGEO #include "ium.h"
-lplageo
option (see the Makefile
).
Furthermore, the following things have changed with respect to the C
version: First of all, the type vertex
is now a synonym for the
class pl_vec
. The Line
structure has changed:
Likewise, thetypedef struct _Line { /* also used for splines */ bool closed; /* true if closed curve (polygon) */ short arrow; /* two bits for two arrows */ float arsize; /* size of arrows */ pl_vecreray v; /* vertices of polyline */ } Line;
Arc
structure has changed to
typedef struct _Arc { short arrow; /* two bits for two arrows */ float arsize; /* size of arrows */ pl_arc arc; /* center, radius, two angles */ } Arc;
If you want to correctly handle ellipses in all their glory, you will
need to correctly deal with the tfm
field in the Circle
structure. If you have C++, and are using the PLAGEO version of
iumio
, you can use the class Transform. Set
IUM_TRANSFORM
before including ium.h as follows:
After compilation, link with tpliumio.o and with transform.o (see the Makefile).#include <plageo.h> #define IUM_PLAGEO #define IUM_TRANSFORM #include "ium.h"
With this option, the definition of the Circle
structure has
changed to:
Here,typedef struct _Circle { Transform tfm; } Circle;
tfm
is the planar affine transformation that maps the unit
circle into the ellipse you want to describe.
Transform is a class defined in "transform.h"
as follows.
The first constructor makes an identity transformation, the second one takes a pointer to an array of sixclass Transform { public: s_coord A[6]; // transformation matrix Transform(void); Transform(const s_coord *); void apply(pl_vec&); void lapply(pl_vec&); // apply without translation pl_vec transl(void); // return translational part void stretch(const s_coord, const s_coord); // apply stretch to tfm void transform(const pl_rotra&); // apply pl_rotra to tfm Transform inverse(void); // return inverse transformation int is_similar(void); // true if affinely similar void premult(const Transform&); // premultiply second matrix };
s_coord
s.