summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/adaptagrams/libavoid/connector.h
blob: 74571396e74666c845d77bd87327a8670a4979e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
/*
 * vim: ts=4 sw=4 et tw=0 wm=0
 *
 * libavoid - Fast, Incremental, Object-avoiding Line Router
 *
 * Copyright (C) 2004-2015  Monash University
 *
 * 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.
 * See the file LICENSE.LGPL distributed with the library.
 *
 * Licensees holding a valid commercial license may use this file in
 * accordance with the commercial license agreement provided with the 
 * library.
 *
 * 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.  
 *
 * Author(s):   Michael Wybrow
*/

//! @file    connector.h
//! @brief   Contains the interface for the ConnRef class.


#ifndef AVOID_CONNECTOR_H
#define AVOID_CONNECTOR_H

#include <utility>
#include <list>
#include <vector>

#include "libavoid/dllexport.h"
#include "libavoid/vertices.h"
#include "libavoid/geometry.h"
#include "libavoid/connend.h"


namespace Avoid {

class Router;
class ConnRef;
class JunctionRef;
class ShapeRef;
typedef std::list<ConnRef *> ConnRefList;


//! @brief  Describes the type of routing that is performed for each 
//!         connector.
enum ConnType {
    ConnType_None       = 0,
    //! @brief  The connector path will be a shortest-path poly-line that
    //!         routes around obstacles.
    ConnType_PolyLine   = 1,
    //! @brief  The connector path will be a shortest-path orthogonal 
    //!         poly-line (only vertical and horizontal line segments) that
    //!         routes around obstacles.
    ConnType_Orthogonal = 2
};

//! @brief  A checkpoint is a point that the route for a particular connector
//!         must visit.  They may optionally be given an arrival/departure
//!         direction.
//!
class AVOID_EXPORT Checkpoint
{
    public:
        //! @brief  A point that a route must visit.
        //!
        //! The connector will be able to enter and leave this checkpoint from 
        //! any direction.
        //!
        //! @param[in] p  The Point that must be visited.
        Checkpoint(const Point& p)
            : point(p),
              arrivalDirections(ConnDirAll),
              departureDirections(ConnDirAll)
        {
        }
        //! @brief  A point that a route must visit.
        //!
        //! The connector will be able to enter and leave this checkpoint from 
        //! the directions specified.  Give Avoid::ConnDirAll to specify all
        //! directions.
        //!
        //! @param[in] p  The Point that must be visited.
        //! @param[in] ad Avoid::ConnDirFlags denoting arrival directions for
        //!               the connector portion leading up to this checkpoint.
        //! @param[in] dd Avoid::ConnDirFlags denoting departure directions 
        //!               for the connector portion leading away from this 
        //!               checkpoint.
        Checkpoint(const Point& p, ConnDirFlags ad, ConnDirFlags dd)
            : point(p),
              arrivalDirections(ad),
              departureDirections(dd)
        {
        }
        // Default constructor.
        Checkpoint()
            : point(Point()),
              arrivalDirections(ConnDirAll),
              departureDirections(ConnDirAll)
        {
        }

