LRay.java

Java source for LRay - Java Raytracer, 871 lines. Raw file

import java.awt.image.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Image;
import java.awt.Font;



//---- MAIN APPLET -----------------------------------------------------------//

public class LRay extends java.applet.Applet implements Runnable {

  Thread runThread;
  Image im = null;

  public void init() {
    System.out.println("LKS Raytrace applet");
  }

  public void start() {
    if(runThread == null) {
      runThread = new Thread(this);
      runThread.start();
    }
  }

  public void stop() {
    if(runThread != null) {
      runThread.stop();
      runThread = null;
    }
  }

  public void run() {

    int w = this.getBounds().width;
    int h = this.getBounds().height;

    im = createImage(w, h);

    Texture texOrange = new Texture(1.0, 0.7, 0.0, 0.0, 0.5);
    Texture texPurple = new Texture(0.7, 0.0, 1.0, 0.0, 0.5);
    Texture texWhite  = new Texture(1.0, 1.0, 1.0, 0.0, 0.7);
    Texture texBlack  = new Texture(0.0, 0.0, 0.0, 0.0, 0.0);
    Texture texMirror  = new Texture(0.45, 0.45, 0.45, 0.0, 0.7);
    Texture texChecks  = new Texture(new ChecksColourMap(0.6, 0.6, 0.6, 0.3, 0.3, 0.3), 0.7);
    Texture texSky  = new Texture(new ChecksColourMap(0.6, 0.8, 1.0, 0.2, 0.3, 0.5), 0.3);

    Scene s = new Scene();

    s.addLight(new Light(1.0, 5.0, 2.0, 1.0, 1.0, 1.0));
    s.addLight(new Light(-1.0, 7.0, 3.0, 0.7, 0.7, 0.7));

//    s.addEntity(new Sphere(0.0, 0.0, 0.0, 20000.0, texBlack));

    s.addEntity(new Plane( 0.0, 1.0,  0.0, -1.5, texChecks));

    s.addEntity(new Sphere( 1.0, -1.0, 6.0, 1.0, texOrange));
    s.addEntity(new Sphere( 1.0,  3.0, 6.0, 1.5, texPurple));
    s.addEntity(new Sphere(-1.0,  2.0, 5.0, 1.2, texSky));

    s.addEntity(new Sphere(-3.0,  1.0, 6.0, 1.5, texWhite ));
    s.addEntity(new Sphere(-3.0, -1.0, 6.0, 1.5, texPurple));

    s.addEntity(new Sphere( 4.0, 3.5, 4.0, 0.7, texWhite));

    s.addEntity(new Sphere( 3.0, -1.5,  2.0, 0.7, texWhite));
    s.addEntity(new Sphere( 4.0, -1.5,  5.0, 0.7, texPurple));
    s.addEntity(new Sphere( 5.0, -1.5,  8.0, 0.7, texWhite));
    s.addEntity(new Sphere( 6.0, -1.5, 11.0, 0.7, texPurple));
    s.addEntity(new Sphere( 7.0, -1.5, 14.0, 0.7, texWhite));
    s.addEntity(new Sphere( 8.0, -1.5, 17.0, 0.7, texPurple));

    Graphics img = im.getGraphics();
    Color c;

    for(int j=0; j<h; j+=32) {
      for(int i=0; i<w; i+=32) {
        img.setColor(s.getColor((i-(w*0.5))/(h*0.5), ((h*0.5)-j)/(h*0.5)));
        img.fillRect(i, j, 32, 32);
      }
      repaint();
    }

    repaint();

    for(int j=0; j<h; j+=4) {
      for(int i=0; i<w; i+=4) {
        img.setColor(s.getColor((i-(w*0.5))/(h*0.5), ((h*0.5)-j)/(h*0.5)));
        img.fillRect(i, j, 4, 4);
      }
      if((j&31) == 0) repaint();
    }

    repaint();

    for(int j=0; j<h; j+=4) {
      for(int i=0; i<w; i+=4) {

        img.setColor(s.getColor((i+2-(w*0.5))/(h*0.5), ((h*0.5)-j)/(h*0.5)));
        img.fillRect(i+2, j, 2, 2);

        img.setColor(s.getColor((i-(w*0.5))/(h*0.5), ((h*0.5)-j-2)/(h*0.5)));
        img.fillRect(i, j+2, 2, 2);

        img.setColor(s.getColor((i+2-(w*0.5))/(h*0.5), ((h*0.5)-j-2)/(h*0.5)));
        img.fillRect(i+2, j+2, 2, 2);
      }
      if((j&15) == 0) repaint();
    }

    repaint();


    for(int j=0; j<h; j+=2) {
      for(int i=0; i<w; i+=2) {

        img.setColor(s.getColor((i+1-(w*0.5))/(h*0.5), ((h*0.5)-j)/(h*0.5)));
        img.fillRect(i+1, j, 1, 1);

        img.setColor(s.getColor((i-(w*0.5))/(h*0.5), ((h*0.5)-j-1)/(h*0.5)));
        img.fillRect(i, j+1, 1, 1);

        img.setColor(s.getColor((i+1-(w*0.5))/(h*0.5), ((h*0.5)-j-1)/(h*0.5)));
        img.fillRect(i+1, j+1, 1, 1);
      }
      if((j&7) == 0) repaint();
    }

    repaint();

    double r, g, b;

    for(int j=0; j<h; j++) {
      for(int i=0; i<w; i++) {

        r = 0; g = 0; b = 0;

        for(int y=0; y<3; y++) for(int x=0; x<3; x++) {
          c = s.getColor((i+x*(1.0/3.0)-(w*0.5))/(h*0.5), ((h*0.5)-j-y*(1.0/3.0))/(h*0.5));
          r += c.getRed();
          g += c.getGreen();
          b += c.getBlue();
        }

        img.setColor(new Color((int)(r/9.0), (int)(g/9.0), (int)(b/9.0)));
        img.fillRect(i, j, 1, 1);
      }
      if((j&3) == 0) repaint();
    }

    repaint();

  }

