Adaptagrams
libavoid — Documented code example

libavoid is a C++ library. Its code is all within the Avoid namespace.

First, you must create an instance of the router.

To add a shape (obstacle) to the router, you first create a Avoid::ShapeRef by giving the bounding box of the obstacle. This adds the shape to the router (and cause rerouting of connectors it intersects). It also passes ownership of the shapeRef object to the router instance, though it is still fine for you to keep a reference to it.

// Create the ShapeRef:
Avoid::Rectangle rectangle(Avoid::Point(20.0, 35.0), Avoid::Point(40.0, 12.0));
Avoid::ShapeRef *shapeRef = new Avoid::ShapeRef(router, rectangle);

or

Avoid::Polygon shapePoly(3);
shapePoly.ps[0] = Avoid::Point(1.0, 1.0);
shapePoly.ps[1] = Avoid::Point(2.5, 1.5);
shapePoly.ps[2] = Avoid::Point(1.5, 2.5);
Avoid::ShapeRef *shapeRef = new Avoid::ShapeRef(router, shapePoly);

The relevant prototypes (all in the Avoid namespace) are as follows. If a shape ID is specified, it should be non-zero and unique among all shapes and connectors.

Avoid::Rectangle(const Avoid::Point& topLeft, const Avoid::Point& bottomRight);
Avoid::Rectangle(const Avoid::Point& centre, const double width, const double height);
Avoid::ShapeRef(Avoid::Router *router, const Avoid::Polygon& polygon, unsigned int id = 0);

To move or resize a shape already in the router, you do the following:

router->moveShape(shapeRef, newPolygon);

or

double xmove = 20, ymove = 15;
router->moveShape(shapeRef, xmove, ymove);

In its default mode the router will queue multiple shape movements and perform the changes to the visibility graph in an optimised order. Thus you make several calls to Avoid::Router::moveShape() for different shapes and then tell the router to process the moves. This tend to be useful in interactive applications where the user may move multiple shapes at once.

router->moveShape(shapeRef1, newPolygon1);
router->moveShape(shapeRef2, newPolygon2);

To delete a shape from the router (and reroute connectors that then have a better path) you do the following.

router->deleteShape(shapeRef);

This will cause the router to free the memory for the shapeRef. You should discard your reference to the shapeRef after this call.

To add a new connector to the router, you do the following:

Avoid::ConnEnd srcPt(Avoid::Point(1.2, 0.5));
Avoid::ConnEnd dstPt(Avoid::Point(3.0, 4.0));
Avoid::ConnRef *connRef = new Avoid::ConnRef(router, srcPt, dstPt);

This passes ownership of the connRef object to the router instance, though it is still fine for you to keep a reference to it.

To remove a connector from the router:

router->deleteConnector(connRef);

This will cause the router to free the memory for the connRef. You should discard your reference to the connRef after this call.

You can set a function to be called when the connector needs to be redrawn. When called, this function will be passed the pointer given as a second argument to Avoid::ConnRef::setCallback():

void connCallback(void *ptr)
{
Avoid::ConnRef *connRef = (Avoid::ConnRef *) ptr;
printf("Connector %u needs rerouting!\n", connRef->id());
}
connRef->setCallback(connCallback, connRef);

The callback will be triggered by movement, addition and deletion of shapes, as well as by adjustment of the connector endpoints, or by processing a transaction that includes any of these events. You can check if a connector path has changed, and hence the object requires repainting (say because a better path is available due to a shape being deleted):

if (connRef->needsRepaint()) ...

If you want to trigger the callback for a connector after moving its endpoints (or when it is first created you can do this via:

connRef->processTransaction();

You can then get the new path as follows:

const Avoid::PolyLine route = connRef->dispayRoute();
for (size_t i = 0; i < route.size(); ++i)
{
Avoid::Point point = route.at(i);
printf("%f, %f\n", point.x, point.y);
}

Obviously the alternative to using the callback mechanism is to iterate through all connectors and check their needsRepaint() value after having called processTransaction().

You can update the endpoints of a connector with:

Avoid::ConnEnd newSrcPt(Avoid::Point(6, 3));
Avoid::ConnEnd newDstPt(Avoid::Point(12, 67));
connRef->setEndpoints(newSrcPt, newDstPt);

or

Avoid::ConnEnd newSrcPt(Avoid::Point(6, 3));
connRef->setSourceEndpoint(newSrcPt);
Avoid::ConnEnd newDstPt(Avoid::Point(6, 3));
connRef->setDestEndpoint(newDstPt);

You can also create connection pins on shapes and attach connectors directly to these. Then when you move or resize the shapes, the connector endpoints attached to them will be automatically rerouted.

You can create a connection pin as follows:

const unsigned int CENTRE = 1;
new Avoid::ShapeConnectionPin(shapeRef, CENTRE, Avoid::ATTACH_POS_CENTRE, Avoid::ATTACH_POS_CENTRE);

This one connects to the centre of the shape, but the position can be specified anywhere within the shape as a proportion of the shape's width and height.

You can then attach a connector to the connection pin be doing the following:

Avoid::ConnEnd newSrcPt(shapeRef, CENTRE);
connRef->setSourceEndpoint(newSrcPt);

See also a short example: example.cpp in the libavoid/tests directory