        Point point;
        ConnDirFlags arrivalDirections;
        ConnDirFlags departureDirections;
};


//! @brief   The ConnRef class represents a connector object.
//!
//! Connectors are a (possible multi-segment) line between two points.
//! They are routed intelligently so as not to overlap any of the shape
//! objects in the Router scene.
//! 
//! Routing penalties can be applied, resulting in more aesthetically pleasing
//! connector paths with fewer segments or less severe bend-points.
//!
//! You can set a function to be called when the connector has been rerouted
//! and needs to be redrawn.  Alternatively, you can query the connector's
//! needsRepaint() function to determine this manually.
//!
//! Usually, it is expected that you would create a ConnRef for each connector 
//! in your diagram and keep that reference in your own connector class.
//!
class AVOID_EXPORT ConnRef
{
    public:
        //! @brief Constructs a connector with no endpoints specified.
        //!
        //! The constructor requires a valid Router instance.  This router
        //! will take ownership of the connector.  Hence, you should not
        //! call the destructor yourself, but should instead call
        //! Router::deleteConnector() and the router instance will remove
        //! and then free the connector's memory.
        //!
        //! @note Regarding IDs:
        //!       You can let libavoid manually handle IDs by not specifying
        //!       them.  Alternatively, you can specify all IDs yourself, but 
        //!       you must be careful to makes sure that each object in the 
        //!       scene (shape, connector, cluster, etc) is given a unique, 
        //!       positive ID.  This uniqueness is checked if assertions are
        //!       enabled, but if not and there are clashes then strange 
        //!       things can happen.
        //!
        //! @param[in]  router  The router scene to place the connector into.
        //! @param[in]  id      Optionally, a positive integer ID unique
        //!                     among all objects.
        //!
        ConnRef(Router *router, const unsigned int id = 0);
        //! @brief Constructs a connector with endpoints specified.
        //!
        //! The constructor requires a valid Router instance.  This router
        //! will take ownership of the connector.  Hence, you should not
        //! call the destructor yourself, but should instead call
        //! Router::deleteConnector() and the router instance will remove
        //! and then free the connector's memory.
        //!
        //! If an ID is not specified, then one will be assigned to the shape.
        //! If assigning an ID yourself, note that it should be a unique 
        //! positive integer.  Also, IDs are given to all objects in a scene,
        //! so the same ID cannot be given to a shape and a connector for 
        //! example.
        //!
        //! @param[in]  router  The router scene to place the connector into.
        //! @param[in]  id      A unique positive integer ID for the connector.
        //! @param[in]  src     The source endpoint of the connector.
        //! @param[in]  dst     The destination endpoint of the connector.
        //!
        ConnRef(Router *router, const ConnEnd& src, const ConnEnd& dst,
                const unsigned int id = 0);

// To prevent C++ objects from being destroyed in garbage collected languages
// when the libraries are called from SWIG, we hide the declarations of the
// destructors and prevent generation of default destructors.
#ifndef SWIG
        //! @brief  Connector reference destuctor.
        //!
        //! Do not call this yourself, instead call
        //! Router::deleteConnector().  Ownership of this object
        //! belongs to the router scene.
        ~ConnRef();
#endif        
        //! @brief  Sets both a new source and destination endpoint for this 
        //!         connector.
        //!
        //! If the router is using transactions, then this action will occur
        //! the next time Router::processTransaction() is called.  See
        //! Router::setTransactionUse() for more information.
        //!
        //! @param[in]  srcPoint  New source endpoint for the connector.
        //! @param[in]  dstPoint  New destination endpoint for the connector.
        void setEndpoints(const ConnEnd& srcPoint, const ConnEnd& dstPoint);
        
        //! @brief  Sets just a new source endpoint for this connector.
        //!
        //! If the router is using transactions, then this action will occur
        //! the next time Router::processTransaction() is called.  See
        //! Router::setTransactionUse() for more information.
        //!
        //! @param[in]  srcPoint  New source endpoint for the connector.
        void setSourceEndpoint(const ConnEnd& srcPoint);
        
        //! @brief  Sets just a new destination endpoint for this connector.
        //!
        //! If the router is using transactions, then this action will occur
        //! the next time Router::processTransaction() is called.  See
        //! Router::setTransactionUse() for more information.
        //!
        //! @param[in]  dstPoint  New destination endpoint for the connector.
        void setDestEndpoint(const ConnEnd& dstPoint);
        
        //! @brief   Returns the ID of this connector.
        //! @returns The ID of the connector. 
        unsigned int id(void) const;
        
        //! @brief   Returns a pointer to the router scene this connector is in.
        //! @returns A pointer to the router scene for this connector.
        Router *router(void) const;

        //! @brief   Returns an indication of whether this connector has a 
        //!          new route and thus needs to be repainted.
        //!
        //! If the connector has been rerouted and need repainting, the  
        //! displayRoute() method can be called to get a reference to the 
        //! new route.
        //!
        //! @returns Returns true if the connector requires repainting, or 
        //!          false if it does not.
        bool needsRepaint(void) const;
        
        //! @brief   Returns a reference to the current raw "debug" route for 
        //!          the connector.
        //!
        //! This is a raw "debug" shortest path version of the route, where 
        //! each line segment in the route may be made up of multiple collinear
        //! line segments.  It also has no post-processing (i.e., centering,
        //! nudging apart of overlapping paths, or curving of corners) applied 
        //! to it.  A route to display to the user can be obtained by calling
        //! displayRoute().
        //!
        //! @returns The PolyLine route for the connector.
        const PolyLine& route(void) const;
        
        //! @brief   Returns a reference to the current display version of the
        //!          route for the connector.
        //! 
        //! The display version of a route has been simplified to collapse all
        //! collinear line segments into single segments.  It also has all
        //! post-processing applied to the route, including centering, curved
        //! corners and nudging apart of overlapping segments.
        //! 
        //! @returns The PolyLine display route for the connector.
        PolyLine& displayRoute(void);
        
        //! @brief   Sets a callback function that will called to indicate that
        //!          the connector needs rerouting.
        //!
        //! The cb function will be called when shapes are added to, removed 
        //! from or moved about on the page.  The pointer ptr will be passed 
        //! as an argument to the callback function.
        //!
        //! @param[in]  cb   A pointer to the callback function.
        //! @param[in]  ptr  A generic pointer that will be passed to the 
        //!                  callback function.
        void setCallback(void (*cb)(void *), void *ptr);
        
        //! @brief   Returns the type of routing performed for this connector.
        //! @return  The type of routing performed.
        //!
        ConnType routingType(void) const;
        
        //! @brief       Sets the type of routing to be performed for this 
        //!              connector.
        //! 
        //! If a call to this method changes the current type of routing 
        //! being used for the connector, then it will get rerouted during
        //! the next processTransaction() call, or immediately if 
        //! transactions are not being used.
        //!
        //! @param type  The type of routing to be performed.
        //!
        void setRoutingType(ConnType type);
       
        //! @brief   Splits a connector in the centre of the segmentNth 
        //!          segment and creates a junction point there as well 
        //!          as a second connector.
        //!
        //! The new junction and connector will be automatically added to 
        //! the router scene.  A slight preference will be given to the 
        //! connectors connecting to the junction in the same orientation
        //! the line segment already existed in.
        //!
        //! @return  A pair containing pointers to the new JunctionRef and 
        //!          ConnRef.
        std::pair<JunctionRef *, ConnRef *> splitAtSegment(
                const size_t segmentN);

        //! @brief  Allows the user to specify a set of checkpoints that this
        //!         connector will route via.
        //!
        //! When routing, the connector will attempt to visit each of the 
        //! points in the checkpoints list in order.  It will route from the
        //! source point to the first checkpoint, to the second checkpoint, 
        //! etc.  If a checkpoint is unreachable because it lies inside an
        //! obstacle, then that checkpoint will be skipped.
        //! 
        //! @param[in] checkpoints  An ordered list of Checkpoints that the 
        //!                         connector will attempt to route via.
        void setRoutingCheckpoints(const std::vector<Checkpoint>& checkpoints);

        //! @brief   Returns the current set of routing checkpoints for this
        //!          connector.
        //! @returns The ordered list of Checkpoints that this connector will 
        //!          route via.
        std::vector<Checkpoint> routingCheckpoints(void) const;
        
