Skip to content

Instantly share code, notes, and snippets.

@mkmik
Created July 17, 2011 23:23
Show Gist options
  • Save mkmik/1088231 to your computer and use it in GitHub Desktop.
Save mkmik/1088231 to your computer and use it in GitHub Desktop.
class CSquareSerializer extends CSquareHelpers {
case class RelativePoint(val x: Int, val y: Int)
implicit def tuple2relpoint(t: (Int, Int)) = RelativePoint(t._1, t._2)
sealed trait Component
case class CoarseComponent(val q: Int) extends Component
case class FineComponent(val q: Int, val x: Int, val y: Int) extends Component
case class TopComponent(val q: Int, val x: Int, val y: Int) extends Component
def serialize(square: CSquare) = {
val size = square.size
val disp = square.displacement
val lastGranularity = round(10 * size / pow(10, round(log(size) / log(10.0)))) / 10.0
val normalizedSize = if (lastGranularity == 0.5) size / 5 else size
// compute list of square component sizes: e.g. [10, 1, 0.1, 0.01, 0.001])
val sizes = from(-1).map(a => pow(10, -a)).takeWhile(_ >= normalizedSize)
val x = disp(0) / 10
val y = disp(1) / 10
val decX = x - x.floor
val decY = y - y.floor
// clear rounding issues
def clean(x: Double) = round(x * 100000) / 100000.0
// create a stream of digits
def digits(x: Double) = clean(x).toString.slice(2, 1 + sizes.size).map(_.toString.toInt).padTo(sizes.size - 1, 0)
def format(q: Component): String = q match {
case CoarseComponent(q) => "%s".format(q)
case FineComponent(q, x, y) => "%s%s%s".format(q, y, x)
case TopComponent(q, x, y) => "%s%d%02d".format(q, y, x)
}
// handle last quadrant when the final granularity is 0.5
def smartQuadrantTuple(a: ((Int, Int), Int)): Component = smartQuadrant(a._1, a._2)
def smartQuadrant(relPoint: RelativePoint, n: Int): Component = {
val q = quadrant(relPoint)
if ((lastGranularity == 0.5) && (n == sizes.size - 2))
CoarseComponent(q.q)
else
q
}
def quadrant(a: RelativePoint): FineComponent = a match {
case RelativePoint(x, y) if x < 5 && y < 5 => FineComponent(1, x, y)
case RelativePoint(x, y) if x >= 5 && y < 5 => FineComponent(2, x, y)
case RelativePoint(x, y) if x < 5 && y >= 5 => FineComponent(3, x, y)
case RelativePoint(x, y) if x >= 5 && y >= 5 => FineComponent(4, x, y)
}
def topQuadrant = {
val v = square.quadrant * DenseVector(1, 1)
(v(0), v(1)) match {
case (x, y) if x > 0 && y > 0 => 1
case (x, y) if x > 0 && y < 0 => 3
case (x, y) if x < 0 && y < 0 => 5
case (x, y) if x < 0 && y > 0 => 7
}
}
// the first component is computed differently, using specifi codes for top level quadrants
val topComponent = TopComponent(topQuadrant, floor(x).toInt, floor(y).toInt)
// the rest is more regular, we just have to stream the x,y digits
// and generate code components for the subquadrant
val rest = (digits(decX) zip digits(decY)).zipWithIndex.map(smartQuadrantTuple).toList
// and then glue the formatted components together
(topComponent :: rest).map(format).mkString(":")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment