Move SSD detector component to typed Kotlin

ober

6f42f0f9eb4e837149abba1b0e616cba7536367d

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index a91baf4..234350d 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -40,6 +40,23 @@
            (mut h : Float32)
            (detector : String)))))
 
+    (typed-kotlin-file "com/sfb/ssdreview/Component.kt"
+      (typed-library (com sfb ssdreview)
+        (export make-Component Component? Component-x1 Component-x1-set!
+                Component-y1 Component-y1-set!
+                Component-x2 Component-x2-set!
+                Component-y2 Component-y2-set!
+                Component-area Component-area-set!
+                Component-detector)
+        (type Int32)
+        (record Component
+          ((mut x1 : Int32)
+           (mut y1 : Int32)
+           (mut x2 : Int32)
+           (mut y2 : Int32)
+           (mut area : Int32)
+           (detector : String)))))
+
     (typed-kotlin-file "com/sfb/ssdreview/SsdGroup.kt"
       (typed-library (com sfb ssdreview)
         (export make-SsdGroup SsdGroup? SsdGroup-id
@@ -2395,6 +2412,18 @@
        "import kotlin.math.min"
        "import kotlin.math.roundToInt"
        ""
+       "private fun componentW(component: Component): Int = component.x2 - component.x1 + 1"
+       ""
+       "private fun componentH(component: Component): Int = component.y2 - component.y1 + 1"
+       ""
+       "private fun componentAdd(component: Component, x: Int, y: Int) {"
+       "    component.x1 = min(component.x1, x)"
+       "    component.y1 = min(component.y1, y)"
+       "    component.x2 = max(component.x2, x)"
+       "    component.y2 = max(component.y2, y)"
+       "    component.area += 1"
+       "}"
+       ""
        "object SsdDetector {"
        "    fun detectCells(bitmap: Bitmap): MutableList<SsdCell> {"
        "        val width = bitmap.width"
@@ -2548,7 +2577,7 @@
        "                    val idx = queue.removeFirst()"
        "                    val cx = idx % width"
        "                    val cy = idx / width"
-       "                    acc.add(cx, cy)"
+       "                    componentAdd(acc, cx, cy)"
        "                    for (ny in max(0, cy - 1)..min(height - 1, cy + 1)) {"
        "                        for (nx in max(0, cx - 1)..min(width - 1, cx + 1)) {"
        "                            val nidx = ny * width + nx"
@@ -2566,8 +2595,8 @@
        "    }"
        ""
        "    private fun componentCell(component: Component, detector: String): SsdCell? {"
-       "        val w = component.w"
-       "        val h = component.h"
+       "        val w = componentW(component)"
+       "        val h = componentH(component)"
        "        val bboxArea = w * h"
        "        if (component.area !in 40..2000 || w !in 6..80 || h !in 6..80) return null"
        "        val density = component.area.toFloat() / max(1, bboxArea).toFloat()"
@@ -2596,8 +2625,8 @@
        "    }"
        ""
        "    private fun contourCell(component: Component, edge: BooleanArray, width: Int, height: Int): SsdCell? {"
-       "        val w = component.w"
-       "        val h = component.h"
+       "        val w = componentW(component)"
+       "        val h = componentH(component)"
        "        val bboxArea = w * h"
        "        if (bboxArea !in 50..2400 || w !in 7..90 || h !in 7..90) return null"
        "        val aspect = w.toFloat() / max(1, h).toFloat()"
@@ -2781,26 +2810,6 @@
        ""
        "    private fun edgeAt(edge: BooleanArray, width: Int, height: Int, x: Int, y: Int): Boolean ="
        "        x in 0 until width && y in 0 until height && edge[y * width + x]"
-       ""
-       "    private class Component("
-       "        var x1: Int,"
-       "        var y1: Int,"
-       "        var x2: Int,"
-       "        var y2: Int,"
-       "        var area: Int,"
-       "        val detector: String"
-       "    ) {"
-       "        val w: Int get() = x2 - x1 + 1"
-       "        val h: Int get() = y2 - y1 + 1"
-       ""
-       "        fun add(x: Int, y: Int) {"
-       "            x1 = min(x1, x)"
-       "            y1 = min(y1, y)"
-       "            x2 = max(x2, x)"
-       "            y2 = max(y2, y)"
-       "            area += 1"
-       "        }"
-       "    }"
        "}"
        ))
     (kotlin-file-lines "com/sfb/ssdreview/SsdVisionBridge.kt"