@@ -27,20 +27,45 @@ public class FortuneSweep {
2727 /// Service Data Structures
2828 private var eventQueue : PriorityQueue < Event > !
2929 private var beachline : Beachline !
30- private var sweepLineY : Double = 0
30+ private var sweepLineY : Double = 0 {
31+ didSet {
32+ watcher? . updateSweepline ( y: sweepLineY)
33+ }
34+ }
3135 private var firstSiteY : Double ?
3236
33- private var container : Rectangle !
37+ private var container : Rectangle ! {
38+ didSet {
39+ watcher? . updateContainer ( rectangle: container)
40+ }
41+ }
3442 private var clipper : Rectangle !
3543
3644 /// Debug Data structures
3745 private var logger : FortuneSweepLogging ?
38- private var currentStep : Int = 0
46+ private var watcher : FortuneSweepProgressWatching ?
47+ private var currentStep : Int = 0 {
48+ didSet {
49+ watcher? . step = currentStep
50+ }
51+ }
3952 /// Result Data Structure
4053 private( set) var diagram : Diagram !
4154
42- public init ( logger: FortuneSweepLogging ? = nil ) {
43- self . logger = logger
55+
56+ /// Initialises new FortuneSweep instance
57+ /// NB: `logger` and especially `watcher` will affect performance!
58+ /// Use them only for debug purposes!
59+ ///
60+ /// - Parameters:
61+ /// - logger: Optional. Calls `log` function when importand events happen
62+ /// - watcher: Optional. **Affects performance!** Tracks diagram building process. May be used for visualisation as it includes useful scaffolding.
63+ public init (
64+ logger: FortuneSweepLogging ? = nil ,
65+ intermediate: FortuneSweepProgressWatching ? = nil
66+ ) {
67+ self . logger = nil //logger
68+ self . watcher = intermediate
4469 }
4570
4671 public func compute(
@@ -50,12 +75,15 @@ public class FortuneSweep {
5075 ) {
5176 self . diagram = diagram
5277 self . clipper = clippingRect
53- let filtered = sites
54- . filter { clipper. contains ( $0) }
55- . map { Event ( point: $0) }
56-
78+ let filteredSites = sites. filter { clipper. contains ( $0) }
79+
80+ let events = filteredSites. map { Event ( point: $0) }
81+
82+ /// Intermediate state
83+ watcher? . prepare ( sites: filteredSites, clipper: clippingRect)
84+
5785 /// Diagram is a whole plane. Do nothing
58- if filtered . isEmpty {
86+ if events . isEmpty {
5987 logger? . log ( " Computation done. No sites inside defined area! " , level: . info)
6088 return
6189 }
@@ -66,10 +94,10 @@ public class FortuneSweep {
6694
6795 eventQueue = PriorityQueue (
6896 ascending: true ,
69- startingValues: filtered
97+ startingValues: events
7098 )
7199
72- logger? . log ( " Computation started!" , level: . info)
100+ logger? . log ( " \n \n Computation started!" , level: . info)
73101 var finished = false
74102 while !finished {
75103 step ( )
@@ -85,7 +113,7 @@ public class FortuneSweep {
85113 /// 2. Check the event type and process the event appropriately
86114 private func step( ) {
87115 currentStep += 1
88- logger? . log ( " Step : \( currentStep) " , level: . info)
116+ logger? . log ( " \n Step : \( currentStep) " , level: . info)
89117
90118 if let event = eventQueue. pop ( ) {
91119 switch event. kind {
@@ -102,6 +130,7 @@ public class FortuneSweep {
102130 /// - Parameter event: **Site Event** to process
103131 private func processSiteEvent( _ event: Event ) {
104132 logger? . log ( " Site Event: \( event. point) " , level: . info)
133+ watcher? . siteEvent ( site: event. point)
105134
106135 /// #Step 1:
107136 /// Update **Sweepline** position
@@ -125,6 +154,7 @@ public class FortuneSweep {
125154 /// Create new **Cell** record in **Voronoi Diagram**
126155 container. expandToContainPoint ( event. point)
127156 diagram. createCell ( root)
157+ watcher? . createCell ( cell: root. cell)
128158 return
129159 }
130160
@@ -144,6 +174,7 @@ public class FortuneSweep {
144174
145175 /// 1. Create new **Cell** record in **Voronoi Diagram**
146176 diagram. createCell ( arc)
177+ watcher? . createCell ( cell: arc. cell)
147178
148179 /// 2. Create proper *HalfEdge* records. Arc with the same *y* can only appear
149180 /// to the right of existing one as in case of *y* coordinate is shared they are sorted by *x*
@@ -172,8 +203,17 @@ public class FortuneSweep {
172203 /// Create new **Cell** record in **Voronoi Diagram**
173204 container. expandToContainPoint ( event. point)
174205 diagram. createCell ( newArc)
175-
176-
206+ watcher? . createCell ( cell: newArc. cell)
207+
208+ /// Notify `intermediate` if needed
209+ if let intermediate = self . watcher {
210+ let parabola = Parabola ( focus: newArc. prev!. point!, directrixY: sweepLineY)
211+ let breakPoint = Point (
212+ x: event. point. x,
213+ y: parabola. resolve ( x: event. point. x)
214+ )
215+ intermediate. updateCurentBreakpoint ( point: breakPoint)
216+ }
177217
178218 /// #Step 3:
179219 /// If the arc we broke has circle event, than this event is false-alarm and has to be removed
@@ -246,6 +286,10 @@ public class FortuneSweep {
246286 newArc. rightHalfEdge = newArc. leftHalfEdge
247287 next. leftHalfEdge = prev. rightHalfEdge
248288 }
289+
290+ if let intermediate = self . watcher {
291+ intermediate. updateBeachline ( arcs: beachline. getArcs ( ) )
292+ }
249293 }
250294
251295
@@ -297,13 +341,16 @@ public class FortuneSweep {
297341 nextArc. leftHalfEdge = nextLHE
298342
299343 makeTwins ( prevArc. rightHalfEdge, nextArc. leftHalfEdge)
344+
345+ watcher? . createVertex ( vertex: vertex)
300346 }
301347
302348
303349 /// Processes circle event and performs all the necessary actions
304350 /// - Parameter event: **Circle Event**
305351 private func processCircleEvent( _ event: Event ) {
306352 logger? . log ( " Circle Event: \( event. point) " , level: . info)
353+ watcher? . circleEvent ( point: event. point)
307354
308355 guard let arc = event. arc,
309356 let left = arc. prev,
@@ -350,8 +397,6 @@ public class FortuneSweep {
350397 /// - arc: **Beachline** arc to add event
351398 /// - circle: Circle represented by three points
352399 private func createCircleEvent( _ arc: Arc ) {
353- logger? . log ( " Create Circle Event for Arc: \( arc. point!) " , level: . info)
354-
355400 let left = arc. prev
356401 let right = arc. next
357402 guard let circle = checkCircleEvent ( left: left, mid: arc, right: right) else {
@@ -366,6 +411,9 @@ public class FortuneSweep {
366411 arc. event = event
367412
368413 eventQueue. push ( event)
414+
415+ logger? . log ( " Create Circle Event for Arc: \( arc. point!) " , level: . info)
416+ watcher? . addUpcomingCircleEvent ( circle: event. circle!)
369417 }
370418
371419 /// Removes circle event from the **Priority Queue** and removes event from the **Arc**
@@ -379,10 +427,11 @@ public class FortuneSweep {
379427 return
380428 }
381429
430+ watcher? . removeFalseAlarmCircleEvent ( circle: event. circle!)
431+ logger? . log ( " Remove Circle Event for Arc: \( arc. point!) " , level: . info)
432+
382433 eventQueue. removeAll ( event)
383434 arc. event = nil
384-
385- logger? . log ( " Remove Circle Event for Arc: \( arc. point!) " , level: . info)
386435 }
387436
388437
@@ -714,3 +763,28 @@ extension Vector2D {
714763 Site ( x: dx, y: dy)
715764 }
716765}
766+
767+ public struct BeachlineSegment {
768+ let parabola : Parabola
769+ let lBoundX : Double
770+ let rBoundX : Double
771+ }
772+
773+ extension Beachline {
774+ public func getArcs( ) -> [ BeachlineSegment ] {
775+ var arcs = [ BeachlineSegment] ( )
776+ var arc = minimum
777+ while arc != nil {
778+ let ( lb, rb) = arc!. bounds ( sweeplineY)
779+ arcs. append (
780+ BeachlineSegment (
781+ parabola: Parabola ( focus: arc!. point!, directrixY: sweeplineY) ,
782+ lBoundX: lb,
783+ rBoundX: rb
784+ )
785+ )
786+ arc = arc? . next
787+ }
788+ return arcs
789+ }
790+ }
0 commit comments