comparison cmd/isoareas/main.go @ 4551:b5b23b6d76f1 iso-areas

Move own algorith to separate file.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Tue, 01 Oct 2019 11:07:33 +0200
parents aa2d0006e742
children 7ed5a4a94efc
comparison
equal deleted inserted replaced
4550:aa2d0006e742 4551:b5b23b6d76f1
13 13
14 package main 14 package main
15 15
16 import ( 16 import (
17 "bufio" 17 "bufio"
18 "container/list"
19 "fmt" 18 "fmt"
20 "io" 19 "io"
21 "log" 20 "log"
22 "math" 21 "math"
23 "math/bits"
24 "math/rand" 22 "math/rand"
25 "os" 23 "os"
26 "runtime"
27 "strconv" 24 "strconv"
28 "strings" 25 "strings"
29 "sync"
30 "time" 26 "time"
31 27
32 svg "github.com/ajstarks/svgo" 28 svg "github.com/ajstarks/svgo"
33 29
34 "gemma.intevation.de/gemma/pkg/octree" 30 "gemma.intevation.de/gemma/pkg/octree"
87 83
88 func check(err error) { 84 func check(err error) {
89 if err != nil { 85 if err != nil {
90 log.Fatalf("error: %v\n", err) 86 log.Fatalf("error: %v\n", err)
91 } 87 }
92 }
93
94 type indexedArc struct {
95 arc int32
96 index int32
97 }
98
99 func glue(a, b octree.LineStringZ) octree.LineStringZ {
100
101 a1, a2 := a[0], a[len(a)-1]
102 b1, b2 := b[0], b[len(b)-1]
103
104 if a1.EpsEquals2D(b2) {
105 c := make(octree.LineStringZ, len(a)-1+len(b))
106 copy(c, b)
107 copy(c[len(b):], a[1:])
108 return c
109 }
110
111 if a2.EpsEquals2D(b1) {
112 c := make(octree.LineStringZ, len(a)-1+len(b))
113 copy(c, a)
114 copy(c[len(a):], b[1:])
115 return c
116 }
117
118 if a1.EpsEquals2D(b1) {
119 c := make(octree.LineStringZ, len(a)-1+len(b))
120 copy(c, b)
121 c[:len(b)].Reverse()
122 copy(c[len(b):], a[1:])
123 return c
124 }
125
126 if a2.EpsEquals2D(b2) {
127 c := make(octree.LineStringZ, len(a)-1+len(b))
128 copy(c, a)
129 copy(c[len(a):], b[:len(b)-1])
130 c[len(a):].Reverse()
131 return c
132 }
133
134 return nil
135 }
136
137 func connectArcs(tri *octree.Triangulation, cuts []indexedArc, arcs *[]octree.LineStringZ) {
138
139 unique := map[int32]struct{}{}
140 for i := range cuts {
141 unique[cuts[i].arc] = struct{}{}
142 }
143 before := len(unique)
144
145 origLen := int32(len(*arcs))
146
147 for i := range cuts {
148 if cuts[i].arc >= origLen {
149 // already has a connected arc assigned.
150 continue
151 }
152
153 line := (*arcs)[cuts[i].arc]
154
155 var modified []int
156 visited := map[int32]struct{}{}
157
158 var recursive func(int32)
159 recursive = func(idx int32) {
160 visited[idx] = struct{}{}
161
162 ns := neighbors(tri, idx)
163 for _, n := range ns {
164 n /= 3
165 if _, already := visited[n]; already {
166 continue
167 }
168
169 arcIdx := findArc(n, cuts)
170 if arcIdx == -1 {
171 continue
172 }
173 arc := cuts[arcIdx].arc
174 if arc >= origLen {
175 // already a new arc.
176 continue
177 }
178 oline := (*arcs)[arc]
179
180 nline := glue(line, oline)
181 if nline == nil {
182 // not joinable
183 continue
184 }
185 line = nline
186 modified = append(modified, arcIdx)
187 recursive(cuts[arcIdx].index)
188 }
189 }
190 recursive(cuts[i].index)
191 if len(modified) > 0 {
192 // alloc a new line an fix the references.
193 nidx := int32(len(*arcs))
194 *arcs = append(*arcs, line)
195 cuts[i].arc = nidx
196 for _, j := range modified {
197 cuts[j].arc = nidx
198 }
199 }
200 }
201
202 unique = map[int32]struct{}{}
203 for i := range cuts {
204 unique[cuts[i].arc] = struct{}{}
205 }
206 log.Printf("unique arcs: before: %d after %d\n",
207 before, len(unique))
208 }
209
210 func intersectTriangles(
211 tri *octree.Triangulation,
212 heights []float64,
213 min, max octree.Vertex,
214 ) [][]octree.LineStringZ {
215
216 cuts := make([][]indexedArc, len(heights))
217
218 classes := make([][]int32, len(heights)+1)
219
220 var arcs []octree.LineStringZ
221
222 all:
223 for i := int32(0); i < int32(len(tri.Triangles)); i += 3 {
224 ti := tri.Triangles[i : i+3]
225 v0 := tri.Points[ti[0]]
226 v1 := tri.Points[ti[1]]
227 v2 := tri.Points[ti[2]]
228 min := math.Min(v0.Z, math.Min(v1.Z, v2.Z))
229
230 if min > heights[len(heights)-1] {
231 classes[len(classes)-1] = append(classes[len(classes)-1], i/3)
232 continue
233 }
234 max := math.Max(v0.Z, math.Max(v1.Z, v2.Z))
235
236 for j, h := range heights {
237
238 var l float64
239 if j > 0 {
240 l = heights[j-1]
241 } else {
242 l = -math.MaxFloat64
243 }
244
245 if l < min && max < h {
246 classes[j] = append(classes[j], i/3)
247 continue all
248 }
249 if min > h || max < h {
250 continue
251 }
252 t := octree.Triangle{v0, v1, v2}
253 cut := t.IntersectHorizontal(h)
254 if len(cut) >= 2 {
255 arc := int32(len(arcs))
256 arcs = append(arcs, cut)
257 cuts[j] = append(cuts[j], indexedArc{arc: arc, index: i / 3})
258 }
259 }
260 }
261
262 // connect the arcs in a cut list to longer arcs.
263
264 for _, c := range cuts {
265 connectArcs(tri, c, &arcs)
266 }
267
268 func() {
269 out, err := os.Create("arcs.svg")
270 if err != nil {
271 log.Printf("err: %v\n", err)
272 return
273 }
274 defer func() {
275 out.Close()
276 log.Println("writing arcs done")
277 }()
278
279 buf := bufio.NewWriter(out)
280
281 dumpArcsSVG(
282 buf,
283 min, max,
284 cuts,
285 arcs)
286 buf.Flush()
287 }()
288
289 result := make([][]octree.LineStringZ, len(classes))
290
291 jobs := make(chan int)
292
293 var wg sync.WaitGroup
294
295 worker := func() {
296 defer wg.Done()
297
298 pb := polygonBuilder{open: list.New()}
299
300 for i := range jobs {
301 usedArcs := map[int32]struct{}{}
302 var dupes int
303 var isolated, inside, found int
304 c := classes[i]
305
306 allInClass:
307 for _, idx := range c {
308 ns := neighbors(tri, idx)
309 mask := where(ns, c)
310 switch bits.OnesCount8(mask) {
311 case 3:
312 // Totally insides do not contribute to the geometries.
313 inside++
314 continue allInClass
315 case 0:
316 // Isolated are areas w/o connections to the rest.
317 isolated++
318 ti := tri.Triangles[idx*3 : idx*3+3]
319 pb.addTriangle(
320 tri.Points[ti[0]],
321 tri.Points[ti[1]],
322 tri.Points[ti[2]])
323 continue allInClass
324 default:
325 ti := tri.Triangles[idx*3 : idx*3+3]
326 for j := 0; j < 3; j++ {
327 if (mask & (1 << j)) == 0 {
328
329 var curr octree.LineStringZ
330
331 for l := i - 1; l <= i; l++ {
332 if l < 0 || l >= len(cuts) {
333 continue
334 }
335 arcIdx := findArc(ns[j]/3, cuts[l])
336 if arcIdx == -1 {
337 continue
338 }
339 found++
340 aIdx := cuts[l][arcIdx].arc
341 if _, already := usedArcs[aIdx]; already {
342 dupes++
343 continue
344 }
345 usedArcs[aIdx] = struct{}{}
346
347 curr = arcs[aIdx]
348 break
349 }
350
351 if curr == nil {
352 k := (j + 1) % 3
353 front := tri.Points[ti[j]]
354 back := tri.Points[ti[k]]
355 curr = octree.LineStringZ{front, back}
356 }
357
358 segment:
359 for e := pb.open.Front(); e != nil; e = e.Next() {
360 line := e.Value.(octree.LineStringZ)
361 if nline := glue(curr, line); nline != nil {
362 curr = nline
363 pb.open.Remove(e)
364 goto segment
365 }
366 } // all open
367
368 // check if closed
369 if curr[0].EpsEquals2D(curr[len(curr)-1]) {
370 if !curr.CCW() {
371 curr.Reverse()
372 }
373 pb.polygons = append(pb.polygons, curr)
374 } else {
375 pb.open.PushFront(curr)
376 }
377 } // for all border parts
378 }
379 }
380 }
381
382 log.Printf("\t%d: inside: %d / isolated: %d open: %d closed: %d dupes: %d found: %d\n",
383 i, inside, isolated, pb.open.Len(), len(pb.polygons), dupes, found)
384 /*
385 for e := pb.open.Front(); e != nil; e = e.Next() {
386 line := e.Value.(octree.LineStringZ)
387 if !line.CCW() {
388 line.Reverse()
389 }
390 pb.polygons = append(pb.polygons, line)
391 }
392 */
393
394 result[i] = pb.polygons
395 pb.reset()
396 }
397 }
398
399 for n := runtime.NumCPU(); n >= 0; n-- {
400 wg.Add(1)
401 go worker()
402 }
403
404 log.Println("inside classes:")
405 for i := range classes {
406 jobs <- i
407 }
408 close(jobs)
409
410 wg.Wait()
411
412 log.Println("cuts:")
413 for i, c := range cuts {
414 log.Printf("\t%.3f: %d\n", heights[i], len(c))
415 }
416
417 return result
418
419 // TODO: sew segments together.
420
421 }
422
423 type polygonBuilder struct {
424 polygons []octree.LineStringZ
425
426 open *list.List
427 }
428
429 func (pb *polygonBuilder) addTriangle(v0, v1, v2 octree.Vertex) {
430 polygon := octree.LineStringZ{v0, v1, v2, v0}
431 pb.polygons = append(pb.polygons, polygon)
432 }
433
434 func (pb *polygonBuilder) reset() {
435 pb.polygons = pb.polygons[:0]
436 pb.open.Init()
437 }
438
439 func neighbors(t *octree.Triangulation, idx int32) []int32 {
440 idx *= 3
441 return t.Halfedges[idx : idx+3]
442 }
443
444 func findArc(needle int32, haystack []indexedArc) int {
445 lo, hi := 0, len(haystack)-1
446 for lo <= hi {
447 mid := (hi-lo)/2 + lo
448 switch v := haystack[mid].index; {
449 case v < needle:
450 lo = mid + 1
451 case v > needle:
452 hi = mid - 1
453 default:
454 return mid
455 }
456 }
457 return -1
458 }
459
460 func contains(needle int32, haystack []int32) bool {
461 lo, hi := 0, len(haystack)-1
462 for lo <= hi {
463 mid := (hi-lo)/2 + lo
464 switch v := haystack[mid]; {
465 case v < needle:
466 lo = mid + 1
467 case v > needle:
468 hi = mid - 1
469 default:
470 return true
471 }
472 }
473 return false
474 }
475
476 func where(neighbors, indices []int32) byte {
477 var mask byte
478 for i, n := range neighbors {
479 if n >= 0 && contains(n/3, indices) {
480 mask |= 1 << i
481 }
482 }
483 return mask
484 } 88 }
485 89
486 func linear(x1, y1, x2, y2 float64) func(float64) float64 { 90 func linear(x1, y1, x2, y2 float64) func(float64) float64 {
487 // f(x1) = y1 91 // f(x1) = y1
488 // f(x2) = y2 92 // f(x2) = y2