        //! @brief   Returns ConnEnds specifying what this connector is 
        //!          attached to.
        //!
        //! This may be useful during hyperedge rerouting.  You can check the 
        //! type and properties of the ConnEnd objects to find out what this 
        //! connector is attached to.  The ConnEnd::type() will be ConnEndEmpty
        //! if the connector has not had its endpoints initialised.
        //!
        //! @note  If the router is using transactions, you might get 
        //!        unexpected results if you call this after changing objects
        //!        but before calling Router::processTransaction().  In this
        //!        case changes to ConnEnds for the connector may be queued 
        //!        and not yet applied, so you will get old (or empty) values.
        //!
        //! @returns A pair of ConnEnd objects specifying what the connector
        //!          is attached to.
        //!
        std::pair<ConnEnd, ConnEnd> endpointConnEnds(void) const;

        // @brief   Returns the source endpoint vertex in the visibility graph.
        // @returns The source endpoint vertex.
        VertInf *src(void) const;
        // @brief   Returns the destination endpoint vertex in the 
        //          visibility graph.
        // @returns The destination endpoint vertex.
        VertInf *dst(void) const;
        
        //! @brief  Sets a fixed user-specified route for this connector.
        //!
        //! libavoid will no longer calculate object-avoiding paths for this
        //! connector but instead just return the specified route.  The path
        //! of this connector will still be considered for the purpose of 
        //! nudging and routing other non-fixed connectors.
        //!
        //! @note  This will reset the endpoints of the connector to the two
        //!        ends of the given route, which may cause it to become 
        //!        dettached from any shapes or junctions.  You can
        //!        alternatively call setFixedExistingRoute() for connectors
        //!        with valid routes in hyperedges that you would like to
        //!        remain attached.
        //!
        //! @param[in] route  The new fixed route for the connector.
        //! @sa  setFixedExistingRoute()
        //! @sa  clearFixedRoute()
        //!
        void setFixedRoute(const PolyLine& route);

        //! @brief  Sets a fixed existing route for this connector.
        //!
        //! libavoid will no longer calculate object-avoiding paths for this
        //! connector but instead just return the current exisitng route.  
        //! The path of this connector will still be considered for the 
        //! purpose of nudging and routing other non-fixed connectors.
        //!
        //! @note  The endpoints of this connector will remain at their current
        //!        positions, even while remaining 'attached' to shapes 
        //!        or junctions that move.
        //!
        //! @sa  setFixedRoute()
        //! @sa  clearFixedRoute()
        //!
        void setFixedExistingRoute(void);

        //! @brief  Returns whether the connector route is marked as fixed.
        //!
        //! @return True if the connector route is fixed, false otherwise.
        //!
        bool hasFixedRoute(void) const;

        //! @brief  Returns the connector to being automatically routed if it
        //!         was marked as fixed.
        //!
        //! @sa  setFixedRoute()
        //!
        void clearFixedRoute(void);

        void set_route(const PolyLine& route);
        void calcRouteDist(void);
        void makeActive(void);
        void makeInactive(void);
        VertInf *start(void);
        void removeFromGraph(void);
        bool isInitialised(void) const;
        void makePathInvalid(void);
        void setHateCrossings(bool value);
        bool doesHateCrossings(void) const;
        void setEndpoint(const unsigned int type, const ConnEnd& connEnd);
        bool setEndpoint(const unsigned int type, const VertID& pointID, 
                Point *pointSuggestion = nullptr);
        std::vector<Point> possibleDstPinPoints(void) const;
    
    private:
        friend class Router;
        friend class ConnEnd;
        friend class JunctionRef;
        friend class ConnRerouteFlagDelegate;
        friend class HyperedgeImprover;
        friend struct HyperedgeTreeEdge;
        friend struct HyperedgeTreeNode;
        friend class HyperedgeRerouter;

        PolyLine& routeRef(void);
        void freeRoutes(void);
        void performCallback(void);
        bool generatePath(void);
        void generateCheckpointsPath(std::vector<Point>& path,
                std::vector<VertInf *>& vertices);
        void generateStandardPath(std::vector<Point>& path,
                std::vector<VertInf *>& vertices);
        void unInitialise(void);
        void updateEndPoint(const unsigned int type, const ConnEnd& connEnd);
        void common_updateEndPoint(const unsigned int type, ConnEnd connEnd);
        void freeActivePins(void);
        bool getConnEndForEndpointVertex(VertInf *vertex, ConnEnd& connEnd) 
                const;
        std::pair<Obstacle *, Obstacle *> endpointAnchors(void) const;
        void outputCode(FILE *fp) const;
        std::pair<bool, bool> assignConnectionPinVisibility(const bool connect);


