summaryrefslogtreecommitdiff
path: root/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt
blob: 3899db9f12659abec2308843e809aa0ad87fd451 (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
package io.dico.parcels2.blockvisitor

import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec3i

abstract class RegionTraverser {
    fun traverseRegion(region: Region): Iterable<Vec3i> = Iterable { iterator<Vec3i> { build(region) } }

    protected abstract suspend fun SequenceScope<Vec3i>.build(region: Region)

    companion object {
        val upward = create { traverseUpward(it) }
        val downward = create { traverseDownward(it) }
        val forClearing get() = downward
        val forFilling get() = upward

        inline fun create(crossinline builder: suspend SequenceScope<Vec3i>.(Region) -> Unit) = object : RegionTraverser() {
            override suspend fun SequenceScope<Vec3i>.build(region: Region) {
                builder(region)
            }
        }

        private suspend fun SequenceScope<Vec3i>.traverseDownward(region: Region) {
            val origin = region.origin
            val size = region.size
            repeat(size.y) { y ->
                repeat(size.z) { z ->
                    repeat(size.x) { x ->
                        yield(origin.add(x, size.y - y - 1, z))
                    }
                }
            }
        }

        private suspend fun SequenceScope<Vec3i>.traverseUpward(region: Region) {
            val origin = region.origin
            val size = region.size
            repeat(size.y) { y ->
                repeat(size.z) { z ->
                    repeat(size.x) { x ->
                        yield(origin.add(x, size.y - y - 1, z))
                    }
                }
            }
        }

        private fun slice(region: Region, atY: Int): Pair<Region, Region?> {
            if (atY < region.size.y + 1) {
                val first = Region(region.origin, region.size.withY(atY + 1))
                val second = Region(region.origin.withY(atY), region.size.addY(-atY-1))
                return first to second
            }
            return region to null
        }

        fun upToAndDownUntil(y: Int) = create { region ->
            val (bottom, top) = slice(region, y)
            traverseUpward(bottom)
            top?.let { traverseDownward(it) }
        }

    }


}