The following information is old and more than likely outdated. This information is here for archival purposes.
The Python interface to plCurve is implemented with the aid of SWIG.
There is a (hastily written) example of a plCurve visualizer in Python which uses pyopengl and pyside Qt bindings: plcview.py.
As of 2014-05-11, SWIG build support is enabled (and required I think, oops!) in SVN master.
So long as you have a version of plCurve which is set up to build the python interface, installation should be little extra work: It’s possible that Python does not know about the prefix into which plCurve installs itself.
If the following python commands don’t work,
you’ll have to update your PYTHONPATH
environment variable. First
you’ll need to know the prefix to which plCurve installed: If you’ve
added your own prefix argument to the ./configure
directive for
plCurve’s build then you know this already. Otherwise it’s probably
/usr/local
. Add the following bit to your bash init file
(.bash_profile
if you’re on a Mac or .bashrc
if you’re on Linux)
making the obvious substitutions necessary:
Let’s get started! First, it’s probably best to make sure the library loads without issue. Boot up your python console (you are using iPython by now, correct?) and try the following invocation:
Assuming that there were no errors thrown (if there were, it may be a
problem with your PYTHONPATH
. See how you may fix it above.) we’re
hooked up and ready to go. Let’s start out by drawing a random closed arm:
If that worked, you should consider your environment prepared! Even better, the arm ends back at the origin, so things are as they should be. Let’s go a bit deeper into how to use the library.
In the last section, we of course started with the ever-important
importing of the library. We named it pl
then just because it is
bad practice to import all globals of python libraries but short
since we’re presumably going to be calling it a lot! The choice is
yours, but be wary of this. So, if you’re going to be using plCurve in
Python, this should probably be somewhere near the top of the code.
In the first example that we worked above, we generated a random polygon. Other reasons aside, it’s certainly easier to get an interesting (non-contrived) polygon without too much effort by drawing it randomly. Let’s break down the example.
This line initializes a gsl_rng
object in C as we can see in the SWIG interface code
which “extends” gsl_rng
to act more like an honest class with a
standard constructor and destructor. Additionally, SWIG notices that
the declaration of the method set
has the same signature (with first
argument a pointer to a gsl_rng
) as the
library function gsl_rng_set
and cleverly links the two with no additional effort. So if we had
wanted to set a specific seed in our sample experiment above, we could
have added the line (in Python)
before we pulled the random polygon. But how did we create the random
polygon? Similarly to how the interface wraps the gsl_rng
struct as
a class, the majority of the interface specification wraps the
plc_type
(a.k.a plCurve
, this might get a little confusing) struct
(and friends) as a class (with some additional Python directives).
This snippet shows, among other things, that the PlCurve
class which
Python sees is a wrapper around the plc_type
that C knows about. It
has a standard constructor, copy constructor, and destructor. It has
loads of methods, namely the static random_closed_polygon
which
we’ve used in the example code. Its first argument is a gsl_rng *
,
so we passed in the RandomGenerator
object that we had—SWIG
handles the rest. int
s are easy, but it’s not too hard (with a
little extra work) to pass even more complicated Python objects to C
naturally.
Once we’ve created the random polygon, we access its component (which SWIG also knows how to wrap thanks to the interface file) and checked its vertices. If you’re using iPython with its extremely useful smart tab completion, this is a great opportunity to explore a component object:
While acquire
, disown
, append
, own
, next
, this
, and
thisown
are remnants of the SWIGObject base class, the rest of the
options are class members or methods which (should) link appropriately
into the C library code. For example, the component’s member vertices
grabs the polygon’s vertices as a Python list!
The interface as it stands is far from complete, and as plCurve is further developed it will need to be further expanded. Fortunately, this is really not too bad with the aid of SWIG, and in many cases it should be almost trivial. Let’s run through an example.
Recently, the C plc_classify
method has changed (to not rely upon
ancient Fortran code) and, among other things now requires a gsl_rng
argument. We’d like to add classify
as a method of a PlCurve
in
Python, so we’ve added the following lines into the
block for the plc_type
:
Remember that the %extend
directive tells SWIG to add class methods
and members to what it ends up calling a plc_type
(it’s a
PlCurve
); let’s inspect the bit we’ve added in there. Notice that
plc_classify
takes three inputs; a gsl_rng *
, a plc_type *
, and
an int *
. So our wrapper asks for a gsl_rng *
and an int *
but
knows to pass a pointer to the actual plc_type
object by passing
$self
to the C code. It’s important to notice now that the int *
isn’t really an input for the C code: It’s an additional output!
While Python does not have “integer pointer” capabilities, Python
functions can easily return multiple entities as a tuple. This is
what the %typemap
bits above handle: An in typemap tells SWIG not to
ask Python for this integer pointer and instead creates one
itself. The out typemap then combines both the function’s actual
return value and the integer pointer faux return into a tuple which
Python can grok.
The last C subtlety to notice here is that plc_classify
returns a
pointer to a plc_knottype
object; a C programmer understands that
this usually means that the function malloc
s some memory for the
plc_knottype
object and that we (the end user) must be sure to
free
it when we are done. This is what the
%newobject
SWIG directive
is for: Once we tell SWIG that a new object is created by this
function, it will know how to appropriately garbage collect the memory
created in C when the Python references are obsolete. This both
prevents against memory leaks and allows us to be Pythonic with Python
(and not have to micromanage every little bit of the C library in our
scripts!).
As of 2014-05-11, plc_classify
does nothing (or even
segfaults!). This is not the interface or Python’s fault: the function
ccode_from_pd_code
is presently just not implemented!
Something hidden here is that presently, the SWIG interface file also
contains instructions which turn plc_knottype
s into Python objects!
If you had added a function to the interface without this, your method
should still work: SWIG would return you an object which points to the
C data which you could use in other interfaced C functions without
issue. In order to access the data inside of this pointer object
though, you’d need to expand the interface to know more about this
new type.