        Router *m_router;
        unsigned int m_id;
        ConnType m_type;
        bool *m_reroute_flag_ptr;
        bool m_needs_reroute_flag:1;
        bool m_false_path:1;
        bool m_needs_repaint:1;
        bool m_active:1;
        bool m_initialised:1;
        bool m_hate_crossings:1;
        bool m_has_fixed_route:1;
        PolyLine m_route;
        Polygon m_display_route;
        double m_route_dist;
        ConnRefList::iterator m_connrefs_pos;
        VertInf *m_src_vert;
        VertInf *m_dst_vert;
        VertInf *m_start_vert;
        void (*m_callback_func)(void *);
        void *m_connector;
        ConnEnd *m_src_connend;
        ConnEnd *m_dst_connend;
        std::vector<Checkpoint> m_checkpoints;
        std::vector<VertInf *> m_checkpoint_vertices;
};


typedef std::pair<Point *, ConnRef *> PtConnPtrPair;

typedef std::vector< PtConnPtrPair > PointRepVector;
typedef std::list<std::pair<size_t, size_t> > NodeIndexPairLinkList;

class PtOrder
{
    public:
        PtOrder();
        ~PtOrder();
        void addPoints(const size_t dim, const PtConnPtrPair& arg1, 
                const PtConnPtrPair& arg2);
        void addOrderedPoints(const size_t dim, const PtConnPtrPair& innerArg, 
                const PtConnPtrPair& outerArg, bool swapped);
        int positionFor(const size_t dim, const ConnRef *conn);
        PointRepVector sortedPoints(const size_t dim);
    private:
        size_t insertPoint(const size_t dim, const PtConnPtrPair& point);
        void sort(const size_t dim);

        // One for each dimension.
        bool sorted[2];
        PointRepVector nodes[2];
        NodeIndexPairLinkList links[2];
        PointRepVector sortedConnVector[2];
};

typedef std::map<Avoid::Point,PtOrder> PtOrderMap;
typedef std::set<Avoid::Point> PointSet;


const unsigned int CROSSING_NONE = 0;
const unsigned int CROSSING_TOUCHES = 1;
const unsigned int CROSSING_SHARES_PATH = 2;
const unsigned int CROSSING_SHARES_PATH_AT_END = 4;
const unsigned int CROSSING_SHARES_FIXED_SEGMENT = 8;


typedef std::pair<int, unsigned int> CrossingsInfoPair;
typedef std::vector<Avoid::Point> PointList;
typedef std::vector<PointList> SharedPathList;

class ConnectorCrossings
{
    public:
        ConnectorCrossings(Avoid::Polygon& poly, bool polyIsConn, 
                Avoid::Polygon& conn, ConnRef *polyConnRef = nullptr, 
                ConnRef *connConnRef = nullptr);
        void clear(void);
        void countForSegment(size_t cIndex, const bool finalSegment);

        Avoid::Polygon& poly;
        bool polyIsConn; 
        Avoid::Polygon& conn; 
        bool checkForBranchingSegments;
        ConnRef *polyConnRef;
        ConnRef *connConnRef;
        
        unsigned int crossingCount;
        unsigned int crossingFlags;
        PointSet *crossingPoints;
        PtOrderMap *pointOrders;
        SharedPathList *sharedPaths;
        
        double firstSharedPathAtEndLength;
        double secondSharedPathAtEndLength;
};

extern void splitBranchingSegments(Avoid::Polygon& poly, bool polyIsConn,
        Avoid::Polygon& conn, const double tolerance = 0);
extern bool validateBendPoint(VertInf *aInf, VertInf *bInf, VertInf *cInf);

}


#endif