diff --git a/docs/wiki/config/config_preview.yml b/docs/wiki/config/config_preview.yml index d0eacd1..322484c 100644 --- a/docs/wiki/config/config_preview.yml +++ b/docs/wiki/config/config_preview.yml @@ -39,11 +39,10 @@ sensors: # all cells are declared to output the gt # of baseline for each into the console - group: baseline - cells: - - [0, 0, 0] - - [1, 1, 0] - - [2, 2, 0] - - [3, 3, 0] + blocks: + - start: [0, 0] + end: [6, 6] + count: 0 groundtruths: - group: baseline diff --git a/src/main/scala/scim/components/Parser.scala b/src/main/scala/scim/components/Parser.scala index 4ec51fc..62c64c7 100644 --- a/src/main/scala/scim/components/Parser.scala +++ b/src/main/scala/scim/components/Parser.scala @@ -89,7 +89,9 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu private def parseSensor(sensor: Map[String, Any]): Unit = { val groupString = sensor("group").asInstanceOf[String] val parentString: Option[String] = if(sensor.contains("parent")) Some(sensor("parent").asInstanceOf[String]) else None - val cells: Map[Cell, SensorCount] = parseCells(sensor("cells").asInstanceOf[Vector[Any]]) + val cellsParsed: Map[Cell, SensorCount] = if(sensor.contains("cells")) parseCells(sensor("cells").asInstanceOf[Vector[Any]]) else Map() + val blocksParsed: Map[Cell, SensorCount] = if(sensor.contains("blocks")) parseBlocks(sensor("blocks").asInstanceOf[Vector[Any]]) else Map() + val cells = mergeMaps[Cell, SensorCount](blocksParsed, cellsParsed, (a: SensorCount, b: SensorCount) => a.add(b), SensorCount(0)) val group = Group(groupString, parentString) if(!groups.contains(groupString)) { groups(groupString) = group @@ -113,18 +115,41 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu } private def parseCells(cells: Vector[Any]): Map[Cell, SensorCount] = { - val parseCell: Any => (Cell, SensorCount) = (cell: Any) => cell match { + val parseCell: Any => (Cell, SensorCount) = (inCell: Any) => inCell match { case cell: Vector[_] => val cellVector = cell.asInstanceOf[Vector[Int]] (Cell(cellVector(0), cellVector(1)), SensorCount(cellVector(2))) case cell: Map[_, _] => val cellMap = cell.asInstanceOf[Map[String, Int]] (Cell(cellMap("x"), cellMap("y")), SensorCount(cellMap("count"))) - case _ => throw new Exception("Invalid Cell: " + cell.toString) + case _ => throw new Exception("Invalid Cell: " + inCell.toString) } // traversableToMap iterates over the vector while parseCell returns a key value // pair for each element which than gets added to the returned map traversableToMap[Cell, SensorCount](cells, parseCell) } + private def parseBlocks(blocks: Vector[Any]): Map[Cell, SensorCount] = { + val parseBlock: Any => Map[Cell, SensorCount] = (inBlock: Any) => inBlock match { + case block: Map[_, _] => { + val blockMap = block.asInstanceOf[Map[String, Any]] + val start = blockMap("start").asInstanceOf[Vector[Int]] + val end = blockMap("end").asInstanceOf[Vector[Int]] + val count = blockMap("count").asInstanceOf[Int] + val blockCells = Cell(start(0), start(1)).block(Cell(end(0), end(1))) + val countPerCell: Int = count / blockCells.length + var countRest: Int = count % blockCells.length + + val result: Vector[(Cell, SensorCount)] = blockCells.map(cell => { + val sensorCount = SensorCount(countPerCell + (if(countRest > 0) 1 else 0)) + if(countRest > 0) countRest -= 1 + cell -> sensorCount + }) + result.toMap + } + case _ => throw new Exception("Invalid Block: " + inBlock.toString) + } + mergeMaps[Cell, SensorCount](blocks.map(parseBlock), (a: SensorCount, b: SensorCount) => a.add(b), SensorCount(0)) + } + // don't add tickfacor here, this is done in parseTruth with config.time() private def parseTimeLineElement(timeLineElement: Any): (Int, Option[Int]) = { timeLineElement match { diff --git a/src/main/scala/scim/datastruct/Configuration.scala b/src/main/scala/scim/datastruct/Configuration.scala index dd4a279..ee4cc95 100644 --- a/src/main/scala/scim/datastruct/Configuration.scala +++ b/src/main/scala/scim/datastruct/Configuration.scala @@ -126,6 +126,18 @@ case class Cell(x: Int, y: Int) { def distance(otherCell: Cell): Double = { Math.sqrt(Math.pow(x - otherCell.x, 2) + Math.pow(y - otherCell.y, 2)) } + def block(otherCell: Cell): Vector[Cell] = { + val bottomLeft = Cell(math.min(x, otherCell.x), math.min(y, otherCell.y)) + val topRight = Cell(math.max(x, otherCell.x), math.max(y, otherCell.y)) + // add + 1 as topright is included in the block + val xCount = topRight.x - bottomLeft.x + 1 + val yCount = topRight.y - bottomLeft.y + 1 + Vector.tabulate(yCount)(yCoord => { + Vector.tabulate(xCount)(xCoord => { + Cell(xCoord + bottomLeft.x, yCoord + bottomLeft.y) + }) + }).flatten + } } case class Group(name: String, sensorParent: Option[String] = None){ // the sensorParent serves as indicator for the parser that a ground truth is not required diff --git a/src/main/scala/scim/lib/Library.scala b/src/main/scala/scim/lib/Library.scala index 6205425..c93d26c 100644 --- a/src/main/scala/scim/lib/Library.scala +++ b/src/main/scala/scim/lib/Library.scala @@ -152,6 +152,18 @@ package object mapUtil { } } + def mergeMaps[A, B](mapA: Map[A, B], mapB: Map[A, B], add: (B, B) => B, default: B): Map[A, B] = { + mapA ++ mapB.map(e => e._1 -> add(e._2, mapA.getOrElse(e._1, default))) + } + + def mergeMaps[A, B](vector: Vector[Map[A, B]], add: (B, B) => B, default: B): Map[A, B] = { + if(vector.length > 1) { + mergeMaps(vector.head, mergeMaps(vector.tail, add, default), add, default) + } else { + vector.head + } + } + def traversableToMap[A, B](traversable: Traversable[Any], getTuple: (Any) => (A, B) = (e: Any) => e.asInstanceOf[(A, B)]._1 -> e.asInstanceOf[(A, B)]._2): Map[A, B] = { // Traversable[Tuple[A, B]] can be converted via .map // .foldLeft used in favor of .map().toMap as the latter needs two traversals