package net.iximeow.raytrace import Objects._ import java.awt.image.BufferedImage import javax.imageio._ import java.io.File case class Scene(walls: Seq[Surface]) { val buffer = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB) def render(scale: Double = 1, xoff: Int = 0, yoff: Int = 0, color: Int = 0x808000, normals: Boolean = false): Unit = { for (wall <- walls) { wall.renderTo(buffer, scale, 400, 300, color = color) } for (wall <- walls) { wall.normal(0.5).toSegment.renderTo(buffer, scale, 400, 300, color = 0xc00000) } } def save(path: String = "render.png"): Unit = { ImageIO.write(buffer, "png", new File(path)) } def cast(r: Ray, steps: Int): Seq[Segment] = { (0 until steps).foldLeft(Seq.empty[Segment] -> r) { case (p: (Seq[Segment], Ray), i: Int) => { val (prevRay, nextRay) = castSingle(p._2) ((p._1 :+ prevRay.toSegment) -> nextRay): (Seq[Segment], Ray) }}._1 } def castSingle(r: Ray): (Ray, Ray) = { val asSeg = r.toSegment val intersections: Seq[(Surface, Point)] = walls.flatMap(w => { w.intersectChecked(asSeg) .map(x => (w, x)) }) .filter { case (w: Surface, x: Point) => asSeg.tFor(x).map(_ > 0.0000001).getOrElse(false) } /* def isBehind(start: Segment, wall: Surface): Boolean = { val normal = Ray(-wall.y, wall.x, Point(0, 0)) val rebased = Ray(start.x, start.y, Point(0, 0)) val cosAngle = normal.dot(rebased) / (normal.mag * rebased.mag) cosAngle > 0 } val continuedIntersections = intersections .filter(i => { val otherT = i._1.tFor(i._2) otherT.map(t => t >= 0 && t <= 1).getOrElse(true) }) val stoppedIntersections = intersections .filter(i => { val otherT = i._1.tFor(i._2) otherT.map(t => t >= 0 && t <= 1 && isBehind(asSeg, i._1)).getOrElse(false) }) */ def fnMin(x: (Surface, Point), y: (Surface, Point)) = if (asSeg.tFor(x._2).get < asSeg.tFor(y._2).get) x else y /* val firstStop: Option[(Surface, Point)] = stoppedIntersections.reduceOption(fnMin(_, _)) val firstReflect: Option[(Surface, Point)] = continuedIntersections.reduceOption(fnMin(_, _)) (firstStop, firstReflect) match { case (None, None) => (r, Ray(r.x, r.y, r.toSegment.at(1))) case (Some(stop), None) => (r.endingAt(stop._2), Ray(0, 0, r.initial)) case (None, Some(cont)) => cont._1.scatter(r, cont._2) //reflect(cont)// reflect case (Some(stop), Some(cont)) => { if (fnMin(stop, cont) == stop) { (r.endingAt(stop._2), Ray(0, 0, r.initial)) // stop } else { cont._1.scatter(r, cont._2) //reflect(cont) // reflect } } } */ val firstInteraction = intersections // .map(x => {println("maybe " + x._1); x}) .filter(i => i._1.tFor(i._2).map(t => t >= 0 && t <= 1).getOrElse(true)) .reduceOption(fnMin(_, _)) firstInteraction match { case None => (r, Ray(r.x, r.y, r.toSegment.at(1))) case Some(cont) => cont._1.scatter(r, cont._2) } } } object Scene { def generateMirror(r: Double, segments: Int, arcSize: Double, at: Point, rotated: Double): Seq[Segment] = { val sizePerSegment = arcSize / segments val points = (0 to segments) map { i => val angle = i * sizePerSegment + rotated at + Point(Math.cos(angle) * r, Math.sin(angle) * r) } points.sliding(2).map { case Seq(start, end) => Segment.fromPoints(start, end) }.toSeq } def generateParabola(a: Double, b: Double, w: Double, w_i: Double, segments: Int, at: Point, rotated: Double): Seq[Segment] = { val points = (-segments / 2 to segments / 2) map { i => val w_curr = (i / segments.toDouble) * w + w_i at + Point(a * w_curr, b * w_curr * w_curr) } points.sliding(2).map { case Seq(start, end) => Segment.fromPoints(start, end) }.toSeq } def rotate(walls: Seq[Segment], angle: Double) = walls.map(_.rotate(angle)) def rays(number: Int, spacing: Double, centerpoint: Point, direction: Point): Seq[Ray] = { (0 until number).map { i => val x = (i.toDouble - number.toDouble / 2) * spacing val y = 0 val dx = direction.x val dy = direction.y Ray(dx, dy, Point(x, y) + centerpoint) } } }