// draw3D3.java  java draw3D3  root-file-name    
//                             .draw3d may be read and/or written
//                             .stl may be written
//               opens four view windows X-Y  X-Z  Z-Y and ortho
//               opens 3D window and Menu window
//               select color, shape, placement
//               no painters algorithm, yet

import java.*;
import java.awt.*;      // basic drawing
import java.awt.event.*;
import java.util.*;     // get LinkedList
import java.io.*;       // for file read and write
import java.text.*;     // for token

public class draw3D3
{
  final int width  = 400; // window, make square for good negate
  final int height = 400;
  final int Mwidth = 200; // menu
  final int mh     = 20;
  final int mo     = 110;
  final int m2     = 70;
  final int debug  = 1;

  String rootfilename = new String(" ");
  boolean have_filename = false;
  
  int xm, ym, bm; // mouse position (inverted y)
  int x0, y0, z0, x1, y1, z1; // "rubber" coordinates
  int x2, y2, z2, x3, y3, z3, tx, ty, tz; // "extra points"
  
  boolean posX = true;
  boolean posY = true;
  boolean posZ = true;
  boolean solid = true;
  boolean selected = false;
  boolean gridOn = true;
  int grid = 50;        // grid spacing
  int bo = 510;         // for color Menu
  int ba = 45;          // for color Menu
  Color drawColor = new Color(255, 0, 0); // Color of object being drawn
  int objSelect = 0;    // can be sequences
                        // -1 object container, possible internal drawList
                        // 0=cube face 1, 1x=cube third dimension
                        // 2=sphere center and edge, 3x=sphere center third dimension
                        // 4=surface rectangle, 5x=third dimension plane
                        // 6=surface triangle P1,P2, 7x= third dimension P1,P2
                        // 8x=P3, 9x=P3 third dimension

  LinkedList drawList = new LinkedList();  // list of objects, initially empty
  objCube objC;         // temp cube object
  objSphere objS;       // temp sphere object
  objRect objR;         // temp rectangle surface
  objTri objT;          // temp triangle surface
  int instanceCount = 0; // give each object a unique instance
  
  int xp;         // last mouse location during drag
  int yp;
  boolean trackingXY = false; // tracking XY mouse motion
  boolean trackingXZ = false; // tracking XZ mouse motion
  boolean trackingZY = false; // tracking ZY mouse motion
  boolean tempXY = false;     // hold rubber for third dimension
  boolean tempXZ = false;     // hold rubber for third dimension
  boolean tempZY = false;     // hold rubber for third dimension
  
  Frame    fXY, fXZ, fZY, f3D, fMenu;
  Graphics gXY, gXZ, gZY, g3D, gMenu;
  
  draw3D3(String[] args)
  {
    System.out.println("draw3D3 starting");
    if(args.length>=1)
    {
      rootfilename = new String(args[0]);
      have_filename = true;
    }
    objC = new objCube();         // temp cube object
    objS = new objSphere();       // temp sphere object
    objR = new objRect();         // temp rectangle surface
    objT = new objTri();          // temp triangle surface
    // each thread opens its own window
    new MyThread("X-Y (front view)", Mwidth,       height).start();
    new MyThread("X-Z (top view)",   Mwidth,            0).start();
    new MyThread("Z-Y (side view)",  width+Mwidth, height).start();
    new MyThread("3D",               width+Mwidth,      0).start();
    new MyThread("Menu",             0,                 0).start();
    System.out.println("draw3D3 constructor ending");
  } // end draw3D3 constructor
  
  class MyThread extends Thread
  {
    String id;
    Graphics g;
    Frame f;
    
    MyThread(String who, int xLoc, int yLoc)
    {
      id = who;
      // initiate a window for each thread
      f = new Frame();
      f.setTitle("draw3D3 "+who);
      f.setLocation(xLoc, yLoc);
      if(id=="Menu") f.setSize(Mwidth, 2*height);
      else           f.setSize(width,  height);
      f.setBackground(Color.white);
      f.setForeground(Color.black);
      f.setVisible(true);
      g = f.getGraphics(); // every window has its Frame, Graphics
      f.addWindowListener(new WindowAdapter() // and Listeners
      {
        public void windowClosing(WindowEvent e)
        {
          System.exit(0);
        }
      });
      f.addMouseListener (new mousePressHandler());
      f.addMouseListener (new mouseReleaseHandler());
      f.addMouseMotionListener (new mouseMotionHandler());
    } // end MyThread constructor
    
    public void run()
    {
      try
      {
        if(debug>=1)System.out.println(id+" running");
        if     (id=="X-Y (front view)") {fXY=f; gXY=g;}
        else if(id=="X-Z (top view)") {fXZ=f; gXZ=g;}
        else if(id=="Z-Y (side view)") {fZY=f; gZY=g;}
        else if(id=="3D")  {f3D=f; g3D=g;}
        else if(id=="Menu"){fMenu=f; gMenu=g; paint(g);}
        sleep(1);
      }
      catch(InterruptedException e)
      {
      }
      if(debug>=1)System.out.println("run exiting");
    } // end MyThread run
  } // end class MyThread

  public void paint(Graphics g)
  {
    // call methods to do actual drawing
    // System.out.println("paint running");
    drawXY(fXY, gXY);
    drawXZ(fXZ, gXZ);
    drawZY(fZY, gZY);
    draw3d(f3D, g3D);
    drawMenu(fMenu, gMenu);
  } // end paint
  
  public void drawXY(Frame f, Graphics g)
  {
    objList objL;
    // System.out.println("in drawXY");
    g.clearRect(0, 0, width, height);
    g.setColor(Color.black);
    if(gridOn) for(int i=grid; i<width; i=i+grid)
                 for(int j=grid; j<height; j=j+grid)
                   g.drawOval(i, j, 1, 1);
    if(trackingXY || tempXY) rubberRect(g, x0, y0, x1, y1);
    // draw figures per list
    int k = drawList.size();
    int ix[] = new int[k];
    int zz[] = new int[k];
    for(int i=0; i<k; i++)
    {
      objL = (objList)(drawList.get(i));
      ix[i] = i;
      if(posZ)zz[i] = height-objL.bz1; // farthest away first
      else    zz[i] = objL.bz2;
      //System.out.println("XY i="+i+", "+objL.getName());
    }
    sort2up(k,ix,zz);
    for(int i=0; i<k; i++)
    {
      objL = (objList)(drawList.get(ix[i]));
      objL.drawXY(g);
    }
    g.setColor(Color.black);
    if(posX)g.drawString("X", 25, height-5);
    if(!posX)g.drawString("-X", 25, height-5);
    g.drawLine(10, height-10, 22, height-10);
    g.drawLine(22, height-10, 20, height-8);
    g.drawLine(22, height-10, 20, height-12);
    if(posY)g.drawString("Y",  5, height-25);
    if(!posY)g.drawString("-Y",  5, height-25);
    g.drawLine(10, height-10, 10, height-22);
    g.drawLine(10, height-22,  8, height-20);
    g.drawLine(10, height-22, 12, height-20);
  } // end drawXY
  
  public void drawXZ(Frame f, Graphics g)
  {
    objList objL;

    // System.out.println("in drawXZ");    
    g.clearRect(0, 0, width, height);
    g.setColor(Color.black);
    if(gridOn) for(int i=grid; i<width; i=i+grid)
                 for(int j=grid; j<height; j=j+grid)
                   g.drawOval(i, j, 1, 1);
    if(trackingXZ || tempXZ) rubberRect(g, x0, z0, x1, z1);
    // draw figures per list
    int k = drawList.size();
    int ix[] = new int[k];
    int yy[] = new int[k];
    for(int i=0; i<k; i++)
    {
      objL = (objList)(drawList.get(i));
      ix[i] = i;
      if(posY)yy[i] = height-objL.by1; // farthest away first
      else    yy[i] = objL.by2;
      //System.out.println("XZ i="+i+", "+objL.getName());
    }
    sort2up(k,ix,yy);
    for(int i=0; i<k; i++)
    {
      objL = (objList)(drawList.get(ix[i]));
      objL.drawXZ(g);
    }
    g.setColor(Color.black);
    if(posX)g.drawString("X", 25, height-5);
    if(!posX)g.drawString("-X", 25, height-5);
    g.drawLine(10, height-10, 22, height-10);
    g.drawLine(22, height-10, 20, height-8);
    g.drawLine(22, height-10, 20, height-12);
    if(posZ)g.drawString("Z",  5, height-25);
    if(!posZ)g.drawString("-Z",  5, height-25);
       g.drawLine(10, height-10, 10, height-22);
    g.drawLine(10, height-22,  8, height-20);
    g.drawLine(10, height-22, 12, height-20);
  } // end drawXZ
  
  public void drawZY(Frame f, Graphics g)
  {
    objList objL;

    // System.out.println("in drawZY");    
    g.clearRect(0, 0, width, height);
    g.setColor(Color.black);
    if(gridOn) for(int i=grid; i<width; i=i+grid)
                 for(int j=grid; j<height; j=j+grid)
                   g.drawOval(i, j, 1, 1);
    if(trackingZY || tempZY) rubberRect(g, z0, y0, z1, y1);
    // draw figures per list
    int k = drawList.size();
    int ix[] = new int[k];
    int xx[] = new int[k];
    for(int i=0; i<k; i++)
    {
      objL = (objList)(drawList.get(i));
      ix[i] = i;
      if(posX)xx[i] = objL.bx2; // farthest away first
      else    xx[i] = height-objL.bx1;
      //System.out.println("ZY i="+i+", "+objL.getName());
    }
    sort2up(k,ix,xx);
    for(int i=0; i<k; i++)
    {
      objL = (objList)(drawList.get(ix[i]));
      objL.drawZY(g);
    }
    g.setColor(Color.black);
    if(posZ)g.drawString("Z", 25, height-5);
    if(!posZ)g.drawString("-Z", 25, height-5);
    g.drawLine(10, height-10, 22, height-10);
    g.drawLine(22, height-10, 20, height-8);
    g.drawLine(22, height-10, 20, height-12);
    if(posY)g.drawString("Y",  5, height-25);
    if(!posY)g.drawString("-Y",  5, height-25);
       g.drawLine(10, height-10, 10, height-22);
    g.drawLine(10, height-22,  8, height-20);
    g.drawLine(10, height-22, 12, height-20);
  } // end drawZY

