/* * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router * Copyright (C) 2004-2005 Michael Wybrow * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "libavoid/graph.h" #include "libavoid/makepath.h" #include "libavoid/visibility.h" #include "libavoid/debug.h" namespace Avoid { ConnRefList connRefs; ConnRef::ConnRef(const uint id) : _id(id) , _needs_reroute_flag(true) , _false_path(false) , _active(false) , _route_dist(0) , _srcVert(NULL) , _dstVert(NULL) , _initialised(false) , _callback(NULL) , _connector(NULL) { // TODO: Store endpoints and details. _route.pn = 0; _route.ps = NULL; } ConnRef::ConnRef(const uint id, const Point& src, const Point& dst) : _id(id) , _needs_reroute_flag(true) , _false_path(false) , _active(false) , _route_dist(0) , _srcVert(NULL) , _dstVert(NULL) , _initialised(false) , _callback(NULL) , _connector(NULL) { _route.pn = 0; _route.ps = NULL; if (IncludeEndpoints) { bool isShape = false; _srcVert = new VertInf(VertID(id, isShape, 1), src); _dstVert = new VertInf(VertID(id, isShape, 2), dst); vertices.addVertex(_srcVert); vertices.addVertex(_dstVert); makeActive(); _initialised = true; } } ConnRef::~ConnRef() { freeRoute(); if (_srcVert) { vertices.removeVertex(_srcVert); delete _srcVert; _srcVert = NULL; } if (_dstVert) { vertices.removeVertex(_dstVert); delete _dstVert; _dstVert = NULL; } if (_active) { makeInactive(); } } void ConnRef::updateEndPoint(const uint type, const Point& point) { assert((type == (uint) VertID::src) || (type == (uint) VertID::tar)); //assert(IncludeEndpoints); VertInf *altered = NULL; VertInf *partner = NULL; bool isShape = false; if (type == (uint) VertID::src) { if (_srcVert) { _srcVert->Reset(point); } else { _srcVert = new VertInf(VertID(_id, isShape, type), point); vertices.addVertex(_srcVert); } altered = _srcVert; partner = _dstVert; } else // if (type == (uint) VertID::dst) { if (_dstVert) { _dstVert->Reset(point); } else { _dstVert = new VertInf(VertID(_id, isShape, type), point); vertices.addVertex(_dstVert); } altered = _dstVert; partner = _srcVert; } bool knownNew = false; vertexVisibility(altered, partner, knownNew, true); } void ConnRef::makeActive(void) { assert(!_active); // Add to connRefs list. _pos = connRefs.insert(connRefs.begin(), this); _active = true; } void ConnRef::makeInactive(void) { assert(_active); // Remove from connRefs list. connRefs.erase(_pos); _active = false; } void ConnRef::freeRoute(void) { if (_route.ps) { _route.pn = 0; std::free(_route.ps); _route.ps = NULL; } } PolyLine& ConnRef::route(void) { return _route; } void ConnRef::calcRouteDist(void) { _route_dist = 0; for (int i = 1; i < _route.pn; i++) { _route_dist += dist(_route.ps[i], _route.ps[i - 1]); } } bool ConnRef::needsReroute(void) { return (_false_path || _needs_reroute_flag); } void ConnRef::moveRoute(const int& diff_x, const int& diff_y) { for (int i = 0; i < _route.pn; i++) { _route.ps[i].x += diff_x; _route.ps[i].y += diff_y; } } void ConnRef::lateSetup(const Point& src, const Point& dst) { assert(!_initialised); bool isShape = false; _srcVert = new VertInf(VertID(_id, isShape, 1), src); _dstVert = new VertInf(VertID(_id, isShape, 2), dst); vertices.addVertex(_srcVert); vertices.addVertex(_dstVert); makeActive(); _initialised = true; } VertInf *ConnRef::src(void) { return _srcVert; } VertInf *ConnRef::dst(void) { return _dstVert; } bool ConnRef::isInitialised(void) { return _initialised; } void ConnRef::unInitialise(void) { vertices.removeVertex(_srcVert); vertices.removeVertex(_dstVert); makeInactive(); _initialised = false; } void ConnRef::removeFromGraph(void) { for (VertInf *iter = _srcVert; iter != NULL; ) { VertInf *tmp = iter; iter = (iter == _srcVert) ? _dstVert : NULL; // For each vertex. EdgeInfList& visList = tmp->visList; EdgeInfList::iterator finish = visList.end(); EdgeInfList::iterator edge; while ((edge = visList.begin()) != finish) { // Remove each visibility edge delete (*edge); } EdgeInfList& invisList = tmp->invisList; finish = invisList.end(); while ((edge = invisList.begin()) != finish) { // Remove each invisibility edge delete (*edge); } } } void ConnRef::setCallback(void (*cb)(void *), void *ptr) { _callback = cb; _connector = ptr; } void ConnRef::handleInvalid(void) { if (_false_path || _needs_reroute_flag) { if (_callback) { _callback(_connector); } } } void ConnRef::makePathInvalid(void) { _needs_reroute_flag = true; } int ConnRef::generatePath(Point p0, Point p1) { if (!_false_path && !_needs_reroute_flag) { // This connector is up to date. return (int) false; } _false_path = false; _needs_reroute_flag = false; VertInf *src = _srcVert; VertInf *tar = _dstVert; if (!IncludeEndpoints) { lateSetup(p0, p1); // Update as they have just been set by lateSetup. src = _srcVert; tar = _dstVert; bool knownNew = true; vertexVisibility(src, tar, knownNew); vertexVisibility(tar, src, knownNew); } bool *flag = &(_needs_reroute_flag); makePath(this, flag); bool result = true; int pathlen = 1; for (VertInf *i = tar; i != src; i = i->pathNext) { pathlen++; if (i == NULL) { db_printf("Warning: Path not found...\n"); pathlen = 2; tar->pathNext = src; if (InvisibilityGrph) { // TODO: Could we know this edge already? EdgeInf *edge = EdgeInf::existingEdge(src, tar); assert(edge != NULL); edge->addCycleBlocker(); } result = false; break; } if (pathlen > 100) { fprintf(stderr, "ERROR: Should never be here...\n"); exit(1); } } Point *path = (Point *) malloc(pathlen * sizeof(Point)); int j = pathlen - 1; for (VertInf *i = tar; i != src; i = i->pathNext) { if (InvisibilityGrph) { // TODO: Again, we could know this edge without searching. EdgeInf *edge = EdgeInf::existingEdge(i, i->pathNext); edge->addConn(flag); } else { _false_path = true; } path[j--] = i->point; } path[0] = src->point; // Would clear visibility for endpoints here if required. PolyLine& output_route = route(); output_route.pn = pathlen; output_route.ps = path; return (int) result; } //============================================================================ // It's intended this function is called after shape movement has // happened to alert connectors that they need to be rerouted. void callbackAllInvalidConnectors(void) { ConnRefList::iterator fin = connRefs.end(); for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i) { (*i)->handleInvalid(); } } }