/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOUSE. See the GNU
 * General Public License for more details.
 *
 * You should have recieved a copy of the GNU General Public License
 * along with this program; if not write to the Free Software
 * Foundation, inc., 59 Temple Place, Suite 330, Boston MA 02111-1307
 * USA */

/*
 * NurbsCurve.java
 *
 * author   Jonas Forssell, Yuriy Mikhaylovskiy
 */
package j3d;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.Rectangle2D;
import java.io.*;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.tree.*;
import javax.swing.*;

import javax.swing.table.*;
import gui.*;

/**
 * Implementing the NurbsCurve prototype.
 * @author   Timothy F. Rohaly / Jonas Forssell, Yuriy Mikhaylovskiy
 */
public class _NurbSurface extends _Object implements Serializable{
  public Color color;
  protected int uorder, vorder;
  protected int udiv=5;
  protected int vdiv=5;
  protected int numKnots;
  protected Vector geomUDB = new Vector();
  protected Vector geomVDB = new Vector();
  protected Vector geomBDB = new Vector();
  protected XVector nodesDB = new XVector();
  protected XVector elementsDB = new XVector();
  protected Vector arr = new Vector();
  
  
  public _CtrlPoint[][] controlPoints;
  protected float[] uknots, vknots, knots_v;
  private String[] hdr;
  private TableModel tableModel;
  private JTable table;

  public String msh_name = "Dummy";
  public int msh_type = Canvas3D.MESH_Dummy_4;
  public float thickness;
  public int[] mesh_div;
  public Float factor,friction;
  public String contact;
  public Material material = null;

  protected Canvas3D J3D;
  protected PreProcessor PreP;
  protected JComboBox cComboBox1, cComboBox2;
  protected JTextField cTextField1, cTextField2;
  String[] type = {"Shell_BT_4","Shell_C0_3","Contact_Triangle","No mesh"};
  protected int mesh_type_index;
  protected Object material_index;
  protected int masterType = Canvas3D.SURFACE;
  protected JButton jButton1;

  public _NurbSurface(boolean add) {
      this.udiv = 5;
      this.vdiv = 5;
      this.color = Color.CYAN;
      this.thickness = 1.0f;
      this.mesh_div = new int[4];
      this.add = add;
      geometry_type = Canvas3D.SURFACE;
    }
  