  public void update(Graphics g) {
    paint(g);
  }

  public void paint(Graphics g) {
    if(im != null) g.drawImage(im, 0, 0, this);
  }

}




//---- Scene ---------------------------------------------------------------//

class Scene {

  Entity[] entity;
  Light[] light;

  int numEntities = 0;
  int numLights = 0;

  public Scene() {
    entity = new Entity[32];
    light = new Light[32];
  }

  public void addEntity(Entity e) {
    entity[numEntities++] = e;
  }

  public void addLight(Light l) {
    light[numLights++] = l;
  }

  public Color getColor(double x, double y) {
    return getPigmentRecursive(new Ray(0, 0, 0, x, y, 1.0), 5).getColor();
  }

  private Pigment getPigmentRecursive(Ray r, int level) {

    Pigment p = new Pigment(0, 0, 0);

    //- Find the first object that this that this ray intersects

    Intersection in = null;

    for(int i=0; i<numEntities; i++) {
      in = Intersection.getNearest(in, entity[i].intersectRay(r));
    }

    if(in == null) return p;

    //- Get the colour at this point

    Texture t = in.e.getTexture();

    if(t.refl == 0.0 || level == 0) {
      p = t.getPigment(in.getPoint());
    } else if(t.fuzzy == 0.0) {
      p = getPigmentRecursive(in.reflectionRay(), level-1);
      p.blend(t.getPigment(in.getPoint()), 1.0-t.refl);
    } else {
      int i, j, k;
      for(k=-1; k<=1; k++) for(j=-1; j<=1; j++) for(i=-1; i<=1; i++) 
        p.add( getPigmentRecursive(in.reflectionRay().distort(i*t.fuzzy, j*t.fuzzy, k*t.fuzzy), level-1) );
      p.scale(1.0/(27.0));
      p.blend(t.getPigment(in.getPoint()), 1.0-t.refl);
    }

    //- Perform lighting against each light

    Intersection sin;
    Ray sray;
    double diffuse = 0, phong = 0;

    for(int i=0; i<numLights; i++) {

      //  Make a ray to this light

      Vector u = light[i].getDirection(in.getPoint());
      sray = new Ray(in.getPoint(), u);

      //  See if this intersects any objects

      sin = null;
      for(int j=0; j<numEntities; j++) {
        sin = Intersection.getNearest(sin, entity[j].intersectRay(sray));
      }

      //  No objects intersected before reaching light source so calculate lighting value

      if(sin == null || sin.t >= 1) {
        Vector v = in.getReflection();
        u.setLength(1.0);
        v.setLength(1.0);
        double d = u.x*v.x + u.y*v.y + u.z*v.z;
        if(d > 0) {
          d *= d*d;
          d *= d*d;
          d *= d*d;
          phong += d;
        }
        v = in.getNormal();
        d = u.x*v.x + u.y*v.y + u.z*v.z;
        if(d > 0) {
          diffuse += d;
        }
      }

    }

    if(diffuse < 0.3) diffuse = 0.3;

    p.lightDiffuse(diffuse);
    p.lightPhong(phong);

    //- Done

    return p;
  }

}



