object Vec { val X_AXIS = Vec(1,0) val Y_AXIS = Vec(0,1) } case class Vec(x: Double, y: Double) { def dotProduct(v: Vec) = x*v.x+y*v.y def crossProduct(v: Vec) = x*v.y-y*v.x; def length = math.sqrt(x*x+y*y) def rotate(theta: Double) = { val sin = math.sin(theta); val cos = math.cos(theta); Vec(x*cos-y*sin, x*sin+y*cos); } def rotate90 = Vec(-y, x) def +(v: Vec) = Vec(x+v.x, y+v.y) def -(v: Vec) = Vec(x-v.x, y-v.y) def *(d: Double) = Vec(x*d, y*d) def /(d: Double) = Vec(x/d, y/d) def unary_- = Vec(-x, -y) override def toString = f"Vec($x%.3f, $y%.3f)" } object Matrix { def rotationMatrix(theta: Double) = { val v = Vec.X_AXIS.rotate(theta) Matrix(v, v.rotate90) } def apply(a: Double, b: Double, c: Double, d: Double): Matrix = Matrix(Vec(a,c), Vec(b,d)) } case class Matrix(c1: Vec, c2: Vec) { def *(v: Vec) = c1*v.x + c2*v.y def *(d: Double) = Matrix(c1*d, c2*d) def determinant = c1.crossProduct(c2) def inverse = Matrix( c2.y, -c2.x, -c1.y, c1.x) * (1/determinant) override def toString = f"Matrix(${c1.x}%.3f, ${c2.x}%.3f\n" + f" ${c1.y}%.3f, ${c2.y}%.3f)" } object CoordinateSystem { def apply(origin: Vec, rotation: Double, scaleX: Double, scaleY: Double): CoordinateSystem = CoordinateSystem(origin, Matrix(Vec.X_AXIS.rotate(rotation)*scaleX, Vec.Y_AXIS.rotate(rotation)*scaleY)) def apply(origin: Vec, xAxis: Vec, yAxis: Vec): CoordinateSystem = CoordinateSystem(origin, Matrix(xAxis, yAxis)) } case class CoordinateSystem(origin: Vec, matrix: Matrix) { def xAxis = matrix.c1 def yAxis = matrix.c2 val inverseMatrix = matrix.inverse def translate(v: Vec) = matrix*v + origin def translateBack(v: Vec) = inverseMatrix*(v-origin) def move(v: Vec) = CoordinateSystem(origin+v, matrix) def scale(scaleX: Double, scaleY: Double) = CoordinateSystem(origin, xAxis*scaleX, yAxis*scaleY) def rotate(angle: Double): CoordinateSystem = rotate(origin, angle) def rotate(center: Vec, angle: Double): CoordinateSystem = CoordinateSystem((origin-center).rotate(angle)+center, xAxis.rotate(angle), yAxis.rotate(angle)) } val cs = CoordinateSystem(Vec(500,100), Vec(1,1), Vec(-1,1)) val v = Vec(2,3) val v2 = cs.translate(v) println(v2) println(cs.translateBack(v2)) val cs2 = CoordinateSystem(Vec(200,300), math.Pi/4, 2, 2) println(cs2.translate(v)) println(cs2.scale(10,10).translate(v))