/* * A class to read an LC model file into a wire frame format. * * Adapted from the ThreeD.java JDK example. * Kevin Thomas, June 20, 1996. * Cray Research, Inc. */ import java.lang.String; import java.lang.Double; import java.awt.Graphics; import java.awt.Color; import java.awt.Event; import java.io.StreamTokenizer; import java.io.InputStream; import java.io.IOException; class FileFormatException extends Exception { public FileFormatException(String s) { super(s); } } /** The representation of a 3D model */ class lcModel { // Verticies. float vert[]; int tvert[]; int nvert, maxvert; // Edges. int con[]; int ncon, maxcon; // Colors of the edges. int colors[]; int edge_count[]; int ncolors, maxcolors; boolean transformed; Matrix3D mat; float xmin, xmax, ymin, ymax, zmin, zmax; lcModel () { mat = new Matrix3D (); mat.xrot(20); mat.yrot(30); } /** Create a 3D model by parsing an input stream */ lcModel (InputStream is) throws IOException, FileFormatException { this(); StreamTokenizer st = new StreamTokenizer(is); st.eolIsSignificant(true); scan: while (true) { switch (st.nextToken()) { default: break scan; case StreamTokenizer.TT_EOL: break; case StreamTokenizer.TT_WORD: if ("LCX".equals(st.sval)) { skipSegment(st); } else if ("materials".equals(st.sval)) { skipSegment(st); } else if ("blocks".equals(st.sval)) { readBlocks(st); } else if ("pulses".equals(st.sval)) { skipSegment(st); } else if ("calculations".equals(st.sval)) { skipSegment(st); } else if ("multiports".equals(st.sval)) { skipSegment(st); } else if ("sweeps".equals(st.sval)) { skipSegment(st); } else if ("planes".equals(st.sval)) { skipSegment(st); } else if ("model_parameters".equals(st.sval)) { skipSegment(st); } else if ("mesh_parameters".equals(st.sval)) { skipSegment(st); } else if ("plot_parameters".equals(st.sval)) { skipSegment(st); } else if ("seq_parameters".equals(st.sval)) { skipSegment(st); } else if ("far_parameters".equals(st.sval)) { skipSegment(st); } else if ("dialogs".equals(st.sval)) { skipSegment(st); } else if ("notes".equals(st.sval)) { skipSegment(st); } else { // Skip unknown line while (st.nextToken() != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF); } } } is.close(); if (st.ttype != StreamTokenizer.TT_EOF) throw new FileFormatException(st.toString()); } // Read the blocks segment of an LCX file. void readBlocks( StreamTokenizer st ) throws IOException { int reading = 0; int v = 0; double xmin = 0, ymin = 0, zmin = 0; double xmax = 0, ymax = 0, zmax = 0; double xrot = 0, yrot = 0, zrot = 0; int color = 1, visible = 1, shape = 1, axis = 1; scan: while (true) { switch (st.nextToken()) { default: break scan; // EOF case StreamTokenizer.TT_EOL: break; case StreamTokenizer.TT_WORD: if ("end".equals(st.sval) && (reading == 0)) { break scan; } else if ("min".equals(st.sval)) { xmin = readDouble(st); ymin = readDouble(st); zmin = readDouble(st); } else if ("max".equals(st.sval)) { xmax = readDouble(st); ymax = readDouble(st); zmax = readDouble(st); } else if ("rotation".equals(st.sval)) { xrot = readDouble(st); yrot = readDouble(st); zrot = readDouble(st); // System.out.println("Block rotate by: "+xrot+", "+yrot+", "+zrot); } else if ("color".equals(st.sval)) { if (st.nextToken() == StreamTokenizer.TT_NUMBER) color = (int) st.nval; } else if ("visible".equals(st.sval)) { if (st.nextToken() == StreamTokenizer.TT_NUMBER) visible = (int) st.nval; } else if ("shape".equals(st.sval)) { if (st.nextToken() == StreamTokenizer.TT_NUMBER) shape = (int) st.nval; } else if ("axis".equals(st.sval)) { if (st.nextToken() == StreamTokenizer.TT_NUMBER) axis = (int) st.nval; } else if ("name".equals(st.sval)) { reading = 1; shape = 1; } else if ("end".equals(st.sval)) { reading = 0; if (visible != 0) { if (shape == 1) { // Rectangular. float p[] = new float[24]; int i = 0; p[i++] = (float) xmin; p[i++] = (float) ymin; p[i++] = (float) zmin; p[i++] = (float) xmin; p[i++] = (float) ymin; p[i++] = (float) zmax; p[i++] = (float) xmin; p[i++] = (float) ymax; p[i++] = (float) zmin; p[i++] = (float) xmin; p[i++] = (float) ymax; p[i++] = (float) zmax; p[i++] = (float) xmax; p[i++] = (float) ymin; p[i++] = (float) zmin; p[i++] = (float) xmax; p[i++] = (float) ymin; p[i++] = (float) zmax; p[i++] = (float) xmax; p[i++] = (float) ymax; p[i++] = (float) zmin; p[i++] = (float) xmax; p[i++] = (float) ymax; p[i++] = (float) zmax; if ((xrot != 0) || (yrot != 0) || (zrot != 0)) { Matrix3D tmat = new Matrix3D (); tmat.unit(); float x1 = (float) - (xmin + xmax) / 2.0f; float y1 = (float) - (ymin + ymax) / 2.0f; float z1 = (float) - (zmin + zmax) / 2.0f; tmat.translate(x1,y1,z1); // translate to center tmat.xrot(xrot); tmat.yrot(yrot); tmat.zrot(zrot); x1 = -x1; y1 = -y1; z1 = -z1; tmat.translate(x1,y1,z1); // translate back float tp[] = new float[24]; tmat.transform(p,tp,8); for (i=0;i < 24;i++) p[i] = tp[i]; // System.out.println("Rotated by: "+xrot+", "+yrot+", "+zrot); } for (i=0;i < 24;i+=3) addVert(p[i],p[i+1],p[i+2]); addEdge(v+0,v+1,color); addEdge(v+0,v+2,color); addEdge(v+0,v+4,color); addEdge(v+1,v+3,color); addEdge(v+1,v+5,color); addEdge(v+2,v+3,color); addEdge(v+2,v+6,color); addEdge(v+3,v+7,color); addEdge(v+4,v+5,color); addEdge(v+4,v+6,color); addEdge(v+5,v+7,color); addEdge(v+6,v+7,color); v += 8; } else if (shape == 2) { // Cylindrical. float div_per_quad = 4; float xcenter = (float) (xmax + xmin) / 2; float ycenter = (float) (ymax + ymin) / 2; float zcenter = (float) (zmax + zmin) / 2; float xsize = (float) (xmax - xmin); float ysize = (float) (ymax - ymin); float zsize = (float) (zmax - zmin); if (axis == 0) { // Along X-axis. float radius = (ysize < zsize) ? ysize/2 : zsize/2; float inc = (float) 3.14159 / 2 / div_per_quad; float max = div_per_quad * 4 + 1; float theta = 0; int i = 0; for (i=0;i < max;i++) { float ydist = radius * (float) Math.cos(theta); float zdist = radius * (float) Math.sin(theta); float px1 = (float) xmin; float py1 = ycenter + ydist; float pz1 = zcenter + zdist; float px2 = (float) xmax; float py2 = ycenter + ydist; float pz2 = zcenter + zdist; addVert(px1,py1,pz1); addVert(px2,py2,pz2); v += 2; if (i != 0) { addEdge(v-1,v-3,color); // upper addEdge(v-2,v-4,color); // lower addEdge(v-1,v-2,color); // vertical } theta += inc; } } else if (axis == 1) { // Along Y-axis. float radius = (xsize < zsize) ? xsize/2 : zsize/2; float inc = (float) 3.14159 / 2 / div_per_quad; float max = div_per_quad * 4 + 1; float theta = 0; int i = 0; for (i=0;i < max;i++) { float xdist = radius * (float) Math.cos(theta); float zdist = radius * (float) Math.sin(theta); float px1 = xcenter + xdist; float py1 = (float) ymin; float pz1 = zcenter + zdist; float px2 = xcenter + xdist; float py2 = (float) ymax; float pz2 = zcenter + zdist; addVert(px1,py1,pz1); addVert(px2,py2,pz2); v += 2; if (i != 0) { addEdge(v-1,v-3,color); // upper addEdge(v-2,v-4,color); // lower addEdge(v-1,v-2,color); // vertical } theta += inc; } } else if (axis == 2) { // Along Z-axis. float radius = (xsize < ysize) ? xsize/2 : ysize/2; float inc = (float) 3.14159 / 2 / div_per_quad; float max = div_per_quad * 4 + 1; float theta = 0; int i = 0; for (i=0;i < max;i++) { float xdist = radius * (float) Math.cos(theta); float ydist = radius * (float) Math.sin(theta); float px1 = xcenter + xdist; float py1 = ycenter + ydist; float pz1 = (float) zmin; float px2 = xcenter + xdist; float py2 = ycenter + ydist; float pz2 = (float) zmax; addVert(px1,py1,pz1); addVert(px2,py2,pz2); v += 2; if (i != 0) { addEdge(v-1,v-3,color); // upper addEdge(v-2,v-4,color); // lower addEdge(v-1,v-2,color); // vertical } theta += inc; } } } } } while (st.nextToken() != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF); break; } } } // Gross hack to read a floating point value in scientific notation. double readDouble( StreamTokenizer st ) throws IOException { // read mantissa if (st.nextToken() != StreamTokenizer.TT_NUMBER) { st.pushBack(); // System.out.println("no mantissa: "+st.sval); return 0.0; } double mant = st.nval; // read E if (st.nextToken() != StreamTokenizer.TT_WORD) { st.pushBack(); // System.out.println("no exponent: "+mant); return mant; } if ("E".equals(st.sval) || ("e".equals(st.sval))) { // read exponent st.nextToken(); // get negative exponent or '+" sign if (st.ttype == '+') st.nextToken(); // read positive exponent if (st.ttype != StreamTokenizer.TT_NUMBER) { // System.out.println("bad exponent: "+st.sval+mant); st.pushBack(); return mant; } int expo = (int) st.nval; double v = mant * Math.pow(10,expo); // System.out.println("read value: "+v); return v; } else { // System.out.println("bad exponent char: "+st.sval+" "+mant); st.pushBack(); return mant; } } /* * Skip a segment of the LCX file. */ void skipSegment( StreamTokenizer st ) throws IOException { int reading = 0; scan: while (true) { switch (st.nextToken()) { case StreamTokenizer.TT_EOF: break scan; // End of file case StreamTokenizer.TT_EOL: break; case StreamTokenizer.TT_WORD: if ("end".equals(st.sval) && (reading == 0)) { break scan; // End of segment } else if ("name".equals(st.sval)) { reading = 1; } else if ("end".equals(st.sval)) { reading = 0; } while (st.nextToken() != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF); break; default: while (st.nextToken() != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF); } } } /** Add a vertex to this model */ int addVert(float x, float y, float z) { int i = nvert; if (i >= maxvert) if (vert == null) { maxvert = 100; vert = new float[maxvert * 3]; } else { maxvert *= 2; float nv[] = new float[maxvert * 3]; System.arraycopy(vert, 0, nv, 0, vert.length); vert = nv; } i *= 3; vert[i] = x; vert[i + 1] = y; vert[i + 2] = z; return nvert++; } /** Add a line from vertex p1 to vertex p2 */ void addEdge(int p1, int p2, int color) { int i = ncon; if (p1 >= nvert || p2 >= nvert) return; // Set the color for this edge. addColor(color); // If out of edge space, allocate more. if (i >= maxcon) { if (con == null) { maxcon = 100; con = new int[maxcon]; } else { maxcon *= 2; int nv[] = new int[maxcon]; System.arraycopy(con, 0, nv, 0, con.length); con = nv; } } // Sort the end points. if (p1 > p2) { int t = p1; p1 = p2; p2 = t; } // Save the edge. con[i] = (p1 << 16) | p2; ncon = i + 1; } /** Set a new color. */ void addColor(int color) { int i = ncolors; int current = ncolors - 1; // Increment edge count if color already in use. if ((ncolors > 0) && (color == colors[current])) { edge_count[current]++; return; } // Allocate more colors if out of space. if (i >= maxcolors) { if (colors == null) { maxcolors = 100; colors = new int[maxcolors]; edge_count = new int[maxcolors]; } else { maxcolors *= 2; int nv[] = new int[maxcolors]; System.arraycopy(colors, 0, nv, 0, colors.length); colors = nv; int ne[] = new int[maxcolors]; System.arraycopy(edge_count, 0, ne, 0, edge_count.length); edge_count = ne; } } // if (current >= 0) System.out.println("color count = "+edge_count[current]); // Save this new color. colors[i] = color; edge_count[i] = 1; ncolors = i + 1; } /** Transform all the points in this model */ void transform() { if (transformed || nvert <= 0) return; if (tvert == null || tvert.length < nvert * 3) tvert = new int[nvert*3]; mat.transform(vert, tvert, nvert); transformed = true; } static Color gr[]; /* A list of RGB (actually BGR) triples for each color index. */ static double rgb[] = { 1.00, 1.00, 1.00, /* white */ 0.88, 0.88, 1.00, 0.75, 0.75, 1.00, 0.62, 0.62, 1.00, 0.50, 0.50, 1.00, 0.38, 0.38, 1.00, 0.20, 0.20, 0.90, /* dark red */ 0.50, 0.00, 1.00, 0.80, 0.00, 1.00, 1.00, 0.00, 1.00, /* magenta */ 1.00, 0.00, 0.80, 1.00, 0.00, 0.50, 1.00, 0.00, 0.00, /* blue */ 1.00, 0.13, 0.00, 1.00, 0.25, 0.00, 1.00, 0.32, 0.00, 1.00, 0.38, 0.00, 1.00, 0.46, 0.00, 1.00, 1.00, 0.00, /* cyan */ 0.46, 1.00, 0.00, 0.38, 1.00, 0.00, 0.30, 1.00, 0.00, 0.25, 1.00, 0.00, 0.13, 1.00, 0.00, 0.00, 1.00, 0.00, /* green */ 0.00, 1.00, 0.13, 0.00, 1.00, 0.25, 0.00, 1.00, 0.30, 0.00, 1.00, 0.38, 0.00, 1.00, 0.46, 0.00, 1.00, 1.00, /* yellow */ 0.00, 0.46, 1.00, 0.00, 0.38, 1.00, 0.00, 0.25, 1.00, 0.00, 0.13, 1.00, 0.00, 0.00, 1.00 /* red */ }; /** Paint this model to a graphics context. It uses the matrix associated with this model to map from model space to screen space. The next version of the browser should have double buffering, which will make this *much* nicer */ void paint(Graphics g) { if (vert == null || nvert <= 0) return; transform(); if (gr == null) { gr = new Color[36]; for (int i = 0; i < 36; i++) { gr[i] = new Color((float)rgb[3*i+2],(float)rgb[3*i+1],(float)rgb[3*i]); } } int lg = -1; int lim = ncon; int c[] = con; int v[] = tvert; if (lim <= 0 || nvert <= 0) return; int cindex = -1; int ecount = 0; for (int i = 0; i < lim; i++) { int T = c[i]; int p1 = ((T >> 16) & 0xFFFF) * 3; int p2 = (T & 0xFFFF) * 3; if (ecount == 0) { // Set next edge color from the list. cindex++; ecount = edge_count[cindex]; int color = colors[cindex] - 1; if (color != lg) { lg = color; g.setColor(gr[color]); } } ecount--; g.drawLine(v[p1], v[p1 + 1], v[p2], v[p2 + 1]); } } /** Find the bounding box of this model */ void findBB() { if (nvert <= 0) return; float v[] = vert; float xmin = v[0], xmax = xmin; float ymin = v[1], ymax = ymin; float zmin = v[2], zmax = zmin; for (int i = nvert * 3; (i -= 3) > 0;) { float x = v[i]; if (x < xmin) xmin = x; if (x > xmax) xmax = x; float y = v[i + 1]; if (y < ymin) ymin = y; if (y > ymax) ymax = y; float z = v[i + 2]; if (z < zmin) zmin = z; if (z > zmax) zmax = z; } this.xmax = xmax; this.xmin = xmin; this.ymax = ymax; this.ymin = ymin; this.zmax = zmax; this.zmin = zmin; } }