//---- Vector --------------------------------------------------------------//

class Vector {

  public double x, y, z;

  public Vector(double x, double y, double z) {
    this.x = x; this.y = y; this.z = z;
  }

  public Vector(Vector v) {
    this.x = v.x; this.y = v.y; this.z = v.z;
  }

  public double getX() { return x; }
  public double getY() { return y; }
  public double getZ() { return z; }

  public void setX() { this.x = x; }
  public void setY() { this.y = y; }
  public void setZ() { this.z = z; }

  public double getLength() {
    return Math.sqrt(x*x+y*y+z*z);
  }

  public void setLength(double d) {
    double dd = d/Math.sqrt(x*x+y*y+z*z);
    x *= dd; y *= dd; z *= dd;
  }

  public double dotProduct(Vector v) {
    return x*v.x + y*v.y + z*v.z;
  }

  public static double dotProduct(Vector u, Vector v) {
    return u.x*v.x + u.y*v.y + u.z*v.z;
  }

  public Vector crossProduct(Vector v) {
    return new Vector(y*v.z-z*v.y, z*v.x-x*v.z, x*v.y-y*v.x);
  }

  public static Vector crossProduct(Vector u, Vector v) {
    return new Vector(u.y*v.z-u.z*v.y, u.z*v.x-u.x*v.z, u.x*v.y-u.y*v.x);
  }

}



//---- Ray -----------------------------------------------------------------//

class Ray {

  public double x, y, z;
  public double dx, dy, dz;

  public Ray(Vector origin, Vector direction) {
    x = origin.x; y = origin.y; z = origin.z;
    dx = direction.x; dy = direction.y; dz = direction.z;
  }

  public Ray(double x, double y, double z, double dx, double dy, double dz) {
    this.x = x; this.y = y; this.z = z;
    this.dx = dx; this.dy = dy; this.dz = dz;
  }

  public Ray fuzzy(double f) {
    double d = Math.sqrt(dx*dx+dy*dy+dz*dz);
    return new Ray(x, y, z, dx + d*(Math.random()*f*2-f), dy + d*(Math.random()*f*2-f), dz + d*(Math.random()*f*2-f));
  }

  public Ray distort(double fx, double fy, double fz) {
    double d = Math.sqrt(dx*dx+dy*dy+dz*dz);
    return new Ray(x, y, z, dx + d*fz, dy + d*fy, dz + d*fz);
  }

}



//---- Intersection --------------------------------------------------------//

class Intersection {

  public double x, y, z;
  public double nx, ny, nz;
  public double reflx, refly, reflz;

  public double t;

  public Entity e;

  public Intersection(Entity e, double t) {
    this.e = e; this.t = t;
  }

  public boolean isNearer(Intersection inter) {
    return inter == null || (t > 0 && t < inter.t);
  }

  public static Intersection getNearest(Intersection in1, Intersection in2) {
    if(in1 == null) return in2;
    if(in2 == null) return in1;
    if(in1.t < 0.000001 && in2.t < 0.000001) return null;
    if(in1.t < 0.000001) return in2;
    if(in2.t < 0.000001) return in1;
    if(in1.t < in2.t) return in1;
    return in2;
  }

  public void setPoint(double x, double y, double z) {
    this.x = x; this.y = y; this.z = z;
  }

  public Vector getPoint() {
    return new Vector(x, y, z);
  }

  public void setNormal(double x, double y, double z) {
    this.nx = x; this.ny = y; this.nz = z;
  }

  public Vector getNormal() {
    return new Vector(nx, ny, nz);
  }

  public void setReflection(double x, double y, double z) {
    this.reflx = x; this.refly = y; this.reflz = z;
  }

