CSGparser.java
package com.gwief.jTrace;
/*
CSGparser.java by Damian Newport
Parses the given URL filling the given scene3D with the described model
*/
import java.io.*;
import java.net.*;
import java.util.*;
public class CSGparser {
Scene3D scene;
double width;
double height;
public String error = "Unknown Error!";
StreamTokenizer st;
Vector Names = new Vector();
Vector Lights = new Vector();
URL page;
public CSGparser(Scene3D Scene, double Width, double Height){
scene = Scene;
width = Width;
height = Height;
}
public final boolean parse(String filename){
/* Camera */
// Camera cam = new Camera(new Vector3D(0,0,-10), new Vector3D(0,0,10), new Vector3D(-1,0,0), width, height, 40);
Camera cam = new Camera(new Vector3D(0,0,-10), 0,0,0, width, height, 40);
scene.setcamera(cam);
if(filename.compareTo("none") == 0){
/* Demo Render */
Shape3D planea = new Shape3D("planea", 5);
planea.transform.addRotateX(-90);
planea.material.setcolor(1,0,0);
scene.addshape(planea);
Light lighta = new Light(new Vector3D(0,0,-10), new Color3D(1,1,1), 20);
scene.addlight(lighta);
scene.setCSGstart(planea);
return true;
}
if (! getfile(filename)){
return false;
}
if (! parser()){
return false;
}
return true;
}
boolean getfile(String address){
try {
page = new URL(address);
getData(page);
} catch (MalformedURLException e){
error = "Bad URL: " + address;
return false;
}
return true;
}
boolean getData(URL url){
URLConnection conn = null;
InputStreamReader in;
BufferedReader data;
try {
conn = this.page.openConnection();
conn.connect();
in = new InputStreamReader(conn.getInputStream());
data = new BufferedReader(in);
/* setup tokeniser */
st = new StreamTokenizer(data);
st.slashSlashComments(true);
st.slashStarComments(true);
st.lowerCaseMode(true);
st.quoteChar(034);
} catch(IOException e) {
error = "Error! " + e.toString();
return false;
}
return true;
}
int nexttoken (){
try {
return st.nextToken();
}catch (IOException e){
error = "Error! " + e.toString();
return st.TT_EOF;
}
}
boolean parser (){
final String SHAPE = "shape";
final String CSGNODE = "node";
final String CSGSTART = "startnode";
final String LIGHT = "light";
final String SCENE = "scene";
boolean done = false;
int c;
while (! done) {
c = nexttoken();
switch (c) {
case StreamTokenizer.TT_EOF:
return true;
case StreamTokenizer.TT_WORD:
if (st.sval.compareTo(SHAPE) == 0){
if (! shape()){
return false;
}
}else if (st.sval.compareTo(CSGNODE) == 0){
if (! csgnode()){
return false;
}
}else if (st.sval.compareTo(CSGSTART) == 0){
if (! csgstart()){
return false;
}
}else if (st.sval.compareTo(LIGHT) == 0){
if (! light()){
return false;
}
}else if (st.sval.compareTo(SCENE) == 0){
if (! doscene()){
return false;
}
}
break;
default:
error = "Unexpected " + st.toString();
return false;
}
}
return false;
}
boolean shape (){
int c;
boolean built = false;
final String BOX = "box";
final String SPHERE = "sphere";
final String CYLINDER = "cylinder";
final String CONE = "cone";
final String HYPERBOLOID = "hyperboloid";
int expect = 'N';
int Shape = 0; // default shape = sphere
String Shapename = "";
Shape3D thisShape = new Shape3D("temp");
while (true){
c = nexttoken();
switch (c) {
case StreamTokenizer.TT_EOF:
error = "Unexpected End Of File";
return false;
case StreamTokenizer.TT_WORD:
if (expect == 'S'){
if (st.sval.compareTo(SPHERE) == 0){
Shape = 0;
}else if (st.sval.compareTo(BOX) == 0){
Shape = 1;
}else if (st.sval.compareTo(CYLINDER) == 0){
Shape = 2;
}else if (st.sval.compareTo(CONE) == 0){
Shape = 3;
}else if (st.sval.compareTo(HYPERBOLOID) == 0){
Shape = 4;
}else {
error = "Unknown shape '" + st.sval + "' on line " + st.lineno();
return false;
}
expect = ')';
break;
}else if (expect == 'N'){
Shapename = st.sval;
expect = '(';
}else{
error = "Unexpected " + st.toString();
return false;
}
break;
default:
if (c == expect){
switch (expect){
case '(':
expect = 'S';
break;
case ')':
expect = '{';
if (! CheckNames(Shapename)){
thisShape = addshape(Shapename,Shape);
built = true;
}else{
error = "Error! Name '" + Shapename + "' already taken, at line " + st.lineno();
return false;
}
break;
case '{':
if (built == true){
if (! shapeop(thisShape)){
return false;
}
return true;
}else{
error = "Unexpected " + st.toString();
return false;
}
default:
error = "Unexpected " + st.toString();
return false;
}
}
break;
}
}
}
/* returns true if the name is found */
private final boolean CheckNames(String a){
Iterator it = Names.iterator();
Node temp;
while(it.hasNext()){
temp = (Node)it.next();
if (a.compareTo(temp.name) == 0){
return true;
}
}
return false;
}
/* returns the CSGnode associated with the name */
private final CSGnode getName(String a){
Iterator it = Names.iterator();
Node temp;
while(it.hasNext()){
temp = (Node)it.next();
if (a.compareTo(temp.name) == 0){
return temp.getCSGnode();
}
}
return null;
}
private final Shape3D addshape(String name, int type){
Shape3D temp = new Shape3D(name, type);
scene.addshape(temp);
Node nod = new Node(temp, name);
Names.add(nod);
return temp;
}
private final CSGnode addnode(String name, CSGnode nodea, int op, CSGnode nodeb){
CSGnode temp = new CSGnode(nodea, op, nodeb);
Names.add(new Node(temp, name));
return temp;
}
private final boolean shapeop(Shape3D shape){
final String TRANSLATE = "translate";
final String ROTATEX = "rotatex";
final String ROTATEY = "rotatey";
final String ROTATEZ = "rotatez";
final String ROTATE = "rotate";
final String SCALE = "scale";
final String COLOR = "color";
final String N = "shine";
final String KS = "specular";
final String KD = "diffuse";
final String TRANSPARENCY = "transparency";
final String REFLECTION = "reflection";
final String DENSITY = "density";
boolean tra = false; // have we translated
boolean rot = false; // have we rotated
boolean sca = false; // have we scaled
final char COMMAND = 'C';
final char NUMBER = 'N';
char expect = COMMAND;
String op = "";
boolean triad = false;
int count = 0;
double num1=0;
double num2=0;
double num3=0;
int c;
while (true) {
c = nexttoken();
switch (c) {
case StreamTokenizer.TT_EOF:
error = "Unexpected End Of File";
return false;
case StreamTokenizer.TT_WORD:
if(COMMAND == expect){
if (st.sval.compareTo(TRANSLATE) == 0){
op = TRANSLATE;
triad = true;
}else if (st.sval.compareTo(ROTATEX) == 0){
op = ROTATEX;
triad = false;
}else if (st.sval.compareTo(ROTATEY) == 0){
op = ROTATEY;
triad = false;
}else if (st.sval.compareTo(ROTATEZ) == 0){
op = ROTATEZ;
triad = false;
}else if (st.sval.compareTo(ROTATE) == 0){
op = ROTATE;
triad = true;
}else if (st.sval.compareTo(SCALE) == 0){
op = SCALE;
triad = true;
}else if (st.sval.compareTo(COLOR) == 0){
op = COLOR;
triad = true;
}else if (st.sval.compareTo(N) == 0){
op = N;
triad = false;
}else if (st.sval.compareTo(KS) == 0){
op = KS;
triad = false;
}else if (st.sval.compareTo(KD) == 0){
op = KD;
triad = false;
}else if (st.sval.compareTo(TRANSPARENCY) == 0){
op = TRANSPARENCY;
triad = false;
}else if (st.sval.compareTo(REFLECTION) == 0){
op = REFLECTION;
triad = false;
}else if (st.sval.compareTo(DENSITY) == 0){
op = DENSITY;
triad = false;
}else{
error = "Unexpected " + st.toString();
return false;
}
expect = '(';
break;
}else{
error = "Unexpected " + st.toString();
return false;
}
case StreamTokenizer.TT_NUMBER:
if (expect == NUMBER){
if (triad == false){
num1 = st.nval;
expect = ')';
}else{
if(count == 0){
num1 = st.nval;
count++;
expect = ',';
}else if(count == 1){
num2 = st.nval;
count++;
expect = ',';
}else if(count == 2){
num3 = st.nval;
count++;
expect = ')';
}else{
error = "Unexpected " + st.toString();
return false;
}
}
break;
}else{
error = "Unexpected " + st.toString();
return false;
}
default:
if (c == expect){
switch (expect){
case '(':
expect = NUMBER;
break;
case ')':
expect = ';';
break;
case ',':
expect = NUMBER;
break;
case ';':
if (op.compareTo(TRANSLATE) == 0){
if(Math.abs(num1) > rayTrace.MAX){
error = "x translation outside valid range on line " + st.lineno();
return false;
}
if(Math.abs(num2) > rayTrace.MAX){
error = "y translation outside valid range on line " + st.lineno();
return false;
}
if(Math.abs(num3) > rayTrace.MAX){
error = "z translation outside valid range on line " + st.lineno();
return false;
}
shape.transform.addTranslate(num1,num2,num3);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(ROTATEX) == 0){
shape.transform.addRotateX(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(ROTATEY) == 0){
shape.transform.addRotateY(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(ROTATEZ) == 0){
shape.transform.addRotateZ(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(ROTATE) == 0){
shape.transform.addRotate(num1,num2,num3);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(SCALE) == 0){
if ((num1 < rayTrace.TINY) || (num2 < rayTrace.TINY) || (num3 < rayTrace.TINY)){
error = "Unable to scale by ~ 0, on line " + st.lineno();
return false;
}
shape.transform.addScale(num1,num2,num3);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(COLOR) == 0){
if((num1 > 1) || (num1 < 0)){
error = "red outside valid range on line " + st.lineno();
return false;
}
if((num2 > 1) || (num2 < 0)){
error = "green valid range on line " + st.lineno();
return false;
}
if((num3 > 1) || (num3 < 0)){
error = "blue valid range on line " + st.lineno();
return false;
}
shape.material.setcolor(num1,num2,num3);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(N) == 0){
shape.material.setn(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(KS) == 0){
if((num1 > 1) || (num1 < 0)){
error = "Ks outside valid range on line " + st.lineno();
return false;
}
shape.material.setKs(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(KD) == 0){
if((num1 > 1) || (num1 < 0)){
error = "Kd outside valid range on line " + st.lineno();
return false;
}
shape.material.setKd(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(REFLECTION) == 0){
if((num1 > 1) || (num1 < 0)){
error = "reflection outside valid range on line " + st.lineno();
return false;
}
shape.material.setreflect(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(TRANSPARENCY) == 0){
if((num1> 1) || (num1 < 0)){
error = "transparency outside valid range on line " + st.lineno();
return false;
}
shape.material.settransparency(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(DENSITY) == 0){
if((num1 > 1) || (num1 < 0)){
error = "density outside valid range on line " + st.lineno();
return false;
}
shape.material.setdensity(num1);
count = 0;
expect = COMMAND;
break;
}else{
error = "Unexpected " + st.toString();
return false;
}
default:
error = "Unexpected " + st.toString();
return false;
}
break;
}else if (c == '}'){
if (expect == COMMAND){
return true;
}else{
error = "Unexpected " + st.toString();
return false;
}
}else{
error = "Unexpected " + st.toString();
return false;
}
}
}
}
private final boolean csgnode(){
final String ADD = "add";
final String SUBTRACT = "subtract";
final String INTERSECT = "intersect";
final String BAH = "difference";
final char NODE = 'N';
final char NAME = 'Q';
final char OP = 'O';
char expect = NAME;
String name = "";
int op = -1;
boolean built = false;
boolean donenode1 = false;
CSGnode node1;
CSGnode node2 = node1 = new CSGnode();
CSGnode thisnode = new CSGnode();
int c;
while (true) {
c = nexttoken();
switch (c) {
case StreamTokenizer.TT_EOF:
error = "Unexpected End Of File";
return false;
case StreamTokenizer.TT_WORD:
if (expect == NODE){
if (CheckNames(st.sval)){
if (! donenode1){
node1 = getName(st.sval);
donenode1 = true;
expect = ',';
break;
}else{
node2 = getName(st.sval);
expect = ')';
break;
}
}else{
error = "Unrecognised CSGnode or Shape '" + st.sval + "' on line " + st.lineno();
return false;
}
}else if (expect == NAME){
if (! CheckNames(st.sval)){
name = st.sval;
expect = '(';
break;
}else{
error = "Error one line " + st.lineno() + " Name '" + st.sval + "' already taken";
return false;
}
}else if (expect == OP){
if (st.sval.compareTo(ADD) == 0){
op = 0;
expect = ',';
break;
}else if (st.sval.compareTo(INTERSECT) == 0){
op = 1;
expect = ',';
break;
}else if (st.sval.compareTo(BAH) == 0){
op = 2;
expect = ',';
break;
}else if (st.sval.compareTo(SUBTRACT) == 0){
op = 3;
expect = ',';
break;
}else{
error = "Unexpected " + st.toString();
return false;
}
}else{
error = "Unexpected " + st.toString();
return false;
}
default:
if (c == expect){
switch (expect){
case '(':
expect = NODE;
break;
case ')':
thisnode = addnode(name, node1, op, node2);
built = true;
expect = '{';
break;
case '{':
if (built == true){
if (! nodeop(thisnode)){
return false;
}
return true;
}else{
error = "Unexpected " + st.toString();
return false;
}
case ',':
if (op == -1){
expect = OP;
break;
}else{
expect = NODE;
break;
}
default:
error = "Unexpected " + st.toString();
return false;
}
}else{
error = "Unexpected " + st.toString();
return false;
}
}
}
}
private final boolean nodeop(CSGnode node){
final String TRANSLATE = "translate";
final String ROTATEX = "rotatex";
final String ROTATEY = "rotatey";
final String ROTATEZ = "rotatez";
final String ROTATE = "rotate";
final String SCALE = "scale";
boolean tra = false; // have we translated
boolean rot = false; // have we rotated
boolean sca = false; // have we scaled
final char COMMAND = 'C';
final char NUMBER = 'N';
char expect = COMMAND;
String op = "";
boolean triad = false;
int count = 0;
double num1=0;
double num2=0;
double num3=0;
int c;
while (true) {
c = nexttoken();
switch (c) {
case StreamTokenizer.TT_EOF:
error = "Unexpected End Of File";
return false;
case StreamTokenizer.TT_WORD:
if(COMMAND == expect){
if (st.sval.compareTo(TRANSLATE) == 0){
op = TRANSLATE;
triad = true;
}else if (st.sval.compareTo(ROTATEX) == 0){
op = ROTATEX;
triad = false;
}else if (st.sval.compareTo(ROTATEY) == 0){
op = ROTATEY;
triad = false;
}else if (st.sval.compareTo(ROTATEZ) == 0){
op = ROTATEZ;
triad = false;
}else if (st.sval.compareTo(ROTATE) == 0){
op = ROTATE;
triad = true;
}else if (st.sval.compareTo(SCALE) == 0){
op = SCALE;
triad = true;
}else{
error = "Unexpected " + st.toString();
return false;
}
expect = '(';
break;
}else{
error = "Unexpected " + st.toString();
return false;
}
case StreamTokenizer.TT_NUMBER:
if (expect == NUMBER){
if (triad == false){
num1 = st.nval;
expect = ')';
}else{
if(count == 0){
num1 = st.nval;
count++;
expect = ',';
}else if(count == 1){
num2 = st.nval;
count++;
expect = ',';
}else if(count == 2){
num3 = st.nval;
count++;
expect = ')';
}else{
error = "Unexpected " + st.toString();
return false;
}
}
break;
}else{
error = "Unexpected " + st.toString();
return false;
}
default:
if (c == expect){
switch (expect){
case '(':
expect = NUMBER;
break;
case ')':
expect = ';';
break;
case ',':
expect = NUMBER;
break;
case ';':
if (op.compareTo(TRANSLATE) == 0){
if(Math.abs(num1) > rayTrace.MAX){
error = "x translation outside valid range on line " + st.lineno();
return false;
}
if(Math.abs(num2) > rayTrace.MAX){
error = "y translation outside valid range on line " + st.lineno();
return false;
}
if(Math.abs(num3) > rayTrace.MAX){
error = "z translation outside valid range on line " + st.lineno();
return false;
}
node.transform.addTranslate(num1,num2,num3);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(ROTATEX) == 0){
node.transform.addRotateX(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(ROTATEY) == 0){
node.transform.addRotateY(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(ROTATEZ) == 0){
node.transform.addRotateZ(num1);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(ROTATE) == 0){
node.transform.addRotate(num1,num2,num3);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(SCALE) == 0){
if ((num1 < rayTrace.TINY) || (num2 < rayTrace.TINY) || (num3 < rayTrace.TINY)){
error = "Unable to scale by ~ 0, on line " + st.lineno();
return false;
}
node.transform.addScale(num1,num2,num3);
count = 0;
expect = COMMAND;
break;
}else{
error = "Unexpected " + st.toString();
return false;
}
default:
error = "Unexpected " + st.toString();
return false;
}
break;
}else if (c == '}'){
if (expect == COMMAND){
return true;
}else{
error = "Unexpected " + st.toString();
return false;
}
}else{
error = "Unexpected " + st.toString();
return false;
}
}
}
}
private final boolean csgstart(){
int c = nexttoken();
if (CheckNames(st.sval)){
String temp = st.sval;
c = nexttoken();
if (c == ';'){
scene.setCSGstart(getName(temp));
return true;
}else{
error = "Error! Unexpected " + st.toString() + " Expecting ';'";
return false;
}
}
error = "Unrecognised CSGnode or Shape '" + st.sval + "' on line " + st.lineno();
return false;
}
private final boolean light(){
Light light = new Light();
String Name = "";
final String COLOR = "color";
final String POSITION = "position";
final String INTENSITY = "intensity";
final char COMMAND = 'C';
final char NUMBER = 'N';
final char NAME = 'L';
char expect = NAME;
String op = "";
boolean triad = false;
boolean built = false;
int count = 0;
double num1=0;
double num2=0;
double num3=0;
int c;
while (true) {
c = nexttoken();
switch (c) {
case StreamTokenizer.TT_EOF:
error = "Unexpected End Of File";
return false;
case StreamTokenizer.TT_WORD:
if(NAME == expect){
Name = st.sval;
expect = '{';
break;
}else if(COMMAND == expect){
if (st.sval.compareTo(COLOR) == 0){
op = COLOR;
triad = true;
expect = '(';
}else if (st.sval.compareTo(POSITION) == 0){
op = POSITION;
triad = true;
expect = '(';
}else if (st.sval.compareTo(INTENSITY) == 0){
op = INTENSITY;
triad = false;
expect = '(';
}else{
error = "Unexpected " + st.toString();
return false;
}
break;
}else{
error = "Unexpected " + st.toString();
return false;
}
case StreamTokenizer.TT_NUMBER:
if (expect == NUMBER){
if (triad == false){
num1 = st.nval;
expect = ')';
}else{
if(count == 0){
num1 = st.nval;
count++;
expect = ',';
}else if(count == 1){
num2 = st.nval;
count++;
expect = ',';
}else if(count == 2){
num3 = st.nval;
count++;
expect = ')';
}else{
error = "Unexpected " + st.toString();
return false;
}
}
break;
}else{
error = "Unexpected " + st.toString();
return false;
}
default:
if (c == expect){
switch (expect){
case '{':
light = addlight(Name);
built = true;
expect = COMMAND;
break;
case '(':
expect = NUMBER;
break;
case ')':
expect = ';';
break;
case ',':
expect = NUMBER;
break;
case ';':
if (op.compareTo(COLOR) == 0){
if((num1 > 1) || (num1 < 0)){
error = "red outside valid range on line " + st.lineno();
return false;
}
if((num2 > 1) || (num2 < 0)){
error = "green outside valid range on line " + st.lineno();
return false;
}
if((num3 > 1) || (num3 < 0)){
error = "blue outside valid range on line " + st.lineno();
return false;
}
light.setcolor(num1,num2,num2);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(POSITION) == 0){
if(Math.abs(num1) > rayTrace.MAX){
error = "x position outside valid range on line " + st.lineno();
return false;
}
if(Math.abs(num2) > rayTrace.MAX){
error = "y position outside valid range on line " + st.lineno();
return false;
}
if(Math.abs(num3) > rayTrace.MAX){
error = "z position outside valid range on line " + st.lineno();
return false;
}
light.setposition(num1,num2,num3);
count = 0;
expect = COMMAND;
break;
}else if (op.compareTo(INTENSITY) == 0){
if (num1 < 0){
error = "Intensity cannot be negative on line " + st.lineno();
return false;
}
light.setintensity(num1);
count = 0;
expect = COMMAND;
break;
}else{
error = "Unexpected " + st.toString();
return false;
}
default:
error = "Unexpected " + st.toString();
return false;
}
break;
}else if (c == '}'){
if (expect == COMMAND){
return true;
}else{
error = "Unexpected " + st.toString();
return false;
}
}else{
error = "Unexpected " + st.toString();
return false;
}
}
}
}
/* returns true if the name is found */
private final boolean CheckLights(String a){
Iterator it = Lights.iterator();
Node temp;
while(it.hasNext()){
temp = (Node)it.next();
if (a.compareTo(temp.name) == 0){
return true;
}
}
return false;
}
/* returns the Light associated with the name */
private final Light getLight(String a){
Iterator it = Lights.iterator();
Node temp;
while(it.hasNext()){
temp = (Node)it.next();
if (a.compareTo(temp.name) == 0){
return temp.getLight();
}
}
return null;
}
private final Light addlight(String name){
Light temp = new Light();
scene.addlight(temp);
Node nod = new Node(temp, name);
Lights.add(nod);
return temp;
}
private final boolean doscene(){
return true;
}
}