package net.iximeow.raytrace import java.awt.image.BufferedImage object Objects { /* * 3d nah case class Point(x: Double, y: Double, z: Double) case class Plane(pitch: Double, roll: Double, altitude: Double) case class BoundedPlane(pitch: Double, roll: Double, center: Point, h: Point, w: Point) */ case class Point(x: Double, y: Double) { def +(other: Point): Point = Point(x + other.x, y + other.y) def -(other: Point): Point = Point(x - other.x, y - other.y) def /(scale: Double): Point = Point(x / scale, y / scale) def *(scale: Double): Point = Point(x * scale, y * scale) def magnitude: Double = distTo(Point.Zero) def distTo(other: Point) = Math.sqrt(sqDistTo(other)) def sqDistTo(other: Point) = (other.x - x) * (other.x - x) + (other.y - y) * (other.y - y) def dot(other: Point): Double = x * other.x + y * other.y } object Point { val Zero = Point(0, 0) } case class Line(m: Double, b: Double) object Line { def fromPoints(p1: Point, p2: Point): Line = { val m = (p2.y - p1.y) / (p2.x - p1.x) val b = p1.y - m*p1.x Line(m, b) } } /* * Segments are defined for t in [0, 1] */ case class Segment(x: Double, y: Double, initial: Point) { def at(t: Double): Point = Point(x * t, y * t) + initial def apply = at _ def length = Math.sqrt(x * x + y * y) def intersect(other: Segment): Double = { /* * P1 = ai + t * a * P2 = bi + u * b * P1 == P2, so * ai + t * a = bi + u * b * ... * ai.x + t * a.x = bi.x + u * b.x * ai.y + t * a.y = bi.y + u * b.y * t = (bi.x + u * b.x - ai.x) / a.x * ai.y + a.y * (bi.x + u * b.x - ai.x) / a.x = bi.y + u * b.y * ai.y + a.y * bi.x / a.x + u * b.x * a.y / a.x - ai.x * a.y / a.x = bi.y + u * b.y * ai.y - bi.y + (a.y / a.x) (bi.x - ai.x) = u * b.y - u * b.x * a.y / a.x * ai.y - bi.y + (a.y / a.x) (bi.x - ai.x) / (b.y - b.x * a.y / a.x) = u * * (a.x * (ai.y - bi.y) + a.y * (bi.x - ai.x)) / (b.y - b.x * a.y) */ val u = ( x * (initial.y - other.initial.y) + y * (other.initial.x - initial.x) ) / ( other.y * x - other.x * y ) u } def intersectChecked(other: Segment): Option[Point] = { val u = intersect(other) if (u >= 0 && u <= 1) { //println("Intersection is at u=" + u) Some(other.at(u)) } else { None } } def tFor(p: Point): Option[Double] = { (x, y) match { case (0, _) => Some((p.y - initial.y) / y) case (_, 0) => Some((p.x - initial.x) / x) case (_, _) => { val xT = (p.x - initial.x) / x val yT = (p.y - initial.y) / y if (Math.abs(xT - yT) < 0.000001) { Some(xT) } else { None } } } } def rotate(angle: Double, about: Point = Point(0, 0)): Segment = { val start = this.at(0) val end = this.at(1) val newStart = { val offset = start - about val m = offset.magnitude val newAngle = Math.atan2(offset.y, offset.x) + angle Point(Math.cos(newAngle) * m, Math.sin(newAngle) * m) + about } val newEnd = { val offset = end - about val m = offset.magnitude val newAngle = Math.atan2(offset.y, offset.x) + angle Point(Math.cos(newAngle) * m, Math.sin(newAngle) * m) + about } Segment.fromPoints(newStart, newEnd) } def renderTo(buf: BufferedImage, scale: Double = 1, xoff: Int = 0, yoff: Int = 0, color: Int = 0x808000): Unit = { try { for (i <- (0 to 100)) { val point = this.at(i / 100.0) buf.setRGB(Math.round((point.x * scale).toFloat) + xoff, Math.round((point.y * scale).toFloat) + yoff, color) } } catch { case (x: ArrayIndexOutOfBoundsException) => { /* well, we're not properly rendering a region so uh just ignore the failure i guess lol */ } } } def normal: Segment = { val normalMag = Math.sqrt(x * x + y * y) val finalNormMult = 1.5 / normalMag Ray(-y * finalNormMult, x * finalNormMult, (at(0) + at(1)) / 2).toSegment } } object Segment { def fromPoints(p1: Point, p2: Point): Segment = { val (x0, y0) = (p2.x - p1.x, p2.y - p1.y) Segment(x0, y0, p1) } def fromPointWithAngle(p: Point, angle: Double): Segment = { val (x0, y0) = (Math.cos(angle), Math.sin(angle)) Segment(x0, y0, p) } } case class Ray(x: Double, y: Double, initial: Point) { def toSegment: Segment = Segment(x, y, initial) def endingAt(p: Point): Ray = { val intersectAt = this.toSegment.tFor(p) intersectAt.map(t => Ray(x * t, y * t, initial) ).getOrElse(this) } def dot(other: Ray): Double = { x * other.x + y * other.y } def mag: Double = { Math.sqrt(x * x + y * y) } } }