  public Vector getReflection() {
    return new Vector(reflx, refly, reflz);
  }

  public Ray reflectionRay() {
    return new Ray(x, y, z, reflx, refly, reflz);
  }

}



//---- Pigment -------------------------------------------------------------//

class Pigment {

  public static Pigment red = new Pigment(1.0, 0.0, 0.0);
  public static Pigment green = new Pigment(0.0, 1.0, 0.0);
  public static Pigment blue = new Pigment(0.0, 0.0, 1.0);
  public static Pigment cyan = new Pigment(0.0, 1.0, 1.0);
  public static Pigment yellow = new Pigment(1.0, 1.0, 0.0);
  public static Pigment magenta = new Pigment(0.0, 1.0, 1.0);

  public static Pigment orange = new Pigment(1.0, 0.7, 0.0);
  public static Pigment purple = new Pigment(0.7, 0.0, 1.0);

  public static Pigment black = new Pigment(0.0, 0.0, 0.0);
  public static Pigment grey  = new Pigment(0.5, 0.5, 0.5);
  public static Pigment white = new Pigment(1.0, 1.0, 1.0);

  public double r, g, b, f;

  public Pigment() {
    r = 0.0; g = 0.0; b = 0.0; f = 0.0;
  }

  public Pigment(Pigment p) {
    r = p.r; g = p.g; b = p.b; f = p.f;
  }

  public Pigment(double r, double g, double b) {
    this.r = r; this.g = g; this.b = b; this.f = 0.0;
  }

  public Pigment(double r, double g, double b, double f) {
    this.r = r; this.g = g; this.b = b; this.f = f;
  }

  public void blend(Pigment p, double f) {
    r = p.r*f + r*(1-f);
    g = p.g*f + g*(1-f);
    b = p.b*f + b*(1-f);
  }

  public void scale(double f) {
    r *= f; g *= f; b *= f;
  }

  public void add(Pigment p) {
    r += p.r; g += p.g; b += p.b;
  }

  public void lightDiffuse(double f) {
    r *= f; g *= f; b *= f;
  }

  public void lightPhong(double f) {
    r = r*(1-f)+f; g = g*(1-f)+f; b = b*(1-f)+f;
  }

  public Color getColor() {
    int cr = (int)(255.0*r), cg = (int)(255.0*g), cb = (int)(255.0*b);
    if(cr < 0) cr = 0; if(cg < 0) cg = 0; if(cb < 0) cb = 0;
    if(cr > 255) cr = 255; if(cg > 255) cg = 255; if(cb > 255) cb = 255;
    return new Color(cr, cg, cb);
  }

}



//---- ColourMap -----------------------------------------------------------//

abstract class ColourMap {
  abstract public Pigment getPigment(double x, double y, double z);
  abstract public Pigment getPigment(Vector v);
}



//---- ColourMap/SolidColourMap --------------------------------------------//

abstract class SolidColourMap extends ColourMap {

  Pigment p;

  public SolidColourMap(Pigment p) {
    this.p = p;
  }

  public Pigment getPigment(double x, double y, double z) {
    return new Pigment(p);
  }

  public Pigment getPigment(Vector v) {
    return new Pigment(p);
  }
}



//---- ColourMap/ChecksColourMap -------------------------------------------//

class ChecksColourMap extends ColourMap {

  Pigment p1, p2;

  public ChecksColourMap(double r1, double g1, double b1, double r2, double g2, double b2) {
    p1 = new Pigment(r1, g1, b1); p2 = new Pigment(r2, g2, b2);
  }

  public ChecksColourMap(Pigment p1, Pigment p2) {
    this.p1 = p1; this.p2 = p2;
  }

  public Pigment getPigment(double x, double y, double z) {
    int i = (int)x, j = (int)y, k = (int)z;
    if(x<0) i--;
    if(y<0) j--;
    if(z<0) k--;
    return new Pigment( ((i^j^k)&1) == 1 ? p2 : p1 );
  }

  public Pigment getPigment(Vector v) {
    return getPigment(v.x, v.y, v.z);
  }

}



//---- ColourMap/SkyColourMap ----------------------------------------------//

//  Doesn't work properly yet!

class SkyColourMap extends ColourMap {

  double[][][] d;

  Pigment p1, p2;

