|
@@ -10,6 +10,7 @@ import android.util.Log
|
|
|
import androidx.compose.ui.geometry.Offset
|
|
|
import androidx.compose.ui.geometry.Rect
|
|
|
import androidx.compose.ui.geometry.Size
|
|
|
+import androidx.compose.ui.geometry.center
|
|
|
import androidx.compose.ui.graphics.ImageBitmap
|
|
|
import androidx.compose.ui.graphics.asImageBitmap
|
|
|
import androidx.lifecycle.ViewModel
|
|
@@ -44,10 +45,18 @@ class WorkViewModel : ViewModel() {
|
|
|
|
|
|
val processedData = MutableStateFlow(ProcessedData())
|
|
|
|
|
|
+ val contourPath = MutableStateFlow(listOf<Offset>())
|
|
|
+ val lengthPath = MutableStateFlow(listOf<Offset>())
|
|
|
+ val widthPath = MutableStateFlow(listOf<Offset>())
|
|
|
+
|
|
|
val mapData = MutableStateFlow(mapOf<String, String>())
|
|
|
|
|
|
val imageState = MutableStateFlow(ImageState())
|
|
|
|
|
|
+ private var isEditingVertex = false // 正在编辑目标信息
|
|
|
+ private var editingPath = -1 // 正在编辑的路径,0:轮廓,1:长,2:宽
|
|
|
+ private var editingPoint = -1 // 正在编辑的顶点坐标的索引
|
|
|
+
|
|
|
private var isAlgRunning: Boolean = false
|
|
|
|
|
|
private var canvasSize = Size(1f, 1f)
|
|
@@ -107,7 +116,7 @@ class WorkViewModel : ViewModel() {
|
|
|
imageState.value = ImageState(zoom = zoom)
|
|
|
}
|
|
|
|
|
|
- fun getProcessedData() {
|
|
|
+ fun processData() {
|
|
|
processScope.launch {
|
|
|
val original = imageData.value
|
|
|
val name = original.name.substringBeforeLast(".")
|
|
@@ -130,7 +139,7 @@ class WorkViewModel : ViewModel() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- Log.i(TAG, "--- $scale, $topLeft")
|
|
|
+ Log.i(TAG, "scale: $scale, topLeft: $topLeft")
|
|
|
|
|
|
val store = ProcessedData(bitmap)
|
|
|
store.setMap(storedMap)
|
|
@@ -171,6 +180,7 @@ class WorkViewModel : ViewModel() {
|
|
|
final?.let {
|
|
|
processedData.value = it
|
|
|
updateInfoMap()
|
|
|
+ Log.i(TAG, "update processedData")
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -196,6 +206,71 @@ class WorkViewModel : ViewModel() {
|
|
|
mapData.value = map
|
|
|
}
|
|
|
|
|
|
+ fun transformPoints() {
|
|
|
+ val sizeValid = canvasSize.width * canvasSize.height > 1
|
|
|
+ val hasData = processedData.value.bitmap != null
|
|
|
+ Log.i(TAG, "size valid:$sizeValid, has data: $hasData")
|
|
|
+ if (sizeValid && hasData) {
|
|
|
+ Log.i(TAG, "image:$imageSize, canvas:$canvasSize")
|
|
|
+ convertContour()
|
|
|
+ convertWidth()
|
|
|
+ convertLength()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 给定屏幕上一个位置,查找目标身上处于这个位置的点在数组中的索引
|
|
|
+ * 本函数会依次遍历轮廓、长、宽各个数组,找到数组中处于该位置的点
|
|
|
+ * @param position 屏幕上点的坐标,这里是相对于画布左上角的位移
|
|
|
+ * @return Pair<Int, Int>,第一个 Int 是数组的标记,0:轮廓,1:长,2:宽;第二个 Int 表示点在数组中的索引。
|
|
|
+ * @return Pair(-1, -1),表示没有找到目标点
|
|
|
+ */
|
|
|
+ fun findVertexAt(position: Offset): Pair<Int, Int> {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 给定屏幕上一个位置,在数组中查找此坐标附近的点
|
|
|
+ * 由于画布经过了缩放、平移等操作,所以需要把数组中的坐标经过相同的变换映射到新的坐标
|
|
|
+ * @param list 画布上点的数组,这里指轮廓,长或宽的数组
|
|
|
+ * @return 点在画布上的位置,-1 表示查无此点
|
|
|
+ */
|
|
|
+ fun findVertex(list: List<Offset>): Int {
|
|
|
+ val center = canvasSize.center
|
|
|
+ val distance = 20f
|
|
|
+
|
|
|
+ for (i in list.indices) {
|
|
|
+ val p = list[i]
|
|
|
+ val pToCenter = p - center
|
|
|
+ val p1ToCenter = pToCenter * scale.value + offset.value
|
|
|
+ val p1 = p1ToCenter + center
|
|
|
+
|
|
|
+ val rect = Rect(position, distance)
|
|
|
+ if (rect.contains(p1)) {
|
|
|
+ return i
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -1
|
|
|
+ }
|
|
|
+
|
|
|
+ var pathIndex = -1
|
|
|
+ var pointIndex = -1
|
|
|
+
|
|
|
+ val paths: List<List<Offset>> = listOf(
|
|
|
+ contourPath.value,
|
|
|
+ lengthPath.value,
|
|
|
+ widthPath.value
|
|
|
+ )
|
|
|
+
|
|
|
+ for (i in paths.indices) {
|
|
|
+ pointIndex = findVertex(paths[i])
|
|
|
+ if (pointIndex >= 0) {
|
|
|
+ pathIndex = i
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Pair(pathIndex, pointIndex)
|
|
|
+ }
|
|
|
+
|
|
|
fun executeImageAction(action: ViewAction) {
|
|
|
when (action) {
|
|
|
ViewAction.Back -> {}
|
|
@@ -224,22 +299,66 @@ class WorkViewModel : ViewModel() {
|
|
|
|
|
|
fun onDrag(position: Offset) {
|
|
|
|
|
|
-// Log.i(TAG, "drag $position")
|
|
|
+ // Log.i(TAG, "drag $position")
|
|
|
|
|
|
+ // 开始拖动
|
|
|
if (dragStartPos == null) {
|
|
|
dragStartPos = position
|
|
|
+
|
|
|
+ if (imageState.value.editMode) {
|
|
|
+ val (pathIndex, pointIndex) = findVertexAt(position)
|
|
|
+ isEditingVertex = pathIndex >= 0 && pointIndex >= 0
|
|
|
+ if (isEditingVertex) {
|
|
|
+ editingPath = pathIndex
|
|
|
+ editingPoint = pointIndex
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Log.i(
|
|
|
+ TAG, "editing:$isEditingVertex, path: $editingPath, " +
|
|
|
+ "point:$editingPoint"
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
- offset.value = lastOffset + position - dragStartPos!!
|
|
|
+ if (isEditingVertex) {
|
|
|
+ // 拖动目标
|
|
|
+ val newVertex =
|
|
|
+ (position - offset.value - canvasSize.center) / scale
|
|
|
+ .value + canvasSize.center
|
|
|
+
|
|
|
+ if (editingPath == 0) {
|
|
|
+ val newPath = contourPath.value.toMutableList()
|
|
|
+ newPath[editingPoint] = newVertex
|
|
|
+ contourPath.value = newPath
|
|
|
+ } else if (editingPath == 1) {
|
|
|
+ val newPath = lengthPath.value.toMutableList()
|
|
|
+ newPath[editingPoint] = newVertex
|
|
|
+ lengthPath.value = newPath
|
|
|
+ } else if (editingPath == 2) {
|
|
|
+ val newPath = widthPath.value.toMutableList()
|
|
|
+ newPath[editingPoint] = newVertex
|
|
|
+ widthPath.value = newPath
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 拖动图像
|
|
|
+ offset.value = lastOffset + position - dragStartPos!!
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ // 结束拖动
|
|
|
fun onDragEnd() {
|
|
|
dragStartPos = null
|
|
|
dragEndPos = null
|
|
|
- lastOffset = offset.value
|
|
|
|
|
|
- backToVisibleArea()
|
|
|
- updateVisibleArea()
|
|
|
+ if (isEditingVertex) {
|
|
|
+ isEditingVertex = false
|
|
|
+ // 拖动目标结束
|
|
|
+ } else {
|
|
|
+ // 拖动图像结束
|
|
|
+ lastOffset = offset.value
|
|
|
+ backToVisibleArea()
|
|
|
+ updateVisibleArea()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private fun zoomIn() {
|
|
@@ -338,6 +457,40 @@ class WorkViewModel : ViewModel() {
|
|
|
|
|
|
visibleArea.value = Rect(Offset(px1, py1), Offset(px2, py2))
|
|
|
}
|
|
|
+
|
|
|
+ private fun convertContour() {
|
|
|
+ val list = mutableListOf<Offset>()
|
|
|
+ processedData.value.contour.forEach { p: PointF ->
|
|
|
+ list.add(imageToCanvas(Offset(p.x, p.y)))
|
|
|
+ }
|
|
|
+ contourPath.value = list
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun convertLength() {
|
|
|
+ val list = mutableListOf<Offset>()
|
|
|
+ processedData.value.lengthLine.forEach { p: PointF ->
|
|
|
+ list.add(imageToCanvas(Offset(p.x, p.y)))
|
|
|
+ }
|
|
|
+ lengthPath.value = list
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun convertWidth() {
|
|
|
+ val list = mutableListOf<Offset>()
|
|
|
+ processedData.value.widthLine.forEach { p: PointF ->
|
|
|
+ list.add(imageToCanvas(Offset(p.x, p.y)))
|
|
|
+ }
|
|
|
+ widthPath.value = list
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用 Fit 模式填充画布时,将图像上点的坐标转换为画布上点的坐标
|
|
|
+ */
|
|
|
+ private fun imageToCanvas(origin: Offset): Offset {
|
|
|
+ val zoom = zoomToFit(canvasSize, imageSize)
|
|
|
+ val offsetToImageCenter = origin - imageSize.center
|
|
|
+ val final = offsetToImageCenter * zoom + canvasSize.center
|
|
|
+ return final
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
data class ImageData(
|