Move SSD area bounds to typed Kotlin

ober

eb2b1d8892ff82730abdd0445c14fe3ba8230389

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index e1ab6d6..e619e15 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -161,6 +161,7 @@
                 SsdCell-detector
                 ssdCellCx ssdCellCy ssdCellRect
                 rectOverlapArea rectCenterInside iou adjacent
+                maxFloat32 minFloat32
                 bboxForCells mergeDuplicateCellsSorted)
         (type Float32)
         (type FloatArray)
@@ -463,6 +464,8 @@
                 ssdSessionFindCellById ssdSessionCellsByIds
                 ssdSessionBBoxForCellIds ssdSessionRecomputeGroup
                 appendUniqueCellIdsToGroup
+                ssdSessionBoundedSsdArea ssdAreaSelectionTooSmall
+                ssdSessionStoreSsdArea ssdSessionSsdAreaOutsideCount
                 ssdSessionSetSsdArea ssdSessionPruneGroupsOutsideSsdArea
                 createManualCells
                 ssdSessionNextCellId ssdSessionNextGroupId
@@ -523,6 +526,13 @@
                 (begin
                   (mutable-list-add! out group)
                   out)))))
+        (def (ssdSessionSsdAreaOutsideCount (session : SsdSession)) : Int32
+          (if (nullable-null? (SsdSession-ssdArea session))
+            (int32 0)
+            (list-size
+              (ssdSessionGroupsOutsideArea
+                session
+                (nullable-get (SsdSession-ssdArea session))))))
         (def (ssdSessionPruneGroupsOutsideSsdArea (session : SsdSession)) : Int32
           (if (nullable-null? (SsdSession-ssdArea session))
             (int32 0)
@@ -551,28 +561,38 @@
                             group))
                         ignored)))
                   (list-size outside))))))
+        (def (ssdSessionBoundedSsdArea (session : SsdSession)
+                                       (rect : FloatArray)) : FloatArray
+          (let ((maxX (float32 (SsdSession-imageWidth session)))
+                (maxY (float32 (SsdSession-imageHeight session))))
+            (float-array
+              (minFloat32 maxX (maxFloat32 (float32 0.0) (float-array-ref rect (int32 0))))
+              (minFloat32 maxY (maxFloat32 (float32 0.0) (float-array-ref rect (int32 1))))
+              (minFloat32 maxX (maxFloat32 (float32 0.0) (float-array-ref rect (int32 2))))
+              (minFloat32 maxY (maxFloat32 (float32 0.0) (float-array-ref rect (int32 3)))))))
+        (def (ssdAreaSelectionTooSmall (area : FloatArray)) : Bool
+          (or (< (float32-abs (- (float-array-ref area (int32 0))
+                                  (float-array-ref area (int32 2))))
+                 (float32 10.0))
+              (< (float32-abs (- (float-array-ref area (int32 1))
+                                  (float-array-ref area (int32 3))))
+                 (float32 10.0))))
+        (def (ssdSessionStoreSsdArea (session : SsdSession) (area : FloatArray)) : Unit
+          (SsdSession-ssdArea-set!
+            session
+            (nullable-some
+              (float-array
+                (minFloat32 (float-array-ref area (int32 0))
+                            (float-array-ref area (int32 2)))
+                (minFloat32 (float-array-ref area (int32 1))
+                            (float-array-ref area (int32 3)))
+                (maxFloat32 (float-array-ref area (int32 0))
+                            (float-array-ref area (int32 2)))
+                (maxFloat32 (float-array-ref area (int32 1))
+                            (float-array-ref area (int32 3)))))))
         (def (ssdSessionSetSsdArea (session : SsdSession) (area : FloatArray)) : Int32
           (begin
-            (SsdSession-ssdArea-set!
-              session
-              (nullable-some
-                (float-array
-                  (if (< (float-array-ref area (int32 0))
-                         (float-array-ref area (int32 2)))
-                    (float-array-ref area (int32 0))
-                    (float-array-ref area (int32 2)))
-                  (if (< (float-array-ref area (int32 1))
-                         (float-array-ref area (int32 3)))
-                    (float-array-ref area (int32 1))
-                    (float-array-ref area (int32 3)))
-                  (if (> (float-array-ref area (int32 0))
-                         (float-array-ref area (int32 2)))
-                    (float-array-ref area (int32 0))
-                    (float-array-ref area (int32 2)))
-                  (if (> (float-array-ref area (int32 1))
-                         (float-array-ref area (int32 3)))
-                    (float-array-ref area (int32 1))
-                    (float-array-ref area (int32 3))))))
+            (ssdSessionStoreSsdArea session area)
             (ssdSessionPruneGroupsOutsideSsdArea session)))
         (def (manualCellColumns (count : Int32)) : Int32
           (for/fold ((columns (int32 1)))
@@ -3378,8 +3398,6 @@
        "import java.util.UUID"
        "import kotlin.concurrent.thread"
        "import kotlin.system.exitProcess"
-       "import kotlin.math.max"
-       "import kotlin.math.min"
        ""
        "class MainActivity : Activity() {"
        "    private lateinit var truthStore: TruthStore"
@@ -3807,30 +3825,16 @@
 	       ""
 	       "    private fun applySsdArea(rect: FloatArray) {"
 	       "        val session = currentSession ?: return"
-	       "        val area = floatArrayOf("
-	       "            rect[0].coerceIn(0f, session.imageWidth.toFloat()),"
-	       "            rect[1].coerceIn(0f, session.imageHeight.toFloat()),"
-	       "            rect[2].coerceIn(0f, session.imageWidth.toFloat()),"
-	       "            rect[3].coerceIn(0f, session.imageHeight.toFloat())"
-	       "        )"
-	       "        if ((area[2] - area[0]) < 10f || (area[3] - area[1]) < 10f) {"
-	       "            finishSelectInteraction()"
-	       "            setStatus(\"SSD area ignored: selection was too small\")"
-	       "            return"
-	       "        }"
-	       "        session.ssdArea = floatArrayOf("
-	       "            min(area[0], area[2]),"
-	       "            min(area[1], area[3]),"
-	       "            max(area[0], area[2]),"
-	       "            max(area[1], area[3])"
-	       "        )"
-	       "        val beforeGroups = session.groups.size"
-	      "        val outsideCount = session.groups.count { !ssdSessionGroupInsideSsdArea(session, it) }"
-	       "        val removed = if (beforeGroups > 0 && outsideCount < beforeGroups) {"
-	      "            ssdSessionPruneGroupsOutsideSsdArea(session)"
-	       "        } else {"
-	       "            0"
-	       "        }"
+       "        val area = ssdSessionBoundedSsdArea(session, rect)"
+       "        if (ssdAreaSelectionTooSmall(area)) {"
+       "            finishSelectInteraction()"
+       "            setStatus(\"SSD area ignored: selection was too small\")"
+       "            return"
+       "        }"
+       "        val beforeGroups = session.groups.size"
+       "        ssdSessionStoreSsdArea(session, area)"
+       "        val outsideCount = ssdSessionSsdAreaOutsideCount(session)"
+       "        val removed = ssdSessionPruneGroupsOutsideSsdArea(session)"
 	       "        selectedGroupId = session.groups.firstOrNull { it.id == selectedGroupId }?.id"
 	       "            ?: session.groups.firstOrNull()?.id"
 	       "        reviewView.ssdArea = session.ssdArea"