  public SkyColourMap(double r1, double g1, double b1, double r2, double g2, double b2) {
    p1 = new Pigment(r1, g1, b1); p2 = new Pigment(r2, g2, b2);
    createNoise();
  }

  public SkyColourMap(Pigment p1, Pigment p2) {
    this.p1 = p1; this.p2 = p2;
    createNoise();
  }

  private void createNoise() {
    d = new double[8][8][8];
    for(int k=0; k<8; k++) for(int j=0; j<8; j++) for(int i=0; i<8; i++) d[k][j][i] = Math.random();
  }

  public Pigment getPigment(double x, double y, double z) {
    int i = (int)(x*5), j = (int)(y*5), k = (int)(z*5);
    if(x<0) i--;
    if(y<0) j--;
    if(z<0) k--;
    i &= 7; j &= 7; k &= 7;
    int ii = (i+1)&7, jj = (j+1)&7, kk = (k+1)&7;

    double dx = x*5-i, dy = y*5-j, dz = z*5-k;

    double dd = d[k ][j ][i ]*(1.0-dx)*(1.0-dy)*(1.0-dz) +
                d[k ][j ][ii]*(    dx)*(1.0-dy)*(1.0-dz) +
                d[k ][jj][i ]*(1.0-dx)*(    dy)*(1.0-dz) +
                d[k ][jj][ii]*(    dx)*(    dy)*(1.0-dz) +
                d[kk][j ][i ]*(1.0-dx)*(1.0-dy)*(    dz) +
                d[kk][j ][ii]*(    dx)*(1.0-dy)*(    dz) +
                d[kk][jj][i ]*(1.0-dx)*(    dy)*(    dz) +
                d[kk][jj][ii]*(    dx)*(    dy)*(    dz);

    dd = dx;

    return new Pigment(p1.r*dd+p2.r*(1-dd), p1.g*dd+p2.g*(1-dd), p1.b*dd+p2.b*(1-dd));
  }

  public Pigment getPigment(Vector v) {
    return getPigment(v.x, v.y, v.z);
  }

}



//---- BumpMap -------------------------------------------------------------//

class BumpMap {

  public BumpMap() {
  }

  public Vector getBump(Vector v, Vector n) {
    return n;
  }

}



//---- SurfaceMap ----------------------------------------------------------//

class SurfaceMap {

  public SurfaceMap() {
  }

}



//---- Texture -------------------------------------------------------------//

class Texture {

  public ColourMap colour = null;
  public double colr=0.0, colg=0.0, colb=0.0, colf=0.0;
  public Pigment pigment;

  public SurfaceMap surface = null;
  public double refl=0.0;
  public double fuzzy=0.0;

  public BumpMap bump = null;

  public Texture() { }

  public Texture(ColourMap c) {
    colour = c;
  }

  public Texture(ColourMap c, double refl) {
    colour = c; this.refl = refl;
  }

  public Texture(ColourMap c, SurfaceMap s) {
    colour = c; surface = s;
  }

  public Texture(ColourMap c, SurfaceMap s, BumpMap b) {
    colour = c; surface = s; bump = b;
  }

  public Texture(double r, double g, double b) {
    colr = r; colg = g; colb = b;
    pigment = new Pigment(r, g, b);
  }

  public Texture(double r, double g, double b, double f) {
    colr = r; colg = g; colb = b; colf = f;
    pigment = new Pigment(r, g, b, f);
  }

  public Texture(double r, double g, double b, double f, double refl) {
    colr = r; colg = g; colb = b; colf = f; this.refl=refl;
    pigment = new Pigment(r, g, b, f);
  }

  public Pigment getPigment(Vector v) {
    if(colour == null)
      return new Pigment(colr, colg, colb);
    else
      return colour.getPigment(v);
  }

}



//---- Light ---------------------------------------------------------------//

class Light {

  double x, y, z;
  double r, g, b;

  public Light(Vector v, double r, double g, double b) {
    x = v.x; y = v.y; z = v.z; this.r = r; this.g = g; this.b = b;
  }

  public Light(double x, double y, double z, double r, double g, double b) {
    this.x = x; this.y = y; this.z = z; this.r = r; this.g = g; this.b = b;
  }

  public Vector getDirection(double vx, double vy, double vz) {
    Vector vv = new Vector(x-vx, y-vy, z-vz);
    return vv;
  }

