Move SSD manual cell creation to typed Kotlin

ober

1aabd01c6819e8103bec5e762b66165feabbf08e

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 6cdbe40..5735f9d 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -333,6 +333,7 @@
                 ssdSessionCellInsideSsdArea ssdSessionGroupInsideSsdArea
                 ssdSessionFindCellById ssdSessionRecomputeGroup
                 ssdSessionSetSsdArea ssdSessionPruneGroupsOutsideSsdArea
+                createManualCells
                 ssdSessionNextCellId ssdSessionNextGroupId)
         (type Int32)
         (type FloatArray)
@@ -441,6 +442,57 @@
                     (float-array-ref area (int32 1))
                     (float-array-ref area (int32 3))))))
             (ssdSessionPruneGroupsOutsideSsdArea session)))
+        (def (manualCellColumns (count : Int32)) : Int32
+          (for/fold ((columns (int32 1)))
+                    ((i (in-range (int32 0) count)))
+            (if (< (* columns columns) count)
+              (+ columns (int32 1))
+              columns)))
+        (def (manualCellRows (count : Int32) (columns : Int32)) : Int32
+          (let ((rows (/ (+ count (- columns (int32 1))) columns)))
+            (if (< rows (int32 1)) (int32 1) rows)))
+        (def (createManualCells (session : SsdSession)
+                                (rect : FloatArray)
+                                (count : Int32)) : (MutableList String)
+          (let ((columns (manualCellColumns count)))
+            (let ((rows (manualCellRows count columns))
+                  (width (let ((candidate (- (float-array-ref rect (int32 2))
+                                             (float-array-ref rect (int32 0)))))
+                           (if (> candidate (float32 1.0))
+                             candidate
+                             (float32 1.0))))
+                  (height (let ((candidate (- (float-array-ref rect (int32 3))
+                                              (float-array-ref rect (int32 1)))))
+                            (if (> candidate (float32 1.0))
+                              candidate
+                              (float32 1.0)))))
+              (for/fold ((ids (mutable-list-empty String)))
+                        ((i (in-range (int32 0) count)))
+                (let ((row (/ i columns))
+                      (column (mod i columns)))
+                  (let ((x1 (+ (float-array-ref rect (int32 0))
+                               (/ (* width (float32 column))
+                                  (float32 columns))))
+                        (y1 (+ (float-array-ref rect (int32 1))
+                               (/ (* height (float32 row))
+                                  (float32 rows))))
+                        (x2 (+ (float-array-ref rect (int32 0))
+                               (/ (* width (float32 (+ column (int32 1))))
+                                  (float32 columns))))
+                        (y2 (+ (float-array-ref rect (int32 1))
+                               (/ (* height (float32 (+ row (int32 1))))
+                                  (float32 rows)))))
+                    (let ((cell (make-SsdCell
+                                  (ssdSessionNextCellId session)
+                                  x1
+                                  y1
+                                  (- x2 x1)
+                                  (- y2 y1)
+                                  "android-manual")))
+                      (begin
+                        (mutable-list-add! (SsdSession-cells session) cell)
+                        (mutable-list-add! ids (SsdCell-id cell))
+                        ids))))))))
         (def (ssdSessionFindCellById (session : SsdSession)
                                      (id : String)) : (Nullable SsdCell)
           (for/fold ((found (nullable-none SsdCell)))
@@ -1803,10 +1855,8 @@
        "import java.util.UUID"
        "import kotlin.concurrent.thread"
        "import kotlin.system.exitProcess"
-       "import kotlin.math.ceil"
        "import kotlin.math.max"
        "import kotlin.math.min"
-       "import kotlin.math.sqrt"
        ""
        "class MainActivity : Activity() {"
        "    private lateinit var truthStore: TruthStore"
@@ -2828,27 +2878,6 @@
        "        focusBoxTypeDropdown(dialog, boxType)"
        "    }"
        ""
-       "    private fun createManualCells(session: SsdSession, rect: FloatArray, count: Int): MutableList<String> {"
-       "        val ids = mutableListOf<String>()"
-       "        val columns = ceil(sqrt(count.toDouble())).toInt().coerceAtLeast(1)"
-       "        val rows = ceil(count.toDouble() / columns.toDouble()).toInt().coerceAtLeast(1)"
-       "        val width = max(1f, rect[2] - rect[0])"
-       "        val height = max(1f, rect[3] - rect[1])"
-       "        for (row in 0 until rows) {"
-       "            for (column in 0 until columns) {"
-       "                if (ids.size >= count) break"
-       "                val x1 = rect[0] + width * column / columns"
-       "                val y1 = rect[1] + height * row / rows"
-       "                val x2 = rect[0] + width * (column + 1) / columns"
-       "                val y2 = rect[1] + height * (row + 1) / rows"
-       "                val cell = SsdCell(ssdSessionNextCellId(session), x1, y1, x2 - x1, y2 - y1, \"android-manual\")"
-       "                session.cells.add(cell)"
-       "                ids.add(cell.id)"
-       "            }"
-       "        }"
-       "        return ids"
-       "    }"
-       ""
        "    private fun saveTruth() {"
        "        val session = currentSession ?: return"
        "        saveSessionAsync("