comparison dwtx/draw2d/graph/Path.d @ 98:95307ad235d9

Added Draw2d code, still work in progress
author Frank Benoit <benoit@tionex.de>
date Sun, 03 Aug 2008 00:52:14 +0200
parents
children 2d6540440fe6
comparison
equal deleted inserted replaced
96:b492ba44e44d 98:95307ad235d9
1 /*******************************************************************************
2 * Copyright (c) 2004, 2005 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 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module dwtx.draw2d.graph.Path;
14
15 import dwt.dwthelper.utils;
16 import dwtx.dwtxhelper.Collection;
17
18 import dwtx.draw2d.PositionConstants;
19 import dwtx.draw2d.geometry.Point;
20 import dwtx.draw2d.geometry.PointList;
21 import dwtx.draw2d.graph.Segment;
22 import dwtx.draw2d.graph.Vertex;
23 import dwtx.draw2d.graph.Obstacle;
24
25 /**
26 * A Path representation for the ShortestPathRouting. A Path has a start and end point
27 * and may have bendpoints. The output of a path is accessed via the method
28 * <code>getPoints()</code>.
29 *
30 * This class is for internal use only.
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 : ArrayList {
40
41 Segment pop() {
42 return cast(Segment)remove(size() - 1);
43 }
44
45 Obstacle popObstacle() {
46 return cast(Obstacle)remove(size() - 1);
47 }
48
49 void push(Object obj) {
50 add(obj);
51 }
52
53 }
54
55 private static const Point CURRENT;
56 private static const double EPSILON = 1.04;
57 private static const Point NEXT;
58 private static const double OVAL_CONSTANT = 1.13;
59
60 static this(){
61 CURRENT = new Point();
62 NEXT = new Point();
63 }
64
65 /**
66 * The bendpoint constraints. The path must go through these bendpoints.
67 */
68 PointList bendpoints;
69 /**
70 * An arbitrary data field which can be used to map a Path back to some client object.
71 */
72 public Object data;
73 List excludedObstacles;
74 List grownSegments;
75 /**
76 * this field is for internal use only. It is true whenever a property has been changed
77 * which requires the solver to resolve this path.
78 */
79 public bool isDirty = true;
80
81 bool isInverted = false;
82 bool isMarked = false;
83 PointList points;
84
85 /**
86 * The previous cost ratio of the path. The cost ratio is the actual path length divided
87 * by the length from the start to the end.
88 */
89 private double prevCostRatio;
90 List segments;
91
92 private SegmentStack stack;
93 Vertex start, end;
94 private Path subPath;
95 double threshold;
96 Set visibleObstacles;
97 Set visibleVertices;
98
99 /**
100 * Constructs a new path.
101 * @since 3.0
102 */
103 public this() {
104 segments = new ArrayList();
105 grownSegments = new ArrayList();
106 points = new PointList();
107 visibleVertices = new HashSet();
108 stack = new SegmentStack();
109 visibleObstacles = new HashSet();
110 excludedObstacles = new ArrayList();
111 }
112
113 /**
114 * Constructs a new path with the given data.
115 * @since 3.0
116 * @param data an arbitrary data field
117 */
118 public this(Object data) {
119 this();
120 this.data = data;
121 }
122
123 /**
124 * Constructs a new path with the given data, start and end point.
125 *
126 * @param start the start point for this path
127 * @param end the end point for this path
128 */
129 public this(Point start, Point end) {
130 this(new Vertex(start, null), new Vertex(end, null));
131 }
132
133 /**
134 * Creates a path between the given vertices.
135 *
136 * @param start start vertex
137 * @param end end vertex
138 */
139 this(Vertex start, Vertex end) {
140 this();
141 this.start = start;
142 this.end = end;
143 }
144
145 /**
146 * Attempts to add all segments between the given obstacles to the visibility graph.
147 * @param source the source obstacle
148 * @param target the target obstacle
149 */
150 private void addAllSegmentsBetween(Obstacle source, Obstacle target) {
151 addConnectingSegment(new Segment(source.bottomLeft, target.bottomLeft),
152 source, target, false, false);
153 addConnectingSegment(new Segment(source.bottomRight, target.bottomRight),
154 source, target, true, true);
155 addConnectingSegment(new Segment(source.topLeft, target.topLeft),
156 source, target, true, true);
157 addConnectingSegment(new Segment(source.topRight, target.topRight),
158 source, target, false, false);
159
160 if (source.bottom() is target.bottom()) {
161 addConnectingSegment(new Segment(source.bottomLeft, target.bottomRight),
162 source, target, false, true);
163 addConnectingSegment(new Segment(source.bottomRight, target.bottomLeft),
164 source, target, true, false);
165 }
166 if (source.y is target.y) {
167 addConnectingSegment(new Segment(source.topLeft, target.topRight),
168 source, target, true, false);
169 addConnectingSegment(new Segment(source.topRight, target.topLeft),
170 source, target, false, true);
171 }
172 if (source.x is target.x) {
173 addConnectingSegment(new Segment(source.bottomLeft, target.topLeft),
174 source, target, false, true);
175 addConnectingSegment(new Segment(source.topLeft, target.bottomLeft),
176 source, target, true, false);
177 }
178 if (source.right() is target.right()) {
179 addConnectingSegment(new Segment(source.bottomRight, target.topRight),
180 source, target, true, false);
181 addConnectingSegment(new Segment(source.topRight, target.bottomRight),
182 source, target, false, true);
183 }
184 }
185
186 /**
187 * Attempts to add a segment between the given obstacles to the visibility graph. This
188 * method is specifically written for the case where the two obstacles intersect and contains
189 * a bool as to whether to check the diagonal that includes the top right point of the
190 * other obstacle.
191 *
192 * @param segment the segment to check
193 * @param o1 the first obstacle
194 * @param o2 the second obstacle
195 * @param checkTopRight1 whether or not to check the diagonal containing top right point
196 */
197 private void addConnectingSegment(Segment segment, Obstacle o1, Obstacle o2,
198 bool checkTopRight1, bool checkTopRight2) {
199 if (threshold !is 0
200 && (segment.end.getDistance(end) + segment.end.getDistance(start) > threshold
201 || segment.start.getDistance(end) + segment.start.getDistance(start) > threshold))
202 return;
203
204 if (o2.containsProper(segment.start) || o1.containsProper(segment.end))
205 return;
206
207 if (checkTopRight1 && segment.intersects(o1.x, o1.bottom() - 1, o1.right() - 1, o1.y))
208 return;
209 if (checkTopRight2 && segment.intersects(o2.x, o2.bottom() - 1, o2.right() - 1, o2.y))
210 return;
211 if (!checkTopRight1 && segment.intersects(o1.x, o1.y, o1.right() - 1, o1.bottom() - 1))
212 return;
213 if (!checkTopRight2 && segment.intersects(o2.x, o2.y, o2.right() - 1, o2.bottom() - 1))
214 return;
215
216 stack.push(o1);
217 stack.push(o2);
218 stack.push(segment);
219 }
220
221 /**
222 * Adds an obstacle to the visibility graph and generates new segments
223 * @param newObs the new obstacle, should not be in the graph already
224 */
225 private void addObstacle(Obstacle newObs) {
226 visibleObstacles.add(newObs);
227 Iterator oItr = (new HashSet(visibleObstacles)).iterator();
228 while (oItr.hasNext()) {
229 Obstacle currObs = cast(Obstacle)oItr.next();
230 if (newObs !is currObs)
231 addSegmentsFor(newObs, currObs);
232 }
233 addPerimiterSegments(newObs);
234 addSegmentsFor(start, newObs);
235 addSegmentsFor(end, newObs);
236 }
237
238 /**
239 * Adds the segments along the perimiter of an obstacle to the visiblity graph queue.
240 * @param obs the obstacle
241 */
242 private void addPerimiterSegments(Obstacle obs) {
243 Segment seg = new Segment(obs.topLeft, obs.topRight);
244 stack.push(obs);
245 stack.push(null);
246 stack.push(seg);
247 seg = new Segment(obs.topRight, obs.bottomRight);
248 stack.push(obs);
249 stack.push(null);
250 stack.push(seg);
251 seg = new Segment(obs.bottomRight, obs.bottomLeft);
252 stack.push(obs);
253 stack.push(null);
254 stack.push(seg);
255 seg = new Segment(obs.bottomLeft, obs.topLeft);
256 stack.push(obs);
257 stack.push(null);
258 stack.push(seg);
259 }
260
261 /**
262 * Attempts to add a segment to the visibility graph.
263 * First checks to see if the segment is outside the threshold oval. Then it compares the segment
264 * against all obstacles. If it is clean, the segment is finally added to the graph.
265 *
266 * @param segment the segment
267 * @param exclude1 an obstacle to exclude from the search
268 * @param exclude2 another obstacle to exclude from the search
269 * @param allObstacles the list of all obstacles
270 */
271 private void addSegment(Segment segment, Obstacle exclude1, Obstacle exclude2, List allObstacles) {
272 if (threshold !is 0
273 && (segment.end.getDistance(end) + segment.end.getDistance(start) > threshold
274 || segment.start.getDistance(end) + segment.start.getDistance(start) > threshold))
275 return;
276
277 for (int i = 0; i < allObstacles.size(); i++) {
278 Obstacle obs = cast(Obstacle)allObstacles.get(i);
279
280 if (obs is exclude1 || obs is exclude2 || obs.exclude)
281 continue;
282
283 if (segment.intersects(obs.x, obs.y, obs.right() - 1, obs.bottom() - 1)
284 || segment.intersects(obs.x, obs.bottom() - 1, obs.right() - 1, obs.y)
285 || obs.containsProper(segment.start)
286 || obs.containsProper(segment.end)) {
287 if (!visibleObstacles.contains(obs))
288 addObstacle(obs);
289 return;
290 }
291 }
292
293 linkVertices(segment);
294 }
295
296 /**
297 * Adds the segments between the given obstacles.
298 * @param source source obstacle
299 * @param target target obstacle
300 */
301 private void addSegmentsFor(Obstacle source, Obstacle target) {
302 if (source.intersects(target))
303 addAllSegmentsBetween(source, target);
304 else if (target.bottom() - 1 < source.y)
305 addSegmentsTargetAboveSource(source, target);
306 else if (source.bottom() - 1 < target.y)
307 addSegmentsTargetAboveSource(target, source);
308 else if (target.right() - 1 < source.x)
309 addSegmentsTargetBesideSource(source, target);
310 else
311 addSegmentsTargetBesideSource(target, source);
312 }
313
314 /**
315 * Adds the segments between the given obstacles.
316 * @param source source obstacle
317 * @param target target obstacle
318 */
319 private void addSegmentsFor(Vertex vertex, Obstacle obs) {
320 Segment seg = null;
321 Segment seg2 = null;
322
323 switch (obs.getPosition(vertex)) {
324 case PositionConstants.SOUTH_WEST :
325 case PositionConstants.NORTH_EAST :
326 seg = new Segment(vertex, obs.topLeft);
327 seg2 = new Segment(vertex, obs.bottomRight);
328 break;
329 case PositionConstants.SOUTH_EAST :
330 case PositionConstants.NORTH_WEST :
331 seg = new Segment(vertex, obs.topRight);
332 seg2 = new Segment(vertex, obs.bottomLeft);
333 break;
334 case PositionConstants.NORTH :
335 seg = new Segment(vertex, obs.topLeft);
336 seg2 = new Segment(vertex, obs.topRight);
337 break;
338 case PositionConstants.EAST :
339 seg = new Segment(vertex, obs.bottomRight);
340 seg2 = new Segment(vertex, obs.topRight);
341 break;
342 case PositionConstants.SOUTH :
343 seg = new Segment(vertex, obs.bottomRight);
344 seg2 = new Segment(vertex, obs.bottomLeft);
345 break;
346 case PositionConstants.WEST :
347 seg = new Segment(vertex, obs.topLeft);
348 seg2 = new Segment(vertex, obs.bottomLeft);
349 break;
350 default:
351 if (vertex.x is obs.x) {
352 seg = new Segment(vertex, obs.topLeft);
353 seg2 = new Segment(vertex, obs.bottomLeft);
354 } else if (vertex.y is obs.y) {
355 seg = new Segment(vertex, obs.topLeft);
356 seg2 = new Segment(vertex, obs.topRight);
357 } else if (vertex.y is obs.bottom() - 1) {
358 seg = new Segment(vertex, obs.bottomLeft);
359 seg2 = new Segment(vertex, obs.bottomRight);
360 } else if (vertex.x is obs.right() - 1) {
361 seg = new Segment(vertex, obs.topRight);
362 seg2 = new Segment(vertex, obs.bottomRight);
363 } else {
364 throw new RuntimeException("Unexpected vertex conditions"); //$NON-NLS-1$
365 }
366 }
367
368 stack.push(obs);
369 stack.push(null);
370 stack.push(seg);
371 stack.push(obs);
372 stack.push(null);
373 stack.push(seg2);
374 }
375
376 private void addSegmentsTargetAboveSource(Obstacle source, Obstacle target) {
377 //target located above source
378 Segment seg = null;
379 Segment seg2 = null;
380 if (target.x > source.x) {
381 seg = new Segment(source.topLeft, target.topLeft);
382 if (target.x < source.right() - 1)
383 seg2 = new Segment(source.topRight, target.bottomLeft);
384 else
385 seg2 = new Segment(source.bottomRight, target.topLeft);
386 } else if (source.x is target.x) {
387 seg = new Segment(source.topLeft, target.bottomLeft);
388 seg2 = new Segment(source.topRight, target.bottomLeft);
389 } else {
390 seg = new Segment(source.bottomLeft, target.bottomLeft);
391 seg2 = new Segment(source.topRight, target.bottomLeft);
392 }
393
394 stack.push(source);
395 stack.push(target);
396 stack.push(seg);
397 stack.push(source);
398 stack.push(target);
399 stack.push(seg2);
400 seg = null;
401 seg2 = null;
402
403 if (target.right() < source.right()) {
404 seg = new Segment(source.topRight, target.topRight);
405 if (target.right() - 1 > source.x)
406 seg2 = new Segment(source.topLeft, target.bottomRight);
407 else
408 seg2 = new Segment(source.bottomLeft, target.topRight);
409 } else if (source.right() is target.right()) {
410 seg = new Segment(source.topRight, target.bottomRight);
411 seg2 = new Segment(source.topLeft, target.bottomRight);
412 } else {
413 seg = new Segment(source.bottomRight, target.bottomRight);
414 seg2 = new Segment(source.topLeft, target.bottomRight);
415 }
416
417 stack.push(source);
418 stack.push(target);
419 stack.push(seg);
420 stack.push(source);
421 stack.push(target);
422 stack.push(seg2);
423 }
424
425 private void addSegmentsTargetBesideSource(Obstacle source, Obstacle target) {
426 //target located above source
427 Segment seg = null;
428 Segment seg2 = null;
429 if (target.y > source.y) {
430 seg = new Segment(source.topLeft, target.topLeft);
431 if (target.y < source.bottom() - 1)
432 seg2 = new Segment(source.bottomLeft, target.topRight);
433 else
434 seg2 = new Segment(source.bottomRight, target.topLeft);
435 } else if (source.y is target.y) {
436 //degenerate case
437 seg = new Segment(source.topLeft, target.topRight);
438 seg2 = new Segment(source.bottomLeft, target.topRight);
439 } else {
440 seg = new Segment(source.topRight, target.topRight);
441 seg2 = new Segment(source.bottomLeft, target.topRight);
442 }
443 stack.push(source);
444 stack.push(target);
445 stack.push(seg);
446 stack.push(source);
447 stack.push(target);
448 stack.push(seg2);
449 seg = null;
450 seg2 = null;
451
452 if (target.bottom() < source.bottom()) {
453 seg = new Segment(source.bottomLeft, target.bottomLeft);
454 if (target.bottom() - 1 > source.y)
455 seg2 = new Segment(source.topLeft, target.bottomRight);
456 else
457 seg2 = new Segment(source.topRight, target.bottomLeft);
458 } else if (source.bottom() is target.bottom()) {
459 seg = new Segment(source.bottomLeft, target.bottomRight);
460 seg2 = new Segment(source.topLeft, target.bottomRight);
461 } else {
462 seg = new Segment(source.bottomRight, target.bottomRight);
463 seg2 = new Segment(source.topLeft, target.bottomRight);
464 }
465 stack.push(source);
466 stack.push(target);
467 stack.push(seg);
468 stack.push(source);
469 stack.push(target);
470 stack.push(seg2);
471 }
472
473 /**
474 *
475 */
476 void cleanup() {
477 //segments.clear();
478 visibleVertices.clear();
479 }
480
481 /**
482 * Begins the creation of the visibility graph with the first segment
483 * @param allObstacles list of all obstacles
484 */
485 private void createVisibilityGraph(List allObstacles) {
486 stack.push(null);
487 stack.push(null);
488 stack.push(new Segment(start, end));
489
490 while (!stack.isEmpty())
491 addSegment(stack.pop(), stack.popObstacle(), stack.popObstacle(), allObstacles);
492 }
493
494 /**
495 * Once the visibility graph is constructed, this is called to label the graph and
496 * determine the shortest path. Returns false if no path can be found.
497 *
498 * @return true if a path can be found.
499 */
500 private bool determineShortestPath() {
501 if (!labelGraph())
502 return false;
503 Vertex vertex = end;
504 prevCostRatio = end.cost / start.getDistance(end);
505
506 Vertex nextVertex;
507 while (!vertex.opEquals(start)) {
508 nextVertex = vertex.label;
509 if (nextVertex is null)
510 return false;
511 Segment s = new Segment(nextVertex, vertex);
512 segments.add(s);
513 vertex = nextVertex;
514 }
515
516 Collections.reverse(segments);
517 return true;
518 }
519
520 /**
521 * Resets all necessary fields for a solve.
522 */
523 void fullReset() {
524 visibleVertices.clear();
525 segments.clear();
526 if (prevCostRatio is 0) {
527 double distance = start.getDistance(end);
528 threshold = distance * OVAL_CONSTANT;
529 } else
530 threshold = prevCostRatio * EPSILON * start.getDistance(end);
531 visibleObstacles.clear();
532 resetPartial();
533 }
534
535 /**
536 * Creates the visibility graph and returns whether or not a shortest path could be
537 * determined.
538 *
539 * @param allObstacles the list of all obstacles
540 * @return true if a shortest path was found
541 */
542 bool generateShortestPath(List allObstacles) {
543 createVisibilityGraph(allObstacles);
544
545 if (visibleVertices.size() is 0)
546 return false;
547
548 return determineShortestPath();
549 }
550
551 /**
552 * Returns the list of constrained points through which this path must pass or
553 * <code>null</code>.
554 * @see #setBendPoints(PointList)
555 * @return list of bend points
556 */
557 public PointList getBendPoints() {
558 return bendpoints;
559 }
560
561 /**
562 * Returns the end point for this path
563 * @return end point for this path
564 */
565 public Point getEndPoint() {
566 return end;
567 }
568
569 /**
570 * Returns the solution to this path.
571 *
572 * @return the points for this path.
573 */
574 public PointList getPoints() {
575 return points;
576 }
577
578 /**
579 * Returns the start point for this path
580 * @return start point for this path
581 */
582 public Point getStartPoint() {
583 return start;
584 }
585
586 /**
587 * Returns a subpath for this path at the given segment
588 *
589 * @param currentSegment the segment at which the subpath should be created
590 * @return the new path
591 */
592 Path getSubPath(Segment currentSegment) {
593 // ready new path
594 Path newPath = new Path(currentSegment.start, end);
595 newPath.grownSegments = new ArrayList(grownSegments.subList(
596 grownSegments.indexOf(currentSegment),
597 grownSegments.size()));
598
599 // fix old path
600 grownSegments = new ArrayList(grownSegments.subList(
601 0, grownSegments.indexOf(currentSegment) + 1));
602 end = currentSegment.end;
603
604 subPath = newPath;
605 return newPath;
606 }
607
608 /**
609 * Resets the vertices that this path has traveled prior to this segment. This is called
610 * when the path has become inverted and needs to rectify any labeling mistakes it made
611 * before it knew it was inverted.
612 * @param currentSegment the segment at which the path found it was inverted
613 */
614 void invertPriorVertices(Segment currentSegment) {
615 int stop = grownSegments.indexOf(currentSegment);
616 for (int i = 0; i < stop; i++) {
617 Vertex vertex = (cast(Segment)grownSegments.get(i)).end;
618 if (vertex.type is Vertex.INNIE)
619 vertex.type = Vertex.OUTIE;
620 else
621 vertex.type = Vertex.INNIE;
622 }
623 }
624
625 /**
626 * Returns true if this obstacle is in the visibility graph
627 * @param obs the obstacle
628 * @return true if obstacle is in the visibility graph
629 */
630 bool isObstacleVisible(Obstacle obs) {
631 return visibleObstacles.contains(obs);
632 }
633
634 /**
635 * Labels the visibility graph to assist in finding the shortest path
636 * @return false if there was a gap in the visibility graph
637 */
638 private bool labelGraph() {
639 int numPermanentNodes = 1;
640 Vertex vertex = start;
641 Vertex neighborVertex = null;
642 vertex.isPermanent = true;
643 double newCost;
644 while (numPermanentNodes !is visibleVertices.size()) {
645 List neighbors = vertex.neighbors;
646 if (neighbors is null)
647 return false;
648 // label neighbors if they have a new shortest path
649 for (int i = 0; i < neighbors.size(); i++) {
650 neighborVertex = cast(Vertex)neighbors.get(i);
651 if (!neighborVertex.isPermanent) {
652 newCost = vertex.cost + vertex.getDistance(neighborVertex);
653 if (neighborVertex.label is null) {
654 neighborVertex.label = vertex;
655 neighborVertex.cost = newCost;
656 } else if (neighborVertex.cost > newCost) {
657 neighborVertex.label = vertex;
658 neighborVertex.cost = newCost;
659 }
660 }
661 }
662 // find the next none-permanent, labeled vertex with smallest cost
663 double smallestCost = 0;
664 Vertex tempVertex = null;
665 Iterator v = visibleVertices.iterator();
666 while (v.hasNext()) {
667 tempVertex = cast(Vertex)v.next();
668 if (!tempVertex.isPermanent && tempVertex.label !is null
669 && (tempVertex.cost < smallestCost || smallestCost is 0)) {
670 smallestCost = tempVertex.cost;
671 vertex = tempVertex;
672 }
673 }
674 // set the new vertex to permanent.
675 vertex.isPermanent = true;
676 numPermanentNodes++;
677 }
678 return true;
679 }
680
681 /**
682 * Links two vertices together in the visibility graph
683 * @param segment the segment to add
684 */
685 private void linkVertices(Segment segment) {
686 if (segment.start.neighbors is null)
687 segment.start.neighbors = new ArrayList();
688 if (segment.end.neighbors is null)
689 segment.end.neighbors = new ArrayList();
690
691 if (!segment.start.neighbors.contains(segment.end)) {
692 segment.start.neighbors.add(segment.end);
693 segment.end.neighbors.add(segment.start);
694 }
695
696 visibleVertices.add(segment.start);
697 visibleVertices.add(segment.end);
698 }
699
700 /**
701 * Called to reconnect a subpath back onto this path. Does a depth-first search to
702 * reconnect all paths. Should be called after sorting.
703 */
704 void reconnectSubPaths() {
705 if (subPath !is null) {
706 subPath.reconnectSubPaths();
707
708 Segment changedSegment = cast(Segment)subPath.grownSegments.remove(0);
709 Segment oldSegment = cast(Segment)grownSegments.get(grownSegments.size() - 1);
710
711 oldSegment.end = changedSegment.end;
712 grownSegments.addAll(subPath.grownSegments);
713
714 subPath.points.removePoint(0);
715 points.removePoint(points.size() - 1);
716 points.addAll(subPath.points);
717
718 visibleObstacles.addAll(subPath.visibleObstacles);
719
720 end = subPath.end;
721 subPath = null;
722 }
723 }
724
725
726 /**
727 * Refreshes the exclude field on the obstacles in the list. Excludes all obstacles
728 * that contain the start or end point for this path.
729 * @param allObstacles list of all obstacles
730 */
731 void refreshExcludedObstacles(List allObstacles) {
732 excludedObstacles.clear();
733
734 for (int i = 0; i < allObstacles.size(); i++) {
735 Obstacle o = cast(Obstacle)allObstacles.get(i);
736 o.exclude = false;
737
738 if (o.contains(start)) {
739 if (o.containsProper(start))
740 o.exclude = true;
741 else {
742 /*
743 * $TODO Check for corners. If the path begins exactly at the corner of
744 * an obstacle, the exclude should also be true.
745 *
746 * Or, change segment intersection so that two segments that share an
747 * endpoint do not intersect.
748 */
749 }
750 }
751
752 if (o.contains(end)) {
753 if (o.containsProper(end))
754 o.exclude = true;
755 else {
756 //check for corners. See above statement.
757 }
758 }
759
760 if (o.exclude && !excludedObstacles.contains(o))
761 excludedObstacles.add(o);
762 }
763 }
764
765 /**
766 * Resets the fields for everything in the solve after the visibility graph steps.
767 */
768 void resetPartial() {
769 isMarked = false;
770 isInverted = false;
771 subPath = null;
772 isDirty = false;
773 grownSegments.clear();
774 points.removeAllPoints();
775 }
776
777 /**
778 * Sets the list of bend points to the given list and dirties the path.
779 * @param bendPoints the list of bend points
780 */
781 public void setBendPoints(PointList bendPoints) {
782 this.bendpoints = bendPoints;
783 isDirty = true;
784 }
785
786 /**
787 * Sets the end point for this path to the given point.
788 * @param end the new end point for this path
789 */
790 public void setEndPoint(Point end) {
791 if (end.opEquals(this.end))
792 return;
793 this.end = new Vertex(end, null);
794 isDirty = true;
795 }
796
797 /**
798 * Sets the start point for this path to the given point.
799 * @param start the new start point for this path
800 */
801 public void setStartPoint(Point start) {
802 if (start.opEquals(this.start))
803 return;
804 this.start = new Vertex(start, null);
805 isDirty = true;
806 }
807
808 /**
809 * Returns <code>true</code> if the path is clean and intersects the given obstacle. Also
810 * dirties the path in the process.
811 * @since 3.0
812 * @param obs the obstacle
813 * @return <code>true</code> if a clean path touches the obstacle
814 */
815 bool testAndSet(Obstacle obs) {
816 if (isDirty)
817 return false;
818 //This will never actually happen because obstacles are not stored by identity
819 if (excludedObstacles.contains(obs))
820 return false;
821
822 Segment seg1 = new Segment(obs.topLeft, obs.bottomRight);
823 Segment seg2 = new Segment(obs.topRight, obs.bottomLeft);
824
825 for (int s = 0; s < points.size() - 1; s++) {
826 points.getPoint(CURRENT, s);
827 points.getPoint(NEXT, s + 1);
828
829 if (seg1.intersects(CURRENT, NEXT) || seg2.intersects(CURRENT, NEXT)
830 || obs.contains(CURRENT) || obs.contains(NEXT)) {
831 isDirty = true;
832 return true;
833 }
834 }
835 return false;
836 }
837
838 }