  public Vector getDirection(Vector v) {
    Vector vv = new Vector(x-v.x, y-v.y, z-v.z);
    return vv;
  }

}



//---- Entity --------------------------------------------------------------//

abstract class Entity {

  private Texture t;

  public void setTexture(Texture t) {
    this.t = t;
  }

  public Texture getTexture() {
    return this.t;
  }

  abstract public Intersection intersectRay(Ray r);
}



//---- Entity/Sphere -------------------------------------------------------//

class Sphere extends Entity {

  double x, y, z;
  double r;

  public Sphere(Vector v, double r) {
    x = v.x; y = v.y; z = v.z; this.r = r;
  }

  public Sphere(double x, double y, double z, double r) {
    this.x = x; this.y = y; this.z = z; this.r = r;
  }

  public Sphere(double x, double y, double z, double r, Texture t) {
    this.x = x; this.y = y; this.z = z; this.r = r; super.setTexture(t);
  }

  public Intersection intersectRay(Ray ray) {

    double a, b, c, d, t1, t2;
    Intersection in = null;

    a = ray.dx*ray.dx + ray.dy*ray.dy + ray.dz*ray.dz;
    b = 2*(ray.dx*(ray.x-x) + ray.dy*(ray.y-y) + ray.dz*(ray.z-z));
    c = (ray.x-x)*(ray.x-x) + (ray.y-y)*(ray.y-y) + (ray.z-z)*(ray.z-z) - r*r;
    d = b*b-4*a*c;

    if(d > 0) {
      d = Math.sqrt(d);

      t2 = (-b + d) / (2*a);
      t1 = (-b - d) / (2*a);

      in = Intersection.getNearest(new Intersection((Entity)this, t1), new Intersection((Entity)this, t2));

      if(in != null) {

        in.x = ray.x + in.t*ray.dx;
        in.y = ray.y + in.t*ray.dy;
        in.z = ray.z + in.t*ray.dz;

        a = 1.0/r;
        in.nx = a*(in.x - x);
        in.ny = a*(in.y - y);
        in.nz = a*(in.z - z);

        a = - (ray.dx*in.nx + ray.dy*in.ny + ray.dz*in.nz);
        in.reflx = ray.dx + 2*in.nx*a;
        in.refly = ray.dy + 2*in.ny*a;
        in.reflz = ray.dz + 2*in.nz*a;

      }
    }

    return in;
  }

}



//---- Entity/Plane --------------------------------------------------------//

class Plane extends Entity {

  double x, y, z;
  double nx, ny, nz;

  public Plane(Vector n, Vector v, Texture t) {
    x = v.x; y = v.y; z = v.z; nx = n.x; ny = n.y; nz = n.z; super.setTexture(t);
    normalise();
  }

  public Plane(Vector n, double d, Texture t) {
    x = d*n.x; y = d*n.y; z = d*n.z; nx = n.x; ny = n.y; nz = n.z; super.setTexture(t);
    normalise();
  }

  public Plane(double nx, double ny, double nz, double d, Texture t) {
    x = d*nx; y = d*ny; z = d*nz; this.nx = nx; this.ny = ny; this.nz = nz; super.setTexture(t);
    normalise();
  }

  private void normalise() {
    double n = 1.0/Math.sqrt(nx*nx+ny*ny+nz*nz);
    nx *= n; ny *= n; nz *= n;
  }

  public Intersection intersectRay(Ray ray) {
    Intersection in = null;

    double t1, t2, t, a;

    t1 = ray.dx*nx + ray.dy*ny + ray.dz*nz;
    t2 = (ray.x-x)*nx + (ray.y-y)*ny + (ray.z-z)*nz;

    t = -t2/t1;

    if(t > 0.000001) {
      in = new Intersection(this, t);
      in.x = ray.x + t*ray.dx;
      in.y = ray.y + t*ray.dy;
      in.z = ray.z + t*ray.dz;

      in.setNormal(nx, ny, nz);

      a = - (ray.dx*in.nx + ray.dy*in.ny + ray.dz*in.nz);
      in.reflx = ray.dx + 2*in.nx*a;
      in.refly = ray.dy + 2*in.ny*a;
      in.reflz = ray.dz + 2*in.nz*a;
    }

    return in;
  }

}



//---- END OF CODE ---------------------------------------------------------//