Created
July 17, 2011 23:23
-
-
Save mkmik/1088231 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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