  private void writeObject(ObjectOutputStream out)  throws IOException{
    out.writeObject(controlPoints);
    out.writeObject(uknots);
    out.writeObject(vknots);
    out.writeInt(udiv);
    out.writeInt(vdiv);
    out.writeObject(color);
    out.writeInt(geometry_type);
    out.writeObject(msh_name);
    out.writeInt(msh_type);
    out.writeObject(type);
    out.writeFloat(thickness);
    out.writeObject(mesh_div);
    out.writeObject(factor);
    out.writeObject(friction);
    out.writeObject(contact);
    out.writeObject(material);
    out.writeObject(material_index);
    out.writeObject(geomUDB);
    out.writeObject(geomVDB);
    out.writeObject(geomBDB);
    out.writeObject(nodesDB);
    out.writeObject(elementsDB);
  }
  
  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
    controlPoints = (_CtrlPoint[][])in.readObject();
    uknots = (float[])in.readObject();
    vknots = (float[])in.readObject();
    udiv = in.readInt();
    vdiv = in.readInt();
    color = (Color)in.readObject();
    geometry_type = in.readInt();
    msh_name = (String)in.readObject();
    msh_type = in.readInt();
    type = (String[])in.readObject();
    thickness = in.readFloat();
    mesh_div = (int[]) in.readObject();
    factor = (Float) in.readObject();
    friction = (Float) in.readObject();
    contact = (String) in.readObject();
    material = (Material)in.readObject();
    material_index = in.readObject();
    geomUDB = (Vector)in.readObject();
    geomVDB = (Vector)in.readObject();
    geomBDB = (Vector)in.readObject();
    nodesDB = (XVector)in.readObject();
    elementsDB = (XVector)in.readObject();
    selected = false;
    // for(int i=0; i<nodesDB.size(); i++) ((_Object)nodesDB.elementAt(i)).set_Id(i+"");
    // for(int i=0; i<elementsDB.size(); i++) ((_Object)elementsDB.elementAt(i)).set_Id(i+"");
    
  }

  public void reset(){
      // Generate geometry
    generate(udiv,vdiv);
      // Generate mesh 
    mesh(msh_type,0.0f);
  }


  /**
   * This method generates the points which makes up the curve.
   * Note: before calling this method, make sure you have set the following
   * knots 			- the knot vector
   * controlPoints 	- The controlPoint vector
   * order			- Curve segment degree
   * 
   * @param div		The number of divisions for the curve (number of points)
   */
  
  
  public void generate(int udiv, int vdiv) {

    int numUControlPoints = controlPoints.length;
    int numVControlPoints = controlPoints[0].length;
    int numUKnots         = uknots.length;
    int numVKnots         = vknots.length;
    geomUDB = new Vector();
    geomVDB = new Vector();
    geomBDB = new Vector();

    // Generate two middle curves and a boundary to repesent the surface geometry
    
    this.generateRefinedCurve(geomUDB, numUControlPoints, uorder, uknots, numVControlPoints, vorder, vknots, controlPoints,-1f,0.5f,true);
    this.generateRefinedCurve(geomVDB, numUControlPoints, uorder, uknots, numVControlPoints, vorder, vknots, controlPoints, 0.5f,-1f,true);

    this.generateRefinedCurve(geomBDB, numUControlPoints, uorder, uknots, numVControlPoints, vorder, vknots, controlPoints, -1f, 0.0f,true);
    this.generateRefinedCurve(geomBDB, numUControlPoints, uorder, uknots, numVControlPoints, vorder, vknots, controlPoints, 1.0f, -1f,true);
    this.generateRefinedCurve(geomBDB, numUControlPoints, uorder, uknots, numVControlPoints, vorder, vknots, controlPoints, -1f, 1.0f,false);
    this.generateRefinedCurve(geomBDB, numUControlPoints, uorder, uknots, numVControlPoints, vorder, vknots, controlPoints, 0.0f, -1f,false);
  }

  private void generateRefinedCurve(Vector db, int n, int p, float[] U, int m, int q, float[] V, _CtrlPoint[][] P, float u, float v, boolean positive)  {
      
      int numKnots = (u < 0 ? U.length : V.length);
      int order = (u < 0 ? p : q);
      Vector tmp = new Vector();
      float distance,ts,te;
      _Point s,e,a;
      
      // Create start vector of u-values along the curve. Use the knot vector and avoid double knots
      tmp.add(new Float(u < 0 ? U[order] : V[order]));
      for (int i=order+1; i < numKnots-order; i++) 
          if (((Float)tmp.lastElement()).floatValue() != (u < 0 ? U[i] : V[i])) tmp.add(new Float(u < 0 ? U[i] : V[i]));
      
      // Refine the vector to within tolerance
      for (int i=0; i<tmp.size()-1; i++) {
          ts = ((Float)tmp.elementAt(i)).floatValue();
          te = ((Float)tmp.elementAt(i+1)).floatValue();
          
          s = lib3D.surfacePoint(n, p, U, m, q, V, P, u < 0 ? ts : u, v < 0 ? ts : v);
          e = lib3D.surfacePoint(n, p, U, m, q, V, P, u < 0 ? te : u, v < 0 ? te : v);
          a = lib3D.surfacePoint(n, p, U, m, q, V, P, u < 0 ? (ts+te)/2f : u, v < 0 ? (ts+te)/2f : v);
          
          distance = lib3D.distancePoint2Line(a,s,e);
          
          if (distance > Canvas3D.GeometricTolerance) {
              tmp.insertElementAt(new Float((ts+te)/2.0f),i+1);
              i--;
          }
      }
      
      // Add resulting points to the geometric database
      if (positive)
          for (int i=0; i<tmp.size(); i++) {
              db.add(lib3D.surfacePoint(n, p, U, m, q, V, P, u < 0 ? ((Float)tmp.elementAt(i)).floatValue() : u, v < 0 ? ((Float)tmp.elementAt(i)).floatValue() : v));
          } else
              for (int i=tmp.size()-1; i>=0; i--) {
                  db.add(lib3D.surfacePoint(n, p, U, m, q, V, P, u < 0 ? ((Float)tmp.elementAt(i)).floatValue() : u, v < 0 ? ((Float)tmp.elementAt(i)).floatValue() : v));
              }    
      
  }
  
  
  public Object[] get_Array(Canvas3D j3d){
    _Point tmp1,tmp2;
    arr = new Vector();
    Object[] shparr;

    // Generate geometry
    for (int i=0; i<geomUDB.size()-1; i++) {
        tmp1 = (_Point)geomUDB.elementAt(i);
        tmp2 = (_Point)geomUDB.elementAt(i+1);
      arr.add(new shpLine(tmp1.x,tmp1.y,tmp1.z,tmp2.x,tmp2.y,tmp2.z, selected ? j3d.SELECTCOLOR : color));
    }
    for (int i=0; i<geomVDB.size()-1; i++) {
        tmp1 = (_Point)geomVDB.elementAt(i);
        tmp2 = (_Point)geomVDB.elementAt(i+1);
      arr.add(new shpLine(tmp1.x,tmp1.y,tmp1.z,tmp2.x,tmp2.y,tmp2.z, selected ? j3d.SELECTCOLOR : color));
    }
    for (int i=0; i<geomBDB.size()-1; i++) {
        tmp1 = (_Point)geomBDB.elementAt(i);
        tmp2 = (_Point)geomBDB.elementAt(i+1);
      arr.add(new shpLine(tmp1.x,tmp1.y,tmp1.z,tmp2.x,tmp2.y,tmp2.z, selected ? j3d.SELECTCOLOR : color));
    }
    
    for (int i=0; i<arr.size(); i++)
        ((shp)arr.elementAt(i)).setShow(show);

    // Generate nodes
    for(int i=0; i<nodesDB.rowsize(); i++)
        for(int j=0; j<nodesDB.colsize(); j++)
        {
            _Node nd = (_Node)nodesDB.elementAt(i,j);
            shparr = nd.get_Array(j3d);
            for (int k=0; k<shparr.length; k++)
                arr.add(shparr[k]);
        }

    // Generate elements
    for(int i=0; i<elementsDB.rowsize(); i++)
        for(int j=0; j<elementsDB.colsize(); j++)
        {
            Object el = elementsDB.elementAt(i,j);
            
            if(el instanceof _Element4) {
                _Element4 el4 = (_Element4)el;
                shparr = el4.get_Array(j3d);
                for (int k=0; k<shparr.length; k++)
                    arr.add(shparr[k]);
            }
            
            if(el instanceof _Element3) {
                _Element3 el3 = (_Element3)el;
                shparr = el3.get_Array(j3d);
                for (int k=0; k<shparr.length; k++)
                    arr.add(shparr[k]);
            }
        }
 
    return arr.toArray();
  }

  /**
   * This method generates the nodes and elements which makes up the surface mesh.
   * Note: before calling this method, make sure you have set the following
   * knots 			- the knot vectors
   * controlPoints 	- The controlPoint array
   * order			- Curve segment degrees
   *
   * Each edge has a number of divisions (div[0] -> div[3])
   *                                                     [2] 
   * They are defined in the following way          +------------+
   * div[0] = divisions of curve u=0-1, v=0         |            |
   * div[1] = divisions of curve u=1, v=0-1     [3] |            | [1]
   * div[2] = divisions of curve u=0-1, v=1         |            |
   * div[3] = divisions of curve u=0, v=0-1         +------------+
   *                                                     [0]
   * 
   * An algorithm will be developed to provide a mesh with minimum number of
   * triangle elements and good quality based on the constraints put on the
   * edges of the surface.
   * 
   * For now, we settle for using only constant divisions defined by div[0] and div[1]
   *    
   * @param type	The element type for the mesh
   * @param size	The length of each element assuming uniform mesh
   */
  
  public void mesh(int type, float msh_size){
      nodesDB = new XVector();
      elementsDB = new XVector();
      
      int numUControlPoints = controlPoints.length;
      int numVControlPoints = controlPoints[0].length;
      int numUKnots         = uknots.length;
      int numVKnots         = vknots.length;
      float length;
      _Point np;
      _Node nn;
      _Element4 ne;
      _Element3 nf;
      
      
      if (msh_type != Canvas3D.MESH_Dummy_4) {
          
          // Generate mesh nodes
          for (int i=0; i<=mesh_div[0]; i++) {
              for (int j=0; j<=mesh_div[1]; j++){
                  np = lib3D.surfacePoint(numUControlPoints, uorder, uknots, numVControlPoints, vorder, vknots, controlPoints, (float)i/(float)mesh_div[0], (float)j/(float)mesh_div[1]);
                  nn = new _Node(np.x,np.y,np.z);
                  nn.set_Id(Id + "." + i + "." + j);
                  nn.setMaster_type(Canvas3D.SURFACE);
                  nn.setShow(show);
                  nodesDB.add(nn);
              }
              if (i < mesh_div[0]) nodesDB.addRow();
          }
          
          // Generate mesh elements
          for (int i=0; i<mesh_div[0]; i++) {
              for (int j=0; j<mesh_div[1]; j++){
                  switch (type) {
                  case Canvas3D.MESH_Shell_BT_4:
                      if (this.areClose((_Node)nodesDB.elementAt(i,j),(_Node)nodesDB.elementAt(i+1,j))) {
                          nf = new _Element3((_Node)nodesDB.elementAt(i,j),(_Node)nodesDB.elementAt(i+1,j+1),(_Node)nodesDB.elementAt(i,j+1),Canvas3D.MESH_Shell_C0_3,material,thickness);
                          nf.set_Id(Id + "." + i +"." + j + ".1");
                          nf.setShow(show);
                          elementsDB.add(nf);
                      }
                      else
                          if (this.areClose((_Node)nodesDB.elementAt(i+1,j),(_Node)nodesDB.elementAt(i+1,j+1))) {
                              nf = new _Element3((_Node)nodesDB.elementAt(i,j),(_Node)nodesDB.elementAt(i+1,j),(_Node)nodesDB.elementAt(i,j+1),Canvas3D.MESH_Shell_C0_3,material,thickness);
                              nf.set_Id(Id + "." + i +"." + j + ".1");
                              nf.setShow(show);
                              elementsDB.add(nf);
                          }
                          else
                              if (this.areClose((_Node)nodesDB.elementAt(i+1,j+1),(_Node)nodesDB.elementAt(i,j+1))) {
                                  nf = new _Element3((_Node)nodesDB.elementAt(i,j),(_Node)nodesDB.elementAt(i+1,j),(_Node)nodesDB.elementAt(i,j+1),Canvas3D.MESH_Shell_C0_3,material,thickness);
                                  nf.set_Id(Id + "." + i +"." + j + ".1");
                                  nf.setShow(show);
                                  elementsDB.add(nf);
                              }
                              else
                                  if (this.areClose((_Node)nodesDB.elementAt(i,j),(_Node)nodesDB.elementAt(i,j+1))) {
                                      nf = new _Element3((_Node)nodesDB.elementAt(i,j),(_Node)nodesDB.elementAt(i+1,j),(_Node)nodesDB.elementAt(i+1,j+1),Canvas3D.MESH_Shell_C0_3,material,thickness);
                                      nf.set_Id(Id + "." + i +"." + j + ".1");
                                      nf.setShow(show);
                                      elementsDB.add(nf);
                                  }
                                  else {
                                      ne = new _Element4((_Node)nodesDB.elementAt(i,j),(_Node)nodesDB.elementAt(i+1,j),(_Node)nodesDB.elementAt(i+1,j+1),(_Node)nodesDB.elementAt(i,j+1),type,material,thickness);
                                      ne.set_Id(Id + "." + i +"." + j);
                                      ne.setShow(show);
                                      elementsDB.add(ne);
                                  }
                      break;
                  case Canvas3D.MESH_Shell_C0_3:
                      nf = new _Element3((_Node)nodesDB.elementAt(i,j),(_Node)nodesDB.elementAt(i+1,j),(_Node)nodesDB.elementAt(i+1,j+1),type,material,thickness);
                      nf.set_Id(Id + "." + i +"." + j + ".1");
                      nf.setShow(show);
                      elementsDB.add(nf);
                      nf = new _Element3((_Node)nodesDB.elementAt(i+1,j+1),(_Node)nodesDB.elementAt(i,j+1),(_Node)nodesDB.elementAt(i,j),type,material,thickness);
                      nf.set_Id(Id + "." + i +"." + j + ".2");
                      nf.setShow(show);
                      elementsDB.add(nf);
                      break;
                  case Canvas3D.MESH_Contact_Triangle:
                      nf = new _Element3((_Node)nodesDB.elementAt(i,j),(_Node)nodesDB.elementAt(i+1,j),(_Node)nodesDB.elementAt(i+1,j+1),type,material,thickness);
                      nf.set_Id(Id + "." + i +"." + j + ".1");
                      nf.setShow(show);
                      elementsDB.add(nf);
                      nf = new _Element3((_Node)nodesDB.elementAt(i+1,j+1),(_Node)nodesDB.elementAt(i,j+1),(_Node)nodesDB.elementAt(i,j),type,material,thickness);
                      nf.set_Id(Id + "." + i +"." + j + ".2");
                      nf.setShow(show);
                      elementsDB.add(nf);
                      break;
                  case Canvas3D.MESH_Dummy_4:
                      ne = new _Element4((_Node)nodesDB.elementAt(i,j),(_Node)nodesDB.elementAt(i+1,j),(_Node)nodesDB.elementAt(i+1,j+1),(_Node)nodesDB.elementAt(i,j+1),type,material,thickness);
                      ne.set_Id(Id + "." + i +"." + j);
                      ne.setShow(show);
                      elementsDB.add(ne);
                      break;
                  }
              }
              if (i < mesh_div[0] - 1) elementsDB.addRow();
          }
      }
  }
  
  public boolean delete() {
        for (int i = 0; i < elementsDB.rowsize(); i++)
            for (int j = 0; j < elementsDB.colsize(); j++) {
                if (((_Object) elementsDB.elementAt(i, j)).isSelected()) {
                    elementsDB.removeElementAt(i, j);
                    j--;
                }
            }
        for (int i = 0; i < nodesDB.rowsize(); i++)
            for (int j = 0; j < nodesDB.colsize(); j++) {
                _Node nd = (_Node) nodesDB.elementAt(i, j);
                if (nd.isSelected()) {
                    boolean using = false;
                    for (int k = 0; k < elementsDB.rowsize(); k++)
                        for (int l = 0; l < elementsDB.colsize(); l++) {
                            _Node[] nds = ((_Object) elementsDB.elementAt(k, l))
                                    .get_Nodes();
                            for (int m = 0; m < nds.length; m++) {
                                if (nds[m].equals(nd))
                                    using = true;
                            }
                        }
                    if (!using) {
                        nodesDB.removeElementAt(i, j);
                        j--;
                    }
                }
            }
        return selected;
    }

  public void Select(String st) {
        selected = st != null;
        if(st==null){
            for(int i=0; i<nodesDB.rowsize(); i++) 
                for (int j=0; j<nodesDB.colsize(); j++)
                    ((_Node)nodesDB.elementAt(i,j)).Select(st);
            for(int i=0; i<elementsDB.rowsize(); i++) 
                for (int j=0; j<elementsDB.colsize(); j++)
                    ((_Object)elementsDB.elementAt(i,j)).Select(st);
          }
    }

  public boolean isSelected(){return selected;}

  public void addSelectedObjects(Vector v) {
      for(int i=0; i<nodesDB.rowsize(); i++) 
          for (int j=0; j<nodesDB.colsize(); j++)
              ((_Node)nodesDB.elementAt(i,j)).addSelectedObjects(v);
      for(int i=0; i<elementsDB.rowsize(); i++) 
          for (int j=0; j<elementsDB.colsize(); j++)
              ((_Object)elementsDB.elementAt(i,j)).addSelectedObjects(v);
      
  }
  
  
  public String toString(){ return "Nurb Surface ID="+Id;}

  public void transform3D(Matrix3D t){
    if(!selected || processed)return;
    for (int i=0; i<controlPoints.length; i++)
        for (int j=0; j<controlPoints[0].length; j++){
      controlPoints[i][j].Select("");
      controlPoints[i][j].transform3D(t);
      controlPoints[i][j].Select(null);
    }
    reset();
  }

  public MutableTreeNode get_TreeNode() {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode("Mesh");
        DefaultMutableTreeNode n_nodes = new DefaultMutableTreeNode("Nodes");
        DefaultMutableTreeNode n_elements = new DefaultMutableTreeNode("Elements");

        for (int i = 0; i < nodesDB.rowsize(); i++)
            for (int j = 0; j < nodesDB.colsize(); j++)
                n_nodes.add(((_Object) nodesDB.elementAt(i, j)).get_TreeNode());
        node.add(n_nodes);

        for (int i = 0; i < elementsDB.rowsize(); i++)
            for (int j = 0; j < elementsDB.colsize(); j++)
                n_elements.add(((_Object) elementsDB.elementAt(i, j))
                        .get_TreeNode());
        node.add(n_elements);

        return node;
    }

  
  public JPanel getEditPanel(Canvas3D j3d, PreProcessor pp){
      this.J3D = j3d;
      this.PreP = pp;
      cTextField1 = new JTextField("" + thickness);
      
      JPanel cPanel0 = new JPanel();
      cPanel0.setLayout(new BoxLayout(cPanel0, BoxLayout.Y_AXIS));
      cComboBox1 = new JComboBox(type);
      cComboBox2 = new JComboBox();
      JPanel cPanel1 = new JPanel();
      JPanel cPanel2 = new JPanel();
      
      cPanel0.add(cComboBox1);
    	cComboBox1.setForeground(Color.blue);
    	cComboBox1.setSelectedIndex(mesh_type_index);
        cComboBox1.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                int c = e.getKeyCode();

                if (c == KeyEvent.VK_ENTER) {
                    cTextField1.requestFocus();
                    cTextField1.selectAll();
                }
            }
        });
    	
      cPanel0.add(cPanel1);
      	cPanel1.setLayout(new GridLayout());
      	cPanel1.add(new JLabel("Thickness"), null);
      	cPanel1.add(cTextField1, null);
        cTextField1.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                int c = e.getKeyCode();

                if (c == KeyEvent.VK_ENTER) {
                    cComboBox2.requestFocus();
                }
            }
        });
        	
      cPanel0.add(cPanel2);
      	cPanel2.setLayout(new BorderLayout());
      	cPanel2.add(new JLabel("Material"), BorderLayout.WEST);
      	cPanel2.add(cComboBox2, BorderLayout.EAST);
      	cComboBox2.setActionCommand("");
      	cComboBox2.setLightWeightPopupEnabled(false);
        cComboBox2.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                int c = e.getKeyCode();

                if (c == KeyEvent.VK_ENTER) {
                    jButton1.requestFocus();
                }
            }
        });

      for(Enumeration en = PreP.MatDB.keys(); en.hasMoreElements();)  cComboBox2.addItem(en.nextElement());
      if (material_index == null)
          cComboBox2.setSelectedIndex(0);
      else
          cComboBox2.setSelectedItem(material_index);
      
      return cPanel0;
  }

  protected void update(ActionEvent e) {
      mesh_type_index = cComboBox1.getSelectedIndex();
      if(mesh_type_index==0) msh_type = Canvas3D.MESH_Shell_BT_4;
      if(mesh_type_index==1) msh_type = Canvas3D.MESH_Shell_C0_3;
      if(mesh_type_index==2) msh_type = Canvas3D.MESH_Contact_Triangle;
      if(mesh_type_index==3) msh_type = Canvas3D.MESH_Dummy_4;
      
      thickness = Float.parseFloat(cTextField1.getText());
      material_index = cComboBox2.getSelectedItem();
      material = (Material)PreP.MatDB.get(material_index+"");
  }
  
  public _Node[] get_Nodes(){
      _Node[] arr = new _Node[nodesDB.rowsize()*nodesDB.colsize()];
      for (int i = 0; i < nodesDB.rowsize(); i++)
            for (int j = 0; j < nodesDB.colsize(); j++)
                arr[i*nodesDB.colsize()+j] = (_Node) nodesDB.elementAt(i, j);
      return arr;
    }
  
  public _Object[] get_Elements(){
      _Object[] arr = new _Object[elementsDB.rowsize()*elementsDB.colsize()];
      for(int i=0; i<elementsDB.rowsize(); i++) 
          for(int j=0; j<elementsDB.colsize(); j++)
              arr[i*elementsDB.colsize()+j] = (_Object)elementsDB.elementAt(i,j);
      return arr;
    }
  public _Object duplicate(Canvas3D out, boolean add){ 
      _NurbSurface o = null;
      try {
        o = (_NurbSurface)this.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    
      return o; 
      }
  
   public Vector3D getCenter() {
       int numUControlPoints = controlPoints.length;
       int numVControlPoints = controlPoints[0].length;

       _Point s = lib3D.surfacePoint(numUControlPoints, uorder, uknots, numVControlPoints, vorder, vknots, controlPoints, 0.5f, 0.5f);

       return s.getVector();
   }

   public boolean isPickPoint(int x, int y, boolean shw) {
       boolean check = false;
       int s = geomUDB.size() + geomVDB.size() + geomBDB.size() - 3;
       _Node nd;
       _Object el;
       
       // Select this nurb surface if geometry is picked
       if (show == shw)
       for (int i=0; i< s; i++) 
           check = (((shp)arr.elementAt(i)).isPickPoint(x,y) == true ? true : check);
       
       // Check nodes
       for(int i=0; i<nodesDB.rowsize(); i++)
           for(int j=0; j<nodesDB.colsize(); j++)
           {
               nd = (_Node)nodesDB.elementAt(i,j);
               if (nd.isPickPoint(x,y,shw)) nd.Select("");
           }
       
       // Check elements
       for(int i=0; i<elementsDB.rowsize(); i++)
           for(int j=0; j<elementsDB.colsize(); j++)
           {
               el = (_Object)elementsDB.elementAt(i,j);
               if (el.isPickPoint(x,y,shw)) el.Select("");
           }
       
       
       return check;
   }

public boolean isPickPoint(Rectangle2D r, boolean shw) {
       boolean check = true;
       int s = geomUDB.size() + geomVDB.size() + geomBDB.size() - 3;
       _Node nd;
       _Object el;
       
       if (show == shw)
       for (int i=0; i< s; i++) 
       	check = (((shp)arr.elementAt(i)).isPickPoint(r) == false ? false : check);
       
       // Check nodes
       for(int i=0; i<nodesDB.rowsize(); i++)
           for(int j=0; j<nodesDB.colsize(); j++)
           {
               nd = (_Node)nodesDB.elementAt(i,j);
               if (nd.isPickPoint(r,shw)) nd.Select("");
           }
       
       // Check elements
       for(int i=0; i<elementsDB.rowsize(); i++)
           for(int j=0; j<elementsDB.colsize(); j++)
           {
               el = (_Object)elementsDB.elementAt(i,j);
               if (el.isPickPoint(r,shw)) el.Select("");
           }
       
       return check;
   }
   
   private boolean areClose(_Node n1, _Node n2) {
       float d,dx,dy,dz;
       
       dx = n2.x - n1.x;
       dy = n2.y - n1.y;
       dz = n2.z - n1.z;
       
       d = dx*dx + dy*dy + dz*dz;
       
       if (d > Canvas3D.NODE_MERGE_TOLERANCE*Canvas3D.NODE_MERGE_TOLERANCE) return false;
       
       return true;
   }
   
   
   
}