  public void draw3d(Frame f, Graphics g)
  {
    objList objL;

    // System.out.println("in draw3d");
    g.clearRect(0, 0, width, height);
    // 3D render figures per list
    // draw figures per list
    int k = drawList.size();
    int ix[] = new int[k];
    int xy[] = new int[k];
    for(int i=0; i<k; i++)
    {
      objL = (objList)(drawList.get(i));
      ix[i] = i;
               // farthest away first no flipping, break z ties
      xy[i] = 1000*(height-objL.bz1) + objL.bx1 - objL.by2;
      //System.out.println("ZY i="+i+", "+objL.getName());
    }
    sort2up(k,ix,xy);
    for(int i=0; i<k; i++)
    {
      objL = (objList)(drawList.get(ix[i]));
      objL.draw3d(g);
    }
    g.setColor(Color.black);
    g.drawString("X", 25, height-5);
    g.drawLine(10, height-10, 22, height-10);
    g.drawLine(22, height-10, 20, height-8);
    g.drawLine(22, height-10, 20, height-12);
    g.drawString("Y",  5, height-25);
    g.drawLine(10, height-10, 10, height-22);
    g.drawLine(10, height-22,  8, height-20);
    g.drawLine(10, height-22, 12, height-20);
    g.drawString("Z", 20, height-20);
    g.drawLine(10, height-10, 17, height-17);
    g.drawLine(17, height-17, 17, height-15);
    g.drawLine(17, height-17, 15, height-17);
  } // end draw3d
  
  void rubberRect(Graphics g, int x0, int y0, int x1 , int y1)
  { 
    // can apply to all figures 
    // draw a rubber rectangle, mouse down, tracks mouse
    int x,y,x2,y2,x3,y3; // local coordinates
    x2=x0;
    x3=x1;
    if(x1<x0) {x2=x1; x3=x0;};
    y2=y0;
    y3=y1;
    if(y1<y0) {y2=y1; y3=y0;};
    g.setColor(Color.black);
    for(x=x2; x<x3-3; x=x+8) // Java does not seem to have a
    {                        // dashed or stippled rectangle or line
      g.drawLine(x, y0, x+4, y0);
      g.drawLine(x, y1, x+4, y1);
    }
    for(y=y2; y<y3-3; y=y+8)
    {
      g.drawLine(x0, y, x0, y+4);
      g.drawLine(x1, y, x1, y+4);
    }
  } // end rubberRect

