Project

General

Profile

Download (24.4 KB) Statistics
| Branch: | Tag: | Revision:
1
/*******************************************************************************
2
 * Copyright (c) 2004, 2010 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.draw2d.graph;
12

    
13
import java.util.ArrayList;
14
import java.util.Collections;
15
import java.util.HashSet;
16
import java.util.Iterator;
17
import java.util.List;
18
import java.util.Set;
19

    
20
import org.eclipse.draw2d.PositionConstants;
21
import org.eclipse.draw2d.geometry.Point;
22
import org.eclipse.draw2d.geometry.PointList;
23

    
24
/**
25
 * A Path representation for the ShortestPathRouting. A Path has a start and end
26
 * point and may have bendpoints. The output of a path is accessed via the
27
 * method <code>getPoints()</code>.
28
 * 
29
 * This class is for internal use only.
30
 * 
31
 * @author Whitney Sorenson
32
 * @since 3.0
33
 */
34
public class Path {
35

    
36
	/**
37
	 * A Stack of segments.
38
	 */
39
	private static class SegmentStack extends ArrayList {
40

    
41
		Segment pop() {
42
			return (Segment) remove(size() - 1);
43
		}
44

    
45
		Obstacle popObstacle() {
46
			return (Obstacle) remove(size() - 1);
47
		}
48

    
49
		void push(Object obj) {
50
			add(obj);
51
		}
52

    
53
	}
54

    
55
	private static final Point CURRENT = new Point();
56
	private static final double EPSILON = 1.04;
57
	private static final Point NEXT = new Point();
58
	private static final double OVAL_CONSTANT = 1.13;
59

    
60
	/**
61
	 * The bendpoint constraints. The path must go through these bendpoints.
62
	 */
63
	PointList bendpoints;
64
	/**
65
	 * An arbitrary data field which can be used to map a Path back to some
66
	 * client object.
67
	 */
68
	public Object data;
69
	List excludedObstacles;
70
	List grownSegments;
71
	/**
72
	 * this field is for internal use only. It is true whenever a property has
73
	 * been changed which requires the solver to resolve this path.
74
	 */
75
	public boolean isDirty = true;
76

    
77
	boolean isInverted = false;
78
	boolean isMarked = false;
79
	PointList points;
80

    
81
	/**
82
	 * The previous cost ratio of the path. The cost ratio is the actual path
83
	 * length divided by the length from the start to the end.
84
	 */
85
	private double prevCostRatio;
86
	List segments;
87

    
88
	private SegmentStack stack;
89
	Vertex start, end;
90
	private Path subPath;
91
	double threshold;
92
	Set visibleObstacles;
93
	Set visibleVertices;
94

    
95
	/**
96
	 * Constructs a new path.
97
	 * 
98
	 * @since 3.0
99
	 */
100
	public Path() {
101
		segments = new ArrayList();
102
		grownSegments = new ArrayList();
103
		points = new PointList();
104
		visibleVertices = new HashSet();
105
		stack = new SegmentStack();
106
		visibleObstacles = new HashSet();
107
		excludedObstacles = new ArrayList();
108
	}
109

    
110
	/**
111
	 * Constructs a new path with the given data.
112
	 * 
113
	 * @since 3.0
114
	 * @param data
115
	 *            an arbitrary data field
116
	 */
117
	public Path(Object data) {
118
		this();
119
		this.data = data;
120
	}
121

    
122
	/**
123
	 * Constructs a new path with the given data, start and end point.
124
	 * 
125
	 * @param start
126
	 *            the start point for this path
127
	 * @param end
128
	 *            the end point for this path
129
	 */
130
	public Path(Point start, Point end) {
131
		this(new Vertex(start, null), new Vertex(end, null));
132
	}
133

    
134
	/**
135
	 * Creates a path between the given vertices.
136
	 * 
137
	 * @param start
138
	 *            start vertex
139
	 * @param end
140
	 *            end vertex
141
	 */
142
	Path(Vertex start, Vertex end) {
143
		this();
144
		this.start = start;
145
		this.end = end;
146
	}
147

    
148
	/**
149
	 * Attempts to add all segments between the given obstacles to the
150
	 * visibility graph.
151
	 * 
152
	 * @param source
153
	 *            the source obstacle
154
	 * @param target
155
	 *            the target obstacle
156
	 */
157
	private void addAllSegmentsBetween(Obstacle source, Obstacle target) {
158
		addConnectingSegment(new Segment(source.bottomLeft, target.bottomLeft),
159
				source, target, false, false);
160
		addConnectingSegment(
161
				new Segment(source.bottomRight, target.bottomRight), source,
162
				target, true, true);
163
		addConnectingSegment(new Segment(source.topLeft, target.topLeft),
164
				source, target, true, true);
165
		addConnectingSegment(new Segment(source.topRight, target.topRight),
166
				source, target, false, false);
167

    
168
		if (source.bottom() == target.bottom()) {
169
			addConnectingSegment(new Segment(source.bottomLeft,
170
					target.bottomRight), source, target, false, true);
171
			addConnectingSegment(new Segment(source.bottomRight,
172
					target.bottomLeft), source, target, true, false);
173
		}
174
		if (source.y == target.y) {
175
			addConnectingSegment(new Segment(source.topLeft, target.topRight),
176
					source, target, true, false);
177
			addConnectingSegment(new Segment(source.topRight, target.topLeft),
178
					source, target, false, true);
179
		}
180
		if (source.x == target.x) {
181
			addConnectingSegment(
182
					new Segment(source.bottomLeft, target.topLeft), source,
183
					target, false, true);
184
			addConnectingSegment(
185
					new Segment(source.topLeft, target.bottomLeft), source,
186
					target, true, false);
187
		}
188
		if (source.right() == target.right()) {
189
			addConnectingSegment(new Segment(source.bottomRight,
190
					target.topRight), source, target, true, false);
191
			addConnectingSegment(new Segment(source.topRight,
192
					target.bottomRight), source, target, false, true);
193
		}
194
	}
195

    
196
	/**
197
	 * Attempts to add a segment between the given obstacles to the visibility
198
	 * graph. This method is specifically written for the case where the two
199
	 * obstacles intersect and contains a boolean as to whether to check the
200
	 * diagonal that includes the top right point of the other obstacle.
201
	 * 
202
	 * @param segment
203
	 *            the segment to check
204
	 * @param o1
205
	 *            the first obstacle
206
	 * @param o2
207
	 *            the second obstacle
208
	 * @param checkTopRight1
209
	 *            whether or not to check the diagonal containing top right
210
	 *            point
211
	 */
212
	private void addConnectingSegment(Segment segment, Obstacle o1,
213
			Obstacle o2, boolean checkTopRight1, boolean checkTopRight2) {
214
		if (threshold != 0
215
				&& (segment.end.getDistance(end)
216
						+ segment.end.getDistance(start) > threshold || segment.start
217
						.getDistance(end) + segment.start.getDistance(start) > threshold))
218
			return;
219

    
220
		if (o2.containsProper(segment.start) || o1.containsProper(segment.end))
221
			return;
222

    
223
		if (checkTopRight1
224
				&& segment.intersects(o1.x, o1.bottom() - 1, o1.right() - 1,
225
						o1.y))
226
			return;
227
		if (checkTopRight2
228
				&& segment.intersects(o2.x, o2.bottom() - 1, o2.right() - 1,
229
						o2.y))
230
			return;
231
		if (!checkTopRight1
232
				&& segment.intersects(o1.x, o1.y, o1.right() - 1,
233
						o1.bottom() - 1))
234
			return;
235
		if (!checkTopRight2
236
				&& segment.intersects(o2.x, o2.y, o2.right() - 1,
237
						o2.bottom() - 1))
238
			return;
239

    
240
		stack.push(o1);
241
		stack.push(o2);
242
		stack.push(segment);
243
	}
244

    
245
	/**
246
	 * Adds an obstacle to the visibility graph and generates new segments
247
	 * 
248
	 * @param newObs
249
	 *            the new obstacle, should not be in the graph already
250
	 */
251
	private void addObstacle(Obstacle newObs) {
252
		visibleObstacles.add(newObs);
253
		Iterator oItr = new HashSet(visibleObstacles).iterator();
254
		while (oItr.hasNext()) {
255
			Obstacle currObs = (Obstacle) oItr.next();
256
			if (newObs != currObs)
257
				addSegmentsFor(newObs, currObs);
258
		}
259
		addPerimiterSegments(newObs);
260
		addSegmentsFor(start, newObs);
261
		addSegmentsFor(end, newObs);
262
	}
263

    
264
	/**
265
	 * Adds the segments along the perimiter of an obstacle to the visiblity
266
	 * graph queue.
267
	 * 
268
	 * @param obs
269
	 *            the obstacle
270
	 */
271
	private void addPerimiterSegments(Obstacle obs) {
272
		Segment seg = new Segment(obs.topLeft, obs.topRight);
273
		stack.push(obs);
274
		stack.push(null);
275
		stack.push(seg);
276
		seg = new Segment(obs.topRight, obs.bottomRight);
277
		stack.push(obs);
278
		stack.push(null);
279
		stack.push(seg);
280
		seg = new Segment(obs.bottomRight, obs.bottomLeft);
281
		stack.push(obs);
282
		stack.push(null);
283
		stack.push(seg);
284
		seg = new Segment(obs.bottomLeft, obs.topLeft);
285
		stack.push(obs);
286
		stack.push(null);
287
		stack.push(seg);
288
	}
289

    
290
	/**
291
	 * Attempts to add a segment to the visibility graph. First checks to see if
292
	 * the segment is outside the threshold oval. Then it compares the segment
293
	 * against all obstacles. If it is clean, the segment is finally added to
294
	 * the graph.
295
	 * 
296
	 * @param segment
297
	 *            the segment
298
	 * @param exclude1
299
	 *            an obstacle to exclude from the search
300
	 * @param exclude2
301
	 *            another obstacle to exclude from the search
302
	 * @param allObstacles
303
	 *            the list of all obstacles
304
	 */
305
	private void addSegment(Segment segment, Obstacle exclude1,
306
			Obstacle exclude2, List allObstacles) {
307
		if (threshold != 0
308
				&& (segment.end.getDistance(end)
309
						+ segment.end.getDistance(start) > threshold || segment.start
310
						.getDistance(end) + segment.start.getDistance(start) > threshold))
311
			return;
312

    
313
		for (int i = 0; i < allObstacles.size(); i++) {
314
			Obstacle obs = (Obstacle) allObstacles.get(i);
315

    
316
			if (obs == exclude1 || obs == exclude2 || obs.exclude)
317
				continue;
318

    
319
			if (segment.intersects(obs.x, obs.y, obs.right() - 1,
320
					obs.bottom() - 1)
321
					|| segment.intersects(obs.x, obs.bottom() - 1,
322
							obs.right() - 1, obs.y)
323
					|| obs.containsProper(segment.start)
324
					|| obs.containsProper(segment.end)) {
325
				if (!visibleObstacles.contains(obs))
326
					addObstacle(obs);
327
				return;
328
			}
329
		}
330

    
331
		linkVertices(segment);
332
	}
333

    
334
	/**
335
	 * Adds the segments between the given obstacles.
336
	 * 
337
	 * @param source
338
	 *            source obstacle
339
	 * @param target
340
	 *            target obstacle
341
	 */
342
	private void addSegmentsFor(Obstacle source, Obstacle target) {
343
		if (source.intersects(target))
344
			addAllSegmentsBetween(source, target);
345
		else if (target.bottom() - 1 < source.y)
346
			addSegmentsTargetAboveSource(source, target);
347
		else if (source.bottom() - 1 < target.y)
348
			addSegmentsTargetAboveSource(target, source);
349
		else if (target.right() - 1 < source.x)
350
			addSegmentsTargetBesideSource(source, target);
351
		else
352
			addSegmentsTargetBesideSource(target, source);
353
	}
354

    
355
	/**
356
	 * Adds the segments between the given obstacles.
357
	 * 
358
	 * @param source
359
	 *            source obstacle
360
	 * @param target
361
	 *            target obstacle
362
	 */
363
	private void addSegmentsFor(Vertex vertex, Obstacle obs) {
364
		Segment seg = null;
365
		Segment seg2 = null;
366

    
367
		switch (obs.getPosition(vertex)) {
368
		case PositionConstants.SOUTH_WEST:
369
		case PositionConstants.NORTH_EAST:
370
			seg = new Segment(vertex, obs.topLeft);
371
			seg2 = new Segment(vertex, obs.bottomRight);
372
			break;
373
		case PositionConstants.SOUTH_EAST:
374
		case PositionConstants.NORTH_WEST:
375
			seg = new Segment(vertex, obs.topRight);
376
			seg2 = new Segment(vertex, obs.bottomLeft);
377
			break;
378
		case PositionConstants.NORTH:
379
			seg = new Segment(vertex, obs.topLeft);
380
			seg2 = new Segment(vertex, obs.topRight);
381
			break;
382
		case PositionConstants.EAST:
383
			seg = new Segment(vertex, obs.bottomRight);
384
			seg2 = new Segment(vertex, obs.topRight);
385
			break;
386
		case PositionConstants.SOUTH:
387
			seg = new Segment(vertex, obs.bottomRight);
388
			seg2 = new Segment(vertex, obs.bottomLeft);
389
			break;
390
		case PositionConstants.WEST:
391
			seg = new Segment(vertex, obs.topLeft);
392
			seg2 = new Segment(vertex, obs.bottomLeft);
393
			break;
394
		default:
395
			if (vertex.x == obs.x) {
396
				seg = new Segment(vertex, obs.topLeft);
397
				seg2 = new Segment(vertex, obs.bottomLeft);
398
			} else if (vertex.y == obs.y) {
399
				seg = new Segment(vertex, obs.topLeft);
400
				seg2 = new Segment(vertex, obs.topRight);
401
			} else if (vertex.y == obs.bottom() - 1) {
402
				seg = new Segment(vertex, obs.bottomLeft);
403
				seg2 = new Segment(vertex, obs.bottomRight);
404
			} else if (vertex.x == obs.right() - 1) {
405
				seg = new Segment(vertex, obs.topRight);
406
				seg2 = new Segment(vertex, obs.bottomRight);
407
			} else {
408
				throw new RuntimeException("Unexpected vertex conditions"); //$NON-NLS-1$
409
			}
410
		}
411

    
412
		stack.push(obs);
413
		stack.push(null);
414
		stack.push(seg);
415
		stack.push(obs);
416
		stack.push(null);
417
		stack.push(seg2);
418
	}
419

    
420
	private void addSegmentsTargetAboveSource(Obstacle source, Obstacle target) {
421
		// target located above source
422
		Segment seg = null;
423
		Segment seg2 = null;
424
		if (target.x > source.x) {
425
			seg = new Segment(source.topLeft, target.topLeft);
426
			if (target.x < source.right() - 1)
427
				seg2 = new Segment(source.topRight, target.bottomLeft);
428
			else
429
				seg2 = new Segment(source.bottomRight, target.topLeft);
430
		} else if (source.x == target.x) {
431
			seg = new Segment(source.topLeft, target.bottomLeft);
432
			seg2 = new Segment(source.topRight, target.bottomLeft);
433
		} else {
434
			seg = new Segment(source.bottomLeft, target.bottomLeft);
435
			seg2 = new Segment(source.topRight, target.bottomLeft);
436
		}
437

    
438
		stack.push(source);
439
		stack.push(target);
440
		stack.push(seg);
441
		stack.push(source);
442
		stack.push(target);
443
		stack.push(seg2);
444
		seg = null;
445
		seg2 = null;
446

    
447
		if (target.right() < source.right()) {
448
			seg = new Segment(source.topRight, target.topRight);
449
			if (target.right() - 1 > source.x)
450
				seg2 = new Segment(source.topLeft, target.bottomRight);
451
			else
452
				seg2 = new Segment(source.bottomLeft, target.topRight);
453
		} else if (source.right() == target.right()) {
454
			seg = new Segment(source.topRight, target.bottomRight);
455
			seg2 = new Segment(source.topLeft, target.bottomRight);
456
		} else {
457
			seg = new Segment(source.bottomRight, target.bottomRight);
458
			seg2 = new Segment(source.topLeft, target.bottomRight);
459
		}
460

    
461
		stack.push(source);
462
		stack.push(target);
463
		stack.push(seg);
464
		stack.push(source);
465
		stack.push(target);
466
		stack.push(seg2);
467
	}
468

    
469
	private void addSegmentsTargetBesideSource(Obstacle source, Obstacle target) {
470
		// target located above source
471
		Segment seg = null;
472
		Segment seg2 = null;
473
		if (target.y > source.y) {
474
			seg = new Segment(source.topLeft, target.topLeft);
475
			if (target.y < source.bottom() - 1)
476
				seg2 = new Segment(source.bottomLeft, target.topRight);
477
			else
478
				seg2 = new Segment(source.bottomRight, target.topLeft);
479
		} else if (source.y == target.y) {
480
			// degenerate case
481
			seg = new Segment(source.topLeft, target.topRight);
482
			seg2 = new Segment(source.bottomLeft, target.topRight);
483
		} else {
484
			seg = new Segment(source.topRight, target.topRight);
485
			seg2 = new Segment(source.bottomLeft, target.topRight);
486
		}
487
		stack.push(source);
488
		stack.push(target);
489
		stack.push(seg);
490
		stack.push(source);
491
		stack.push(target);
492
		stack.push(seg2);
493
		seg = null;
494
		seg2 = null;
495

    
496
		if (target.bottom() < source.bottom()) {
497
			seg = new Segment(source.bottomLeft, target.bottomLeft);
498
			if (target.bottom() - 1 > source.y)
499
				seg2 = new Segment(source.topLeft, target.bottomRight);
500
			else
501
				seg2 = new Segment(source.topRight, target.bottomLeft);
502
		} else if (source.bottom() == target.bottom()) {
503
			seg = new Segment(source.bottomLeft, target.bottomRight);
504
			seg2 = new Segment(source.topLeft, target.bottomRight);
505
		} else {
506
			seg = new Segment(source.bottomRight, target.bottomRight);
507
			seg2 = new Segment(source.topLeft, target.bottomRight);
508
		}
509
		stack.push(source);
510
		stack.push(target);
511
		stack.push(seg);
512
		stack.push(source);
513
		stack.push(target);
514
		stack.push(seg2);
515
	}
516

    
517
	/**
518
 * 
519
 */
520
	void cleanup() {
521
		// segments.clear();
522
		visibleVertices.clear();
523
	}
524

    
525
	/**
526
	 * Begins the creation of the visibility graph with the first segment
527
	 * 
528
	 * @param allObstacles
529
	 *            list of all obstacles
530
	 */
531
	private void createVisibilityGraph(List allObstacles) {
532
		stack.push(null);
533
		stack.push(null);
534
		stack.push(new Segment(start, end));
535

    
536
		while (!stack.isEmpty())
537
			addSegment(stack.pop(), stack.popObstacle(), stack.popObstacle(),
538
					allObstacles);
539
	}
540

    
541
	/**
542
	 * Once the visibility graph is constructed, this is called to label the
543
	 * graph and determine the shortest path. Returns false if no path can be
544
	 * found.
545
	 * 
546
	 * @return true if a path can be found.
547
	 */
548
	private boolean determineShortestPath() {
549
		if (!labelGraph())
550
			return false;
551
		Vertex vertex = end;
552
		prevCostRatio = end.cost / start.getDistance(end);
553

    
554
		Vertex nextVertex;
555
		while (!vertex.equals(start)) {
556
			nextVertex = vertex.label;
557
			if (nextVertex == null)
558
				return false;
559
			Segment s = new Segment(nextVertex, vertex);
560
			segments.add(s);
561
			vertex = nextVertex;
562
		}
563

    
564
		Collections.reverse(segments);
565
		return true;
566
	}
567

    
568
	/**
569
	 * Resets all necessary fields for a solve.
570
	 */
571
	void fullReset() {
572
		visibleVertices.clear();
573
		segments.clear();
574
		if (prevCostRatio == 0) {
575
			double distance = start.getDistance(end);
576
			threshold = distance * OVAL_CONSTANT;
577
		} else
578
			threshold = prevCostRatio * EPSILON * start.getDistance(end);
579
		visibleObstacles.clear();
580
		resetPartial();
581
	}
582

    
583
	/**
584
	 * Creates the visibility graph and returns whether or not a shortest path
585
	 * could be determined.
586
	 * 
587
	 * @param allObstacles
588
	 *            the list of all obstacles
589
	 * @return true if a shortest path was found
590
	 */
591
	boolean generateShortestPath(List allObstacles) {
592
		createVisibilityGraph(allObstacles);
593

    
594
		if (visibleVertices.size() == 0)
595
			return false;
596

    
597
		return determineShortestPath();
598
	}
599

    
600
	/**
601
	 * Returns the list of constrained points through which this path must pass
602
	 * or <code>null</code>.
603
	 * 
604
	 * @see #setBendPoints(PointList)
605
	 * @return list of bend points
606
	 */
607
	public PointList getBendPoints() {
608
		return bendpoints;
609
	}
610

    
611
	/**
612
	 * Returns the end point for this path
613
	 * 
614
	 * @return end point for this path
615
	 */
616
	public Point getEndPoint() {
617
		return end;
618
	}
619

    
620
	/**
621
	 * Returns the solution to this path.
622
	 * 
623
	 * @return the points for this path.
624
	 */
625
	public PointList getPoints() {
626
		return points;
627
	}
628

    
629
	/**
630
	 * Returns the start point for this path
631
	 * 
632
	 * @return start point for this path
633
	 */
634
	public Point getStartPoint() {
635
		return start;
636
	}
637

    
638
	/**
639
	 * Returns a subpath for this path at the given segment
640
	 * 
641
	 * @param currentSegment
642
	 *            the segment at which the subpath should be created
643
	 * @return the new path
644
	 */
645
	Path getSubPath(Segment currentSegment) {
646
		// ready new path
647
		Path newPath = new Path(currentSegment.start, end);
648
		newPath.grownSegments = new ArrayList(grownSegments.subList(
649
				grownSegments.indexOf(currentSegment), grownSegments.size()));
650

    
651
		// fix old path
652
		grownSegments = new ArrayList(grownSegments.subList(0,
653
				grownSegments.indexOf(currentSegment) + 1));
654
		end = currentSegment.end;
655

    
656
		subPath = newPath;
657
		return newPath;
658
	}
659

    
660
	/**
661
	 * Resets the vertices that this path has traveled prior to this segment.
662
	 * This is called when the path has become inverted and needs to rectify any
663
	 * labeling mistakes it made before it knew it was inverted.
664
	 * 
665
	 * @param currentSegment
666
	 *            the segment at which the path found it was inverted
667
	 */
668
	void invertPriorVertices(Segment currentSegment) {
669
		int stop = grownSegments.indexOf(currentSegment);
670
		for (int i = 0; i < stop; i++) {
671
			Vertex vertex = ((Segment) grownSegments.get(i)).end;
672
			if (vertex.type == Vertex.INNIE)
673
				vertex.type = Vertex.OUTIE;
674
			else
675
				vertex.type = Vertex.INNIE;
676
		}
677
	}
678

    
679
	/**
680
	 * Returns true if this obstacle is in the visibility graph
681
	 * 
682
	 * @param obs
683
	 *            the obstacle
684
	 * @return true if obstacle is in the visibility graph
685
	 */
686
	boolean isObstacleVisible(Obstacle obs) {
687
		return visibleObstacles.contains(obs);
688
	}
689

    
690
	/**
691
	 * Labels the visibility graph to assist in finding the shortest path
692
	 * 
693
	 * @return false if there was a gap in the visibility graph
694
	 */
695
	private boolean labelGraph() {
696
		int numPermanentNodes = 1;
697
		Vertex vertex = start;
698
		Vertex neighborVertex = null;
699
		vertex.isPermanent = true;
700
		double newCost;
701
		while (numPermanentNodes != visibleVertices.size()) {
702
			List neighbors = vertex.neighbors;
703
			if (neighbors == null)
704
				return false;
705
			// label neighbors if they have a new shortest path
706
			for (int i = 0; i < neighbors.size(); i++) {
707
				neighborVertex = (Vertex) neighbors.get(i);
708
				if (!neighborVertex.isPermanent) {
709
					newCost = vertex.cost + vertex.getDistance(neighborVertex);
710
					if (neighborVertex.label == null) {
711
						neighborVertex.label = vertex;
712
						neighborVertex.cost = newCost;
713
					} else if (neighborVertex.cost > newCost) {
714
						neighborVertex.label = vertex;
715
						neighborVertex.cost = newCost;
716
					}
717
				}
718
			}
719
			// find the next none-permanent, labeled vertex with smallest cost
720
			double smallestCost = 0;
721
			Vertex tempVertex = null;
722
			Iterator v = visibleVertices.iterator();
723
			while (v.hasNext()) {
724
				tempVertex = (Vertex) v.next();
725
				if (!tempVertex.isPermanent
726
						&& tempVertex.label != null
727
						&& (tempVertex.cost < smallestCost || smallestCost == 0)) {
728
					smallestCost = tempVertex.cost;
729
					vertex = tempVertex;
730
				}
731
			}
732
			// set the new vertex to permanent.
733
			vertex.isPermanent = true;
734
			numPermanentNodes++;
735
		}
736
		return true;
737
	}
738

    
739
	/**
740
	 * Links two vertices together in the visibility graph
741
	 * 
742
	 * @param segment
743
	 *            the segment to add
744
	 */
745
	private void linkVertices(Segment segment) {
746
		if (segment.start.neighbors == null)
747
			segment.start.neighbors = new ArrayList();
748
		if (segment.end.neighbors == null)
749
			segment.end.neighbors = new ArrayList();
750

    
751
		if (!segment.start.neighbors.contains(segment.end)) {
752
			segment.start.neighbors.add(segment.end);
753
			segment.end.neighbors.add(segment.start);
754
		}
755

    
756
		visibleVertices.add(segment.start);
757
		visibleVertices.add(segment.end);
758
	}
759

    
760
	/**
761
	 * Called to reconnect a subpath back onto this path. Does a depth-first
762
	 * search to reconnect all paths. Should be called after sorting.
763
	 */
764
	void reconnectSubPaths() {
765
		if (subPath != null) {
766
			subPath.reconnectSubPaths();
767

    
768
			Segment changedSegment = (Segment) subPath.grownSegments.remove(0);
769
			Segment oldSegment = (Segment) grownSegments.get(grownSegments
770
					.size() - 1);
771

    
772
			oldSegment.end = changedSegment.end;
773
			grownSegments.addAll(subPath.grownSegments);
774

    
775
			subPath.points.removePoint(0);
776
			points.removePoint(points.size() - 1);
777
			points.addAll(subPath.points);
778

    
779
			visibleObstacles.addAll(subPath.visibleObstacles);
780

    
781
			end = subPath.end;
782
			subPath = null;
783
		}
784
	}
785

    
786
	/**
787
	 * Refreshes the exclude field on the obstacles in the list. Excludes all
788
	 * obstacles that contain the start or end point for this path.
789
	 * 
790
	 * @param allObstacles
791
	 *            list of all obstacles
792
	 */
793
	void refreshExcludedObstacles(List allObstacles) {
794
		excludedObstacles.clear();
795

    
796
		for (int i = 0; i < allObstacles.size(); i++) {
797
			Obstacle o = (Obstacle) allObstacles.get(i);
798
			o.exclude = false;
799

    
800
			if (o.contains(start)) {
801
				if (o.containsProper(start))
802
					o.exclude = true;
803
				else {
804
					/*
805
					 * $TODO Check for corners. If the path begins exactly at
806
					 * the corner of an obstacle, the exclude should also be
807
					 * true.
808
					 * 
809
					 * Or, change segment intersection so that two segments that
810
					 * share an endpoint do not intersect.
811
					 */
812
				}
813
			}
814

    
815
			if (o.contains(end)) {
816
				if (o.containsProper(end))
817
					o.exclude = true;
818
				else {
819
					// check for corners. See above statement.
820
				}
821
			}
822

    
823
			if (o.exclude && !excludedObstacles.contains(o))
824
				excludedObstacles.add(o);
825
		}
826
	}
827

    
828
	/**
829
	 * Resets the fields for everything in the solve after the visibility graph
830
	 * steps.
831
	 */
832
	void resetPartial() {
833
		isMarked = false;
834
		isInverted = false;
835
		subPath = null;
836
		isDirty = false;
837
		grownSegments.clear();
838
		points.removeAllPoints();
839
	}
840

    
841
	/**
842
	 * Sets the list of bend points to the given list and dirties the path.
843
	 * 
844
	 * @param bendPoints
845
	 *            the list of bend points
846
	 */
847
	public void setBendPoints(PointList bendPoints) {
848
		this.bendpoints = bendPoints;
849
		isDirty = true;
850
	}
851

    
852
	/**
853
	 * Sets the end point for this path to the given point.
854
	 * 
855
	 * @param end
856
	 *            the new end point for this path
857
	 */
858
	public void setEndPoint(Point end) {
859
		if (end.equals(this.end))
860
			return;
861
		this.end = new Vertex(end, null);
862
		isDirty = true;
863
	}
864

    
865
	/**
866
	 * Sets the start point for this path to the given point.
867
	 * 
868
	 * @param start
869
	 *            the new start point for this path
870
	 */
871
	public void setStartPoint(Point start) {
872
		if (start.equals(this.start))
873
			return;
874
		this.start = new Vertex(start, null);
875
		isDirty = true;
876
	}
877

    
878
	/**
879
	 * Returns <code>true</code> if the path is clean and intersects the given
880
	 * obstacle. Also dirties the path in the process.
881
	 * 
882
	 * @since 3.0
883
	 * @param obs
884
	 *            the obstacle
885
	 * @return <code>true</code> if a clean path touches the obstacle
886
	 */
887
	boolean testAndSet(Obstacle obs) {
888
		if (isDirty)
889
			return false;
890
		// This will never actually happen because obstacles are not stored by
891
		// identity
892
		if (excludedObstacles.contains(obs))
893
			return false;
894

    
895
		Segment seg1 = new Segment(obs.topLeft, obs.bottomRight);
896
		Segment seg2 = new Segment(obs.topRight, obs.bottomLeft);
897

    
898
		for (int s = 0; s < points.size() - 1; s++) {
899
			points.getPoint(CURRENT, s);
900
			points.getPoint(NEXT, s + 1);
901

    
902
			if (seg1.intersects(CURRENT, NEXT)
903
					|| seg2.intersects(CURRENT, NEXT) || obs.contains(CURRENT)
904
					|| obs.contains(NEXT)) {
905
				isDirty = true;
906
				return true;
907
			}
908
		}
909
		return false;
910
	}
911

    
912
}
(29-29/49)