  public void drawMenu(Frame f, Graphics g)
  {
    // System.out.println("in drawMenu");    
    // clear not needed

    g.setColor(Color.yellow);
    g.fillRect(10,35,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("read "+rootfilename+".draw3d", 20, 50);

    g.setColor(Color.yellow);
    g.fillRect(10,60,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("write "+rootfilename+".draw3d", 20, 75);

    g.setColor(Color.yellow);
    g.fillRect(10,85,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("write "+rootfilename+".stl", 20, 100);

    g.setColor(Color.yellow);
    g.fillRect(10,5+mo,Mwidth/2-15,mh);
    g.setColor(Color.black);
    g.drawString("clear all", 20, 20+mo);

    g.setColor(Color.yellow);
    g.fillRect(Mwidth/2,5+mo,Mwidth/2-10,mh);
    g.setColor(Color.black);
    g.drawString("redraw", Mwidth/2+15, 20+mo);

    if(selected)g.setColor(Color.red);
    if(!selected)g.setColor(Color.yellow);
    g.fillRect(10,30+mo,Mwidth/2-15,mh);
    g.setColor(Color.black);
    g.drawString("select", 20, 45+mo);

    g.setColor(Color.yellow);
    g.fillRect(Mwidth/2,30+mo,Mwidth/2-10,mh);
    g.setColor(Color.black);
    if(solid)g.drawString("solid", Mwidth/2+15, 45+mo);
    if(!solid)g.drawString("wireframe", Mwidth/2+15, 45+mo);
    
    g.setColor(Color.yellow);
    g.fillRect(10,95+m2,Mwidth/2-15,mh);
    g.setColor(Color.black);
    if(posX)  g.drawString("positive X", 20, 110+m2);
    if(!posX) g.drawString("negative X", 20, 110+m2);

    g.setColor(Color.yellow);
    g.fillRect(Mwidth/2,95+m2,Mwidth/2-10,mh);
    g.setColor(Color.black);
    if(posZ)  g.drawString("positive Z", Mwidth/2+15, 110+m2);
    if(!posZ) g.drawString("negative Z", Mwidth/2+15, 110+m2);

    g.setColor(Color.yellow);
    g.fillRect(10,120+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    if(posY)  g.drawString("positive Y", 20, 135+m2);
    if(!posY) g.drawString("negative Y", 20, 135+m2);

    if(objSelect==0) g.setColor(Color.red);
    if(objSelect!=0) g.setColor(Color.yellow);
    g.fillRect(10,155+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("first cube face", 20, 170+m2);

    if(objSelect==1) g.setColor(Color.red);
    if(objSelect!=1) g.setColor(Color.yellow);
    g.fillRect(10,180+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("third dimension, another view", 20, 195+m2);

    if(objSelect==2) g.setColor(Color.red);
    if(objSelect!=2) g.setColor(Color.yellow);
    g.fillRect(10,215+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("sphere center to edge", 20, 230+m2);

    if(objSelect==3) g.setColor(Color.red);
    if(objSelect!=3) g.setColor(Color.yellow);
    g.fillRect(10,240+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("sphere center another view", 20, 255+m2);

    if(objSelect==4) g.setColor(Color.red);
    if(objSelect!=4) g.setColor(Color.yellow);
    g.fillRect(10,275+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("surface rectangle", 20, 290+m2);

    if(objSelect==5) g.setColor(Color.red);
    if(objSelect!=5) g.setColor(Color.yellow);
    g.fillRect(10,300+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("plane, another view", 20, 315+m2);

    if(objSelect==6) g.setColor(Color.red);
    if(objSelect!=6) g.setColor(Color.yellow);
    g.fillRect(10,335+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("triangle, P1,P2", 20, 350+m2);

    if(objSelect==7) g.setColor(Color.red);
    if(objSelect!=7) g.setColor(Color.yellow);
    g.fillRect(10,360+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("triangle P1,P2 another view", 20, 375+m2);

    if(objSelect==8) g.setColor(Color.red);
    if(objSelect!=8) g.setColor(Color.yellow);
    g.fillRect(10,385+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("triangle, P3", 20, 400+m2);

    if(objSelect==9) g.setColor(Color.red);
    if(objSelect!=9) g.setColor(Color.yellow);
    g.fillRect(10,410+m2,Mwidth-20,mh);
    g.setColor(Color.black);
    g.drawString("triangle P3 another view", 20, 425+m2);



    g.setColor(Color.yellow);
    g.fillRect(10,2*height-55, 40, mh);
    g.setColor(Color.black);
    g.drawString("5", 20, 2*height-40);

    g.setColor(Color.yellow);
    g.fillRect(55,2*height-55, 40, mh);
    g.setColor(Color.black);
    g.drawString("10", 65, 2*height-40);

    g.setColor(Color.yellow);
    g.fillRect(100,2*height-55, 40, mh);
    g.setColor(Color.black);
    g.drawString("25", 110, 2*height-40);

    g.setColor(Color.yellow);
    g.fillRect(145,2*height-55, 40, mh);
    g.setColor(Color.black);
    g.drawString("50", 150, 2*height-40);
    
    g.setColor(Color.yellow);
    g.fillRect(10,2*height-30,Mwidth-20,mh);
    g.setColor(Color.black);
    if(gridOn)  g.drawString("turn grid off", 20, 2*height-15);
    if(!gridOn) g.drawString("turn grid on",  20, 2*height-15);

    g.setColor(Color.black);
    g.drawString("Draw Color", 20, bo+ba-25);
    g.setColor(drawColor);
    g.fillRect(100, bo, 40, 40);

    g.setColor(Color.red);
    g.fillRect(10, bo+ba, 40, 40);
        
    g.setColor(Color.green);
    g.fillRect(55, bo+ba, 40, 40);
        
    g.setColor(Color.blue);
    g.fillRect(100, bo+ba, 40, 40);
        
    g.setColor(Color.yellow);
    g.fillRect(145, bo+ba, 40, 40);
        
    g.setColor(Color.cyan);
    g.fillRect(10, bo+2*ba, 40, 40);
        
    g.setColor(Color.magenta);
    g.fillRect(55, bo+2*ba, 40, 40);

    g.setColor(Color.pink);
    g.fillRect(100, bo+2*ba, 40, 40);
        
    g.setColor(Color.orange);
    g.fillRect(145, bo+2*ba, 40, 40);

    g.setColor(Color.lightGray);
    g.fillRect(10, bo+3*ba, 40, 40);

    g.setColor(Color.gray);
    g.fillRect(55, bo+3*ba, 40, 40);

    g.setColor(Color.darkGray);
    g.fillRect(100, bo+3*ba, 40, 40);

    g.setColor(Color.black);
    g.fillRect(145, bo+3*ba, 40, 40);

    Color cj = new Color(175, 175, 255);
    g.setColor(cj);
    g.fillRect(10, bo+4*ba, 40, 40);
        
    cj = new Color(175,255,175);
    g.setColor(cj);
    g.fillRect(55, bo+4*ba, 40, 40);

    cj = new Color(128, 175, 175);
    g.setColor(cj);
    g.fillRect(100, bo+4*ba, 40, 40);
        
    g.setColor(Color.black); // white in box
    g.drawRect(145, bo+4*ba, 40, 40);
  } // end drawMenu

  int snap(int x)
  {
    return ((x+grid/2)/grid)*grid;
  } // end snap



  public abstract class objList // extended below to actual objects
  {
    int objKind=-1; // future not an object, may contain a list
    int bx1, bx2, by1, by2, bz1, bz2; // bounds
    Color objColor;
    String name;
    int instance=0;
    LinkedList drawList = new LinkedList();
                          // initially null,
                          // can be used for hierarchial objects

                                                    
    // Must be provided by extenders
    public abstract void drawXY(Graphics g);
    public abstract void drawZY(Graphics g);
    public abstract void drawXZ(Graphics g);
    public abstract void draw3d(Graphics g);
    public abstract String getName();

    // available for extenders
        public int pnX(int x1, int x2) {if(posX) return x1; return width-x2;}
        public int pnY(int y1, int y2) {if(posY) return y1; return height-y2;}
        public int pnZ(int z1, int z2) {if(posZ) return z1; return height-z2;}
  } // end class objList;
  
  
  class objCube extends objList
  {
    int x1, y1, z1, x2, y2, z2; // diagonal corners
    char need = ' ';
    double c = width/2.0; // center and scale factor
    double sf = 0.5;
    
    public objCube() {}
    public objCube(objCube obj)
                  { x1=obj.x1; y1=obj.y1; z1=obj.z1;
                    x2=obj.x2; y2=obj.y2; z2=obj.z2;
                    bx1=obj.bx1; bx2=obj.bx2; by1=obj.by1; by2=obj.by2;
                    bz1=obj.bz1; bz2=obj.bz2;
                    objColor=obj.objColor; name=obj.name;
                    instance=obj.instance; drawList=obj.drawList;}
    public void misc(Color c, char n)
                    { objColor = drawColor;
                      need = n;
                      name = "Cube";
                      instance = instanceCount++;}
    public void facexy(int xa, int ya, int xb, int yb)
                      { x1=xa<xb?xa:xb; x2=xb<xa?xa:xb;
                        y1=ya<yb?ya:yb; y2=yb<ya?ya:yb;
                        bx1=x1; bx2=x2; by1=y1; by2=y2;}
    public void facez (int za, int zb)
                      { z1=za<zb?za:zb; z2=zb<za?za:zb;
                        bz1=z1; bz2=z2;}
    public void facexz(int xa, int za, int xb, int zb)
                      { x1=xa<xb?xa:xb; x2=xb<xa?xa:xb;
                        z1=za<zb?za:zb; z2=zb<za?za:zb;
                        bx1=x1; bx2=x2; bz1=z1; bz2=z2;}
    public void facey (int ya, int yb)
                      { y1=ya<yb?ya:yb; y2=yb<ya?ya:yb;
                        by1=y1; by2=y2;}
    public void facezy(int za, int ya, int zb, int yb)
                      { z1=za<zb?za:zb; z2=zb<za?za:zb;
                        y1=ya<yb?ya:yb; y2=yb<ya?ya:yb;
                        bz1=z1; bz2=z2; by1=y1; by2=y2;}
    public void facex (int xa, int xb)
                      { x1=xa<xb?xa:xb; x2=xb<xa?xa:xb;
                        bx1=x1; bx2=x2;}
    public void drawXY(Graphics g)
                      {g.setColor(objColor);
                       if(x1==x2 || y1==y2)
                          g.drawLine(pnX(x1,x2), pnY(y1,y2), pnX(x2,x1), pnY(y2,y1));
                       else
                          g.fillRect(pnX(x1,x2), pnY(y1,y2), x2-x1, y2-y1);}
    public void drawZY(Graphics g)
                      {g.setColor(objColor);
                       if(z1==z2 || y1==y2)
                          g.drawLine(pnZ(z1,z2), pnY(y1,y2), pnZ(z2,z1), pnY(y2,y1));
                       else
                          g.fillRect(pnZ(z1,z2), pnY(y1,y2), z2-z1, y2-y1);}
    public void drawXZ(Graphics g)
                      {g.setColor(objColor);
                       if(x1==x2 || z1==z2)
                          g.drawLine(pnX(x1,x2), pnZ(height-z2,height-z1),
                                     pnX(x2,x1), pnZ(height-z1,height-z2));
                       else
                          g.fillRect(pnX(x1,x2), pnZ(height-z2,height-z1),
                                     x2-x1, z2-z1);}
    int xortho(int x, int z)
                      {return (int)(((x-c)+0.7071*(z-c))*sf+c);}
    int yortho(int y, int z)
                      {return (int)(((y-c)-0.7071*(z-c))*sf+c);}
    public void draw3d(Graphics g)
      { // orthographic projection, for drawing ortho polygons:
        int xp[]= new int[4],yp[]= new int[4],np=4;
        int x1f, x2f, y1f, y2f, x1b, x2b, y1b, y2b;  // ortho points
        // translate to origin, project, scale, translate back 
        x1f = xortho(x1, z1); // front
        x2f = xortho(x2, z1);
        y1f = yortho(y1, z1);
        y2f = yortho(y2, z1);
        x1b = xortho(x1, z2); // back
        x2b = xortho(x2, z2);
        y1b = yortho(y1, z2);
        y2b = yortho(y2, z2);
        if(debug>=3)System.out.println("x1f="+x1f+", y1f="+y1f); 
        if(debug>=3)System.out.println("x2f="+x2f+", y2f="+y2f); 
        if(debug>=3)System.out.println("x1b="+x1b+", y1b="+y1b); 
        if(debug>=3)System.out.println("x2b="+x2b+", y2b="+y2b); 
        xp[0]=x1f; xp[1]=x1f; xp[2]=x2f; xp[3]=x2f;
        yp[0]=y1f; yp[1]=y2f; yp[2]=y2f; yp[3]=y1f;
        g.setColor(objColor);
        if(solid)
        {
          g.fillPolygon(xp, yp, np); // front
          g.setColor(Color.black); // else color of wireframe is object color
        }
        g.drawPolygon(xp, yp, np); // front
        xp[0]=x2f; xp[1]=x2f; xp[2]=x2b; xp[3]=x2b;
        yp[0]=y1f; yp[1]=y2f; yp[2]=y2b; yp[3]=y1b;
        g.setColor(objColor);
        if(solid)
        {
          g.fillPolygon(xp, yp, np); // side
          g.setColor(Color.black);
        }
        g.drawPolygon(xp, yp, np); // side
        xp[0]=x1f; xp[1]=x1b; xp[2]=x2b; xp[3]=x2f;
        yp[0]=y1f; yp[1]=y1b; yp[2]=y1b; yp[3]=y1f;
        g.setColor(objColor);
        if(solid)
        {
          g.fillPolygon(xp, yp, np); // top
          g.setColor(Color.black);
        }
        g.drawPolygon(xp, yp, np); // top
        if(!solid) // draw hidden wireframe
        {
          g.setColor(objColor);
          xp[0]=x1b; xp[1]=x1b; xp[2]=x2b; xp[3]=x2b;
          yp[0]=y1b; yp[1]=y2b; yp[2]=y2b; yp[3]=y1b;
          g.drawPolygon(xp, yp, np); // back
          //xp[0]=x2f; xp[1]=x2f; xp[2]=x2b; xp[3]=x2b;
          //yp[0]=y1f; yp[1]=y2f; yp[2]=y2b; yp[3]=y1b;
          //g.drawPolygon(xp, yp, np); // other side
          xp[0]=x1f; xp[1]=x1b; xp[2]=x2b; xp[3]=x2f;
          yp[0]=y2f; yp[1]=y2b; yp[2]=y2b; yp[3]=y2f;
          g.drawPolygon(xp, yp, np); // bottom
        }
      }
    public String getName()
                         { return name+instance+"\n"+
                           "x1="+x1+", y1="+y1+", z1="+z1+"\n"+
                           "x2="+x2+", y2="+y2+", z2="+z2+"\n"; }
  } // end class objCube

  class objSphere extends objList
  {
    int x1, y1, z1, r; // center and radius
    char need = ' ';
    double c = width/2.0; // center and scale factor
    double sf = 0.5;
    
    public objSphere() {}
    public objSphere(objSphere obj)
                    { x1=obj.x1; y1=obj.y1; z1=obj.z1; r=obj.r;
                      bx1=obj.bx1; bx2=obj.bx2; by1=obj.by1; by2=obj.by2;
                      bz1=obj.bz1; bz2=obj.bz2;
                      objColor=obj.objColor; name=obj.name;
                      instance=obj.instance; drawList=obj.drawList;}
    public void misc(Color c, char n)
                    { objColor = drawColor;
                      need = n;
                      name = "Sphere";
                      instance = instanceCount++;}
    public void centxy(int xa, int ya, int xb, int yb)
                      { x1=xa; y1=ya;
                        double dx=(double)(xa-xb); double dy=(double)(ya-yb);
                        r = (int)(Math.sqrt(dx*dx+dy*dy)/2.0);
                        bx1=x1-r; bx2=x1+r; by1=y1-r; by2=y2+r;}
    public void centz (int za)
                      { z1=za;
                        bx1=z1-r; bz2=z1+r;}
    public void centxz(int xa, int za, int xb, int zb)
                      { x1=xa; z1=za;
                        double dx=(double)(xa-xb); double dz=(double)(za-zb);
                        r = (int)(Math.sqrt(dx*dx+dz*dz)/2.0);
                        bx1=x1-r; bx2=x1+r; bz1=z1-r; bz2=z1+r;}
    public void centy (int ya)
                      { y1=ya;
                        by1=y1+r; by2=y1+r;}
    public void centzy(int za, int ya, int zb, int yb)
                      { z1=za; y1=ya;
                        double dz=(double)(za-zb); double dy=(double)(ya-yb);
                        r = (int)(Math.sqrt(dz*dz+dy*dy)/2.0);
                        by1=y1-r; by2=y1+r; bz1=z1-r; bz2=z1+r;}
    public void centx (int xa)
                      { x1=xa;
                        bx1=x1-r; bx2=x1+r;}
    public void drawXY(Graphics g)
                      {g.setColor(objColor);
                       g.fillOval(x1-r, y1-r, 2*r, 2*r);}
    public void drawZY(Graphics g)
                      {g.setColor(objColor);
                       g.fillOval(z1-r, y1-r, 2*r, 2*r);}
    public void drawXZ(Graphics g)
                      {g.setColor(objColor);
                       g.fillOval(x1-r, height-(z1+r), 2*r, 2*r);}
    int xortho(int x, int z)
                      {return (int)(((x-c)+0.7071*(z-c))*sf+c);}
    int yortho(int y, int z)
                      {return (int)(((y-c)-0.7071*(z-c))*sf+c);}
    public void draw3d(Graphics g)
      { // convert coordinates
        int xa = xortho(x1, z1);
        int ya = yortho(y1, z1);
        g.setColor(objColor);
        if(solid)
        {
          g.fillOval(xa-(int)(sf*r), ya-(int)(sf*r),
                    (int)(sf*2.0*r), (int)(sf*2.0*r));
          g.setColor(Color.black);
        }
        double wfx[][] = new double[7][7];
        double wfy[][] = new double[7][7];
        double angx, angy;
        for(int i=0; i<7; i++)
        {
          angy = Math.PI*(double)(i)/(double)6;
          for(int j=0; j<7; j++) 
          {
            angx = Math.PI*(double)(j)/(double)6;
            wfx[i][j] = Math.cos(angy)*Math.sin(angx);
            wfy[i][j] = Math.cos(angx);
            if(debug>=3)System.out.println("wf["+i+"]["+j+"]=("+
                                           wfx[i][j]+","+wfy[i][j]+")");
          }
        }
        for(int i=0; i<7; i++)
        {
          for(int j=0; j<7; j++)
          {
            if(i<6)
              g.drawLine(xa+(int)(wfx[i][j]*sf*r),
                         ya+(int)(wfy[i][j]*sf*r),
                         xa+(int)(wfx[i+1][j]*sf*r),
                         ya+(int)(wfy[i+1][j]*sf*r));
            if(j<6)
              g.drawLine(xa+(int)(wfx[i][j]*sf*r),
                         ya+(int)(wfy[i][j]*sf*r),
                         xa+(int)(wfx[i][j+1]*sf*r),
                         ya+(int)(wfy[i][j+1]*sf*r));
          }
        }
      }
    public String getName()
                         { return name+instance+"\n"+
                           "x1="+x1+", y1="+y1+", z1="+z1+"\n"+
                           "r="+r+"\n"; }
  } // end class objSphere
  
  class objRect extends objList
  {
    int x1, y1, z1, x2, y2, z2; // diagonal corners
    char need = ' ';
    double c = width/2.0; // center and scale factor
    double sf = 0.5;
    
    public objRect() {}
    public objRect(objRect obj)
                  { x1=obj.x1; y1=obj.y1; z1=obj.z1;
                    x2=obj.x2; y2=obj.y2; z2=obj.z2;
                    bx1=obj.bx1; bx2=obj.bx2; by1=obj.by1; by2=obj.by2;
                    bz1=obj.bz1; bz2=obj.bz2;
                    objColor=obj.objColor; name=obj.name;
                    instance=obj.instance; drawList=obj.drawList;}
    public void misc(Color c, char n)
                    { objColor = drawColor;
                      need = n;
                      name = "Rect";
                      instance = instanceCount++;}
    public void facexy(int xa, int ya, int xb, int yb)
                      { x1=xa<xb?xa:xb; x2=xb<xa?xa:xb;
                        y1=ya<yb?ya:yb; y2=yb<ya?ya:yb;
                        bx1=x1; bx2=x2; by1=y1; by2=y2;}
    public void facez (int za, int zb)
                      { z1=za; z2=za;
                        bz1=z1; bz2=z2;}
    public void facexz(int xa, int za, int xb, int zb)
                      { x1=xa<xb?xa:xb; x2=xb<xa?xa:xb;
                        z1=za<zb?za:zb; z2=zb<za?za:zb;
                        bx1=x1; bx2=x2; bz1=z1; bz2=z2;}
    public void facey (int ya, int yb)
                      { y1=ya; y2=ya;
                        by1=y1; by2=y2;}
    public void facezy(int za, int ya, int zb, int yb)
                      { z1=za<zb?za:zb; z2=zb<za?za:zb;
                        y1=ya<yb?ya:yb; y2=yb<ya?ya:yb;
                        by1=y1; by2=y2; bz1=z1; bz2=z2;}
    public void facex (int xa, int xb)
                      { x1=xa; x2=xa;
                        bx1=x1; bx2=x2;}
    public void drawXY(Graphics g)
                      {g.setColor(objColor);
                       if(x1==x2 || y1==y2)
                          g.drawLine(pnX(x1,x2), pnY(y1,y2), pnX(x2,x1), pnY(y2,y1));
                       else
                          g.fillRect(pnX(x1,x2), pnY(y1,y2), x2-x1, y2-y1);}
    public void drawZY(Graphics g)
                      {g.setColor(objColor);
                       if(z1==z2 || y1==y2)
                          g.drawLine(pnZ(z1,z2), pnY(y1,y2), pnZ(z2,z1), pnY(y2,y1));
                       else
                          g.fillRect(pnZ(z1,z2), pnY(y1,y2), z2-z1, y2-y1);}
    public void drawXZ(Graphics g)
                      {g.setColor(objColor);
                       if(x1==x2 || z1==z2)
                          g.drawLine(pnX(x1,x2), pnZ(height-z2,height-z1),
                                     pnX(x2,x1), pnZ(height-z1,height-z2));
                       else
                          g.fillRect(pnX(x1,x2), pnZ(height-z2,height-z1),
                                     x2-x1, z2-z1);}
    int xortho(int x, int z)
                      {return (int)(((x-c)+0.7071*(z-c))*sf+c);}
    int yortho(int y, int z)
                      {return (int)(((y-c)-0.7071*(z-c))*sf+c);}
    public void draw3d(Graphics g)
      { // orthographic projection, for drawing ortho polygons:
        int xp[]= new int[4],yp[]= new int[4],np=4;
        int x1f, x2f, y1f, y2f, x1b, x2b, y1b, y2b;  // ortho points
        // translate to origin, project, scale, translate back 
        x1f = xortho(x1, z1); // front
        x2f = xortho(x2, z1);
        y1f = yortho(y1, z1);
        y2f = yortho(y2, z1);
        x1b = xortho(x1, z2); // back
        x2b = xortho(x2, z2);
        y1b = yortho(y1, z2);
        y2b = yortho(y2, z2);
        if(debug>=3)System.out.println("x1f="+x1f+", y1f="+y1f); 
        if(debug>=3)System.out.println("x2f="+x2f+", y2f="+y2f); 
        if(debug>=3)System.out.println("x1b="+x1b+", y1b="+y1b); 
        if(debug>=3)System.out.println("x2b="+x2b+", y2b="+y2b); 
        xp[0]=x1f; xp[1]=x1f; xp[2]=x2f; xp[3]=x2f;
        yp[0]=y1f; yp[1]=y2f; yp[2]=y2f; yp[3]=y1f;
        g.setColor(objColor);
        if(solid)
        {
          g.fillPolygon(xp, yp, np); // front
          g.setColor(Color.black);
        }
        g.drawPolygon(xp, yp, np); // front
        xp[0]=x2f; xp[1]=x2f; xp[2]=x2b; xp[3]=x2b;
        yp[0]=y1f; yp[1]=y2f; yp[2]=y2b; yp[3]=y1b;
        g.setColor(objColor);
        if(solid)
        {
          g.fillPolygon(xp, yp, np); // side
          g.setColor(Color.black);
        }
        g.drawPolygon(xp, yp, np); // side
        xp[0]=x1f; xp[1]=x1b; xp[2]=x2b; xp[3]=x2f;
        yp[0]=y1f; yp[1]=y1b; yp[2]=y1b; yp[3]=y1f;
        g.setColor(objColor);
        if(solid)
        {
          g.fillPolygon(xp, yp, np); // top
          g.setColor(Color.black);
        }
        g.drawPolygon(xp, yp, np); // top
      }
    public String getName()
                         { return name+instance+"\n"+
                           "x1="+x1+", y1="+y1+", z1="+z1+"\n"+
                           "x2="+x2+", y2="+y2+", z2="+z2+"\n"; }
  } // end class objRect
  
  class objTri extends objList
  {
    int x1, y1, z1, x2, y2, z2, x3, y3, z3; // 3 points
    char need = ' ';
    double c = width/2.0; // center and scale factor
    double sf = 0.5;
    
    public objTri() {}
    public objTri(objTri obj)
                 { x1=obj.x1; y1=obj.y1; z1=obj.z1;
                   x2=obj.x2; y2=obj.y2; z2=obj.z2;
                   x3=obj.x3; y3=obj.y3; z3=obj.z3;
                   bx1=obj.bx1; bx2=obj.bx2; by1=obj.by1; by2=obj.by2;
                   bz1=obj.bz1; bz2=obj.bz2;
                   objColor=obj.objColor; name=obj.name;
                   instance=obj.instance; drawList=obj.drawList;}
    public void misc(Color c, char n)
                    { objColor = drawColor;
                      need = n;
                      name = "Tri";
                      instance = instanceCount++;}
    public void misc3(Color c, char n)
                     { objColor = drawColor;
                       need = n;
                       name = "Tri";}
    public void facexy(int xa, int ya, int xb, int yb)
                      { bx1=xa<xb?xa:xb; bx2=xb<xa?xa:xb;
			by1=ya<yb?ya:yb; by2=yb<ya?ya:yb;
                        x1=xa; x2=xb; y1=ya; y2=yb;}
    public void facexy3(int xa, int ya)
                       { bx1=xa<bx1?xa:bx1; bx2=bx2<xa?xa:bx2;
			 by1=ya<by1?ya:by1; by2=by2<ya?ya:by2;
                         x3=xa; y3=ya;}
    public void facez (int za, int zb)
                      { z1=za; z2=zb;
			bz1=za<zb?za:zb; bz2=zb<za?za:zb;}
    public void facez3(int za)
                      { z3=za;
			bz1=za<bz1?za:bz1; bz2=bz2<za?za:bz2;}
    public void facexz(int xa, int za, int xb, int zb)
                      { bx1=xa<xb?xa:xb; bx2=xb<xa?xa:xb;
                        bz1=za<zb?za:zb; bz2=zb<za?za:zb;
                        x1=xa; x2=xb; z1=za; z2=zb;}
    public void facexz3(int xa, int za)
                       { bx1=xa<bx1?xa:bx1; bx2=bx2<xa?xa:bx2;
			 bz1=za<bz1?za:bz1; bz2=bz2<za?za:bz2;
                         x3=xa; z3=za;}
    public void facey (int ya, int yb)
                      { by1=ya<yb?ya:yb; by2=yb<ya?ya:yb;
                        y1=ya; y2=yb;}
    public void facey3(int ya)
                      { by1=ya<by1?ya:by1; by2=by2<ya?ya:by2;
                        y3=ya;}
    public void facezy(int za, int ya, int zb, int yb)
                      { bz1=za<zb?za:zb; bz2=zb<za?za:zb;
                        by1=ya<yb?ya:yb; by2=yb<ya?ya:yb;
                        z1=za; z2=zb; y1=ya; y2=ya;}
    public void facezy3(int za, int ya)
                       { bz1=za<bz1?za:bz1; bz2=bz2<za?za:bz2;
                         by1=ya<by1?ya:by1; by2=by2<ya?ya:by2;
                         z3=za; y3=ya;}
    public void facex (int xa, int xb)
                      { bx1=xa<xb?xa:xb; bx2=xb<xa?xa:xb;
                        x1=xa; x2=xb;}
    public void facex3(int xa)
                      { bx1=xa<bx1?xa:bx1; bx2=bx2<xa?xa:bx2;
                        x3=xa;}
    public void drawXY(Graphics g)
                      {int xp[]= new int[3],yp[]= new int[3],np=3;
                       xp[0]=x1; xp[1]=x2; xp[2]=x3;
                       yp[0]=y1; yp[1]=y2; yp[2]=y3;
                       g.setColor(objColor);
                       g.fillPolygon(xp, yp, np);
                       g.drawLine(x1, y1, x2, y2);
                       g.drawLine(x2, y2, x3, y3);
                       g.drawLine(x3, y3, x1, y1);}
    public void drawZY(Graphics g)
                      {int xp[]= new int[3],yp[]= new int[3],np=3;
                       xp[0]=z1; xp[1]=z2; xp[2]=z3;
                       yp[0]=y1; yp[1]=y2; yp[2]=y3;
                       g.setColor(objColor);
                       g.fillPolygon(xp, yp, np);
                       g.drawLine(z1, y1, z2, y2);
                       g.drawLine(z2, y2, z3, y3);
                       g.drawLine(z3, y3, z1, y1);}
    public void drawXZ(Graphics g)
                      {int xp[]= new int[3],yp[]= new int[3],np=3;
                       xp[0]=x1; xp[1]=x2; xp[2]=x3;
                       yp[0]=z1; yp[1]=z2; yp[2]=z3;
                       g.setColor(objColor);
                       g.fillPolygon(xp, yp, np);
                       g.drawLine(x1, z1, x2, z2);
                       g.drawLine(x2, z2, x3, z3);
                       g.drawLine(x3, z3, x1, z1);}
    int xortho(int x, int z)
                      {return (int)(((x-c)+0.7071*(z-c))*sf+c);}
    int yortho(int y, int z)
                      {return (int)(((y-c)-0.7071*(z-c))*sf+c);}
    public void draw3d(Graphics g)
      { // orthographic projection, for drawing ortho polygons:
        int xp[]= new int[3],yp[]= new int[3],np=3;
        int x1f, x2f, x3f, y1f, y2f, y3f;  // ortho points
        // translate to origin, project, scale, translate back 
        x1f = xortho(x1, z1); // one face
        x2f = xortho(x2, z2);
        x3f = xortho(x3, z3);
        y1f = yortho(y1, z1);
        y2f = yortho(y2, z2);
        y3f = yortho(y3, z3);
        if(debug>=3)System.out.println("x1f="+x1f+", y1f="+y1f); 
        if(debug>=3)System.out.println("x2f="+x2f+", y2f="+y2f); 
        xp[0]=x1f; xp[1]=x2f; xp[2]=x3f;
        yp[0]=y1f; yp[1]=y2f; yp[2]=y3f;
        g.setColor(objColor);
        if(solid)
        {
          g.fillPolygon(xp, yp, np); // face
          g.setColor(Color.black);
        }
        g.drawPolygon(xp, yp, np); // face
      }
    public String getName()
                         { return name+instance+"\n"+
                           "x1="+x1+", y1="+y1+", z1="+z1+"\n"+
                           "x2="+x2+", y2="+y2+", z2="+z2+"\n"+
                           "x3="+x3+", y3="+y3+", z3="+z3+"\n"; }
  } // end class objTri

  
  boolean in_rect(int x, int y, int w, int h)
  {
    return xm>x && xm<x+w && ym>y && ym<y+h;
  } // end in_rect

  class mousePressHandler extends MouseAdapter
  {
    public void mousePressed (MouseEvent e)
    {
      xm = e.getX();
      ym = e.getY();
      bm = e.getButton();
      Component c = e.getComponent(); // get frame that received mouse press

      // System.out.println("down xm="+xm+"   ym="+ym+"   bm="+bm); // debug print
      // System.out.println("fMenu string="+fMenu.toString()); // debug print
      // System.out.println("component string="+c.toString()); // debug print
      // String s = e.paramString(); // debug print
      // System.out.println("param string="+s); // debug print
          
      if(c == fMenu) // Menu processing
      {
        if(in_rect(10,35,Mwidth-20,mh)) {readDraw3d(); return;}
        if(in_rect(10,60,Mwidth-20,mh)) {writeDraw3d(); return;}
        if(in_rect(10,85,Mwidth-20,mh)) {writeStl(); return;}
        if(in_rect(10,5+mo,Mwidth/2-15,mh)) // clear
                  { drawColor = new Color(255, 0, 0);
                    objSelect = 0;
                    drawList = new LinkedList();
                  }
        if(in_rect(Mwidth/2,5+mo,Mwidth/2-10,mh)) {paint(gXY); return;};
        if(in_rect(10,30+mo,Mwidth/2-15,mh)) selected = !selected;
        if(in_rect(Mwidth/2,30+mo,Mwidth/2-10,mh)) solid = !solid;
        if(in_rect(10,95+m2,Mwidth/2-15,mh)) posX = !posX;
        if(in_rect(Mwidth/2,95+m2,Mwidth/2-10,mh)) posZ = !posZ;
        if(in_rect(10,120+m2,Mwidth-20,mh)) posY = !posY;
        if(in_rect(10,155+m2,Mwidth-20,mh)) objSelect = 0; // start cube
        if(in_rect(10,215+m2,Mwidth-20,mh)) objSelect = 2; // start sphere
        if(in_rect(10,275+m2,Mwidth-20,mh)) objSelect = 4; // surface start
        if(in_rect(10,335+m2,Mwidth-20,mh)) objSelect = 6; // triangle start

        if(in_rect(10,2*height-30,Mwidth-20,mh)) gridOn = !gridOn;
        if(in_rect(10,2*height-55, 40, mh)) grid = 5;
        if(in_rect(55,2*height-55, 40, mh)) grid = 10;
        if(in_rect(100,2*height-55, 40, mh)) grid = 25;
        if(in_rect(145,2*height-55, 40, mh)) grid = 50;

        if(in_rect(10, bo+ba, 40, 40)) drawColor = new Color(255,0,0);
        if(in_rect(55, bo+ba, 40, 40)) drawColor = new Color(0,255,0);
        if(in_rect(100, bo+ba, 40, 40)) drawColor = new Color(0,0,255);
        if(in_rect(145, bo+ba, 40, 40)) drawColor = new Color(255,255,0);
        if(in_rect(10, bo+2*ba, 40, 40)) drawColor = new Color(0,255,255);
        if(in_rect(55, bo+2*ba, 40, 40)) drawColor = new Color(255,0,255);
        if(in_rect(100, bo+2*ba, 40, 40)) drawColor = new Color(255,175,175);
        if(in_rect(145, bo+2*ba, 40, 40)) drawColor = new Color(255,200,0);
        if(in_rect(10, bo+3*ba, 40, 40)) drawColor = new Color(192,192,192);
        if(in_rect(55, bo+3*ba, 40, 40)) drawColor = new Color(128,128,128);
        if(in_rect(100, bo+3*ba, 40, 40)) drawColor = new Color(64,64,64);
        if(in_rect(145, bo+3*ba, 40, 40)) drawColor = new Color(0,0,0);
        if(in_rect(10, bo+4*ba, 40, 40)) drawColor = new Color(175,175,255);
        if(in_rect(55, bo+4*ba, 40, 40)) drawColor = new Color(175,255,175);
        if(in_rect(100, bo+4*ba, 40, 40)) drawColor = new Color(128,175,175);
        if(in_rect(145, bo+4*ba, 40, 40)) drawColor = new Color(255,255,255);
      }

      if(c == fXY) // Front view processing
      {
        x0 = xm;
        y0 = ym;
        if(gridOn) x0 = snap(xm);
        if(gridOn) y0 = snap(ym);
        if(bm==1) // generating
        {
          trackingXY = true;
          x1 = x0;
          y1 = y0; // zero size, may choose to ignore later
        }
        else if(bm==3) // moving
        {
          trackingXY = true;
          x1 = x0;
          y1 = y0; // zero size, may choose to ignore later
        }
      }

      if(c == fXZ) // Top view processing
      {
        x0 = xm;
        z0 = ym;
        if(gridOn) x0 = snap(xm);
        if(gridOn) z0 = snap(ym);
        if(bm==1) // generating
        {
          trackingXZ = true;
          x1 = x0;
          z1 = z0; // zero size, may choose to ignore later
        }
        else if(bm==3) // moving
        {
          trackingXZ = true;
          x1 = x0;
          z1 = z0; // zero size, may choose to ignore later
        }
      }

      if(c == fZY) // Right view processing
      {
        z0 = xm;
        y0 = ym;
        if(gridOn) z0 = snap(xm);
        if(gridOn) y0 = snap(ym);
        if(bm==1) // generating
        {
          trackingZY = true;
          z1 = z0;
          y1 = y0; // zero size, may choose to ignore later
        }
        else if(bm==3) // moving
        {
          trackingZY = true;
          z1 = z0;
          y1 = y0; // zero size, may choose to ignore later
        }
      }

      if(c == f3D) // 3D view processing
      {
        // none at this time, later rendering inputs
      }
          
      paint(gXY); // actually paints all views
    } // end mousePressed
  } // end mousePressedHandler

  class mouseReleaseHandler extends MouseAdapter
  {
    public void mouseReleased (MouseEvent e)
    {
          int dx, dy, dz;
      xm = e.getX();
      ym = e.getY();
      bm = e.getButton();
      Component c = e.getComponent(); // get frame that received mouse press
      // System.out.println("up   xm="+xm+"   ym="+ym+"   bm="+bm); // debug print
      if(c == fXY && trackingXY) // Front view processing
      {
        trackingXY = false; // no more rubber_rect
        // save final figure data for object generation 
        x1 = xm;
        y1 = ym;
        if(gridOn) x1 = snap(xm);
        if(gridOn) y1 = snap(ym); 
        if(bm==1) // generating
        {
          if(objSelect==0)
          {
            objC = new objCube();
            objC.facexy(x0, y0, x1, y1);
            objC.misc(drawColor, 'Z');
            objSelect = 1;
            tempXY = true;
          }
          else if(objSelect==1)
          {
            tempXY = false;
            tempZY = false;
            tempXZ = false;
            if(objC.need=='X') objC.facex(x0, x1);
            if(objC.need=='Y') objC.facey(y0, y1);
            drawList.addLast(new objCube(objC));
            objSelect = 0;
          }
          else if(objSelect==2)
          {
            objS = new objSphere();
            objS.centxy(x0, y0, x1, y1);
            objS.misc(drawColor, 'Z');
            objSelect = 3;
            tempXY = true;
          }
          else if(objSelect==3)
          {
            tempXY = false;
            tempZY = false;
            tempXZ = false;
            if(objS.need=='X') objS.centx(x0);
            if(objS.need=='Y') objS.centy(y0);
            drawList.addLast(new objSphere(objS));
            objSelect = 2;
          }
          else if(objSelect==4)
          {
            objR = new objRect();
            objR.facexy(x0, y0, x1, y1);
            objR.misc(drawColor, 'Z');
            objSelect = 5;
            tempXY = true;
          }
          else if(objSelect==5)
          {
            tempXY = false;
            tempZY = false;
            tempXZ = false;
            if(objR.need=='X') objR.facex(x0, x1);
            if(objR.need=='Y') objR.facey(y0, y1);
            drawList.addLast(new objRect(objR));
            objSelect = 4;
          }
          else if(objSelect==6)
          {
            objT = new objTri();
            objT.facexy(x0, y0, x1, y1);
            objT.misc(drawColor, 'Z');
            objSelect = 7;
            tempXY = true;
          }
          else if(objSelect==7)
          {
            if(objT.need=='X') objT.facex(x0, x1);
            if(objT.need=='Y') objT.facey(y0, y1);
            objSelect = 8;
          }
          else if(objSelect==8)
          {
            objT.facexy3(x0, y0);
            objT.misc3(drawColor, 'Z');
            objSelect = 9;
          }
          else if(objSelect==9)
          {
            tempXY = false;
            tempZY = false;
            tempXZ = false;
            if(objT.need=='X') objT.facex3(x0);
            if(objT.need=='Y') objT.facey3(y0);
            drawList.addLast(new objTri(objT));
            objSelect = 6;
          }
        }
        else if(bm==3) // moving
        {
          // modify selected object
          dx = x1-x0;
          dy = y1-y0;
          dz = 0;
        }
      }
      if(c == fXZ && trackingXZ) // Top view processing
      {
        trackingXZ = false; // no more rubber_rect
        // save final figure data for 'display' to draw 
        x1 = xm;
        z1 = ym;
        if(gridOn) x1 = snap(xm);
        if(gridOn) z1 = snap(ym);
        if(bm==1) // generating
        {
          if(objSelect==0)
          {
            objC = new objCube();
            objC.facexz(x0, height-z0, x1, height-z1);
            objC.misc(drawColor, 'Y');
            objSelect = 1;
                    tempXZ = true;
          }
          else if(objSelect==1)
          {
                    tempXZ = false;
                        tempZY = false;
                        tempXY = false;
            if(objC.need=='X') objC.facex(x0, x1);
            if(objC.need=='Z') objC.facez(z0, z1);
            drawList.addLast(new objCube(objC));
            objSelect = 0;
          }
          else if(objSelect==2)
          {
            objS = new objSphere();
            objS.centxz(x0, height-z0, x1, height-z1);
            objS.misc(drawColor, 'Y');
            objSelect = 3;
                    tempXZ = true;
          }
          else if(objSelect==3)
          {
                    tempXZ = false;
                        tempZY = false;
                        tempXY = false;
            if(objS.need=='X') objS.centx(x0);
            if(objS.need=='Z') objS.centz(z0);
            drawList.addLast(new objSphere(objS));
            objSelect = 2;
          }
          else if(objSelect==4)
          {
            objR = new objRect();
            objR.facexz(x0, z0, x1, z1);
            objR.misc(drawColor, 'Y');
            objSelect = 5;
                    tempXZ = true;
          }
          else if(objSelect==5)
          {
                    tempXZ = false;
                        tempZY = false;
                        tempXY = false;
            if(objR.need=='X') objR.facex(x0, x1);
            if(objR.need=='Z') objR.facez(z0, z1);
            drawList.addLast(new objRect(objR));
            objSelect = 4;
          }
          else if(objSelect==6)
          {
            objT = new objTri();
            objT.facexz(x0, z0, x1, z1);
            objT.misc(drawColor, 'Y');
            objSelect = 7;
            tempXZ = true;
          }
          else if(objSelect==7)
          {
            if(objT.need=='X') objT.facex(x0, x1);
            if(objT.need=='Z') objT.facez(z0, z1);
            objSelect = 8;
          }
          else if(objSelect==8)
          {
            objT.facexz3(x0, z0);
            objT.misc3(drawColor, 'Y');
            objSelect = 9;
          }
          else if(objSelect==9)
          {
            tempXZ = false;
            tempZY = false;
            tempXY = false;
            if(objT.need=='X') objT.facex3(x0);
            if(objT.need=='Z') objT.facez3(z0);
            drawList.addLast(new objTri(objT));
            objSelect = 6;
          }
        }
        else if(bm==3) // moving
        {
          // modify selected object
          dx = x1-x0;
                  dy = 0;
                  dz = z1-z0;
        }
      }
      if(c == fZY && trackingZY) // side view processing
      {
        trackingZY = false; // no more rubber_rect
        // save final figure data for 'display' to draw 
        z1 = xm;
        y1 = ym;
        if(gridOn) z1 = snap(xm);
        if(gridOn) y1 = snap(ym);
        if(bm==1) // generating
        {
          if(objSelect==0)
          {
            objC = new objCube();
            objC.facezy(z0, y0, z1, y1);
            objC.misc(drawColor, 'X');
            objSelect = 1;
                    tempZY = true;
          }
          else if(objSelect==1)
          {
                    tempZY = false;
                        tempXZ = false;
                        tempXY = false;
            if(objC.need=='Z') objC.facez(z0, z1);
            if(objC.need=='Y') objC.facey(y0, y1);
            drawList.addLast(new objCube(objC));
            objSelect = 0;
          }
          else if(objSelect==2)
          {
            objS = new objSphere();
            objS.centzy(z0, y0, z1, y1);
            objS.misc(drawColor, 'X');
            objSelect = 3;
                    tempZY = true;
          }
          else if(objSelect==3)
          {
                    tempZY = false;
                        tempXZ = false;
                        tempXY = false;
            if(objS.need=='Z') objS.centz(z0);
            if(objS.need=='Y') objS.centy(y0);
            drawList.addLast(new objSphere(objS));
            objSelect = 2;
          }
          else if(objSelect==4)
          {
            objR = new objRect();
            objR.facezy(z0, y0, z1, y1);
            objR.misc(drawColor, 'X');
            objSelect = 5;
                    tempZY = true;
          }
          else if(objSelect==5)
          {
                    tempZY = false;
                        tempXZ = false;
                        tempXY = false;
            if(objR.need=='Z') objR.facez(z0, z1);
            if(objR.need=='Y') objR.facey(y0, y1);
            drawList.addLast(new objRect(objR));
            objSelect = 4;
          }
          else if(objSelect==6)
          {
            objT = new objTri();
            objT.facezy(z0, y0, z1, y1);
            objT.misc(drawColor, 'X');
            objSelect = 7;
            tempZY = true;
          }
          else if(objSelect==7)
          {
            if(objT.need=='Z') objT.facez(z0, z1);
            if(objT.need=='Y') objT.facey(y0, y1);
            objSelect = 8;
          }
          else if(objSelect==8)
          {
            objT.facezy3(z0, y0);
            objT.misc3(drawColor, 'X');
            objSelect = 9;
          }
          else if(objSelect==9)
          {
            tempZY = false;
            tempXZ = false;
            tempXY = false;
            if(objT.need=='Z') objT.facez3(z0);
            if(objT.need=='Y') objT.facey3(y0);
            drawList.addLast(new objTri(objT));
            objSelect = 6;
          }
        }
        else if(bm==3) // moving
        {
          // modify selected object
          dx = 0;;
                  dy = y1-y0;
                  dz = z1-z0;
        }
      }
      trackingXY = false;
      trackingXZ = false;
      trackingZY = false;
      paint(gXY); // actually paints all views 
    } // end mouseReleased
  } // end mouseReleaseHandler

  class mouseMotionHandler extends MouseMotionAdapter
  {
    public void mouseDragged (MouseEvent e)
    {
      int xct, yct;
      
      xm = e.getX();
      ym = e.getY();
      bm = e.getButton(); // all buttons
      Component c = e.getComponent(); // get frame that received mouse press
      // System.out.println("drag xm="+xm+"   ym="+ym+"   bm="+bm); // debug print

      if(c == fXY && trackingXY) // Front view processing
      {
        // xc = xc + (xm-xp); // mouse moved (new-old)
        x1 = xm;
        y1 = ym;
        if(gridOn) x1 = snap(xm);
        if(gridOn) y1 = snap(ym);
        paint(gXY);
      }
      if(c == fXZ && trackingXZ) // Top view processing
      {
        // xc = xc + (xm-xp); // mouse moved (new-old)
        x1 = xm;
        z1 = ym;
        if(gridOn) x1 = snap(xm);
        if(gridOn) z1 = snap(ym);
        paint(gXZ);
      }
      if(c == fZY && trackingZY) // side view processing
      {
        // xc = xc + (xm-xp); // mouse moved (new-old)
        z1 = xm;
        y1 = ym;
        if(gridOn) z1 = snap(xm);
        if(gridOn) y1 = snap(ym);
        paint(gXY);
      }
    } // end mouseDragged
  } // end mouseMotionHandler

  void readDraw3d()
  {
    System.out.println("readDraw3d file "+rootfilename+".draw3d");
    if(have_filename)
    {
      drawList = new LinkedList(); // clear
      String input_line;
      int ntok;
      StringTokenizer T;
      String token[] = new String[20];
      int ri, gi, bi; // color parts
      
      try
      {
        FileReader fin = new FileReader(rootfilename+".draw3d");
        BufferedReader file_in = new BufferedReader(fin);
        input_line = file_in.readLine();
        while(input_line != null)
        {
          System.out.println(input_line);
          // get data, build object, link object
          T = new StringTokenizer(input_line," ");
          ntok=0;
          while(T.hasMoreTokens())
          {
            token[ntok] = T.nextToken();
            ntok++;
          }
          if(token[0].equals("draw3d"))
          {
            System.out.println("draw3d"); // first line
          }
          else if(token[0].equals("enddraw3d"))
          {
            System.out.println("enddraw3d"); // last line
            System.out.println("readDraw3d finished");
            paint(gXY); // actually paints all views
            return;    
          }
          else if(token[0].equals("Cube"))
          {
            objC.name = token[0];
            objC.instance = Integer.parseInt(token[1]);
            ri = Integer.parseInt(token[2]);
            gi = Integer.parseInt(token[3]);
            bi = Integer.parseInt(token[4]);
            objC.objColor = new Color(ri, gi, bi); 
            objC.drawList = null;
          }
          else if(token[0].equals("Cubebox"))
          {
            objC.bx1 = Integer.parseInt(token[1]);
            objC.bx2 = Integer.parseInt(token[2]);
            objC.by1 = Integer.parseInt(token[3]);
            objC.by2 = Integer.parseInt(token[4]);
            objC.bz1 = Integer.parseInt(token[5]);
            objC.bz2 = Integer.parseInt(token[6]);
          }
          else if(token[0].equals("Cubexyz"))
          {
            objC.x1 = Integer.parseInt(token[1]);
            objC.x2 = Integer.parseInt(token[2]);
            objC.y1 = Integer.parseInt(token[3]);
            objC.y2 = Integer.parseInt(token[4]);
            objC.z1 = Integer.parseInt(token[5]);
            objC.z2 = Integer.parseInt(token[6]);
            drawList.addLast(new objCube(objC));
          }
          else if(token[0].equals("Sphere"))
          {
            objS.name = token[0];
            objS.instance = Integer.parseInt(token[1]);
            ri = Integer.parseInt(token[2]);
            gi = Integer.parseInt(token[3]);
            bi = Integer.parseInt(token[4]);
            objS.objColor = new Color(ri, gi, bi); 
            objS.drawList = null;
          }
          else if(token[0].equals("Spherebox"))
          {
            objS.bx1 = Integer.parseInt(token[1]);
            objS.bx2 = Integer.parseInt(token[2]);
            objS.by1 = Integer.parseInt(token[3]);
            objS.by2 = Integer.parseInt(token[4]);
            objS.bz1 = Integer.parseInt(token[5]);
            objS.bz2 = Integer.parseInt(token[6]);
          }
          else if(token[0].equals("Spherexyz"))
          {
            objS.x1 = Integer.parseInt(token[1]);
            objS.y1 = Integer.parseInt(token[2]);
            objS.z1 = Integer.parseInt(token[3]);
            objS.r  = Integer.parseInt(token[4]);
            drawList.addLast(new objSphere(objS));
          }
          else if(token[0].equals("Rect"))
          {
            objR.name = token[0];
            objR.instance = Integer.parseInt(token[1]);
            ri = Integer.parseInt(token[2]);
            gi = Integer.parseInt(token[3]);
            bi = Integer.parseInt(token[4]);
            objR.objColor = new Color(ri, gi, bi); 
            objR.drawList = null;
          }
          else if(token[0].equals("Rectbox"))
          {
            objR.bx1 = Integer.parseInt(token[1]);
            objR.bx2 = Integer.parseInt(token[2]);
            objR.by1 = Integer.parseInt(token[3]);
            objR.by2 = Integer.parseInt(token[4]);
            objR.bz1 = Integer.parseInt(token[5]);
            objR.bz2 = Integer.parseInt(token[6]);
          }
          else if(token[0].equals("Rectxyz"))
          {
            objR.x1 = Integer.parseInt(token[1]);
            objR.x2 = Integer.parseInt(token[2]);
            objR.y1 = Integer.parseInt(token[3]);
            objR.y2 = Integer.parseInt(token[4]);
            objR.z1 = Integer.parseInt(token[5]);
            objR.z2 = Integer.parseInt(token[6]);
            drawList.addLast(new objRect(objR));
          }
          else if(token[0].equals("Tri"))
          {
            objT.name = token[0];
            objT.instance = Integer.parseInt(token[1]);
            ri = Integer.parseInt(token[2]);
            gi = Integer.parseInt(token[3]);
            bi = Integer.parseInt(token[4]);
            objT.objColor = new Color(ri, gi, bi); 
            objT.drawList = null;
          }
          else if(token[0].equals("Tribox"))
          {
            objT.bx1 = Integer.parseInt(token[1]);
            objT.bx2 = Integer.parseInt(token[2]);
            objT.by1 = Integer.parseInt(token[3]);
            objT.by2 = Integer.parseInt(token[4]);
            objT.bz1 = Integer.parseInt(token[5]);
            objT.bz2 = Integer.parseInt(token[6]);
          }
          else if(token[0].equals("Trixyz"))
          {
            objT.x1 = Integer.parseInt(token[1]);
            objT.x2 = Integer.parseInt(token[2]);
            objT.x3 = Integer.parseInt(token[3]);
            objT.y1 = Integer.parseInt(token[4]);
            objT.y2 = Integer.parseInt(token[5]);
            objT.y3 = Integer.parseInt(token[6]);
            objT.z1 = Integer.parseInt(token[7]);
            objT.z2 = Integer.parseInt(token[8]);
            objT.z3 = Integer.parseInt(token[9]);
            drawList.addLast(new objTri(objT));
          }
          else
          {
            System.out.println("unknown "+token[0]);
          }
                  
          input_line = file_in.readLine(); // read next input
        }
      }
      catch(FileNotFoundException exception)
      {
        System.out.println(rootfilename+".draw3d  file not found");
      }
      catch(Exception exception) // catches all exceptions
      {
        System.out.println("readDraw3d I/O error.");
      }
      System.out.println("readDraw3d finished");
      paint(gXY); // actually paints all views    
    }
    else
    {
      System.out.println("readDraw3d needs root file name on command line.");
    }
  } // end readDraw3d

  void writeDraw3d()
  {
    System.out.println("writeDraw3d file "+rootfilename+".draw3d");
    if(have_filename)
    {
      try
      {
        FileWriter fout = new FileWriter(rootfilename+".draw3d");
        BufferedWriter b_fout = new BufferedWriter(fout);
        PrintWriter file_out = new PrintWriter(b_fout);

        file_out.println("draw3d data");
        // write out figures per list
        int k = drawList.size();
        objList objL;
        int ri, gi, bi; // color parts
        for(int i=0; i<k; i++)
        {
          objL = (objList)(drawList.get(i));
          ri = (objL.objColor).getRed();
          gi = (objL.objColor).getGreen();
          bi = (objL.objColor).getBlue();
          file_out.println(objL.name+" "+objL.instance+" "+
                           ri+" "+gi+" "+bi);
          if((objL.name).equals("Cube"))
          {
            file_out.println("Cubebox "+objL.bx1+" "+objL.bx2+" "+
                                        objL.by1+" "+objL.by2+" "+
                                        objL.bz1+" "+objL.bz2);
            objC = (objCube)objL;
            file_out.println("Cubexyz "+objC.x1+" "+objC.x2+" "+
                                        objC.y1+" "+objC.y2+" "+
                                        objC.z1+" "+objC.z2);
          }
          else if((objL.name).equals("Sphere"))
          {
            file_out.println("Spherebox "+objL.bx1+" "+objL.bx2+" "+
                                          objL.by1+" "+objL.by2+" "+
                                          objL.bz1+" "+objL.bz2);
            objS = (objSphere)objL;
            file_out.println("Spherexyz "+objS.x1+" "+objS.y1+" "+
                                          objS.z1+" "+objS.r);
          }
          else if((objL.name).equals("Rect"))
          {
            file_out.println("Rectbox "+objL.bx1+" "+objL.bx2+" "+
                                        objL.by1+" "+objL.by2+" "+
                                        objL.bz1+" "+objL.bz2);
            objR = (objRect)objL;
            file_out.println("Rectxyz "+objR.x1+" "+objR.x2+" "+
                                        objR.y1+" "+objR.y2+" "+
                                        objR.z1+" "+objR.z2);
          }
          else if((objL.name).equals("Tri"))
          {
            file_out.println("Tribox "+objL.bx1+" "+objL.bx2+" "+
                                       objL.by1+" "+objL.by2+" "+
                                       objL.bz1+" "+objL.bz2);
            objT = (objTri)objL;
            file_out.println("Trixyz "+objT.x1+" "+objT.x2+" "+objT.x3+" "+
                                       objT.y1+" "+objT.y2+" "+objT.y3+" "+
                                       objT.z1+" "+objT.z2+" "+objT.z3);
          }
        }

        file_out.println("enddraw3d data");
        file_out.close();
        System.out.println("writeDraw3d finished");
      }
      catch(Exception exception) // catches all exceptions
      {
        System.out.println("writeDraw3d I/O error.");
      }
    }
    else
    {
      System.out.println("writeDraw3d needs root file name on command line.");
    }
  } // end writeDraw3d
  
  void writeStl()
  {
    System.out.println("writeStl file "+rootfilename+".stl");
    if(have_filename)
    {
      try
      {
        FileWriter fout = new FileWriter(rootfilename+".stl");
        BufferedWriter b_fout = new BufferedWriter(fout);
        PrintWriter file_out = new PrintWriter(b_fout);

        file_out.println("solid");
        // write out figures per list
        int k = drawList.size();
        objList objL;
        for(int i=0; i<k; i++)
        {
          objL = (objList)(drawList.get(i));
          if((objL.name).equals("Cube"))
          {
            objC = (objCube)objL;
            triStl(objC.x1, objC.y1, objC.z1,
                   objC.x2, objC.y1, objC.z1,
                   objC.x2, objC.y2, objC.z1, file_out);
            triStl(objC.x1, objC.y1, objC.z1,
                   objC.x2, objC.y2, objC.z1,
                   objC.x1, objC.y2, objC.z1, file_out);
            triStl(objC.x1, objC.y1, objC.z2,
                   objC.x2, objC.y1, objC.z2,
                   objC.x2, objC.y2, objC.z2, file_out);
            triStl(objC.x1, objC.y1, objC.z2,
                   objC.x2, objC.y2, objC.z2,
                   objC.x1, objC.y2, objC.z2, file_out);

            triStl(objC.x1, objC.y1, objC.z1,
                   objC.x1, objC.y2, objC.z1,
                   objC.x1, objC.y2, objC.z2, file_out);
            triStl(objC.x1, objC.y1, objC.z1,
                   objC.x1, objC.y2, objC.z2,
                   objC.x1, objC.y1, objC.z2, file_out);
            triStl(objC.x2, objC.y1, objC.z1,
                   objC.x2, objC.y2, objC.z1,
                   objC.x2, objC.y2, objC.z2, file_out);
            triStl(objC.x2, objC.y1, objC.z1,
                   objC.x2, objC.y2, objC.z2,
                   objC.x2, objC.y1, objC.z2, file_out);

            triStl(objC.x1, objC.y1, objC.z1,
                   objC.x1, objC.y1, objC.z2,
                   objC.x1, objC.y1, objC.z2, file_out);
            triStl(objC.x1, objC.y1, objC.z1,
                   objC.x1, objC.y1, objC.z2,
                   objC.x1, objC.y1, objC.z1, file_out);
            triStl(objC.x2, objC.y2, objC.z1,
                   objC.x2, objC.y2, objC.z2,
                   objC.x2, objC.y2, objC.z2, file_out);
            triStl(objC.x2, objC.y2, objC.z1,
                   objC.x2, objC.y2, objC.z2,
                   objC.x2, objC.y2, objC.z1, file_out);
          }
          else if((objL.name).equals("Sphere"))
          {
            objS = (objSphere)objL;
          }
          else if((objL.name).equals("Rect"))
          {
            objR = (objRect)objL;
          }
          else if((objL.name).equals("Tri"))
          {
            objT = (objTri)objL;
          }
        }

        file_out.println("endsolid");
        file_out.close();
        System.out.println("writeStl finished");
      }
      catch(Exception exception) // catches all exceptions
      {
        System.out.println("writeStl I/O error.");
      }
    }
    else
    {
      System.out.println("writeStl needs root file name on command line.");
    }
  } // end writeStl
  
  void triStl(int x1, int y1, int z1, int x2, int y2, int z2,
              int x3, int y3, int z3, PrintWriter file_out)
  {
    double dx1, dx2, dy1, dy2, dz1, dz2, nx, ny, nz, denom;
    double x[] = new double[3];
    double y[] = new double[3];
    double z[] = new double[3];

    x[0] = (double)x1;
    x[1] = (double)x2;
    x[2] = (double)x3;
    y[0] = (double)y1;
    y[1] = (double)y2;
    y[2] = (double)y3;
    z[0] = (double)z1;
    z[1] = (double)z2;
    z[2] = (double)z3;
    /* compute normal */
    dx1 = x[2]-x[0];
    dy1 = y[2]-y[0];
    dz1 = z[2]-z[0];
    dx2 = x[2]-x[1];
    dy2 = y[2]-y[1];
    dz2 = z[2]-z[1];
    nx = dy1*dz2 - dz1*dy2;
    ny = dz1*dx2 - dx1*dz2;
    nz = dx1*dy2 - dy1*dx2;
    denom = Math.sqrt(nx*nx + ny*ny + nz*nz);
    nx = nx/denom;
    ny = ny/denom;
    nz = nz/denom;

    file_out.println("  facet normal "+nx+" "+ny+" "+nz);
    file_out.println("    outer loop");
    file_out.println("      vertex "+x[0]+" "+y[0]+" "+z[0]);
    file_out.println("      vertex "+x[1]+" "+y[1]+" "+z[1]);
    file_out.println("      vertex "+x[2]+" "+y[2]+" "+z[2]);
    file_out.println("    endloop");
    file_out.println("  endfacet");
  }
  
  void sort2up(int k, int ix[], int val[])
  {
    int t;
    for(int i=0; i<k-1; i++)
    {
      for(int j=i+1; j<k; j++)
      {
        if(val[i]>val[j])
        {
          t=val[i]; val[i]=val[j]; val[j]=t;
           t=ix[i];  ix[i]= ix[j];  ix[j]=t;
        }
      }
    }
  }
  
  static public void main(String[] args)
  {
    for(int i=0; i<args.length; i++) System.out.println("arg["+i+"]="+args[i]);
    new draw3D3(args);
    System.out.println("draw3D3 main ending");
  } // end main
} // end class draw3D3




