From 41cd6c47b23975098cd155517790e018670785e7 Mon Sep 17 00:00:00 2001
From: Kenneth Russel <kbrussel@alum.mit.edu>
Date: Mon, 15 Jun 2009 23:12:27 +0000
Subject: Copied JOGL_2_SANDBOX r350 on to trunk; JOGL_2_SANDBOX branch is now
 closed

git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/../svn-server-sync/jogl-demos/trunk@352 3298f667-5e0e-4b4a-8ed4-a3559d26a5f4
---
 src/demos/nurbs/surfaceapp/ActListener.java        |  330 +++
 src/demos/nurbs/surfaceapp/GLListener.java         |  344 +++
 src/demos/nurbs/surfaceapp/MyFloat.java            |   55 +
 src/demos/nurbs/surfaceapp/PrintfFormat.java       | 3090 ++++++++++++++++++++
 src/demos/nurbs/surfaceapp/SliderListener.java     |   36 +
 src/demos/nurbs/surfaceapp/SpinnerListener.java    |   52 +
 src/demos/nurbs/surfaceapp/Surface.java            |  472 +++
 src/demos/nurbs/surfaceapp/SurfaceApp.java         | 1065 +++++++
 .../nurbs/surfaceapp/SurfaceMouseListener.java     |  277 ++
 9 files changed, 5721 insertions(+)
 create mode 100755 src/demos/nurbs/surfaceapp/ActListener.java
 create mode 100755 src/demos/nurbs/surfaceapp/GLListener.java
 create mode 100755 src/demos/nurbs/surfaceapp/MyFloat.java
 create mode 100755 src/demos/nurbs/surfaceapp/PrintfFormat.java
 create mode 100755 src/demos/nurbs/surfaceapp/SliderListener.java
 create mode 100755 src/demos/nurbs/surfaceapp/SpinnerListener.java
 create mode 100755 src/demos/nurbs/surfaceapp/Surface.java
 create mode 100755 src/demos/nurbs/surfaceapp/SurfaceApp.java
 create mode 100755 src/demos/nurbs/surfaceapp/SurfaceMouseListener.java

(limited to 'src/demos/nurbs/surfaceapp')

diff --git a/src/demos/nurbs/surfaceapp/ActListener.java b/src/demos/nurbs/surfaceapp/ActListener.java
new file mode 100755
index 0000000..c55b561
--- /dev/null
+++ b/src/demos/nurbs/surfaceapp/ActListener.java
@@ -0,0 +1,330 @@
+package demos.nurbs.surfaceapp;
+
+import java.awt.AWTEvent;
+import java.awt.event.ActionEvent;
+import java.util.Collections;
+import java.util.Vector;
+
+import javax.swing.AbstractAction;
+import javax.swing.ImageIcon;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+import demos.nurbs.icons.*;
+
+/**
+ * Class reacting to events occuring on toolbar and menu
+ * Třída reagující na události z nástrojové lišty a menu
+ * @author Tomáš Hráský
+ *
+ */
+@SuppressWarnings("serial")
+public class ActListener extends AbstractAction
+{
+  /**
+   * Parent window
+   * Odkaz na rodičovské okno
+   */
+  private SurfaceApp app;
+  /**
+   * FIle chooser object
+   * Objekt pro výběr souboru
+   */
+  private JFileChooser fc;
+
+  /**
+   * Creates new instance with link to parent window
+   * Vytvoří instanci objektu s odkazem na rodičovské okno
+   * @param app parent window
+   */
+  public ActListener(SurfaceApp app) {
+    this.app=app;
+    fc=new JFileChooser("./");
+  }
+
+  /* (non-Javadoc)
+   * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+   */
+  public void actionPerformed(ActionEvent e) {
+    if(e.getActionCommand()==SurfaceApp.PRIDAT_AC_RADEK){
+      if(Surface.getInstance().getPointsInU()>=2){
+        Surface surface=Surface.getInstance();
+        Vector<Float> lastRow=new Vector<Float>();
+        int i;
+        for(i=surface.getCtrlPoints().length-1;i>=surface.getCtrlPoints().length-surface.getPointsInV()*4;i--){
+          lastRow.add(surface.getCtrlPoints()[i]);
+        }
+				
+        Collections.reverse(lastRow);
+				
+        for(int j=0;j<lastRow.size();j+=4){
+          lastRow.set(j,lastRow.get(j)/lastRow.get(j+3));
+          lastRow.set(j+1,lastRow.get(j+1)/lastRow.get(j+3));
+          lastRow.set(j+2,lastRow.get(j+2)/lastRow.get(j+3));
+        }
+				
+				
+        Vector<Float> prevLastRow=new Vector<Float>();
+        for(;i>=surface.getCtrlPoints().length-2*surface.getPointsInV()*4;i--){
+          prevLastRow.add(surface.getCtrlPoints()[i]);
+        }
+        Collections.reverse(prevLastRow);
+
+        for(int j=0;j<prevLastRow.size();j+=4){
+          prevLastRow.set(j,prevLastRow.get(j)/prevLastRow.get(j+3));
+          prevLastRow.set(j+1,prevLastRow.get(j+1)/prevLastRow.get(j+3));
+          prevLastRow.set(j+2,prevLastRow.get(j+2)/prevLastRow.get(j+3));
+        }
+				
+				
+        Vector<Float> diffs=new Vector<Float>();
+        for(i=0;i<prevLastRow.size();i++){
+          if((i+1)%4==0)
+            diffs.add(0f);
+          else
+            diffs.add(lastRow.get(i)-prevLastRow.get(i));
+        }
+				
+				
+        //TODO ošetřit speciální případy (0 nebo 1 řada bodů)
+        //TODO react to special cases - 0 or 1 row of control points
+        Vector<Float> newCtrls=new Vector<Float>();
+        i=0;
+				
+        for(float f:surface.getCtrlPoints()){
+          newCtrls.add(f);
+        }
+        //				newCtrls.addAll(lastRow);
+        for(i=0;i<lastRow.size();i++){
+          newCtrls.add(lastRow.get(i)+diffs.get(i));
+        }
+				
+				
+        float[] newCtrlArr=new float[newCtrls.size()];
+        i=0;
+				
+        for(float f:newCtrls)
+          newCtrlArr[i++]=f;
+				
+        surface.setIsSurfaceFinished(false);
+        surface.setPointsInU(surface.getPointsInU()+1);
+        surface.setCtrlPoints(newCtrlArr);
+      }else{
+        //TODO informaci o tom že to lze jen při dvou řádcích, ano ne dialog, pokud ano, tak zavolá akci nové plochy
+        //TODO inform that this can be done only with two row -> yes/no dialog -> if yes->new surface action
+        //int retval=JOptionPane.showOptionDialog(null,"Malý počet bodů pro vytvoření nového řádku. Přejete si definovat novou plochu?","Definovat novou plochu?",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE,null,null,JOptionPane.YES_OPTION);
+        int retval=JOptionPane.showOptionDialog(null,"Not enough points for newe row. Would you like to define new surface?","Define new surface?",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE,null,null,JOptionPane.YES_OPTION);
+        if(retval==JOptionPane.YES_OPTION)
+          actionPerformed(new ActionEvent(this,AWTEvent.RESERVED_ID_MAX+1,SurfaceApp.NOVA_AC));
+      }
+    }else if(e.getActionCommand()==SurfaceApp.PRIDAT_AC_SLOUPEC){
+      if(Surface.getInstance().getPointsInV()>=2){
+        Surface srf = Surface.getInstance();
+        Vector<Float> leftCol=new Vector<Float>();
+        for(int i=0;i<srf.getCtrlPoints().length;i+=srf.getPointsInV()*4){
+          leftCol.add(srf.getCtrlPoints()[i]);
+          leftCol.add(srf.getCtrlPoints()[i+1]);
+          leftCol.add(srf.getCtrlPoints()[i+2]);
+          leftCol.add(srf.getCtrlPoints()[i+3]);
+        }
+        Vector<Float> nextCol=new Vector<Float>();
+        for(int i=4;i<srf.getCtrlPoints().length;i+=srf.getPointsInV()*4){
+          nextCol.add(srf.getCtrlPoints()[i]);
+          nextCol.add(srf.getCtrlPoints()[i+1]);
+          nextCol.add(srf.getCtrlPoints()[i+2]);
+          nextCol.add(srf.getCtrlPoints()[i+3]);
+        }
+        //			System.out.println(nextCol);
+			
+        for(int j=0;j<leftCol.size();j+=4){
+          leftCol.set(j,leftCol.get(j)/leftCol.get(j+3));
+          leftCol.set(j+1,leftCol.get(j+1)/leftCol.get(j+3));
+          leftCol.set(j+2,leftCol.get(j+2)/leftCol.get(j+3));
+        }
+			
+        for(int j=0;j<nextCol.size();j+=4){
+          nextCol.set(j,nextCol.get(j)/nextCol.get(j+3));
+          nextCol.set(j+1,nextCol.get(j+1)/nextCol.get(j+3));
+          nextCol.set(j+2,nextCol.get(j+2)/nextCol.get(j+3));
+        }
+			
+        Vector<Float> diffs=new Vector<Float>();
+        for(int i=0;i<nextCol.size();i++){
+          if((i+1)%4==0)
+            diffs.add(0f);
+          else
+            diffs.add(leftCol.get(i)-nextCol.get(i));
+					
+        }
+			
+        Vector<Float> newCol=new Vector<Float>();
+        for(int i=0;i<diffs.size();i++){
+          newCol.add(leftCol.get(i)+diffs.get(i));
+        }
+			
+        Vector<float[]> oldPoints=new Vector<float[]>();
+        for(int i=0;i<srf.getCtrlPoints().length;i+=4){
+          float[] pole={srf.getCtrlPoints()[i],srf.getCtrlPoints()[i+1],srf.getCtrlPoints()[i+2],srf.getCtrlPoints()[i+3]};
+          oldPoints.add(pole);
+        }
+			
+			
+        int index=0;
+			
+        Vector<Integer> indexes=new Vector<Integer>();
+			
+        for(int i=index;i<srf.getCtrlPoints().length/4;i+=srf.getPointsInV()){
+          indexes.add(i);
+        }
+			
+        //			System.out.println(indexes);
+        int korekce=0;
+        for(int i=0;i<oldPoints.size();i++)
+          if(indexes.contains(Integer.valueOf(i))){
+            oldPoints.add(i+korekce,null);
+            //					System.out.println(i+korekce);
+            korekce++;
+          }
+        korekce=0;
+        //			for(int i=indexes.size()-1,j=newCol.size()-4;i>=0&&j>=0;i--,j-=4){
+        for(int i=0,j=0;i<indexes.size()&&j<newCol.size();i++,j+=4){
+          float[] pole={newCol.get(j),newCol.get(j+1),newCol.get(j+2),newCol.get(j+3)};
+          oldPoints.set(indexes.get(i)+korekce,pole);
+          korekce++;
+          //				System.out.println(indexes.get(i)+korekce);
+        }
+			
+        float[] newPoints=new float[oldPoints.size()*4];
+        int i=0;
+        for(float[] f:oldPoints){
+          newPoints[i++]=f[0];
+          newPoints[i++]=f[1];
+          newPoints[i++]=f[2];
+          newPoints[i++]=f[3];
+        }
+        srf.setIsSurfaceFinished(false);
+        srf.setPointsInV(srf.getPointsInV()+1);
+        srf.setBodIndex(-1);
+        srf.setCtrlPoints(newPoints);
+      }else{
+        //int retval=JOptionPane.showOptionDialog(null,"Malý počet bodů pro vytvoření nového sloupce. Přejete si definovat novou plochu?","Definovat novou plochu?",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE,null,null,JOptionPane.YES_OPTION);
+        int retval=JOptionPane.showOptionDialog(null,"Not enough points for new column. Would you like to define new surface?","Define new surface?",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE,null,null,JOptionPane.YES_OPTION);
+        if(retval==JOptionPane.YES_OPTION)
+          actionPerformed(new ActionEvent(this,AWTEvent.RESERVED_ID_MAX+1,SurfaceApp.NOVA_AC));
+      }
+    }else if(e.getActionCommand()==SurfaceApp.SMAZAT_AC_RADEK){
+			
+    }else if(e.getActionCommand()==SurfaceApp.SMAZAT_AC_SLOUPEC){
+			
+    }else if(e.getActionCommand()==SurfaceApp.UZAVRENY_AC){
+      app.uzavernyKV();
+    }else if(e.getActionCommand()==SurfaceApp.OTEVRENY_AC){
+      app.otevrenyKV();
+    }else if(e.getActionCommand()==SurfaceApp.ULOZIT_AC){
+      if(fc.showSaveDialog(app)==JFileChooser.APPROVE_OPTION){
+        Surface.getInstance().persist(fc.getSelectedFile());
+      }
+    }else if(e.getActionCommand()==SurfaceApp.NACIST_AC){
+      if(fc.showOpenDialog(app)==JFileChooser.APPROVE_OPTION){
+        try{
+          Surface.getInstance().unPersist(fc.getSelectedFile());
+          app.updateGLCanvas();
+          app.selectMoveButt();
+          app.updateJKnotSlider();
+        }catch(Exception e1){
+          //JOptionPane.showMessageDialog(app,"Chyba při načítání ze souboru","Chyba",JOptionPane.ERROR_MESSAGE);
+          JOptionPane.showMessageDialog(app,"Error reading file","Error",JOptionPane.ERROR_MESSAGE);
+        }
+      }
+    }else if(e.getActionCommand()==SurfaceApp.NOVA_AC){
+      Surface.getInstance().clear();
+      app.getMouseListener().setBodIndex(-1);
+      Surface.getInstance().setBodIndex(-1);
+      app.updateGLCanvas();
+      app.updateJKnotSlider();
+			
+      String retval2=null,retval=null;
+      //retval=JOptionPane.showInputDialog(null,"Zadejte počet bodů ve směru paramteru U (řádků)",new Integer(4));
+      retval=JOptionPane.showInputDialog(null,"Number of control points in U direction (rows)",new Integer(4));
+      if(retval!=null)
+        //retval2=JOptionPane.showInputDialog(null,"Zadejte počet bodů ve směru paramteru V (sloupců)",new Integer(4));
+        retval2=JOptionPane.showInputDialog(null,"Number of control points in V direction (columns)",new Integer(4));
+      if(retval!=null&&retval2!=null){
+        try{
+          int radku=(new Integer(retval)).intValue();
+          int sloupcu=(new Integer(retval2)).intValue();
+					 
+          Surface.getInstance().setPointsInU(radku);
+          Surface.getInstance().setPointsInV(sloupcu);
+					 
+          int krokX=600/sloupcu;
+          int krokZ=-600/radku;
+					 
+          Vector<Float> souradnice=new Vector<Float>();
+          float x = 0,z = 0;
+          for(int i=0;i<radku;i++){
+            z=i*krokZ;
+            for(int j=0;j<sloupcu;j++){
+              x=j*krokX;
+              souradnice.add(x);
+              souradnice.add(0f);//Y
+              souradnice.add(z);
+              souradnice.add(1f);//W
+            }
+          }
+					 
+          float[] ctrlpoints=new float[souradnice.size()];
+          int i=0;
+          for(Float d:souradnice)
+            ctrlpoints[i++]=d.floatValue();
+					 
+          Surface.getInstance().setCtrlPoints(ctrlpoints);
+					 
+					 
+        }catch(NumberFormatException ex){
+          //JOptionPane.showMessageDialog(null,"Chybný formát přirozeného čísla","Chyba!",JOptionPane.ERROR_MESSAGE);
+          JOptionPane.showMessageDialog(null,"Wrong natural number format","Error!",JOptionPane.ERROR_MESSAGE);
+        }
+      }
+			
+      app.updateGLCanvas();
+			
+    }else if(e.getActionCommand()==SurfaceApp.EXIT_AC){
+      //TODO dotaz na uložení ??
+      System.exit(0);
+    }else if(e.getActionCommand()==SurfaceApp.STUPEN_AC){
+      try{
+        String retval2=null;
+        //String retval=JOptionPane.showInputDialog(null,"Zadejte stupeň křivky ve směru parametru U",new Integer(Surface.getInstance().getOrderU()));
+        String retval=JOptionPane.showInputDialog(null,"Degree in U direction",new Integer(Surface.getInstance().getOrderU()));
+        if(retval!=null) 
+          retval2=JOptionPane.showInputDialog(null,"Degree in V direction",new Integer(Surface.getInstance().getOrderV()));
+        if(retval!=null&&retval2!=null){
+          int stupen=(new Integer(retval)).intValue();
+          int stupenV=(new Integer(retval2)).intValue();
+          Surface.getInstance().setOrderU(stupen);
+          Surface.getInstance().setOrderV(stupenV);
+          Surface.getInstance().setIsSurfaceFinished(false);
+        }
+      }catch (NumberFormatException ex){
+        JOptionPane.showMessageDialog(null,"Wrong nutural number format","Error!",JOptionPane.ERROR_MESSAGE);
+      }
+    }else if(e.getActionCommand()==SurfaceApp.INFO_AC){
+      /*
+        JOptionPane.showMessageDialog(null,"Ukázková aplikace rozšířené funkcionality knihovny JOGL\n" +
+        "Autor: Tomáš Hráský\n" +
+        "Součást bakalářské práce na téma Softwarová implementace NURBS křivek a ploch\n" +
+        "2007 Fakulta Informatiky a Managementu UHK\n" +
+        "Pro serializaci objektů využívá open source framework Simple XML - http://simple.sourceforge.net/","O aplikaci",JOptionPane.INFORMATION_MESSAGE,IconFactory.getIcon("demos/nurbs/icons/info.png"));
+      */
+      JOptionPane.showMessageDialog(null,"Example aplication of extended functionality JOGL library\n" +
+                                    "Author: Tomáš Hráský\n" +
+                                    "Part of bachelor's degree thesis Software implementation of NURBS curves and surfaces\n" +
+                                    "2007 Faculty of Informatics and Management University of Hradec Králové\n" +
+                                    "Simple XML framework is used for object serialization - http://simple.sourceforge.net/","About",JOptionPane.INFORMATION_MESSAGE,IconFactory.getIcon("demos/nurbs/icons/info.png"));
+
+    }
+		
+    app.getMouseListener().setActionType(e.getActionCommand());
+  }
+}
diff --git a/src/demos/nurbs/surfaceapp/GLListener.java b/src/demos/nurbs/surfaceapp/GLListener.java
new file mode 100755
index 0000000..ced405d
--- /dev/null
+++ b/src/demos/nurbs/surfaceapp/GLListener.java
@@ -0,0 +1,344 @@
+package demos.nurbs.surfaceapp;
+
+import javax.media.opengl.GL;
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLEventListener;
+import javax.media.opengl.glu.*;
+
+import com.sun.opengl.util.glut.gl2.GLUTgl2;
+import javax.media.opengl.GL2ES1;
+import javax.media.opengl.GL2;
+
+/**
+ * Listener reacting to events occuring on OpenGL canvas
+ * Listener reagující na události na OpenGL plátně
+ * @author Tomáš Hráský
+ *
+ */
+public class GLListener implements GLEventListener {
+  /**
+   * Object realizing OpenGL functions 
+   * objekt realizující základní OpenGL funkce
+   */
+  private GL2 gl;
+
+  /**
+   * GLU object
+   * Objekt realizující funkce nadstavbové knihovny GLU
+   */
+  private GLU glu;
+
+  /**
+   * NURBS object
+   * OpenGL Objekt NURBS křivek a ploch
+   */
+  private GLUnurbs nurbs;
+
+  /**
+   * Parent window
+   * Rodičovské okno
+   */
+  private SurfaceApp app;
+
+  /**
+   * Coords of canvas corners
+   * Viewport (souřadnice rohů plátna)
+   */
+  private int[] viewport;
+
+  /**
+   * Modelview matrix
+   * Matice modelview
+   */
+  private double[] mvmatrix;
+
+  /**
+   * Projection matrix
+   * Projekční matice
+   */
+  private double[] projmatrix;
+	
+	
+  /**
+   * Light source position vector
+   * Vektor zdroje světla
+   */
+  private float[] lightPosition = {0.0f, 1.0f, 0f, 1.0f};
+	
+  /**
+   * Seconf light source position vector
+   * Vektor druhého zdroje světla
+   */
+  private float[] lightPosition3 = {0.0f, 0.0f, 1.0f, 0.0f};
+	
+  /**
+   * Ambient light vector
+   * Ambientní složka světla
+   */
+  private float[] lightAmbient={1f,1f,1f,1f};
+	
+  /**
+   * Difusion material vector
+   * Difuzní složka barvy materiálu
+   */
+  private float[] materialDiffuse={0.8f, 0.4f, 0.4f, 1.0f};
+	
+  /**
+   * Difusion ligh vector
+   * Difúzní složka světla
+   */
+  private float[] lightDiffuse={1f, 1f, 1f, 1.0f};
+	
+  /**
+   * GLUT object
+   * Objekt pro podporu funkcionality GL utility toolkit
+   */
+  private GLUTgl2 glut;
+	
+	
+  /**
+   * Creates new GLListener with link to parent window
+   * Vytvoří nový GLListener s odkazem na rodičovské okno 
+   * @param app parent window
+   */
+  public GLListener(SurfaceApp app) {
+    this.app = app;
+		
+    viewport = new int[4];
+    mvmatrix=new double[16];
+    projmatrix=new double[16];
+		
+  }
+
+  /* (non-Javadoc)
+   * @see javax.media.opengl.GLEventListener#init(javax.media.opengl.GLAutoDrawable)
+   */
+  public void init(GLAutoDrawable drawable) {
+    this.gl = drawable.getGL().getGL2();
+    this.glu = new GLU();
+    this.glut = new GLUTgl2();
+		
+		
+    this.nurbs = glu.gluNewNurbsRenderer();
+    //		gl.glClearColor(0, 0, 0, 0);
+    gl.glClearColor(1, 1, 1, 0);
+    gl.glEnable(GL2.GL_DEPTH_TEST);
+    gl.glDepthFunc(GL2.GL_LESS);
+    gl.glClearDepth(1000.0f);
+    gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST);
+  }
+
+  public void dispose(GLAutoDrawable drawable) {
+    this.gl = null;
+    this.glu = null;
+    this.glut=null;
+    this.nurbs = null;
+  }
+
+  /* (non-Javadoc)
+   * @see javax.media.opengl.GLEventListener#display(javax.media.opengl.GLAutoDrawable)
+   */
+  public void display(GLAutoDrawable drawable) {
+
+    gl.glClear(GL2.GL_COLOR_BUFFER_BIT| GL2.GL_DEPTH_BUFFER_BIT);
+
+    gl.glMatrixMode(GL2.GL_MODELVIEW);
+    gl.glLoadIdentity();
+		
+    glu.gluLookAt(0,0,400, 0,0,0, 0,1,0);
+		
+
+    //		gl.glPushMatrix();
+    gl.glShadeModel(GL2.GL_SMOOTH);
+    gl.glPolygonMode(GL2.GL_FRONT, GL2.GL_FILL);
+    gl.glPolygonMode(GL2.GL_BACK, GL2.GL_FILL);
+    gl.glDisable(GL2.GL_CULL_FACE);
+	    
+    gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_DIFFUSE, materialDiffuse,0);
+    gl.glMaterialfv(GL2.GL_BACK, GL2.GL_DIFFUSE, materialDiffuse,0);
+	    
+    gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, lightPosition,0);
+    gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_DIFFUSE, materialDiffuse,0);
+    gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_AMBIENT, lightAmbient,0);
+    gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_SPECULAR, lightAmbient,0);
+	    
+    gl.glLightfv(GL2.GL_LIGHT2, GL2.GL_POSITION, lightPosition3,0);
+    gl.glLightfv(GL2.GL_LIGHT2, GL2.GL_DIFFUSE, lightDiffuse,0);
+	    
+    if(app.isLightingEnabled())gl.glEnable(GL2.GL_LIGHTING);
+    else gl.glDisable(GL2.GL_LIGHTING);
+    gl.glEnable(GL2.GL_LIGHT0);
+    gl.glEnable(GL2.GL_LIGHT2);
+    //		gl.glPopMatrix();
+	    
+	    
+    gl.glTranslatef(-200,-200,-100);		
+	    
+    gl.glRotatef(app.getXrotation(),1,0,0);
+    gl.glRotatef(app.getYrotation(),0,1,0);
+    gl.glRotatef(app.getZrotation(),0,0,1);
+			
+
+    //		glut.glutSolidTeapot(20);
+
+    float[] knotsU = Surface.getInstance().getKnotsU();
+    float[] knotsV = Surface.getInstance().getKnotsV();
+    float[] ctrlpoints = Surface.getInstance().getCtrlPoints();
+		
+		
+		
+    gl.glGetIntegerv(GL2.GL_VIEWPORT,viewport,0);
+    gl.glGetDoublev(GL2.GL_MODELVIEW_MATRIX,mvmatrix,0);
+    gl.glGetDoublev(GL2.GL_PROJECTION_MATRIX,projmatrix,0);
+		
+    gl.glEnable(GL2.GL_LINE_SMOOTH);
+    gl.glEnable(GL2.GL_BLEND);
+    gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);
+    gl.glHint(GL2.GL_LINE_SMOOTH_HINT, GL2.GL_DONT_CARE);
+	    
+    gl.glLineWidth(3);
+		
+    //TODO getpointsinV or inU 
+    if(Surface.getInstance().isSurfaceFinished()){
+      glu.gluBeginSurface(nurbs);
+      glu.gluNurbsSurface(nurbs,
+                          knotsU.length, 
+                          knotsU,
+                          knotsV.length,
+                          knotsV,
+                          Surface.getInstance().getPointsInV()*4,
+                          4,
+                          ctrlpoints, 
+                          Surface.getInstance().getOrderU(),
+                          Surface.getInstance().getOrderV(),
+                          GL2.GL_MAP2_VERTEX_4);
+      glu.gluEndSurface(nurbs);
+    }
+		
+    gl.glDisable(GL2.GL_LIGHTING);
+		
+    //		gl.glColor3f(1,1,1);
+    gl.glColor3f(0,0,0);
+    gl.glPointSize(5);
+    gl.glBegin(GL2.GL_POINTS);
+    for (int i = 0; i < ctrlpoints.length / 4; i++) {
+      gl.glVertex3d(ctrlpoints[i * 4]/ctrlpoints[i * 4 + 3], ctrlpoints[i * 4 + 1]/ctrlpoints[i * 4 + 3],
+                    ctrlpoints[i * 4 + 2]/ctrlpoints[i * 4 + 3]);
+    }
+    gl.glEnd();
+		
+    //		double[] coords = new double[3];
+    for (int i = 0; i < ctrlpoints.length / 4; i++) {
+      gl.glRasterPos3d(ctrlpoints[i * 4]/ctrlpoints[i * 4 + 3], ctrlpoints[i * 4 + 1]/ctrlpoints[i * 4 + 3],
+                       ctrlpoints[i * 4 + 2]/ctrlpoints[i * 4 + 3]);
+      //gl.glRasterPos2f((int)coords[0], (int)(viewport[3]-coords[1]-1-5));
+      //			gl.glRasterPos2d(20,20);
+      glut.glutBitmapString(GLUTgl2.BITMAP_HELVETICA_18,String.valueOf(i+1));
+
+    }
+
+		
+		
+    //TODO zobrazovat síť - musí to být pomocí dvou vnořených forcyklů
+    //TODO draw mesh - it needs two nested for statements
+    gl.glLineWidth(1);
+    //		gl.glBegin(GL2.GL_LINE_STRIP);
+    int baseIndex=0	;
+		
+		
+		
+		
+    //"příčná žebra"
+    //"cross ribs"
+    for(int i=0;i<Surface.getInstance().getPointsInU();i++){
+      gl.glBegin(GL2.GL_LINE_STRIP);
+      for(int j=0;j<Surface.getInstance().getPointsInV();j++){
+        baseIndex=i*Surface.getInstance().getPointsInV()*4+j*4;
+        gl.glVertex3f(ctrlpoints[baseIndex+0]/ctrlpoints[baseIndex+3],
+                      ctrlpoints[baseIndex+1]/ctrlpoints[baseIndex+3],
+                      ctrlpoints[baseIndex+2]/ctrlpoints[baseIndex+3]);
+      }
+      gl.glEnd();
+    }
+    //"podélná žebra"
+    //"alongway ribs"
+    for(int j=0;j<Surface.getInstance().getPointsInV();j++){
+      gl.glBegin(GL2.GL_LINE_STRIP);
+      for(int i=0;i<Surface.getInstance().getPointsInU();i++){
+        baseIndex=i*Surface.getInstance().getPointsInV()*4+j*4;
+        gl.glVertex3f(ctrlpoints[baseIndex+0]/ctrlpoints[baseIndex+3],
+                      ctrlpoints[baseIndex+1]/ctrlpoints[baseIndex+3],
+                      ctrlpoints[baseIndex+2]/ctrlpoints[baseIndex+3]);
+      }
+      gl.glEnd();
+    }
+		
+    gl.glColor3f(0,0,1);
+    if(Surface.getInstance().getBodIndex()>=0){
+      gl.glPointSize(8);
+      gl.glBegin(GL2.GL_POINTS);
+      int i=Surface.getInstance().getBodIndex();
+      gl.glVertex3d(ctrlpoints[i * 4]/ctrlpoints[i * 4 + 3], ctrlpoints[i * 4 + 1]/ctrlpoints[i * 4 + 3],
+                    ctrlpoints[i * 4 + 2]/ctrlpoints[i * 4 + 3]);
+      gl.glEnd();
+    }
+		
+			
+  }
+
+  /* (non-Javadoc)
+   * @see javax.media.opengl.GLEventListener#reshape(javax.media.opengl.GLAutoDrawable, int, int, int, int)
+   */
+  public void reshape(GLAutoDrawable drawable, int x, int y, int width,
+                      int height) {
+    gl.glViewport(0, 0, width, height);
+    gl.glMatrixMode(GL2.GL_PROJECTION);
+    gl.glLoadIdentity();
+    glu.gluPerspective(65.0, (double) width / height, 0.1, 1000.0);
+    //		gl.glScalef(1, -1, 1);
+    //		gl.glTranslatef(0, -drawable.getHeight(), 0);
+
+  }
+
+  /* (non-Javadoc)
+   * @see javax.media.opengl.GLEventListener#displayChanged(javax.media.opengl.GLAutoDrawable, boolean, boolean)
+   */
+  public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {
+  }
+
+  /**
+   * Returns GLU object in use
+   * Vrací používáný GLU objekt
+   * @return GLU objekt
+   */
+  public GLU getGlu() {
+    return glu;
+  }
+
+  /**
+   * Returns viewpord corners coords
+   * Vrací souřadnice rohů viewportu
+   * @return array with viewport corner's coords
+   */
+  public int[] getViewport() {
+    return viewport;
+  }
+
+  /**
+   * Returns model view matrix
+   * Vrací modelview matici
+   * @return modelview matrix
+   */
+  public double[] getMvmatrix() {
+    return mvmatrix;
+  }
+
+  /**
+   * Returns projection matrix
+   * Vrací projekční matici
+   * @return projection matrix
+   */
+  public double[] getProjmatrix() {
+    return projmatrix;
+  }
+}
diff --git a/src/demos/nurbs/surfaceapp/MyFloat.java b/src/demos/nurbs/surfaceapp/MyFloat.java
new file mode 100755
index 0000000..a9bd7cf
--- /dev/null
+++ b/src/demos/nurbs/surfaceapp/MyFloat.java
@@ -0,0 +1,55 @@
+package demos.nurbs.surfaceapp;
+
+import simple.xml.Attribute;
+import simple.xml.Root;
+
+/**
+ * Class for serializing decimal point number using SimpleXML
+ * Třída umožňující serializaci desetinného čísla ve formátu plovoucí čárky (float)
+ * @author Tomáš Hráský
+ *
+ */
+@Root(name="floatval")
+public class MyFloat {
+  /**
+   * Value
+   * Hodnota
+   */
+  @Attribute(name="val")
+    private float value;
+
+  /**
+   * Constructor, sets value to 0
+   * Konstrktor, hodnota je defaultně 0 
+   */
+  public MyFloat(){
+    value=0;
+  }
+	
+  /**
+   * Creates instance with specified value
+   * Vytvoří instanci objektu s požadovanou hodnotou
+   * @param f value
+   */
+  public MyFloat(float f) {
+    value = f;
+  }
+
+  /**
+   * Returns value of decimal number
+   * Vrací hodnotu des. čísla
+   * @return value
+   */
+  public float getValue() {
+    return value;
+  }
+
+  /**
+   * Sets value
+   * Nastavuje hodnotu objektu
+   * @param value value
+   */
+  public void setValue(float value) {
+    this.value = value;
+  }
+}
diff --git a/src/demos/nurbs/surfaceapp/PrintfFormat.java b/src/demos/nurbs/surfaceapp/PrintfFormat.java
new file mode 100755
index 0000000..153460a
--- /dev/null
+++ b/src/demos/nurbs/surfaceapp/PrintfFormat.java
@@ -0,0 +1,3090 @@
+package demos.nurbs.surfaceapp;
+//
+// (c) 2000 Sun Microsystems, Inc.
+// ALL RIGHTS RESERVED
+// 
+// License Grant-
+// 
+// 
+// Permission to use, copy, modify, and distribute this Software and its 
+// documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is 
+// hereby granted.  
+// 
+// This Software is provided "AS IS".  All express warranties, including any 
+// implied warranty of merchantability, satisfactory quality, fitness for a 
+// particular purpose, or non-infringement, are disclaimed, except to the extent 
+// that such disclaimers are held to be legally invalid.
+// 
+// You acknowledge that Software is not designed, licensed or intended for use in 
+// the design, construction, operation or maintenance of any nuclear facility 
+// ("High Risk Activities").  Sun disclaims any express or implied warranty of 
+// fitness for such uses.  
+//
+// Please refer to the file http://www.sun.com/policies/trademarks/ for further 
+// important trademark information and to 
+// http://java.sun.com/nav/business/index.html for further important licensing 
+// information for the Java Technology.
+//
+
+
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.Locale;
+import java.text.DecimalFormatSymbols;
+
+/**
+ * PrintfFormat allows the formatting of an array of
+ * objects embedded within a string.  Primitive types
+ * must be passed using wrapper types.  The formatting
+ * is controlled by a control string.
+ *<p>
+ * A control string is a Java string that contains a
+ * control specification.  The control specification
+ * starts at the first percent sign (%) in the string,
+ * provided that this percent sign
+ *<ol>
+ *<li>is not escaped protected by a matching % or is
+ * not an escape % character,
+ *<li>is not at the end of the format string, and
+ *<li>precedes a sequence of characters that parses as
+ * a valid control specification.
+ *</ol>
+ *</p><p>
+ * A control specification usually takes the form:
+ *<pre> % ['-+ #0]* [0..9]* { . [0..9]* }+
+ *                { [hlL] }+ [idfgGoxXeEcs]
+ *</pre>
+ * There are variants of this basic form that are
+ * discussed below.</p>
+ *<p>
+ * The format is composed of zero or more directives
+ * defined as follows:
+ *<ul>
+ *<li>ordinary characters, which are simply copied to
+ * the output stream;
+ *<li>escape sequences, which represent non-graphic
+ * characters; and
+ *<li>conversion specifications,  each of which
+ * results in the fetching of zero or more arguments.
+ *</ul></p>
+ *<p>
+ * The results are undefined if there are insufficient
+ * arguments for the format.  Usually an unchecked
+ * exception will be thrown.  If the format is
+ * exhausted while arguments remain, the excess
+ * arguments are evaluated but are otherwise ignored.
+ * In format strings containing the % form of
+ * conversion specifications, each argument in the
+ * argument list is used exactly once.</p>
+ * <p>
+ * Conversions can be applied to the <code>n</code>th
+ * argument after the format in the argument list,
+ * rather than to the next unused argument.  In this
+ * case, the conversion characer % is replaced by the
+ * sequence %<code>n</code>$, where <code>n</code> is
+ * a decimal integer giving the position of the
+ * argument in the argument list.</p>
+ * <p>
+ * In format strings containing the %<code>n</code>$
+ * form of conversion specifications, each argument
+ * in the argument list is used exactly once.</p>
+ *
+ *<h4>Escape Sequences</h4>
+ *<p>
+ * The following table lists escape sequences and
+ * associated actions on display devices capable of
+ * the action.
+ *<table>
+ *<tr><th align=left>Sequence</th>
+ *    <th align=left>Name</th>
+ *    <th align=left>Description</th></tr>
+ *<tr><td>\\</td><td>backlash</td><td>None.
+ *</td></tr>
+ *<tr><td>\a</td><td>alert</td><td>Attempts to alert
+ *          the user through audible or visible
+ *          notification.
+ *</td></tr>
+ *<tr><td>\b</td><td>backspace</td><td>Moves the
+ *          printing position to one column before
+ *          the current position, unless the
+ *          current position is the start of a line.
+ *</td></tr>
+ *<tr><td>\f</td><td>form-feed</td><td>Moves the
+ *          printing position to the initial 
+ *          printing position of the next logical
+ *          page.
+ *</td></tr>
+ *<tr><td>\n</td><td>newline</td><td>Moves the
+ *          printing position to the start of the
+ *          next line.
+ *</td></tr>
+ *<tr><td>\r</td><td>carriage-return</td><td>Moves
+ *          the printing position to the start of
+ *          the current line.
+ *</td></tr>
+ *<tr><td>\t</td><td>tab</td><td>Moves the printing
+ *          position to the next implementation-
+ *          defined horizontal tab position.
+ *</td></tr>
+ *<tr><td>\v</td><td>vertical-tab</td><td>Moves the
+ *          printing position to the start of the
+ *          next implementation-defined vertical
+ *          tab position.
+ *</td></tr>
+ *</table></p>
+ *<h4>Conversion Specifications</h4>
+ *<p>
+ * Each conversion specification is introduced by
+ * the percent sign character (%).  After the character
+ * %, the following appear in sequence:</p>
+ *<p>
+ * Zero or more flags (in any order), which modify the
+ * meaning of the conversion specification.</p>
+ *<p>
+ * An optional minimum field width.  If the converted
+ * value has fewer characters than the field width, it
+ * will be padded with spaces by default on the left;
+ * t will be padded on the right, if the left-
+ * adjustment flag (-), described below, is given to
+ * the field width.  The field width takes the form
+ * of a decimal integer.  If the conversion character
+ * is s, the field width is the the minimum number of
+ * characters to be printed.</p>
+ *<p>
+ * An optional precision that gives the minumum number
+ * of digits to appear for the d, i, o, x or X
+ * conversions (the field is padded with leading
+ * zeros); the number of digits to appear after the
+ * radix character for the e, E, and f conversions,
+ * the maximum number of significant digits for the g
+ * and G conversions; or the maximum number of
+ * characters to be written from a string is s and S
+ * conversions.  The precision takes the form of an
+ * optional decimal digit string, where a null digit
+ * string is treated as 0.  If a precision appears
+ * with a c conversion character the precision is
+ * ignored.
+ * </p>
+ *<p>
+ * An optional h specifies that a following d, i, o,
+ * x, or X conversion character applies to a type 
+ * short argument (the argument will be promoted
+ * according to the integral promotions and its value
+ * converted to type short before printing).</p>
+ *<p>
+ * An optional l (ell) specifies that a following
+ * d, i, o, x, or X conversion character applies to a
+ * type long argument.</p>
+ *<p>
+ * A field width or precision may be indicated by an
+ * asterisk (*) instead of a digit string.  In this
+ * case, an integer argument supplised the field width
+ * precision.  The argument that is actually converted
+ * is not fetched until the conversion letter is seen,
+ * so the the arguments specifying field width or
+ * precision must appear before the argument (if any)
+ * to be converted.  If the precision argument is
+ * negative, it will be changed to zero.  A negative
+ * field width argument is taken as a - flag, followed
+ * by a positive field width.</p>
+ * <p>
+ * In format strings containing the %<code>n</code>$
+ * form of a conversion specification, a field width
+ * or precision may be indicated by the sequence
+ * *<code>m</code>$, where m is a decimal integer
+ * giving the position in the argument list (after the
+ * format argument) of an integer argument containing
+ * the field width or precision.</p>
+ * <p>
+ * The format can contain either numbered argument
+ * specifications (that is, %<code>n</code>$ and
+ * *<code>m</code>$), or unnumbered argument
+ * specifications (that is % and *), but normally not
+ * both.  The only exception to this is that %% can
+ * be mixed with the %<code>n</code>$ form.  The
+ * results of mixing numbered and unnumbered argument
+ * specifications in a format string are undefined.</p>
+ *
+ *<h4>Flag Characters</h4>
+ *<p>
+ * The flags and their meanings are:</p>
+ *<dl>
+ * <dt>'<dd> integer portion of the result of a
+ *      decimal conversion (%i, %d, %f, %g, or %G) will
+ *      be formatted with thousands' grouping
+ *      characters.  For other conversions the flag
+ *      is ignored.  The non-monetary grouping
+ *      character is used.
+ * <dt>-<dd> result of the conversion is left-justified
+ *      within the field.  (It will be right-justified
+ *      if this flag is not specified).</td></tr>
+ * <dt>+<dd> result of a signed conversion always
+ *      begins with a sign (+ or -).  (It will begin
+ *      with a sign only when a negative value is
+ *      converted if this flag is not specified.)
+ * <dt>&lt;space&gt;<dd> If the first character of a
+ *      signed conversion is not a sign, a space
+ *      character will be placed before the result.
+ *      This means that if the space character and +
+ *      flags both appear, the space flag will be
+ *      ignored.
+ * <dt>#<dd> value is to be converted to an alternative
+ *      form.  For c, d, i, and s conversions, the flag
+ *      has no effect.  For o conversion, it increases
+ *      the precision to force the first digit of the
+ *      result to be a zero.  For x or X conversion, a
+ *      non-zero result has 0x or 0X prefixed to it,
+ *      respectively.  For e, E, f, g, and G
+ *      conversions, the result always contains a radix
+ *      character, even if no digits follow the radix
+ *      character (normally, a decimal point appears in
+ *      the result of these conversions only if a digit
+ *      follows it).  For g and G conversions, trailing
+ *      zeros will not be removed from the result as
+ *      they normally are.
+ * <dt>0<dd> d, i, o, x, X, e, E, f, g, and G
+ *      conversions, leading zeros (following any
+ *      indication of sign or base) are used to pad to
+ *      the field width;  no space padding is
+ *      performed.  If the 0 and - flags both appear,
+ *      the 0 flag is ignored.  For d, i, o, x, and X
+ *      conversions, if a precision is specified, the
+ *      0 flag will be ignored. For c conversions,
+ *      the flag is ignored.
+ *</dl>
+ *
+ *<h4>Conversion Characters</h4>
+ *<p>
+ * Each conversion character results in fetching zero
+ * or more arguments.  The results are undefined if
+ * there are insufficient arguments for the format.
+ * Usually, an unchecked exception will be thrown.
+ * If the format is exhausted while arguments remain,
+ * the excess arguments are ignored.</p>
+ *
+ *<p>
+ * The conversion characters and their meanings are:
+ *</p>
+ *<dl>
+ * <dt>d,i<dd>The int argument is converted to a
+ *        signed decimal in the style [-]dddd.  The
+ *        precision specifies the minimum number of
+ *        digits to appear;  if the value being
+ *        converted can be represented in fewer
+ *        digits, it will be expanded with leading
+ *        zeros.  The default precision is 1.  The
+ *        result of converting 0 with an explicit
+ *        precision of 0 is no characters.
+ * <dt>o<dd> The int argument is converted to unsigned
+ *        octal format in the style ddddd.  The
+ *        precision specifies the minimum number of
+ *        digits to appear;  if the value being
+ *        converted can be represented in fewer
+ *        digits, it will be expanded with leading
+ *        zeros.  The default precision is 1.  The
+ *        result of converting 0 with an explicit
+ *        precision of 0 is no characters.
+ * <dt>x<dd> The int argument is converted to unsigned
+ *        hexadecimal format in the style dddd;  the
+ *        letters abcdef are used.  The precision
+ *        specifies the minimum numberof digits to
+ *        appear; if the value being converted can be
+ *        represented in fewer digits, it will be
+ *        expanded with leading zeros.  The default
+ *        precision is 1.  The result of converting 0
+ *        with an explicit precision of 0 is no
+ *        characters.
+ * <dt>X<dd> Behaves the same as the x conversion
+ *        character except that letters ABCDEF are
+ *        used instead of abcdef.
+ * <dt>f<dd> The floating point number argument is
+ *        written in decimal notation in the style
+ *        [-]ddd.ddd, where the number of digits after
+ *        the radix character (shown here as a decimal
+ *        point) is equal to the precision
+ *        specification.  A Locale is used to determine
+ *        the radix character to use in this format.
+ *        If the precision is omitted from the
+ *        argument, six digits are written after the
+ *        radix character;  if the precision is
+ *        explicitly 0 and the # flag is not specified,
+ *        no radix character appears.  If a radix
+ *        character appears, at least 1 digit appears
+ *        before it.  The value is rounded to the
+ *        appropriate number of digits.
+ * <dt>e,E<dd>The floating point number argument is
+ *        written in the style [-]d.ddde{+-}dd
+ *        (the symbols {+-} indicate either a plus or
+ *        minus sign), where there is one digit before
+ *        the radix character (shown here as a decimal
+ *        point) and the number of digits after it is
+ *        equal to the precision.  A Locale is used to
+ *        determine the radix character to use in this
+ *        format.  When the precision is missing, six
+ *        digits are written after the radix character;
+ *        if the precision is 0 and the # flag is not
+ *        specified, no radix character appears.  The
+ *        E conversion will produce a number with E
+ *        instead of e introducing the exponent.  The
+ *        exponent always contains at least two digits.
+ *        However, if the value to be written requires
+ *        an exponent greater than two digits,
+ *        additional exponent digits are written as
+ *        necessary.  The value is rounded to the
+ *        appropriate number of digits.
+ * <dt>g,G<dd>The floating point number argument is
+ *        written in style f or e (or in sytle E in the
+ *        case of a G conversion character), with the
+ *        precision specifying the number of
+ *        significant digits.  If the precision is
+ *        zero, it is taken as one.  The style used
+ *        depends on the value converted:  style e
+ *        (or E) will be used only if the exponent
+ *        resulting from the conversion is less than
+ *        -4 or greater than or equal to the precision.
+ *        Trailing zeros are removed from the result.
+ *        A radix character appears only if it is
+ *        followed by a digit.
+ * <dt>c,C<dd>The integer argument is converted to a
+ *        char and the result is written.
+ *
+ * <dt>s,S<dd>The argument is taken to be a string and
+ *        bytes from the string are written until the
+ *        end of the string or the number of bytes 
+ *        indicated by the precision specification of
+ *        the argument is reached.  If the precision
+ *        is omitted from the argument, it is taken to
+ *        be infinite, so all characters up to the end
+ *        of the string are written.
+ * <dt>%<dd>Write a % character;  no argument is
+ *        converted.
+ *</dl>
+ *<p>
+ * If a conversion specification does not match one of
+ * the above forms, an IllegalArgumentException is
+ * thrown and the instance of PrintfFormat is not
+ * created.</p>
+ *<p>
+ * If a floating point value is the internal
+ * representation for infinity, the output is
+ * [+]Infinity, where Infinity is either Infinity or
+ * Inf, depending on the desired output string length.
+ * Printing of the sign follows the rules described
+ * above.</p>
+ *<p>
+ * If a floating point value is the internal
+ * representation for "not-a-number," the output is
+ * [+]NaN.  Printing of the sign follows the rules
+ * described above.</p>
+ *<p>
+ * In no case does a non-existent or small field width
+ * cause truncation of a field;  if the result of a
+ * conversion is wider than the field width, the field
+ * is simply expanded to contain the conversion result.
+ *</p>
+ *<p>
+ * The behavior is like printf.  One exception is that
+ * the minimum number of exponent digits is 3 instead
+ * of 2 for e and E formats when the optional L is used
+ * before the e, E, g, or G conversion character.  The
+ * optional L does not imply conversion to a long long
+ * double. </p>
+ * <p>
+ * The biggest divergence from the C printf
+ * specification is in the use of 16 bit characters.
+ * This allows the handling of characters beyond the
+ * small ASCII character set and allows the utility to
+ * interoperate correctly with the rest of the Java
+ * runtime environment.</p>
+ *<p>
+ * Omissions from the C printf specification are
+ * numerous.  All the known omissions are present
+ * because Java never uses bytes to represent
+ * characters and does not have pointers:</p>
+ *<ul>
+ * <li>%c is the same as %C.
+ * <li>%s is the same as %S.
+ * <li>u, p, and n conversion characters. 
+ * <li>%ws format.
+ * <li>h modifier applied to an n conversion character.
+ * <li>l (ell) modifier applied to the c, n, or s
+ * conversion characters.
+ * <li>ll (ell ell) modifier to d, i, o, u, x, or X
+ * conversion characters.
+ * <li>ll (ell ell) modifier to an n conversion
+ * character.
+ * <li>c, C, d,i,o,u,x, and X conversion characters
+ * apply to Byte, Character, Short, Integer, Long
+ * types.
+ * <li>f, e, E, g, and G conversion characters apply
+ * to Float and Double types.
+ * <li>s and S conversion characters apply to String
+ * types.
+ * <li>All other reference types can be formatted
+ * using the s or S conversion characters only.
+ *</ul>
+ * <p>
+ * Most of this specification is quoted from the Unix
+ * man page for the sprintf utility.</p>
+ *
+ * @author Allan Jacobs
+ * @version 1
+ * Release 1: Initial release.
+ * Release 2: Asterisk field widths and precisions    
+ *            %n$ and *m$
+ *            Bug fixes
+ *              g format fix (2 digits in e form corrupt)
+ *              rounding in f format implemented
+ *              round up when digit not printed is 5
+ *              formatting of -0.0f
+ *              round up/down when last digits are 50000...
+ */
+public class PrintfFormat {
+  /**
+   * Constructs an array of control specifications
+   * possibly preceded, separated, or followed by
+   * ordinary strings.  Control strings begin with
+   * unpaired percent signs.  A pair of successive
+   * percent signs designates a single percent sign in
+   * the format.
+   * @param fmtArg  Control string.
+   * @exception IllegalArgumentException if the control
+   * string is null, zero length, or otherwise
+   * malformed.
+   */
+  public PrintfFormat(String fmtArg)
+      throws IllegalArgumentException {
+    this(Locale.getDefault(),fmtArg);
+  }
+  /**
+   * Constructs an array of control specifications
+   * possibly preceded, separated, or followed by
+   * ordinary strings.  Control strings begin with
+   * unpaired percent signs.  A pair of successive
+   * percent signs designates a single percent sign in
+   * the format.
+   * @param fmtArg  Control string.
+   * @exception IllegalArgumentException if the control
+   * string is null, zero length, or otherwise
+   * malformed.
+   */
+  public PrintfFormat(Locale locale,String fmtArg)
+      throws IllegalArgumentException {
+    dfs = new DecimalFormatSymbols(locale);
+    int ePos=0;
+    ConversionSpecification sFmt=null;
+    String unCS = this.nonControl(fmtArg,0);
+    if (unCS!=null) {
+      sFmt = new ConversionSpecification();
+      sFmt.setLiteral(unCS);
+      vFmt.addElement(sFmt);
+    }
+    while(cPos!=-1 && cPos<fmtArg.length()) {
+      for (ePos=cPos+1; ePos<fmtArg.length();
+                    ePos++) {
+        char c=0;
+        c = fmtArg.charAt(ePos);
+        if (c == 'i') break;
+        if (c == 'd') break;
+        if (c == 'f') break;
+        if (c == 'g') break;
+        if (c == 'G') break;
+        if (c == 'o') break;
+        if (c == 'x') break;
+        if (c == 'X') break;
+        if (c == 'e') break;
+        if (c == 'E') break;
+        if (c == 'c') break;
+        if (c == 's') break;
+        if (c == '%') break;
+      }
+      ePos=Math.min(ePos+1,fmtArg.length());
+      sFmt = new ConversionSpecification(
+        fmtArg.substring(cPos,ePos));
+      vFmt.addElement(sFmt);
+      unCS = this.nonControl(fmtArg,ePos);
+      if (unCS!=null) {
+        sFmt = new ConversionSpecification();
+        sFmt.setLiteral(unCS);
+        vFmt.addElement(sFmt);
+      }
+    }
+  }
+  /**
+   * Return a substring starting at
+   * <code>start</code> and ending at either the end
+   * of the String <code>s</code>, the next unpaired
+   * percent sign, or at the end of the String if the
+   * last character is a percent sign.
+   * @param s  Control string.
+   * @param start Position in the string
+   *     <code>s</code> to begin looking for the start
+   *     of a control string.
+   * @return the substring from the start position
+   *     to the beginning of the control string.
+   */
+  private String nonControl(String s,int start) {
+    String ret="";
+    cPos=s.indexOf("%",start);
+    if (cPos==-1) cPos=s.length();
+    return s.substring(start,cPos);
+  }
+  /**
+   * Format an array of objects.  Byte, Short,
+   * Integer, Long, Float, Double, and Character
+   * arguments are treated as wrappers for primitive
+   * types.
+   * @param o The array of objects to format.
+   * @return  The formatted String.
+   */
+  public String sprintf(Object[] o) {
+    Enumeration e = vFmt.elements();
+    ConversionSpecification cs = null;
+    char c = 0;
+    int i=0;
+    StringBuffer sb=new StringBuffer();
+    while (e.hasMoreElements()) {
+      cs = (ConversionSpecification)
+        e.nextElement();
+      c = cs.getConversionCharacter();
+      if (c=='\0') sb.append(cs.getLiteral());
+      else if (c=='%') sb.append("%");
+      else {
+        if (cs.isPositionalSpecification()) {
+          i=cs.getArgumentPosition()-1;
+          if (cs.isPositionalFieldWidth()) {
+            int ifw=cs.getArgumentPositionForFieldWidth()-1;
+            cs.setFieldWidthWithArg(((Integer)o[ifw]).intValue());
+          }
+          if (cs.isPositionalPrecision()) {
+            int ipr=cs.getArgumentPositionForPrecision()-1;
+            cs.setPrecisionWithArg(((Integer)o[ipr]).intValue());
+          }
+        }
+        else {
+          if (cs.isVariableFieldWidth()) {
+            cs.setFieldWidthWithArg(((Integer)o[i]).intValue());
+            i++;
+          }
+          if (cs.isVariablePrecision()) {
+            cs.setPrecisionWithArg(((Integer)o[i]).intValue());
+            i++;
+          }
+        }
+        if (o[i] instanceof Byte)
+          sb.append(cs.internalsprintf(
+          ((Byte)o[i]).byteValue()));
+        else if (o[i] instanceof Short)
+          sb.append(cs.internalsprintf(
+          ((Short)o[i]).shortValue()));
+        else if (o[i] instanceof Integer)
+          sb.append(cs.internalsprintf(
+          ((Integer)o[i]).intValue()));
+        else if (o[i] instanceof Long)
+          sb.append(cs.internalsprintf(
+          ((Long)o[i]).longValue()));
+        else if (o[i] instanceof Float)
+          sb.append(cs.internalsprintf(
+          ((Float)o[i]).floatValue()));
+        else if (o[i] instanceof Double)
+          sb.append(cs.internalsprintf(
+          ((Double)o[i]).doubleValue()));
+        else if (o[i] instanceof Character)
+          sb.append(cs.internalsprintf(
+          ((Character)o[i]).charValue()));
+        else if (o[i] instanceof String)
+          sb.append(cs.internalsprintf(
+          (String)o[i]));
+        else
+          sb.append(cs.internalsprintf(
+          o[i]));
+        if (!cs.isPositionalSpecification())
+          i++;
+      }
+    }
+    return sb.toString();
+  }
+  /**
+   * Format nothing.  Just use the control string.
+   * @return  the formatted String.
+   */
+  public String sprintf() {
+    Enumeration e = vFmt.elements();
+    ConversionSpecification cs = null;
+    char c = 0;
+    StringBuffer sb=new StringBuffer();
+    while (e.hasMoreElements()) {
+      cs = (ConversionSpecification)
+        e.nextElement();
+      c = cs.getConversionCharacter();
+      if (c=='\0') sb.append(cs.getLiteral());
+      else if (c=='%') sb.append("%");
+    }
+    return sb.toString();
+  }
+  /**
+   * Format an int.
+   * @param x The int to format.
+   * @return  The formatted String.
+   * @exception IllegalArgumentException if the
+   *     conversion character is f, e, E, g, G, s,
+   *     or S.
+   */
+  public String sprintf(int x)
+      throws IllegalArgumentException {
+    Enumeration e = vFmt.elements();
+    ConversionSpecification cs = null;
+    char c = 0;
+    StringBuffer sb=new StringBuffer();
+    while (e.hasMoreElements()) {
+      cs = (ConversionSpecification)
+        e.nextElement();
+      c = cs.getConversionCharacter();
+      if (c=='\0') sb.append(cs.getLiteral());
+      else if (c=='%') sb.append("%");
+      else sb.append(cs.internalsprintf(x));
+    }
+    return sb.toString();
+  }
+  /**
+   * Format an long.
+   * @param x The long to format.
+   * @return  The formatted String.
+   * @exception IllegalArgumentException if the
+   *     conversion character is f, e, E, g, G, s,
+   *     or S.
+   */
+  public String sprintf(long x)
+      throws IllegalArgumentException {
+    Enumeration e = vFmt.elements();
+    ConversionSpecification cs = null;
+    char c = 0;
+    StringBuffer sb=new StringBuffer();
+    while (e.hasMoreElements()) {
+      cs = (ConversionSpecification)
+        e.nextElement();
+      c = cs.getConversionCharacter();
+      if (c=='\0') sb.append(cs.getLiteral());
+      else if (c=='%') sb.append("%");
+      else sb.append(cs.internalsprintf(x));
+    }
+    return sb.toString();
+  }
+  /**
+   * Format a double.
+   * @param x The double to format.
+   * @return  The formatted String.
+   * @exception IllegalArgumentException if the
+   *     conversion character is c, C, s, S,
+   *     d, d, x, X, or o.
+   */
+  public String sprintf(double x)
+      throws IllegalArgumentException {
+    Enumeration e = vFmt.elements();
+    ConversionSpecification cs = null;
+    char c = 0;
+    StringBuffer sb=new StringBuffer();
+    while (e.hasMoreElements()) {
+      cs = (ConversionSpecification)
+        e.nextElement();
+      c = cs.getConversionCharacter();
+      if (c=='\0') sb.append(cs.getLiteral());
+      else if (c=='%') sb.append("%");
+      else sb.append(cs.internalsprintf(x));
+    }
+    return sb.toString();
+  }
+  /**
+   * Format a String.
+   * @param x The String to format.
+   * @return  The formatted String.
+   * @exception IllegalArgumentException if the
+   *   conversion character is neither s nor S.
+   */
+  public String sprintf(String x)
+      throws IllegalArgumentException {
+    Enumeration e = vFmt.elements();
+    ConversionSpecification cs = null;
+    char c = 0;
+    StringBuffer sb=new StringBuffer();
+    while (e.hasMoreElements()) {
+      cs = (ConversionSpecification)
+        e.nextElement();
+      c = cs.getConversionCharacter();
+      if (c=='\0') sb.append(cs.getLiteral());
+      else if (c=='%') sb.append("%");
+      else sb.append(cs.internalsprintf(x));
+    }
+    return sb.toString();
+  }
+  /**
+   * Format an Object.  Convert wrapper types to
+   * their primitive equivalents and call the
+   * appropriate internal formatting method. Convert
+   * Strings using an internal formatting method for
+   * Strings. Otherwise use the default formatter
+   * (use toString).
+   * @param x the Object to format.
+   * @return  the formatted String.
+   * @exception IllegalArgumentException if the
+   *    conversion character is inappropriate for
+   *    formatting an unwrapped value.
+   */
+  public String sprintf(Object x)
+      throws IllegalArgumentException {
+    Enumeration e = vFmt.elements();
+    ConversionSpecification cs = null;
+    char c = 0;
+    StringBuffer sb=new StringBuffer();
+    while (e.hasMoreElements()) {
+      cs = (ConversionSpecification)
+        e.nextElement();
+      c = cs.getConversionCharacter();
+      if (c=='\0') sb.append(cs.getLiteral());
+      else if (c=='%') sb.append("%");
+      else {
+        if (x instanceof Byte)
+          sb.append(cs.internalsprintf(
+          ((Byte)x).byteValue()));
+        else if (x instanceof Short)
+          sb.append(cs.internalsprintf(
+          ((Short)x).shortValue()));
+        else if (x instanceof Integer)
+          sb.append(cs.internalsprintf(
+          ((Integer)x).intValue()));
+        else if (x instanceof Long)
+          sb.append(cs.internalsprintf(
+          ((Long)x).longValue()));
+        else if (x instanceof Float)
+          sb.append(cs.internalsprintf(
+          ((Float)x).floatValue()));
+        else if (x instanceof Double)
+          sb.append(cs.internalsprintf(
+          ((Double)x).doubleValue()));
+        else if (x instanceof Character)
+          sb.append(cs.internalsprintf(
+          ((Character)x).charValue()));
+        else if (x instanceof String)
+          sb.append(cs.internalsprintf(
+          (String)x));
+        else
+          sb.append(cs.internalsprintf(x));
+      }
+    }
+    return sb.toString();
+  }
+  /**
+   *<p>
+   * ConversionSpecification allows the formatting of
+   * a single primitive or object embedded within a
+   * string.  The formatting is controlled by a
+   * format string.  Only one Java primitive or
+   * object can be formatted at a time.
+   *<p>
+   * A format string is a Java string that contains
+   * a control string.  The control string starts at
+   * the first percent sign (%) in the string,
+   * provided that this percent sign
+   *<ol>
+   *<li>is not escaped protected by a matching % or
+   *     is not an escape % character,
+   *<li>is not at the end of the format string, and
+   *<li>precedes a sequence of characters that parses
+   *     as a valid control string.
+   *</ol>
+   *<p>
+   * A control string takes the form:
+   *<pre> % ['-+ #0]* [0..9]* { . [0..9]* }+
+   *                { [hlL] }+ [idfgGoxXeEcs]
+   *</pre>
+   *<p>
+   * The behavior is like printf.  One (hopefully the
+   * only) exception is that the minimum number of
+   * exponent digits is 3 instead of 2 for e and E
+   * formats when the optional L is used before the
+   * e, E, g, or G conversion character.  The 
+   * optional L does not imply conversion to a long
+   * long double.
+   */
+  private class ConversionSpecification {
+    /**
+     * Constructor.  Used to prepare an instance
+     * to hold a literal, not a control string.
+     */
+    ConversionSpecification() { }
+    /**
+     * Constructor for a conversion specification.
+     * The argument must begin with a % and end
+     * with the conversion character for the
+     * conversion specification.
+      * @param fmtArg  String specifying the
+     *     conversion specification.
+      * @exception IllegalArgumentException if the
+     *     input string is null, zero length, or
+     *     otherwise malformed.
+     */
+    ConversionSpecification(String fmtArg)
+        throws IllegalArgumentException {
+      if (fmtArg==null)
+        throw new NullPointerException();
+      if (fmtArg.length()==0)
+        throw new IllegalArgumentException(
+        "Control strings must have positive"+
+        " lengths.");
+      if (fmtArg.charAt(0)=='%') {
+        fmt = fmtArg;
+        pos=1;
+        setArgPosition();
+        setFlagCharacters();
+        setFieldWidth();
+        setPrecision();
+        setOptionalHL();
+        if (setConversionCharacter()) {
+          if (pos==fmtArg.length()) {
+            if(leadingZeros&&leftJustify)
+              leadingZeros=false;
+            if(precisionSet&&leadingZeros){
+              if(conversionCharacter=='d'
+              ||conversionCharacter=='i'
+              ||conversionCharacter=='o'
+              ||conversionCharacter=='x')
+              {
+                leadingZeros=false;
+              }
+            }
+          }
+          else
+            throw new IllegalArgumentException(
+            "Malformed conversion specification="+
+            fmtArg);
+        }
+        else
+          throw new IllegalArgumentException(
+          "Malformed conversion specification="+
+          fmtArg);
+      }
+      else
+        throw new IllegalArgumentException(
+        "Control strings must begin with %.");
+    }
+    /**
+     * Set the String for this instance.
+     * @param s the String to store.
+     */
+    void setLiteral(String s) {
+      fmt = s;
+    }
+    /**
+     * Get the String for this instance.  Translate
+     * any escape sequences.
+     *
+     * @return s the stored String.
+     */
+    String getLiteral() {
+      StringBuffer sb=new StringBuffer();
+      int i=0;
+      while (i<fmt.length()) {
+        if (fmt.charAt(i)=='\\') {
+          i++;
+          if (i<fmt.length()) {
+            char c=fmt.charAt(i);
+            switch(c) {
+            case 'a':
+              sb.append((char)0x07);
+              break;
+            case 'b':
+              sb.append('\b');
+              break;
+            case 'f':
+              sb.append('\f');
+              break;
+            case 'n':
+              sb.append(System.getProperty("line.separator"));
+              break;
+            case 'r':
+              sb.append('\r');
+              break;
+            case 't':
+              sb.append('\t');
+              break;
+            case 'v':
+              sb.append((char)0x0b);
+              break;
+            case '\\':
+              sb.append('\\');
+              break;
+            }
+            i++;
+          }
+          else
+            sb.append('\\');
+        }
+        else
+          i++;
+      }
+      return fmt;
+    }
+    /**
+     * Get the conversion character that tells what
+     * type of control character this instance has.
+     *
+     * @return the conversion character.
+     */
+    char getConversionCharacter() {
+      return conversionCharacter;
+    }
+    /**
+     * Check whether the specifier has a variable
+     * field width that is going to be set by an
+     * argument.
+     * @return <code>true</code> if the conversion
+     *   uses an * field width; otherwise
+     *   <code>false</code>.
+     */
+    boolean isVariableFieldWidth() {
+      return variableFieldWidth;
+    }
+    /**
+     * Set the field width with an argument.  A
+     * negative field width is taken as a - flag
+     * followed by a positive field width.
+     * @param fw the field width.
+     */
+    void setFieldWidthWithArg(int fw) {
+      if (fw<0) leftJustify = true;
+      fieldWidthSet = true;
+      fieldWidth = Math.abs(fw);
+    }
+    /**
+     * Check whether the specifier has a variable
+     * precision that is going to be set by an
+     * argument.
+     * @return <code>true</code> if the conversion
+     *   uses an * precision; otherwise
+     *   <code>false</code>.
+     */
+    boolean isVariablePrecision() {
+      return variablePrecision;
+    }
+    /**
+     * Set the precision with an argument.  A
+     * negative precision will be changed to zero.
+     * @param pr the precision.
+     */
+    void setPrecisionWithArg(int pr) {
+      precisionSet = true;
+      precision = Math.max(pr,0);
+    }
+    /**
+     * Format an int argument using this conversion
+      * specification.
+     * @param s the int to format.
+     * @return the formatted String.
+     * @exception IllegalArgumentException if the
+     *     conversion character is f, e, E, g, or G.
+     */
+    String internalsprintf(int s)
+        throws IllegalArgumentException {
+      String s2 = "";
+      switch(conversionCharacter) {
+      case 'd':
+      case 'i':
+        if (optionalh)
+          s2 = printDFormat((short)s);
+        else if (optionall)
+          s2 = printDFormat((long)s);
+        else
+          s2 = printDFormat(s);
+        break;
+      case 'x':
+      case 'X':
+        if (optionalh)
+          s2 = printXFormat((short)s);
+        else if (optionall)
+          s2 = printXFormat((long)s);
+        else
+          s2 = printXFormat(s);
+        break;
+      case 'o':
+        if (optionalh)
+          s2 = printOFormat((short)s);
+        else if (optionall)
+          s2 = printOFormat((long)s);
+        else
+          s2 = printOFormat(s);
+        break;
+      case 'c':
+      case 'C':
+        s2 = printCFormat((char)s);
+        break;
+      default:
+        throw new IllegalArgumentException(
+          "Cannot format a int with a format using a "+
+          conversionCharacter+
+          " conversion character.");
+      }
+      return s2;
+    }
+    /**
+     * Format a long argument using this conversion
+     * specification.
+     * @param s the long to format.
+     * @return the formatted String.
+     * @exception IllegalArgumentException if the
+     *     conversion character is f, e, E, g, or G.
+     */
+    String internalsprintf(long s)
+        throws IllegalArgumentException {
+      String s2 = "";
+      switch(conversionCharacter) {
+      case 'd':
+      case 'i':
+        if (optionalh)
+          s2 = printDFormat((short)s);
+        else if (optionall)
+          s2 = printDFormat(s);
+        else
+          s2 = printDFormat((int)s);
+        break;
+      case 'x':
+      case 'X':
+        if (optionalh)
+          s2 = printXFormat((short)s);
+        else if (optionall)
+          s2 = printXFormat(s);
+        else
+          s2 = printXFormat((int)s);
+        break;
+      case 'o':
+        if (optionalh)
+          s2 = printOFormat((short)s);
+        else if (optionall)
+          s2 = printOFormat(s);
+        else
+          s2 = printOFormat((int)s);
+        break;
+      case 'c':
+      case 'C':
+        s2 = printCFormat((char)s);
+        break;
+      default:
+        throw new IllegalArgumentException(
+        "Cannot format a long with a format using a "+
+        conversionCharacter+" conversion character.");
+      }
+      return s2;
+    }
+    /**
+     * Format a double argument using this conversion
+     * specification.
+     * @param s the double to format.
+     * @return the formatted String.
+     * @exception IllegalArgumentException if the
+     *     conversion character is c, C, s, S, i, d,
+     *     x, X, or o.
+     */
+    String internalsprintf(double s)
+        throws IllegalArgumentException {
+      String s2 = "";
+      switch(conversionCharacter) {
+      case 'f':
+        s2 = printFFormat(s);
+        break;
+      case 'E':
+      case 'e':
+        s2 = printEFormat(s);
+        break;
+      case 'G':
+      case 'g':
+        s2 = printGFormat(s);
+        break;
+      default:
+        throw new IllegalArgumentException("Cannot "+
+        "format a double with a format using a "+
+        conversionCharacter+" conversion character.");
+      }
+      return s2;
+    }
+    /**
+     * Format a String argument using this conversion
+     * specification.
+     * @param s the String to format.
+     * @return the formatted String.
+     * @exception IllegalArgumentException if the
+     *   conversion character is neither s nor S.
+     */
+    String internalsprintf(String s)
+        throws IllegalArgumentException {
+      String s2 = "";
+      if(conversionCharacter=='s'
+      || conversionCharacter=='S')
+        s2 = printSFormat(s);
+      else
+        throw new IllegalArgumentException("Cannot "+
+        "format a String with a format using a "+
+        conversionCharacter+" conversion character.");
+      return s2;
+    }
+    /**
+     * Format an Object argument using this conversion
+     * specification.
+     * @param s the Object to format.
+     * @return the formatted String.
+     * @exception IllegalArgumentException if the
+     *     conversion character is neither s nor S.
+     */
+    String internalsprintf(Object s) {
+      String s2 = "";
+      if(conversionCharacter=='s'
+      || conversionCharacter=='S')
+        s2 = printSFormat(s.toString());
+      else
+        throw new IllegalArgumentException(
+          "Cannot format a String with a format using"+
+          " a "+conversionCharacter+
+          " conversion character.");
+      return s2;
+    }
+    /**
+     * For f format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the
+     * left.  '+' character means that the conversion
+     * will always begin with a sign (+ or -).  The
+     * blank flag character means that a non-negative
+     * input will be preceded with a blank.  If both
+     * a '+' and a ' ' are specified, the blank flag
+     * is ignored.  The '0' flag character implies that
+     * padding to the field width will be done with
+     * zeros instead of blanks.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the number of digits
+     * to appear after the radix character.  Padding is
+     * with trailing 0s.
+     */
+    private char[] fFormatDigits(double x) {
+      // int defaultDigits=6;
+      String sx,sxOut;
+      int i,j,k;
+      int n1In,n2In;
+      int expon=0;
+      boolean minusSign=false;
+      if (x>0.0)
+        sx = Double.toString(x);
+      else if (x<0.0) {
+        sx = Double.toString(-x);
+        minusSign=true;
+      }
+      else {
+        sx = Double.toString(x);
+        if (sx.charAt(0)=='-') {
+          minusSign=true;
+          sx=sx.substring(1);
+        }
+      }
+      int ePos = sx.indexOf('E');
+      int rPos = sx.indexOf('.');
+      if (rPos!=-1) n1In=rPos;
+      else if (ePos!=-1) n1In=ePos;
+      else n1In=sx.length();
+      if (rPos!=-1) {
+        if (ePos!=-1) n2In = ePos-rPos-1;
+        else n2In = sx.length()-rPos-1;
+      }
+      else
+        n2In = 0;
+      if (ePos!=-1) {
+        int ie=ePos+1;
+        expon=0;
+        if (sx.charAt(ie)=='-') {
+          for (++ie; ie<sx.length(); ie++)
+            if (sx.charAt(ie)!='0') break;
+          if (ie<sx.length())
+            expon=-Integer.parseInt(sx.substring(ie));
+        }
+        else {
+          if (sx.charAt(ie)=='+') ++ie;
+          for (; ie<sx.length(); ie++)
+            if (sx.charAt(ie)!='0') break;
+          if (ie<sx.length())
+            expon=Integer.parseInt(sx.substring(ie));
+        }
+      }
+      int p;
+      if (precisionSet) p = precision;
+      else p = defaultDigits-1;
+      char[] ca1 = sx.toCharArray();
+      char[] ca2 = new char[n1In+n2In];
+      char[] ca3,ca4,ca5;
+      for (j=0; j<n1In; j++)
+        ca2[j] = ca1[j];
+      i = j+1;
+      for (k=0; k<n2In; j++,i++,k++)
+        ca2[j] = ca1[i];
+      if (n1In+expon<=0) {
+        ca3 = new char[-expon+n2In];
+        for (j=0,k=0; k<(-n1In-expon); k++,j++)
+          ca3[j]='0';
+        for (i=0; i<(n1In+n2In); i++,j++)
+          ca3[j]=ca2[i];
+      }
+      else
+        ca3 = ca2;
+      boolean carry=false;
+      if (p<-expon+n2In) {
+        if (expon<0) i = p;
+        else i = p+n1In;
+        carry=checkForCarry(ca3,i);
+        if (carry)
+          carry=startSymbolicCarry(ca3,i-1,0);
+      }
+      if (n1In+expon<=0) {
+        ca4 = new char[2+p];
+        if (!carry) ca4[0]='0';
+        else ca4[0]='1';
+        if(alternateForm||!precisionSet||precision!=0){
+          ca4[1]='.';
+          for(i=0,j=2;i<Math.min(p,ca3.length);i++,j++)
+            ca4[j]=ca3[i];
+          for (; j<ca4.length; j++) ca4[j]='0';
+        }
+      }
+      else {
+        if (!carry) {
+          if(alternateForm||!precisionSet
+          ||precision!=0)
+            ca4 = new char[n1In+expon+p+1];
+          else
+            ca4 = new char[n1In+expon];
+          j=0;
+        }
+        else {
+          if(alternateForm||!precisionSet
+          ||precision!=0)
+            ca4 = new char[n1In+expon+p+2];
+          else
+            ca4 = new char[n1In+expon+1];
+          ca4[0]='1';
+          j=1;
+        }
+        for (i=0; i<Math.min(n1In+expon,ca3.length); i++,j++)
+          ca4[j]=ca3[i];
+        for (; i<n1In+expon; i++,j++)
+          ca4[j]='0';
+        if(alternateForm||!precisionSet||precision!=0){
+          ca4[j]='.'; j++;
+          for (k=0; i<ca3.length && k<p; i++,j++,k++)
+            ca4[j]=ca3[i];
+          for (; j<ca4.length; j++) ca4[j]='0';
+        }
+      }
+      int nZeros=0;
+      if (!leftJustify && leadingZeros) {
+        int xThousands=0;
+        if (thousands) {
+          int xlead=0;
+          if (ca4[0]=='+'||ca4[0]=='-'||ca4[0]==' ')
+            xlead=1;
+          int xdp=xlead;
+          for (; xdp<ca4.length; xdp++)
+            if (ca4[xdp]=='.') break;
+          xThousands=(xdp-xlead)/3;
+        }
+        if (fieldWidthSet)
+          nZeros = fieldWidth-ca4.length;
+        if ((!minusSign&&(leadingSign||leadingSpace))||minusSign)
+          nZeros--;
+        nZeros-=xThousands;
+        if (nZeros<0) nZeros=0;
+      }
+      j=0;
+      if ((!minusSign&&(leadingSign||leadingSpace))||minusSign) {
+        ca5 = new char[ca4.length+nZeros+1];
+        j++;
+      }
+      else
+        ca5 = new char[ca4.length+nZeros];
+      if (!minusSign) {
+        if (leadingSign) ca5[0]='+';
+        if (leadingSpace) ca5[0]=' ';
+      }
+      else
+        ca5[0]='-';
+      for (i=0; i<nZeros; i++,j++)
+        ca5[j]='0';
+      for (i=0; i<ca4.length; i++,j++) ca5[j]=ca4[i];
+  
+      int lead=0;
+      if (ca5[0]=='+'||ca5[0]=='-'||ca5[0]==' ')
+        lead=1;
+      int dp=lead;
+      for (; dp<ca5.length; dp++)
+        if (ca5[dp]=='.') break;
+      int nThousands=(dp-lead)/3;
+      // Localize the decimal point.
+      if (dp<ca5.length)
+        ca5[dp]=dfs.getDecimalSeparator();
+      char[] ca6 = ca5;
+      if (thousands && nThousands>0) {
+        ca6 = new char[ca5.length+nThousands+lead];
+        ca6[0]=ca5[0];
+        for (i=lead,k=lead; i<dp; i++) {
+          if (i>0 && (dp-i)%3==0) {
+            // ca6[k]=',';
+            ca6[k]=dfs.getGroupingSeparator();
+            ca6[k+1]=ca5[i];
+            k+=2;
+          }
+          else {
+            ca6[k]=ca5[i]; k++;
+          }
+        }
+        for (; i<ca5.length; i++,k++) {
+          ca6[k]=ca5[i];
+		}
+      }
+      return ca6;
+    }
+	/**
+	 * An intermediate routine on the way to creating
+	 * an f format String.  The method decides whether
+	 * the input double value is an infinity,
+	 * not-a-number, or a finite double and formats
+	 * each type of input appropriately.
+	 * @param x the double value to be formatted.
+	 * @return the converted double value.
+	 */
+    private String fFormatString(double x) {
+      boolean noDigits=false;
+      char[] ca6,ca7;
+      if (Double.isInfinite(x)) {
+        if (x==Double.POSITIVE_INFINITY) {
+          if (leadingSign) ca6 = "+Inf".toCharArray();
+          else if (leadingSpace)
+            ca6 = " Inf".toCharArray();
+          else ca6 = "Inf".toCharArray();
+        }
+        else
+          ca6 = "-Inf".toCharArray();
+        noDigits = true;
+      }
+      else if (Double.isNaN(x)) {
+        if (leadingSign) ca6 = "+NaN".toCharArray();
+        else if (leadingSpace)
+          ca6 = " NaN".toCharArray();
+        else ca6 = "NaN".toCharArray();
+        noDigits = true;
+      }
+      else
+        ca6 = fFormatDigits(x);
+      ca7 = applyFloatPadding(ca6,false);
+      return new String(ca7);
+    }
+    /**
+     * For e format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the
+     * left.  '+' character means that the conversion
+     * will always begin with a sign (+ or -).  The
+     * blank flag character means that a non-negative
+     * input will be preceded with a blank.  If both a
+     * '+' and a ' ' are specified, the blank flag is
+     * ignored.  The '0' flag character implies that
+     * padding to the field width will be done with
+     * zeros instead of blanks.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear after the radix character.
+     * Padding is with trailing 0s.
+     *
+     * The behavior is like printf.  One (hopefully the
+     * only) exception is that the minimum number of
+     * exponent digits is 3 instead of 2 for e and E
+     * formats when the optional L is used before the
+     * e, E, g, or G conversion character. The optional
+     * L does not imply conversion to a long long
+     * double.
+     */
+    private char[] eFormatDigits(double x,char eChar) {
+      char[] ca1,ca2,ca3;
+      // int defaultDigits=6;
+      String sx,sxOut;
+      int i,j,k,p;
+      int n1In,n2In;
+      int expon=0;
+      int ePos,rPos,eSize;
+      boolean minusSign=false;
+      if (x>0.0)
+        sx = Double.toString(x);
+      else if (x<0.0) {
+        sx = Double.toString(-x);
+        minusSign=true;
+      }
+      else {
+        sx = Double.toString(x);
+        if (sx.charAt(0)=='-') {
+          minusSign=true;
+          sx=sx.substring(1);
+        }
+      }
+      ePos = sx.indexOf('E');
+      if (ePos==-1) ePos = sx.indexOf('e');
+      rPos = sx.indexOf('.');
+      if (rPos!=-1) n1In=rPos;
+      else if (ePos!=-1) n1In=ePos;
+      else n1In=sx.length();
+      if (rPos!=-1) {
+        if (ePos!=-1) n2In = ePos-rPos-1;
+        else n2In = sx.length()-rPos-1;
+      }
+      else
+        n2In = 0;
+      if (ePos!=-1) {
+        int ie=ePos+1;
+        expon=0;
+        if (sx.charAt(ie)=='-') {
+          for (++ie; ie<sx.length(); ie++)
+            if (sx.charAt(ie)!='0') break;
+          if (ie<sx.length())
+            expon=-Integer.parseInt(sx.substring(ie));
+        }
+        else {
+          if (sx.charAt(ie)=='+') ++ie;
+          for (; ie<sx.length(); ie++)
+            if (sx.charAt(ie)!='0') break;
+          if (ie<sx.length())
+            expon=Integer.parseInt(sx.substring(ie));
+        }
+      }
+      if (rPos!=-1) expon += rPos-1;
+      if (precisionSet) p = precision;
+      else p = defaultDigits-1;
+      if (rPos!=-1 && ePos!=-1)
+        ca1=(sx.substring(0,rPos)+
+          sx.substring(rPos+1,ePos)).toCharArray();
+      else if (rPos!=-1)
+        ca1 = (sx.substring(0,rPos)+
+            sx.substring(rPos+1)).toCharArray();
+      else if (ePos!=-1)
+        ca1 = sx.substring(0,ePos).toCharArray();
+      else
+        ca1 = sx.toCharArray();
+      boolean carry=false;
+      int i0=0;
+      if (ca1[0]!='0')
+        i0 = 0;
+      else
+        for (i0=0; i0<ca1.length; i0++)
+          if (ca1[i0]!='0') break;
+      if (i0+p<ca1.length-1) {
+        carry=checkForCarry(ca1,i0+p+1);
+        if (carry)
+          carry = startSymbolicCarry(ca1,i0+p,i0);
+        if (carry) {
+          ca2 = new char[i0+p+1];
+          ca2[i0]='1';
+          for (j=0; j<i0; j++) ca2[j]='0';
+          for (i=i0,j=i0+1; j<p+1; i++,j++)
+            ca2[j] = ca1[i];
+          expon++;
+          ca1 = ca2;
+        }
+      }
+      if (Math.abs(expon)<100 && !optionalL) eSize=4;
+      else eSize=5;
+      if (alternateForm||!precisionSet||precision!=0)
+        ca2 = new char[2+p+eSize];
+      else
+        ca2 = new char[1+eSize];
+      if (ca1[0]!='0') {
+        ca2[0] = ca1[0];
+        j=1;
+      }
+      else {
+        for (j=1; j<(ePos==-1?ca1.length:ePos); j++)
+          if (ca1[j]!='0') break;
+        if ((ePos!=-1 && j<ePos)||
+            (ePos==-1 && j<ca1.length)) {
+          ca2[0] = ca1[j];
+          expon -= j;
+          j++;
+        }
+        else {
+          ca2[0]='0';
+          j=2;
+        }
+      }
+      if (alternateForm||!precisionSet||precision!=0) {
+        ca2[1] = '.';
+        i=2;
+      }
+      else
+        i=1;
+      for (k=0; k<p && j<ca1.length; j++,i++,k++)
+        ca2[i] = ca1[j];
+      for (;i<ca2.length-eSize; i++)
+        ca2[i] = '0';
+      ca2[i++] = eChar;
+      if (expon<0) ca2[i++]='-';
+      else ca2[i++]='+';
+      expon = Math.abs(expon);
+      if (expon>=100) {
+        switch(expon/100) {
+        case 1: ca2[i]='1'; break;
+        case 2: ca2[i]='2'; break;
+        case 3: ca2[i]='3'; break;
+        case 4: ca2[i]='4'; break;
+        case 5: ca2[i]='5'; break;
+        case 6: ca2[i]='6'; break;
+        case 7: ca2[i]='7'; break;
+        case 8: ca2[i]='8'; break;
+        case 9: ca2[i]='9'; break;
+        }
+        i++;
+      }
+      switch((expon%100)/10) {
+      case 0: ca2[i]='0'; break;
+      case 1: ca2[i]='1'; break;
+      case 2: ca2[i]='2'; break;
+      case 3: ca2[i]='3'; break;
+      case 4: ca2[i]='4'; break;
+      case 5: ca2[i]='5'; break;
+      case 6: ca2[i]='6'; break;
+      case 7: ca2[i]='7'; break;
+      case 8: ca2[i]='8'; break;
+      case 9: ca2[i]='9'; break;
+      }
+      i++;
+      switch(expon%10) {
+      case 0: ca2[i]='0'; break;
+      case 1: ca2[i]='1'; break;
+      case 2: ca2[i]='2'; break;
+      case 3: ca2[i]='3'; break;
+      case 4: ca2[i]='4'; break;
+      case 5: ca2[i]='5'; break;
+      case 6: ca2[i]='6'; break;
+      case 7: ca2[i]='7'; break;
+      case 8: ca2[i]='8'; break;
+      case 9: ca2[i]='9'; break;
+      }
+      int nZeros=0;
+      if (!leftJustify && leadingZeros) {
+        int xThousands=0;
+        if (thousands) {
+          int xlead=0;
+          if (ca2[0]=='+'||ca2[0]=='-'||ca2[0]==' ')
+            xlead=1;
+          int xdp=xlead;
+          for (; xdp<ca2.length; xdp++)
+            if (ca2[xdp]=='.') break;
+          xThousands=(xdp-xlead)/3;
+        }
+        if (fieldWidthSet)
+          nZeros = fieldWidth-ca2.length;
+        if ((!minusSign&&(leadingSign||leadingSpace))||minusSign)
+          nZeros--;
+        nZeros-=xThousands;
+        if (nZeros<0) nZeros=0;
+      }
+      j=0;
+      if ((!minusSign&&(leadingSign || leadingSpace))||minusSign) {
+        ca3 = new char[ca2.length+nZeros+1];
+        j++;
+      }
+      else
+        ca3 = new char[ca2.length+nZeros];
+      if (!minusSign) {
+        if (leadingSign) ca3[0]='+';
+        if (leadingSpace) ca3[0]=' ';
+      }
+      else
+        ca3[0]='-';
+      for (k=0; k<nZeros; j++,k++)
+        ca3[j]='0';
+      for (i=0; i<ca2.length && j<ca3.length; i++,j++)
+        ca3[j]=ca2[i];
+  
+      int lead=0;
+      if (ca3[0]=='+'||ca3[0]=='-'||ca3[0]==' ')
+        lead=1;
+      int dp=lead;
+      for (; dp<ca3.length; dp++)
+        if (ca3[dp]=='.') break;
+      int nThousands=dp/3;
+      // Localize the decimal point.
+      if (dp < ca3.length)
+        ca3[dp] = dfs.getDecimalSeparator();
+      char[] ca4 = ca3;
+      if (thousands && nThousands>0) {
+        ca4 = new char[ca3.length+nThousands+lead];
+        ca4[0]=ca3[0];
+        for (i=lead,k=lead; i<dp; i++) {
+          if (i>0 && (dp-i)%3==0) {
+            // ca4[k]=',';
+            ca4[k]=dfs.getGroupingSeparator();
+            ca4[k+1]=ca3[i];
+            k+=2;
+          }
+          else {
+            ca4[k]=ca3[i]; k++;
+          }
+        }
+        for (; i<ca3.length; i++,k++)
+          ca4[k]=ca3[i];
+      }
+      return ca4;
+    }
+    /**
+     * Check to see if the digits that are going to
+     * be truncated because of the precision should
+     * force a round in the preceding digits.
+     * @param ca1 the array of digits
+     * @param icarry the index of the first digit that
+     *     is to be truncated from the print
+     * @return <code>true</code> if the truncation forces
+     *     a round that will change the print
+     */
+    private boolean checkForCarry(char[] ca1,int icarry) {
+      boolean carry=false;
+      if (icarry<ca1.length) {
+        if (ca1[icarry]=='6'||ca1[icarry]=='7'
+        ||ca1[icarry]=='8'||ca1[icarry]=='9') carry=true;
+        else if (ca1[icarry]=='5') {
+          int ii=icarry+1;
+          for (;ii<ca1.length; ii++)
+            if (ca1[ii]!='0') break;
+          carry=ii<ca1.length;
+          if (!carry&&icarry>0) {
+            carry=(ca1[icarry-1]=='1'||ca1[icarry-1]=='3'
+              ||ca1[icarry-1]=='5'||ca1[icarry-1]=='7'
+              ||ca1[icarry-1]=='9');
+          }
+        }
+      }
+      return carry;
+    }
+    /**
+     * Start the symbolic carry process.  The process
+     * is not quite finished because the symbolic
+     * carry may change the length of the string and
+     * change the exponent (in e format).
+     * @param cLast index of the last digit changed
+     *     by the round
+     * @param cFirst index of the first digit allowed
+     *     to be changed by this phase of the round
+     * @return <code>true</code> if the carry forces
+     *     a round that will change the print still
+     *     more
+     */
+    private boolean startSymbolicCarry(
+              char[] ca,int cLast,int cFirst) {
+      boolean carry=true;
+      for (int i=cLast; carry && i>=cFirst; i--) {
+        carry = false;
+        switch(ca[i]) {
+        case '0': ca[i]='1'; break;
+        case '1': ca[i]='2'; break;
+        case '2': ca[i]='3'; break;
+        case '3': ca[i]='4'; break;
+        case '4': ca[i]='5'; break;
+        case '5': ca[i]='6'; break;
+        case '6': ca[i]='7'; break;
+        case '7': ca[i]='8'; break;
+        case '8': ca[i]='9'; break;
+        case '9': ca[i]='0'; carry=true; break;
+        }
+      }
+      return carry;
+    }
+	/**
+	 * An intermediate routine on the way to creating
+	 * an e format String.  The method decides whether
+	 * the input double value is an infinity,
+	 * not-a-number, or a finite double and formats
+	 * each type of input appropriately.
+	 * @param x the double value to be formatted.
+	 * @param eChar an 'e' or 'E' to use in the
+	 *     converted double value.
+	 * @return the converted double value.
+	 */
+    private String eFormatString(double x,char eChar) {
+      boolean noDigits=false;
+      char[] ca4,ca5;
+      if (Double.isInfinite(x)) {
+        if (x==Double.POSITIVE_INFINITY) {
+          if (leadingSign) ca4 = "+Inf".toCharArray();
+          else if (leadingSpace)
+            ca4 = " Inf".toCharArray();
+          else ca4 = "Inf".toCharArray();
+        }
+        else
+          ca4 = "-Inf".toCharArray();
+        noDigits = true;
+      }
+      else if (Double.isNaN(x)) {
+        if (leadingSign) ca4 = "+NaN".toCharArray();
+        else if (leadingSpace)
+          ca4 = " NaN".toCharArray();
+        else ca4 = "NaN".toCharArray();
+        noDigits = true;
+      }
+      else
+        ca4 = eFormatDigits(x,eChar);
+      ca5 = applyFloatPadding(ca4,false);
+      return new String(ca5);
+    }
+    /**
+     * Apply zero or blank, left or right padding.
+     * @param ca4 array of characters before padding is
+     *     finished
+     * @param noDigits NaN or signed Inf
+     * @return a padded array of characters
+     */
+    private char[] applyFloatPadding(
+          char[] ca4,boolean noDigits) {
+      char[] ca5 = ca4;
+      if (fieldWidthSet) {
+        int i,j,nBlanks;
+        if (leftJustify) {
+          nBlanks = fieldWidth-ca4.length;
+          if (nBlanks > 0) {
+            ca5 = new char[ca4.length+nBlanks];
+            for (i=0; i<ca4.length; i++)
+              ca5[i] = ca4[i];
+            for (j=0; j<nBlanks; j++,i++)
+              ca5[i] = ' ';
+          }
+        }
+        else if (!leadingZeros || noDigits) {
+          nBlanks = fieldWidth-ca4.length;
+          if (nBlanks > 0) {
+            ca5 = new char[ca4.length+nBlanks];
+            for (i=0; i<nBlanks; i++)
+              ca5[i] = ' ';
+            for (j=0; j<ca4.length; i++,j++)
+              ca5[i] = ca4[j];
+          }
+        }
+        else if (leadingZeros) {
+          nBlanks = fieldWidth-ca4.length;
+          if (nBlanks > 0) {
+            ca5 = new char[ca4.length+nBlanks];
+            i=0; j=0;
+            if (ca4[0]=='-') { ca5[0]='-'; i++; j++; }
+            for (int k=0; k<nBlanks; i++,k++)
+              ca5[i] = '0';
+            for (; j<ca4.length; i++,j++)
+              ca5[i] = ca4[j];
+          }
+        }
+      }
+      return ca5;
+    }
+    /**
+     * Format method for the f conversion character.
+     * @param x the double to format.
+     * @return the formatted String.
+     */
+    private String printFFormat(double x) {
+      return fFormatString(x);
+    }
+    /**
+     * Format method for the e or E conversion
+     * character.
+     * @param x the double to format.
+     * @return the formatted String.
+     */
+    private String printEFormat(double x) {
+      if (conversionCharacter=='e')
+        return eFormatString(x,'e');
+      else
+        return eFormatString(x,'E');
+    }
+    /**
+     * Format method for the g conversion character.
+     *
+     * For g format, the flag character '-', means that
+     *  the output should be left justified within the
+     * field.  The default is to pad with blanks on the
+     * left.  '+' character means that the conversion
+     * will always begin with a sign (+ or -).  The
+     * blank flag character means that a non-negative
+     * input will be preceded with a blank.  If both a
+     * '+' and a ' ' are specified, the blank flag is
+     * ignored.  The '0' flag character implies that
+     * padding to the field width will be done with
+     * zeros instead of blanks.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear after the radix character.
+     * Padding is with trailing 0s.
+     * @param x the double to format.
+     * @return the formatted String.
+     */
+    private String printGFormat(double x) {
+      String sx,sy,sz,ret;
+      int savePrecision=precision;
+      int i;
+      char[] ca4,ca5;
+      boolean noDigits=false;
+      if (Double.isInfinite(x)) {
+        if (x==Double.POSITIVE_INFINITY) {
+          if (leadingSign) ca4 = "+Inf".toCharArray();
+          else if (leadingSpace)
+            ca4 = " Inf".toCharArray();
+          else ca4 = "Inf".toCharArray();
+        }
+        else
+          ca4 = "-Inf".toCharArray();
+        noDigits = true;
+      }
+      else if (Double.isNaN(x)) {
+        if (leadingSign) ca4 = "+NaN".toCharArray();
+        else if (leadingSpace)
+          ca4 = " NaN".toCharArray();
+        else ca4 = "NaN".toCharArray();
+        noDigits = true;
+      }
+      else {
+        if (!precisionSet) precision=defaultDigits;
+        if (precision==0) precision=1;
+        int ePos=-1;
+        if (conversionCharacter=='g') {
+          sx = eFormatString(x,'e').trim();
+          ePos=sx.indexOf('e');
+        }
+        else {
+          sx = eFormatString(x,'E').trim();
+          ePos=sx.indexOf('E');
+        }
+        i=ePos+1;
+        int expon=0;
+        if (sx.charAt(i)=='-') {
+          for (++i; i<sx.length(); i++)
+            if (sx.charAt(i)!='0') break;
+          if (i<sx.length())
+            expon=-Integer.parseInt(sx.substring(i));
+        }
+        else {
+          if (sx.charAt(i)=='+') ++i;
+          for (; i<sx.length(); i++)
+            if (sx.charAt(i)!='0') break;
+          if (i<sx.length())
+            expon=Integer.parseInt(sx.substring(i));
+        }
+        // Trim trailing zeros.
+        // If the radix character is not followed by
+        // a digit, trim it, too.
+        if (!alternateForm) {
+          if (expon>=-4 && expon<precision)
+            sy = fFormatString(x).trim();
+          else
+            sy = sx.substring(0,ePos);
+          i=sy.length()-1;
+          for (; i>=0; i--)
+            if (sy.charAt(i)!='0') break;
+          if (i>=0 && sy.charAt(i)=='.') i--;
+          if (i==-1) sz="0";
+          else if (!Character.isDigit(sy.charAt(i)))
+            sz=sy.substring(0,i+1)+"0";
+          else sz=sy.substring(0,i+1);
+          if (expon>=-4 && expon<precision)
+            ret=sz;
+          else
+            ret=sz+sx.substring(ePos);
+        }
+        else {
+          if (expon>=-4 && expon<precision)
+            ret = fFormatString(x).trim();
+          else
+            ret = sx;
+        }
+        // leading space was trimmed off during
+        // construction
+        if (leadingSpace) if (x>=0) ret = " "+ret;
+        ca4 = ret.toCharArray();
+      }
+      // Pad with blanks or zeros.
+      ca5 = applyFloatPadding(ca4,false);
+      precision=savePrecision;
+      return new String(ca5);
+    }
+    /**
+     * Format method for the d conversion specifer and
+     * short argument.
+     *
+     * For d format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the
+     * left.  A '+' character means that the conversion
+     * will always begin with a sign (+ or -).  The
+     * blank flag character means that a non-negative
+     * input will be preceded with a blank.  If both a
+     * '+' and a ' ' are specified, the blank flag is
+     * ignored.  The '0' flag character implies that
+     * padding to the field width will be done with
+     * zeros instead of blanks.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear.  Padding is with leading 0s.
+     * @param x the short to format.
+     * @return the formatted String.
+     */
+    private String printDFormat(short x) {
+      return printDFormat(Short.toString(x));
+    }
+    /**
+     * Format method for the d conversion character and
+     * long argument.
+     *
+     * For d format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the
+     * left.  A '+' character means that the conversion
+     * will always begin with a sign (+ or -).  The
+     * blank flag character means that a non-negative
+     * input will be preceded with a blank.  If both a
+     * '+' and a ' ' are specified, the blank flag is
+     * ignored.  The '0' flag character implies that
+     * padding to the field width will be done with
+     * zeros instead of blanks.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear.  Padding is with leading 0s.
+     * @param x the long to format.
+     * @return the formatted String.
+     */
+    private String printDFormat(long x) {
+      return printDFormat(Long.toString(x));
+    }
+    /**
+     * Format method for the d conversion character and
+     * int argument.
+     *
+     * For d format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the
+     * left.  A '+' character means that the conversion
+     * will always begin with a sign (+ or -).  The
+     * blank flag character means that a non-negative
+     * input will be preceded with a blank.  If both a
+     * '+' and a ' ' are specified, the blank flag is
+     * ignored.  The '0' flag character implies that
+     * padding to the field width will be done with
+     * zeros instead of blanks.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear.  Padding is with leading 0s.
+     * @param x the int to format.
+     * @return the formatted String.
+     */
+    private String printDFormat(int x) {
+      return printDFormat(Integer.toString(x));
+    }
+    /**
+     * Utility method for formatting using the d
+     * conversion character.
+     * @param sx the String to format, the result of
+     *     converting a short, int, or long to a
+     *     String.
+     * @return the formatted String.
+     */
+    private String printDFormat(String sx) {
+      int nLeadingZeros=0;
+      int nBlanks=0,n=0;
+      int i=0,jFirst=0;
+      boolean neg = sx.charAt(0)=='-';
+      if (sx.equals("0")&&precisionSet&&precision==0)
+        sx="";
+      if (!neg) {
+        if (precisionSet && sx.length() < precision)
+          nLeadingZeros = precision-sx.length();
+      }
+      else {
+        if (precisionSet&&(sx.length()-1)<precision)
+          nLeadingZeros = precision-sx.length()+1;
+      }
+      if (nLeadingZeros<0) nLeadingZeros=0;
+      if (fieldWidthSet) {
+        nBlanks = fieldWidth-nLeadingZeros-sx.length();
+        if (!neg&&(leadingSign||leadingSpace))
+          nBlanks--;
+      }
+      if (nBlanks<0) nBlanks=0;
+      if (leadingSign) n++;
+      else if (leadingSpace) n++;
+      n += nBlanks;
+      n += nLeadingZeros;
+      n += sx.length();
+      char[] ca = new char[n];
+      if (leftJustify) {
+        if (neg) ca[i++] = '-';
+        else if (leadingSign) ca[i++] = '+';
+        else if (leadingSpace) ca[i++] = ' ';
+        char[] csx = sx.toCharArray();
+        jFirst = neg?1:0;
+        for (int j=0; j<nLeadingZeros; i++,j++) 
+          ca[i]='0';
+        for (int j=jFirst; j<csx.length; j++,i++)
+          ca[i] = csx[j];
+        for (int j=0; j<nBlanks; i++,j++)
+          ca[i] = ' ';
+      }
+      else {
+        if (!leadingZeros) {
+          for (i=0; i<nBlanks; i++)
+            ca[i] = ' ';
+          if (neg) ca[i++] = '-';
+          else if (leadingSign) ca[i++] = '+';
+          else if (leadingSpace) ca[i++] = ' ';
+        }
+        else {
+          if (neg) ca[i++] = '-';
+          else if (leadingSign) ca[i++] = '+';
+          else if (leadingSpace) ca[i++] = ' ';
+          for (int j=0; j<nBlanks; j++,i++)
+            ca[i] = '0';
+        }
+        for (int j=0; j<nLeadingZeros; j++,i++)
+          ca[i] = '0';
+        char[] csx = sx.toCharArray();
+        jFirst = neg?1:0;
+        for (int j=jFirst; j<csx.length; j++,i++)
+          ca[i] = csx[j];
+      }
+      return new String(ca);
+    }
+    /**
+     * Format method for the x conversion character and
+     * short argument.
+     *
+     * For x format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the
+     * left.  The '#' flag character means to lead with
+     * '0x'.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear.  Padding is with leading 0s.
+     * @param x the short to format.
+     * @return the formatted String.
+     */
+    private String printXFormat(short x) {
+      String sx=null;
+      if (x == Short.MIN_VALUE)
+        sx = "8000";
+      else if (x < 0) {
+        String t;
+        if (x==Short.MIN_VALUE)
+          t = "0";
+        else {
+          t = Integer.toString(
+            (~(-x-1))^Short.MIN_VALUE,16);
+          if (t.charAt(0)=='F'||t.charAt(0)=='f')
+            t = t.substring(16,32);
+        }
+        switch (t.length()) {
+        case 1:
+          sx = "800"+t;
+          break;
+        case 2:
+          sx = "80"+t;
+          break;
+        case 3:
+          sx = "8"+t;
+          break;
+        case 4:
+          switch (t.charAt(0)) {
+          case '1':
+            sx = "9"+t.substring(1,4);
+            break;
+          case '2':
+            sx = "a"+t.substring(1,4);
+            break;
+          case '3':
+            sx = "b"+t.substring(1,4);
+            break;
+          case '4':
+            sx = "c"+t.substring(1,4);
+            break;
+          case '5':
+            sx = "d"+t.substring(1,4);
+            break;
+          case '6':
+            sx = "e"+t.substring(1,4);
+            break;
+          case '7':
+            sx = "f"+t.substring(1,4);
+            break;
+          }
+          break;
+        }
+      }
+      else
+        sx = Integer.toString((int)x,16);
+      return printXFormat(sx);
+    }
+    /**
+     * Format method for the x conversion character and
+     * long argument.
+     *
+     * For x format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the
+     * left.  The '#' flag character means to lead with
+     * '0x'.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear.  Padding is with leading 0s.
+     * @param x the long to format.
+     * @return the formatted String.
+     */
+    private String printXFormat(long x) {
+      String sx=null;
+      if (x == Long.MIN_VALUE)
+        sx = "8000000000000000";
+      else if (x < 0) {
+        String t = Long.toString(
+          (~(-x-1))^Long.MIN_VALUE,16);
+        switch (t.length()) {
+        case 1:
+          sx = "800000000000000"+t;
+          break;
+        case 2:
+          sx = "80000000000000"+t;
+          break;
+        case 3:
+          sx = "8000000000000"+t;
+          break;
+        case 4:
+          sx = "800000000000"+t;
+          break;
+        case 5:
+          sx = "80000000000"+t;
+          break;
+        case 6:
+          sx = "8000000000"+t;
+          break;
+        case 7:
+          sx = "800000000"+t;
+          break;
+        case 8:
+          sx = "80000000"+t;
+          break;
+        case 9:
+          sx = "8000000"+t;
+          break;
+        case 10:
+          sx = "800000"+t;
+          break;
+        case 11:
+          sx = "80000"+t;
+          break;
+        case 12:
+          sx = "8000"+t;
+          break;
+        case 13:
+          sx = "800"+t;
+          break;
+        case 14:
+          sx = "80"+t;
+          break;
+        case 15:
+          sx = "8"+t;
+          break;
+        case 16:
+          switch (t.charAt(0)) {
+          case '1':
+            sx = "9"+t.substring(1,16);
+            break;
+          case '2':
+            sx = "a"+t.substring(1,16);
+            break;
+          case '3':
+            sx = "b"+t.substring(1,16);
+            break;
+          case '4':
+            sx = "c"+t.substring(1,16);
+            break;
+          case '5':
+            sx = "d"+t.substring(1,16);
+            break;
+          case '6':
+            sx = "e"+t.substring(1,16);
+            break;
+          case '7':
+            sx = "f"+t.substring(1,16);
+            break;
+          }
+          break;
+        }
+      }
+      else
+        sx = Long.toString(x,16);
+      return printXFormat(sx);
+    }
+    /**
+     * Format method for the x conversion character and
+     * int argument.
+     *
+     * For x format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the
+     * left.  The '#' flag character means to lead with
+     * '0x'.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear.  Padding is with leading 0s.
+     * @param x the int to format.
+     * @return the formatted String.
+     */
+    private String printXFormat(int x) {
+      String sx=null;
+      if (x == Integer.MIN_VALUE)
+        sx = "80000000";
+      else if (x < 0) {
+        String t = Integer.toString(
+          (~(-x-1))^Integer.MIN_VALUE,16);
+        switch (t.length()) {
+        case 1:
+          sx = "8000000"+t;
+          break;
+        case 2:
+          sx = "800000"+t;
+          break;
+        case 3:
+          sx = "80000"+t;
+          break;
+        case 4:
+          sx = "8000"+t;
+          break;
+        case 5:
+          sx = "800"+t;
+          break;
+        case 6:
+          sx = "80"+t;
+          break;
+        case 7:
+          sx = "8"+t;
+          break;
+        case 8:
+          switch (t.charAt(0)) {
+          case '1':
+            sx = "9"+t.substring(1,8);
+            break;
+          case '2':
+            sx = "a"+t.substring(1,8);
+            break;
+          case '3':
+            sx = "b"+t.substring(1,8);
+            break;
+          case '4':
+            sx = "c"+t.substring(1,8);
+            break;
+          case '5':
+            sx = "d"+t.substring(1,8);
+            break;
+          case '6':
+            sx = "e"+t.substring(1,8);
+            break;
+          case '7':
+            sx = "f"+t.substring(1,8);
+            break;
+          }
+          break;
+        }
+      }
+      else
+        sx = Integer.toString(x,16);
+      return printXFormat(sx);
+    }
+    /**
+     * Utility method for formatting using the x
+     * conversion character.
+     * @param sx the String to format, the result of
+     *     converting a short, int, or long to a
+     *     String.
+     * @return the formatted String.
+     */
+    private String printXFormat(String sx) {
+      int nLeadingZeros = 0;
+      int nBlanks = 0;
+      if (sx.equals("0")&&precisionSet&&precision==0)
+        sx="";
+      if (precisionSet)
+        nLeadingZeros = precision-sx.length();
+      if (nLeadingZeros<0) nLeadingZeros=0;
+      if (fieldWidthSet) {
+        nBlanks = fieldWidth-nLeadingZeros-sx.length();
+        if (alternateForm) nBlanks = nBlanks - 2;
+      }
+      if (nBlanks<0) nBlanks=0;
+      int n=0;
+      if (alternateForm) n+=2;
+      n += nLeadingZeros;
+      n += sx.length();
+      n += nBlanks;
+      char[] ca = new char[n];
+      int i=0;
+      if (leftJustify) {
+        if (alternateForm) {
+          ca[i++]='0'; ca[i++]='x';
+        }
+        for (int j=0; j<nLeadingZeros; j++,i++)
+          ca[i]='0';
+        char[] csx = sx.toCharArray();
+        for (int j=0; j<csx.length; j++,i++)
+          ca[i] = csx[j];
+        for (int j=0; j<nBlanks; j++,i++)
+          ca[i] = ' ';
+      }
+      else {
+        if (!leadingZeros)
+          for (int j=0; j<nBlanks; j++,i++)
+            ca[i] = ' ';
+        if (alternateForm) {
+          ca[i++]='0'; ca[i++]='x';
+        }
+        if (leadingZeros)
+          for (int j=0; j<nBlanks; j++,i++)
+            ca[i] = '0';
+        for (int j=0; j<nLeadingZeros; j++,i++)
+          ca[i]='0';
+        char[] csx = sx.toCharArray();
+        for (int j=0; j<csx.length; j++,i++)
+          ca[i] = csx[j];
+      }
+      String caReturn=new String(ca);
+      if (conversionCharacter=='X')
+        caReturn = caReturn.toUpperCase();
+      return caReturn;
+    }
+    /**
+     * Format method for the o conversion character and
+     * short argument.
+     *
+     * For o format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the 
+     * left.  The '#' flag character means that the
+     * output begins with a leading 0 and the precision
+     * is increased by 1.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear.  Padding is with leading 0s.
+     * @param x the short to format.
+     * @return the formatted String.
+     */
+    private String printOFormat(short x) {
+      String sx=null;
+      if (x == Short.MIN_VALUE)
+        sx = "100000";
+      else if (x < 0) {
+        String t = Integer.toString(
+          (~(-x-1))^Short.MIN_VALUE,8);
+        switch (t.length()) {
+        case 1:
+          sx = "10000"+t;
+          break;
+        case 2:
+          sx = "1000"+t;
+          break;
+        case 3:
+          sx = "100"+t;
+          break;
+        case 4:
+          sx = "10"+t;
+          break;
+        case 5:
+          sx = "1"+t;
+          break;
+        }
+      }
+      else
+        sx = Integer.toString((int)x,8);
+      return printOFormat(sx);
+    }
+    /**
+     * Format method for the o conversion character and
+     * long argument.
+     *
+     * For o format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the 
+     * left.  The '#' flag character means that the
+     * output begins with a leading 0 and the precision
+     * is increased by 1.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear.  Padding is with leading 0s.
+     * @param x the long to format.
+     * @return the formatted String.
+     */
+    private String printOFormat(long x) {
+      String sx=null;
+      if (x == Long.MIN_VALUE)
+        sx = "1000000000000000000000";
+      else if (x < 0) {
+        String t = Long.toString(
+          (~(-x-1))^Long.MIN_VALUE,8);
+        switch (t.length()) {
+        case 1:
+          sx = "100000000000000000000"+t;
+          break;
+        case 2:
+          sx = "10000000000000000000"+t;
+          break;
+        case 3:
+          sx = "1000000000000000000"+t;
+          break;
+        case 4:
+          sx = "100000000000000000"+t;
+          break;
+        case 5:
+          sx = "10000000000000000"+t;
+          break;
+        case 6:
+          sx = "1000000000000000"+t;
+          break;
+        case 7:
+          sx = "100000000000000"+t;
+          break;
+        case 8:
+          sx = "10000000000000"+t;
+          break;
+        case 9:
+          sx = "1000000000000"+t;
+          break;
+        case 10:
+          sx = "100000000000"+t;
+          break;
+        case 11:
+          sx = "10000000000"+t;
+          break;
+        case 12:
+          sx = "1000000000"+t;
+          break;
+        case 13:
+          sx = "100000000"+t;
+          break;
+        case 14:
+          sx = "10000000"+t;
+          break;
+        case 15:
+          sx = "1000000"+t;
+          break;
+        case 16:
+          sx = "100000"+t;
+          break;
+        case 17:
+          sx = "10000"+t;
+          break;
+        case 18:
+          sx = "1000"+t;
+          break;
+        case 19:
+          sx = "100"+t;
+          break;
+        case 20:
+          sx = "10"+t;
+          break;
+        case 21:
+          sx = "1"+t;
+          break;
+        }
+      }
+      else
+        sx = Long.toString(x,8);
+      return printOFormat(sx);
+    }
+    /**
+     * Format method for the o conversion character and
+     * int argument.
+     *
+     * For o format, the flag character '-', means that
+     * the output should be left justified within the
+     * field.  The default is to pad with blanks on the
+     * left.  The '#' flag character means that the
+     * output begins with a leading 0 and the precision
+     * is increased by 1.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is to
+     * add no padding.  Padding is with blanks by
+     * default.
+     *
+     * The precision, if set, is the minimum number of
+     * digits to appear.  Padding is with leading 0s.
+     * @param x the int to format.
+     * @return the formatted String.
+     */
+    private String printOFormat(int x) {
+      String sx=null;
+      if (x == Integer.MIN_VALUE)
+        sx = "20000000000";
+      else if (x < 0) {
+        String t = Integer.toString(
+          (~(-x-1))^Integer.MIN_VALUE,8);
+        switch (t.length()) {
+        case 1:
+          sx = "2000000000"+t;
+          break;
+        case 2:
+          sx = "200000000"+t;
+          break;
+        case 3:
+          sx = "20000000"+t;
+          break;
+        case 4:
+          sx = "2000000"+t;
+          break;
+        case 5:
+          sx = "200000"+t;
+          break;
+        case 6:
+          sx = "20000"+t;
+          break;
+        case 7:
+          sx = "2000"+t;
+          break;
+        case 8:
+          sx = "200"+t;
+          break;
+        case 9:
+          sx = "20"+t;
+          break;
+        case 10:
+          sx = "2"+t;
+          break;
+        case 11:
+          sx = "3"+t.substring(1);
+          break;
+        }
+      }
+      else
+        sx = Integer.toString(x,8);
+      return printOFormat(sx);
+    }
+    /**
+     * Utility method for formatting using the o
+     * conversion character.
+     * @param sx the String to format, the result of
+     *     converting a short, int, or long to a
+     *     String.
+     * @return the formatted String.
+     */
+    private String printOFormat(String sx) {
+      int nLeadingZeros = 0;
+      int nBlanks = 0;
+      if (sx.equals("0")&&precisionSet&&precision==0)
+        sx="";
+      if (precisionSet)
+        nLeadingZeros = precision-sx.length();
+      if (alternateForm) nLeadingZeros++;
+      if (nLeadingZeros<0) nLeadingZeros=0;
+      if (fieldWidthSet)
+        nBlanks = fieldWidth-nLeadingZeros-sx.length();
+      if (nBlanks<0) nBlanks=0;
+      int n=nLeadingZeros+sx.length()+nBlanks;
+      char[] ca = new char[n];
+      int i;
+      if (leftJustify) {
+        for (i=0; i<nLeadingZeros; i++) ca[i]='0';
+        char[] csx = sx.toCharArray();
+        for (int j=0; j<csx.length; j++,i++)
+          ca[i] = csx[j];
+        for (int j=0; j<nBlanks; j++,i++) ca[i] = ' ';
+      }
+      else {
+        if (leadingZeros)
+          for (i=0; i<nBlanks; i++) ca[i]='0';
+        else
+          for (i=0; i<nBlanks; i++) ca[i]=' ';
+        for (int j=0; j<nLeadingZeros; j++,i++)
+          ca[i]='0';
+        char[] csx = sx.toCharArray();
+        for (int j=0; j<csx.length; j++,i++)
+          ca[i] = csx[j];
+      }
+      return new String(ca);
+    }
+    /**
+     * Format method for the c conversion character and
+     * char argument.
+     *
+     * The only flag character that affects c format is
+     * the '-', meaning that the output should be left
+     * justified within the field.  The default is to
+     * pad with blanks on the left.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  Padding is with
+     * blanks by default.  The default width is 1.
+     *
+     * The precision, if set, is ignored.
+     * @param x the char to format.
+     * @return the formatted String.
+     */
+    private String printCFormat(char x) {
+      int nPrint = 1;
+      int width = fieldWidth;
+      if (!fieldWidthSet) width = nPrint;
+      char[] ca = new char[width];
+      int i=0;
+      if (leftJustify) {
+        ca[0] = x;
+        for (i=1; i<=width-nPrint; i++) ca[i]=' ';
+      }
+      else {
+        for (i=0; i<width-nPrint; i++) ca[i]=' ';
+        ca[i] = x;
+      }
+      return new String(ca);
+    }
+    /**
+     * Format method for the s conversion character and
+     * String argument.
+     *
+     * The only flag character that affects s format is
+     * the '-', meaning that the output should be left
+     * justified within the field.  The default is to
+     * pad with blanks on the left.
+     *
+     * The field width is treated as the minimum number
+     * of characters to be printed.  The default is the
+     * smaller of the number of characters in the the
+     * input and the precision.  Padding is with blanks
+     * by default.
+     *
+     * The precision, if set, specifies the maximum
+     * number of characters to be printed from the
+     * string.  A null digit string is treated
+     * as a 0.  The default is not to set a maximum
+     * number of characters to be printed.
+     * @param x the String to format.
+     * @return the formatted String.
+     */
+    private String printSFormat(String x) {
+      int nPrint = x.length();
+      int width = fieldWidth;
+      if (precisionSet && nPrint>precision)
+        nPrint=precision;
+      if (!fieldWidthSet) width = nPrint;
+      int n=0;
+      if (width>nPrint) n+=width-nPrint;
+      if (nPrint>=x.length()) n+= x.length();
+      else n+= nPrint;
+      char[] ca = new char[n];
+      int i=0;
+      if (leftJustify) {
+        if (nPrint>=x.length()) {
+          char[] csx = x.toCharArray();
+          for (i=0; i<x.length(); i++) ca[i]=csx[i];
+        }
+        else {
+          char[] csx =
+            x.substring(0,nPrint).toCharArray();
+          for (i=0; i<nPrint; i++) ca[i]=csx[i];
+        }
+        for (int j=0; j<width-nPrint; j++,i++)
+          ca[i]=' ';
+      }
+      else {
+        for (i=0; i<width-nPrint; i++) ca[i]=' ';
+        if (nPrint>=x.length()) {
+          char[] csx = x.toCharArray();
+          for (int j=0; j<x.length(); i++,j++)
+            ca[i]=csx[j];
+        }
+        else {
+          char[] csx =
+            x.substring(0,nPrint).toCharArray();
+          for (int j=0; j<nPrint; i++,j++)
+            ca[i]=csx[j];
+        }
+      }
+      return new String(ca);
+    }
+    /**
+     * Check for a conversion character.  If it is
+     * there, store it.
+     *      * @return <code>true</code> if the conversion
+     *     character is there, and
+     *     <code>false</code> otherwise.
+     */
+    private boolean setConversionCharacter() {
+      /* idfgGoxXeEcs */
+      boolean ret = false;
+      conversionCharacter='\0';
+      if (pos < fmt.length()) {
+        char c = fmt.charAt(pos);
+        if (c=='i'||c=='d'||c=='f'||c=='g'||c=='G'
+        || c=='o' || c=='x' || c=='X' || c=='e'
+        || c=='E' || c=='c' || c=='s' || c=='%') {
+          conversionCharacter = c;
+          pos++;
+          ret = true;
+        }
+      }
+      return ret;
+    }
+    /**
+     * Check for an h, l, or L in a format.  An L is
+     * used to control the minimum number of digits
+     * in an exponent when using floating point
+     * formats.  An l or h is used to control
+     * conversion of the input to a long or short,
+     * respectively, before formatting.  If any of
+     * these is present, store them.
+     */
+    private void setOptionalHL() {
+      optionalh=false;
+      optionall=false;
+      optionalL=false;
+      if (pos < fmt.length()) {
+        char c = fmt.charAt(pos);
+        if (c=='h') { optionalh=true; pos++; }
+        else if (c=='l') { optionall=true; pos++; }
+        else if (c=='L') { optionalL=true; pos++; }
+      }
+    }
+    /**
+     * Set the precision.
+     */
+    private void setPrecision() {
+      int firstPos = pos;
+      precisionSet = false;
+      if (pos<fmt.length()&&fmt.charAt(pos)=='.') {
+        pos++;
+        if ((pos < fmt.length())
+        && (fmt.charAt(pos)=='*')) {
+          pos++;
+          if (!setPrecisionArgPosition()) {
+            variablePrecision = true;
+            precisionSet = true;
+          }
+          return;
+        }
+        else {
+          while (pos < fmt.length()) {
+            char c = fmt.charAt(pos);
+            if (Character.isDigit(c)) pos++;
+            else break;
+          }
+          if (pos > firstPos+1) {
+            String sz = fmt.substring(firstPos+1,pos);
+            precision = Integer.parseInt(sz);
+            precisionSet = true;
+          }
+        }
+      }
+    }
+    /**
+     * Set the field width.
+     */
+    private void setFieldWidth() {
+      int firstPos = pos;
+      fieldWidth = 0;
+      fieldWidthSet = false;
+      if ((pos < fmt.length())
+      && (fmt.charAt(pos)=='*')) {
+        pos++;
+        if (!setFieldWidthArgPosition()) {
+          variableFieldWidth = true;
+          fieldWidthSet = true;
+        }
+      }
+      else {
+        while (pos < fmt.length()) {
+          char c = fmt.charAt(pos);
+          if (Character.isDigit(c)) pos++;
+          else break;
+        }
+        if (firstPos<pos && firstPos < fmt.length()) {
+          String sz = fmt.substring(firstPos,pos);
+          fieldWidth = Integer.parseInt(sz);
+          fieldWidthSet = true;
+        }
+      }
+    }
+    /**
+     * Store the digits <code>n</code> in %n$ forms.
+     */
+    private void setArgPosition() {
+      int xPos;
+      for (xPos=pos; xPos<fmt.length(); xPos++) {
+        if (!Character.isDigit(fmt.charAt(xPos)))
+          break;
+      }
+      if (xPos>pos && xPos<fmt.length()) {
+        if (fmt.charAt(xPos)=='$') {
+          positionalSpecification = true;
+          argumentPosition=
+            Integer.parseInt(fmt.substring(pos,xPos));
+          pos=xPos+1;
+        }
+      }
+    }
+    /**
+     * Store the digits <code>n</code> in *n$ forms.
+     */
+    private boolean setFieldWidthArgPosition() {
+      boolean ret=false;
+      int xPos;
+      for (xPos=pos; xPos<fmt.length(); xPos++) {
+        if (!Character.isDigit(fmt.charAt(xPos)))
+          break;
+      }
+      if (xPos>pos && xPos<fmt.length()) {
+        if (fmt.charAt(xPos)=='$') {
+          positionalFieldWidth = true;
+          argumentPositionForFieldWidth=
+            Integer.parseInt(fmt.substring(pos,xPos));
+          pos=xPos+1;
+          ret=true;
+        }
+      }
+      return ret;
+    }
+    /**
+     * Store the digits <code>n</code> in *n$ forms.
+     */
+    private boolean setPrecisionArgPosition() {
+      boolean ret=false;
+      int xPos;
+      for (xPos=pos; xPos<fmt.length(); xPos++) {
+        if (!Character.isDigit(fmt.charAt(xPos)))
+          break;
+      }
+      if (xPos>pos && xPos<fmt.length()) {
+        if (fmt.charAt(xPos)=='$') {
+          positionalPrecision = true;
+          argumentPositionForPrecision=
+            Integer.parseInt(fmt.substring(pos,xPos));
+          pos=xPos+1;
+          ret=true;
+        }
+      }
+      return ret;
+    }
+    boolean isPositionalSpecification() {
+      return positionalSpecification;
+    }
+    int getArgumentPosition() { return argumentPosition; }
+    boolean isPositionalFieldWidth() {
+      return positionalFieldWidth;
+    }
+    int getArgumentPositionForFieldWidth() {
+      return argumentPositionForFieldWidth;
+    }
+    boolean isPositionalPrecision() {
+      return positionalPrecision;
+    }
+    int getArgumentPositionForPrecision() {
+      return argumentPositionForPrecision;
+    }
+    /**
+     * Set flag characters, one of '-+#0 or a space.
+     */
+    private void setFlagCharacters() {
+      /* '-+ #0 */
+      thousands = false;
+      leftJustify = false;
+      leadingSign = false;
+      leadingSpace = false;
+      alternateForm = false;
+      leadingZeros = false;
+      for ( ; pos < fmt.length(); pos++) {
+        char c = fmt.charAt(pos);
+        if (c == '\'') thousands = true;
+        else if (c == '-') {
+          leftJustify = true;
+          leadingZeros = false;
+        }
+        else if (c == '+') {
+          leadingSign = true;
+          leadingSpace = false;
+        }
+        else if (c == ' ') {
+          if (!leadingSign) leadingSpace = true;
+        }
+        else if (c == '#') alternateForm = true;
+        else if (c == '0') {
+          if (!leftJustify) leadingZeros = true;
+        }
+        else break;
+      }
+    }
+    /**
+     * The integer portion of the result of a decimal
+     * conversion (i, d, u, f, g, or G) will be
+     * formatted with thousands' grouping characters.
+     * For other conversions the flag is ignored.
+     */
+    private boolean thousands = false;
+    /**
+     * The result of the conversion will be
+     * left-justified within the field.
+     */
+    private boolean leftJustify = false;
+    /**
+     * The result of a signed conversion will always
+     * begin with a sign (+ or -).
+     */
+    private boolean leadingSign = false;
+    /**
+     * Flag indicating that left padding with spaces is
+     * specified.
+     */
+    private boolean leadingSpace = false;
+    /**
+     * For an o conversion, increase the precision to
+     * force the first digit of the result to be a
+     * zero.  For x (or X) conversions, a non-zero
+     * result will have 0x (or 0X) prepended to it.
+     * For e, E, f, g, or G conversions, the result
+     * will always contain a radix character, even if
+     * no digits follow the point.  For g and G
+     * conversions, trailing zeros will not be removed
+     * from the result.
+     */
+    private boolean alternateForm = false;
+    /**
+     * Flag indicating that left padding with zeroes is
+     * specified.
+     */
+    private boolean leadingZeros = false;
+    /**
+     * Flag indicating that the field width is *.
+     */
+    private boolean variableFieldWidth = false;
+    /**
+     * If the converted value has fewer bytes than the
+     * field width, it will be padded with spaces or
+     * zeroes.
+     */
+    private int fieldWidth = 0;
+    /**
+     * Flag indicating whether or not the field width
+     * has been set.
+     */
+    private boolean fieldWidthSet = false;
+    /**
+     * The minimum number of digits to appear for the
+     * d, i, o, u, x, or X conversions.  The number of
+     * digits to appear after the radix character for
+     * the e, E, and f conversions.  The maximum number
+     *  of significant digits for the g and G 
+     * conversions.  The maximum number of bytes to be
+     * printed from a string in s and S conversions.
+     */
+    private int precision = 0;
+    /** Default precision. */
+    private final static int defaultDigits=6; 
+    /**
+     * Flag indicating that the precision is *.
+     */
+    private boolean variablePrecision = false;
+    /**
+     * Flag indicating whether or not the precision has
+     * been set.
+     */
+    private boolean precisionSet = false;
+    /*
+     */
+    private boolean positionalSpecification=false;
+    private int argumentPosition=0;
+    private boolean positionalFieldWidth=false;
+    private int argumentPositionForFieldWidth=0;
+    private boolean positionalPrecision=false;
+    private int argumentPositionForPrecision=0;
+    /**
+     * Flag specifying that a following d, i, o, u, x,
+     * or X conversion character applies to a type
+     * short int.
+     */
+    private boolean optionalh = false;
+    /**
+     * Flag specifying that a following d, i, o, u, x,
+     * or X conversion character applies to a type lont
+     * int argument.
+     */
+    private boolean optionall = false;
+    /**
+     * Flag specifying that a following e, E, f, g, or
+     * G conversion character applies to a type double
+     * argument.  This is a noop in Java.
+     */
+    private boolean optionalL = false;
+    /** Control string type. */
+    private char conversionCharacter = '\0';
+    /**
+     * Position within the control string.  Used by
+     * the constructor.
+     */
+    private int pos = 0;
+    /** Literal or control format string. */
+    private String fmt;
+  }
+  /** Vector of control strings and format literals. */
+  private Vector vFmt = new Vector();
+  /** Character position.  Used by the constructor. */
+  private int cPos=0;
+  /** Character position.  Used by the constructor. */
+  private DecimalFormatSymbols dfs=null;
+}
diff --git a/src/demos/nurbs/surfaceapp/SliderListener.java b/src/demos/nurbs/surfaceapp/SliderListener.java
new file mode 100755
index 0000000..0d27fd4
--- /dev/null
+++ b/src/demos/nurbs/surfaceapp/SliderListener.java
@@ -0,0 +1,36 @@
+package demos.nurbs.surfaceapp;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * Class reactiong to events occuring on JSliders controlling rotation
+ * Třída pro zpracování událostí na JSliderech ovládajících rotaci
+ * @author Tomáš Hráský 
+ *
+ */
+public class SliderListener implements ChangeListener {
+
+  /**
+     Parent window
+     *Rodičovské okno 
+     */
+  private SurfaceApp app;
+
+  /**
+   * Creates new instance with link to parent window
+   * Vytvoří novou instanci s odkazem na rodičovské okno
+   * @param app parent window
+   */
+  public SliderListener(SurfaceApp app) {
+    this.app=app;
+  }
+
+  /* (non-Javadoc)
+   * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
+   */
+  public void stateChanged(ChangeEvent e) {
+    app.updateRotationLabels();
+    app.updateGLCanvas();
+  }
+}
diff --git a/src/demos/nurbs/surfaceapp/SpinnerListener.java b/src/demos/nurbs/surfaceapp/SpinnerListener.java
new file mode 100755
index 0000000..46a9d82
--- /dev/null
+++ b/src/demos/nurbs/surfaceapp/SpinnerListener.java
@@ -0,0 +1,52 @@
+package demos.nurbs.surfaceapp;
+
+import javax.swing.JSpinner;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * Listener reacting to event occuring on components editing coords end weight of selected control point
+ * Listener zpracovávající události na komponentách editujících souřadnice a váhu vybraného řídícícho bodu
+ * @author Tomáš Hráský
+ *
+ */
+public class SpinnerListener implements ChangeListener {
+
+  /**
+   * Parent window
+   * Okno aplikace, k němuž listener patří
+   */
+  private SurfaceApp appWindow;
+
+  /**
+   * Creates new instance with link to parent window
+   * Vytvoří instanci objektu s odkazem na rodičovské okno
+   * @param app parent app window
+   */
+  public SpinnerListener(SurfaceApp app) {
+    this.appWindow=app;
+  }
+
+  /* (non-Javadoc)
+   * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
+   */
+  public void stateChanged(ChangeEvent e) {
+    JSpinner src=(JSpinner) e.getSource();
+    //		System.out.println(src.getValue().getClass().toString());
+    float val = 0;
+    if(src.getValue() instanceof Double) val=((Double) src.getValue()).floatValue();
+    if(src.getValue() instanceof Float) val=((Float) src.getValue()).floatValue();
+		
+    if(src.getName()==SurfaceApp.X_SPINNER_NAME)
+      Surface.getInstance().setActiveX(val);
+    if(src.getName()==SurfaceApp.Y_SPINNER_NAME)
+      Surface.getInstance().setActiveY(val);
+    if(src.getName()==SurfaceApp.Z_SPINNER_NAME)
+      Surface.getInstance().setActiveZ(val);
+    if(src.getName()==SurfaceApp.W_SPINNER_NAME)
+      Surface.getInstance().setActiveW(val);
+		
+		
+    appWindow.updateGLCanvas();
+  }
+}
diff --git a/src/demos/nurbs/surfaceapp/Surface.java b/src/demos/nurbs/surfaceapp/Surface.java
new file mode 100755
index 0000000..b96ff72
--- /dev/null
+++ b/src/demos/nurbs/surfaceapp/Surface.java
@@ -0,0 +1,472 @@
+package demos.nurbs.surfaceapp;
+
+import java.io.File;
+import java.util.Vector;
+
+import simple.xml.Element;
+import simple.xml.ElementList;
+import simple.xml.Root;
+import simple.xml.Serializer;
+import simple.xml.load.Persister;
+
+/**
+ * Třída definice NURBS plochy, vystavěna podle návrhového vzoru Singleton
+ * @author Tomáš Hráský
+ *
+ */
+@Root(name="surface")
+public class Surface {
+  /**
+   * Odkaz na instanci třídy
+   */
+  private static Surface singleton;
+  /**
+   * Indikuje, zda je zadání plochy kompletní
+   */
+  @Element(name="finished")
+    private boolean isSurfaceFinished=false;
+
+  /**
+   * Index aktuálního vybraného řídícího bodu
+   */
+  private int bodIndex = -1;
+
+  /**
+   * Stupeň plocy ve směru parametru U
+   */
+  @Element(name="orderU")
+    private int orderU=3;
+	
+  /**
+   * Stupeň plochy ve směru parametru V
+   */
+  @Element(name="orderV")
+    private int orderV=3;
+	
+  /**
+   * Počet řídících bodů ve směru parametru V
+   */
+  @Element(name="pointsInV")
+    private int pointsInV=4;
+	
+  /**
+   * Počet řídících bodů ve směru parametru U
+   */
+  @Element(name="pointsInU")
+    private int pointsInU=4;
+
+	
+  /**
+   * Pole souřadnic řídícíh bodů 
+   * 
+   */
+  private float[] ctrlPoints
+    //	={
+    //	     -150f,-150f, 400f,1f,
+    //	     -50f,-150f, 200f, 1f,
+    //	     50f,-150f,-100f,  1f,
+    //	     150f,-150f, 200f,1f,
+    //	     -150f,-50f, 100f, 1f,
+    //	     -50f,-50f, 300f, 1f,
+    //	     50f,-50f, 0f,  1f,
+    //	     150f,-50f,-100f,1f,
+    //	     -150f, 50f, 400f, 1f,
+    //	     -50f, 50f, 0f, 1f,
+    //	     50f, 50f, 300f,  1f,
+    //	     150f, 50f, 400f,1f,
+    //	     -150f, 150f,-200f, 1f,
+    //	     -50f, 150f,-200f, 1f,
+    //	     50f, 150f, 0f,  1f,
+    //	     150f, 150f,-100f,1f}
+    ;
+  /**
+   * Pole hodnot uzlového vektoru ve směru parametru U
+   */
+  private	float knotsU[]
+    //	       	            ={0.0f, 0.0f, 0.0f, 0.0f,
+    //            1f, 1f, 1.0f, 1.0f}
+    ;
+	
+  /**
+   * Pole hodnot uzlového vektoru ve směru parametru V
+   */
+  private	float knotsV[]
+    //	       	            ={0.0f, 0.0f, 0.0f, 0.0f,
+    //            1f, 1f, 1.0f, 1.0f}
+    ;
+	
+  /**
+   * Kolekce vektor pro persistenci souřadnic řídících bodů
+   */
+  @ElementList(name="ctrlpoints",type=MyFloat.class)
+    private Vector<MyFloat> ctrlVector;
+	
+  /**
+   * Kolekce vektor pro persistenci uzlového vektoru ve směru parametru U
+   */
+  @ElementList(name="knotsU",type=MyFloat.class)
+    private Vector<MyFloat> knotVectorU;
+	
+  /**
+   * Kolekce vektor pro persistenci uzlového vektoru ve směru parametru V
+   */
+  @ElementList(name="knotsV",type=MyFloat.class)
+    private Vector<MyFloat> knotVectorV;
+	
+  /**
+   * Vytvoří prázdnou definici plochy
+   */
+  public void clear(){
+    isSurfaceFinished=false;
+    ctrlPoints=new float[0];
+    knotsU=new float[0];
+    knotsV=new float[0];
+    orderU=3;
+    orderV=3;
+    pointsInU=0;
+    pointsInV=0;
+  }
+	
+  /**
+   * Pomocí framweorku Simple serializuje definici křivky do XML souboru
+   * @param f soubor pro uložení
+   */
+  public void persist(File f){
+    ctrlVector=new Vector<MyFloat>(ctrlPoints.length);
+    knotVectorU=new Vector<MyFloat>(knotsU.length);
+    knotVectorV=new Vector<MyFloat>(knotsV.length);
+		
+    for(Float ff:ctrlPoints)
+      ctrlVector.add(new MyFloat(ff));
+		
+    for(Float ff:knotsU)
+      knotVectorU.add(new MyFloat(ff));
+		
+    for(Float ff:knotsV)
+      knotVectorV.add(new MyFloat(ff));
+		
+    Serializer s=new Persister(); 
+    try {
+      System.out.println("ukládám");
+      s.write(Surface.getInstance(),f);
+    } catch (Exception e1) {
+      e1.printStackTrace();
+    }
+
+
+  }
+	
+  /**
+   * Vytvoří pomocí frameworku Simple křivku z definice uložené v XML souboru 
+   * @param f soubor,z něhož se má definice načíst
+   * @throws Exception chyba při čtení ze souboru
+   */
+  public void unPersist(File f) throws Exception{
+    Serializer s=new Persister();
+    Surface c=s.read(Surface.class,f);
+    initFromSurface(c);
+  }
+	
+  /**
+   * Inicializuje objekt podle jiného objektu typu Curve
+   * @param c referenční objekt - křivka
+   */
+  private void initFromSurface(Surface c) {
+    this.orderU=c.getOrderU();
+    this.orderV=c.getOrderV();
+    this.ctrlPoints=new float[c.getCtrlVector().size()];
+    this.knotsU=new float[c.getKnotVectorU().size()];
+    this.knotsV=new float[c.getKnotVectorV().size()];
+    int i=0;
+    for(MyFloat f:c.getCtrlVector())
+      ctrlPoints[i++]=f.getValue();
+    i=0;
+    for(MyFloat f:c.getKnotVectorU())
+      knotsU[i++]=f.getValue();
+    i=0;
+    for(MyFloat f:c.getKnotVectorV())
+      knotsV[i++]=f.getValue();
+		
+    this.pointsInU=c.getPointsInU();
+    this.pointsInV=c.getPointsInV();
+		
+    this.isSurfaceFinished=c.isSurfaceFinished();
+  }
+
+  /**
+   * Konstruktor, nastaví prázdné hodnoty polí definujících NURBS plochu
+   */
+  private Surface(){
+    //		ctrlPoints=new float[0];
+    //		knotsU=new float[0];
+    //		knotsV=new float[0];
+    //		isSurfaceFinished=false;
+    clear();
+  }
+	
+  /**
+   * Vrací instanci třídy (podle návrhového vzoru Singleton)
+   * @return instance třídy Curve
+   */
+  public static Surface getInstance() {
+    if (singleton == null)
+      singleton = new Surface();
+    return singleton;
+
+  }
+	
+  /**
+   * Vrací pole uzlového vektoru ve směru parametru U
+   * @return pole hodnot uzlového vektoru ve směru parametru U
+   */
+  public float[] getKnotsU() {
+    return this.knotsU;
+  }
+
+  /**
+   * Vrací pole s hodnotami souřadnic řídících bodů
+   * @return pole souřadnic řídících bodů
+   */
+  public float[] getCtrlPoints() {
+    return this.ctrlPoints;
+  }
+
+  /**
+   * Vrací stupeň NURBS plochy ve směru parametru U
+   * @return stupeň NURBS plochy ve směru parametru U
+   */
+  public int getOrderU() {
+    return this.orderU;
+  }
+
+  /**
+   * Vrací index aktuálně vybraného řídícího bodu
+   * @return index aktuálně vybraného řídícího bodu
+   */
+  public int getBodIndex() {
+    return bodIndex;
+  }
+
+  /**
+   * Nastavuje index požadovaného aktuálně vybraného řídícího bodu
+   * @param bodIndex index požadovaného aktuálně vybraného řídícího bodu
+   */
+  public void setBodIndex(int bodIndex) {
+    this.bodIndex = bodIndex;
+  }
+	
+  /**
+   * Vrací X-ovou souadnici aktuálně vybraného řídícího bodu, přepočítává hodnotu z homogenních souřadnic
+   * @return X-ová souadnice aktuálně vybraného řídícího bodu
+   */
+  public float getActiveX(){
+    if(bodIndex>=0){
+      return ctrlPoints[bodIndex*4]/ctrlPoints[bodIndex*4+3];
+    }
+    else return 0;
+  }
+  /**
+   * Vrací Y-ovou souadnici aktuálně vybraného řídícího bodu, přepočítává hodnotu z homogenních souřadnic
+   * @return Y-ová souadnice aktuálně vybraného řídícího bodu
+   */
+  public float getActiveY(){
+    if(bodIndex>=0){
+      return ctrlPoints[bodIndex*4+1]/ctrlPoints[bodIndex*4+3];
+    }
+    else return 0;
+  }
+  /**
+   * Vrací Z-ovou souadnici aktuálně vybraného řídícího bodu, přepočítává hodnotu z homogenních souřadnic
+   * @return Z-ová souadnice aktuálně vybraného řídícího bodu
+   */
+  public float getActiveZ(){
+    if(bodIndex>=0){
+      return ctrlPoints[bodIndex*4+2]/ctrlPoints[bodIndex*4+3];
+    }
+    else return 0;
+  }
+	
+  /**
+   * Vrací váhu aktuálně vybraného řídícího bodu
+   * @return váha aktuálně vybraného řídícího bodu
+   */
+  public float getActiveW(){
+    if(bodIndex>=0){
+      return ctrlPoints[bodIndex*4+3];
+    }
+    else return 0;
+  }
+	
+  /**
+   * Nastavuje X-ovou souadnici aktuálně vybraného řídícího bodu, přepočítává hodnotu do homogenních souřadnic
+   * @param x X-ová souřadnice aktuálně vybraného řídícího bodu
+   */
+  public void setActiveX(float x){
+    if(bodIndex>=0){
+      ctrlPoints[bodIndex*4]=x*ctrlPoints[bodIndex*4+3];
+    }
+  }
+  /**
+   * Nastavuje Y-ovou souadnici aktuálně vybraného řídícího bodu, přepočítává hodnotu do homogenních souřadnic
+   * @param y Y-ová souřadnice aktuálně vybraného řídícího bodu
+   */
+	
+  public void setActiveY(float y){
+    if(bodIndex>=0){
+      ctrlPoints[bodIndex*4+1]=y*ctrlPoints[bodIndex*4+3];
+    }
+  }
+  /**
+   * Nastavuje Z-ovou souadnici aktuálně vybraného řídícího bodu, přepočítává hodnotu do homogenních souřadnic
+   * @param z Z-ová souřadnice aktuálně vybraného řídícího bodu
+   */
+	
+  public void setActiveZ(float z){
+    if(bodIndex>=0){
+      ctrlPoints[bodIndex*4+2]=z*ctrlPoints[bodIndex*4+3];
+    }
+  }
+	
+  /**
+   *Nastavuje váhu aktuálně vybraného řídícího bodu, upravuje hodnoty stávajícíh souřadic vzhledem k váze a použití homogenních souřadnic 
+   * @param w váha aktuálně vybraného řídícího bodu
+   */
+  public void setActiveW(float w){
+    if(bodIndex>=0){
+      float oldW=ctrlPoints[bodIndex*4+3];
+      if(w>0){
+        ctrlPoints[bodIndex*4+3]=w;
+        //úprava souřadnic
+        ctrlPoints[bodIndex*4]=ctrlPoints[bodIndex*4]/oldW*w;
+        ctrlPoints[bodIndex*4+1]=ctrlPoints[bodIndex*4+1]/oldW*w;
+        ctrlPoints[bodIndex*4+2]=ctrlPoints[bodIndex*4+2]/oldW*w;
+      }
+			
+    }
+  }
+
+  /**
+   * Nastavuje uzlový vektor ve směru parametru U
+   * @param knots nový uzlový vektor ve směru parametru U
+   */
+  public void setKnotsU(float[] knots) {
+    this.knotsU = knots;
+  }
+
+  /**
+   * Vrací informaci o stavu dokončení definice křvky
+   * @return true pokud je definice křivky kompletní, jinak false
+   */
+  public boolean isSurfaceFinished() {
+    return isSurfaceFinished;
+  }
+
+  /**
+   * Nastavuje řídící body
+   * @param ctrlPoints pole souřadnic řídících bodů 
+   */
+  public void setCtrlPoints(float[] ctrlPoints) {
+    this.ctrlPoints = ctrlPoints;
+  }
+
+  /**
+   * Nastavuje stav dokončení definice křivky
+   * @param b stav dokončení definice křivky
+   *
+   */
+  public void setIsSurfaceFinished(boolean b) {
+    isSurfaceFinished=b;
+  }
+
+  /**
+   * Vrací vektor souřadnic řídích bodů pro serializaci
+   * @return vektor souřadnic řídících bodů
+   */
+  private Vector<MyFloat> getCtrlVector() {
+    return ctrlVector;
+  }
+
+  /**
+   * Vrací vektor prvků uzlového vektoru ve směru parametru U pro serializaci
+   * @return vektor prvků uzlového vektoru ve směru parametru U
+   */
+  private Vector<MyFloat> getKnotVectorU() {
+    return knotVectorU;
+  }
+
+  /**
+   * Vrací stupeň plochy ve směru parametru U
+   * @param order stupeň plochy ve směru parametru U
+   */
+  public void setOrderU(int order) {
+    this.orderU = order;
+  }
+  /**
+   * Vrací pole uzlového vektoru ve směru parametru V
+   * @return pole hodnot uzlového vektoru ve směru parametru V
+   */
+  public float[] getKnotsV() {
+    return knotsV;
+  }
+  /**
+   * Nastavuje uzlový vektor ve směru parametru V
+   * @param knotsV nový uzlový vektor ve směru parametru V
+   */
+  public void setKnotsV(float[] knotsV) {
+    this.knotsV = knotsV;
+  }
+  /**
+   * Vrací stupeň plochy ve směru parametru V
+   * @return stupeň plochy ve směru parametru V
+   */
+  public int getOrderV() {
+    return orderV;
+  }
+  /**
+   * Nastavuje stupeň NURBS plochy ve směru parametru V
+   * @param orderV stupeň plochy ve směru parametru V
+   */
+  public void setOrderV(int orderV) {
+    this.orderV = orderV;
+  }
+  /**
+   * Vrací vektor prvků uzlového vektoru ve směru parametru V pro serializaci
+   * @return vektor prvků uzlového vektoru ve směru parametru V
+   */
+  private Vector<MyFloat> getKnotVectorV() {
+    return knotVectorV;
+  }
+
+  /**
+   * Vrací počet řídících bodů ve směru parametru V (tj. počet sloupců)
+   * @return počet řídících bodů ve směru parametru V
+   */
+  public int getPointsInV() {
+    return pointsInV;
+  }
+
+  /**
+   * Nastavuje počet řídících bodů ve směru parametru V
+   * @param pointsInV počet řídících bodů ve směru parametru V
+   */
+  public void setPointsInV(int pointsInV) {
+    this.pointsInV = pointsInV;
+  }
+
+  /**
+   * Vrací počet řídících bodů ve směru parametru U (tj. počet řádků)
+   * @return počet řídících bodů ve směru parametru U
+
+  */
+  public int getPointsInU() {
+    return pointsInU;
+  }
+
+  /**
+   * Nastavuje počet řídících bodů ve směru parametru U
+   * @param pointsInU počet řídících bodů ve směru parametru U
+   */
+  public void setPointsInU(int pointsInU) {
+    this.pointsInU = pointsInU;
+  }
+}
diff --git a/src/demos/nurbs/surfaceapp/SurfaceApp.java b/src/demos/nurbs/surfaceapp/SurfaceApp.java
new file mode 100755
index 0000000..4018967
--- /dev/null
+++ b/src/demos/nurbs/surfaceapp/SurfaceApp.java
@@ -0,0 +1,1065 @@
+package demos.nurbs.surfaceapp;
+
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JSeparator;
+import javax.swing.JSlider;
+import javax.swing.JSpinner;
+import javax.swing.JToggleButton;
+import javax.swing.JToolBar;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.ToolTipManager;
+
+import demos.nurbs.icons.*;
+import demos.nurbs.knotslidercomponent.JKnotSlider;
+import javax.media.opengl.awt.GLCanvas;
+
+/**
+ * Main class for application demostrating capabilitues of JOGL library extend by NURBS surface functionalities
+ * Hlavní třída aplikace demonstrující shopnosti knihovny JOGL při práci s NURBS
+ * plochami
+ * 
+ * @author Tomáš Hráský
+ * 
+ */
+@SuppressWarnings("serial")
+public class SurfaceApp extends JFrame implements ActionListener
+{
+
+    /**
+     * X-coord editing component name
+     * Jméno komponenty pro editaci X-ové souřadnice aktuálního bodu
+     */
+    public static final String X_SPINNER_NAME = "xspinner";
+
+    /**
+     * Y-coord editing component name
+     * Jméno komponenty pro editaci Y-ové souřadnice aktuálního bodu
+     */
+    public static final String Y_SPINNER_NAME = "yspinner";
+
+    /**
+     * Z-coord editing component name
+     * Jméno komponenty pro editaci Z-ové souřadnice aktuálního bodu
+     */
+  public static final String Z_SPINNER_NAME = "zspinner";
+
+  /**
+   * Weight editing component name
+   * Jméno komponenty pro editaci váhy aktuálního bodu
+   */
+  public static final String W_SPINNER_NAME = "wspinner";
+
+  /**
+   * U direction knotvector editing component
+   * Jméno komponenty pro editaci uzlového vektoru ve směru parametru U
+   */
+  private static final String U_KNOTSLIDER = "Uknotspinner";
+
+  /**
+   * V direction knotvector editing component
+   * Jméno komponenty pro editaci uzlového vektoru ve směru parametru V
+   */
+  private static final String V_KNOTSLIDER = "Vknotspinner";
+
+  /**
+   * New control point action name
+   * Jméno události přidání řídícího bodu
+   */
+  public static final String PRIDAT_AC = "PRIDAT";
+
+  /**
+   * Degree set event name
+   * Jméno události zadání stupně křivky
+   */
+  public static final String STUPEN_AC = "STUPEN";
+
+  /**
+   * Delete control point event name
+   * Jméno události smazání řídícího bodu
+   */
+  public static final String SMAZAT_AC = "SMAZAT";
+
+  /**
+   * New clamped knotvector event name
+   * Jméno události vytvoření uzavřeného uzlového vektoru
+   */
+  public static final String UZAVRENY_AC = "UZAVRENY";
+
+  /**
+   * New uniform knotvector event name
+   * Jméno události vytvoření otevřeného (uniformního) uzlového vektoru
+   */
+  public static final String OTEVRENY_AC = "OTEVRENY";
+
+  /**
+   * Save surface event name
+   * Jméno události uložení plochy
+   */
+  public static final String ULOZIT_AC = "ULOZIT";
+
+  /**
+   * Load surface event name
+   * Jméno události načetení uložené definice plochy
+   */
+  public static final String NACIST_AC = "NACIST";
+
+  /**
+   * Move control point event name
+   * Jméno události pohybu řídícího bodu
+   */
+  private static final String MOVE_AC = "MOVE";
+
+  /**
+   * New surface event name
+   * Jméno události vytvoření nové plochy
+   */
+  static final String NOVA_AC = "NEWSURFACE";
+
+  /**
+   * Exit app event name
+   * Jméno události ukončení aplikace
+   */
+  public static final String EXIT_AC = "EXIT";
+
+  /**
+   * Show about event name
+   * Jméno události zobrazení okna o aplikaci
+   */
+  public static final String INFO_AC = "INFO";
+
+  /**
+   * Add column of control points event name
+   * Jméno události přidání sloupce řídících bodů
+   */
+  public static final String PRIDAT_AC_SLOUPEC = "ADDCOL";
+
+  /**
+   * Add row of control points event name
+   * Jméno události přidání řádku řídících bodů
+   */
+  public static final String PRIDAT_AC_RADEK = "ADDROW";
+
+  /**
+   * Remove row of control points event name
+   * Jméno události smazání řádku řídících bodů
+   */
+  public static final String SMAZAT_AC_RADEK = "DELROW";
+
+  /**
+   * Remove column of control points event name
+   * Jméno události smazání sloupce řídících bodů
+   */
+  public static final String SMAZAT_AC_SLOUPEC = "DELCOL";
+
+  /**
+   * OpenGL drawing canvas
+   * Plátno pro vykreslování pomocí OpenGL
+   */
+  private GLCanvas glCanvas;
+
+  /**
+   * X-coord editing component
+   * Komponenta pro editaci X-ové souřadnice aktuálního bodu
+   */
+  private JSpinner xSpinner;
+
+  /**
+   * Y-coord editing component
+   * Komponenta pro editaci Y-ové souřadnice aktuálního bodu
+   */
+  private JSpinner ySpinner;
+
+  /**
+   * Weight editing component
+   * Komponenta pro editaci váhy aktuálního bodu
+   */
+  private JSpinner wSpinner;
+
+  /**
+   * Z-coord editing component
+   * Komponenta pro editaci Z-ové souřadnice aktuálního bodu
+   */
+  private JSpinner zSpinner;
+
+  /**
+   * Mouse listener
+   * Listener událostí myši
+   */
+  private SurfaceMouseListener mouseListener;
+
+  /**
+   * U direction knotvector editing component
+   * Komponenta pro editaci uzlového vektoru ve směru parametru U
+   */
+  private JKnotSlider knotSlider;
+
+  /**
+   * V direction knotvector editing component
+   * Komponenta pro editaci uzlového vektoru ve směru parametru V
+   */
+  private JKnotSlider knotSlider2;
+
+  /**
+   * Set move points mode
+   * Tlačítko pro zapnutí módu pohybu řídících bodů
+   */
+  private JToggleButton moveTB;
+
+  /**
+   * X rotation component
+   * Komponenta ovládající otočení definované plochy kolem osy X
+   */
+  private JSlider rotaceXSlider;
+
+  /**
+   * Y rotation component
+   * Komponenta ovládající otočení definované plochy kolem osy Y
+   */
+  private JSlider rotaceYSlider;
+
+  /**
+   * Z rotation component
+   * Komponenta ovládající otočení definované plochy kolem osy Z
+   */
+  private JSlider rotaceZSlider;
+
+  /**
+   * Label for X rotation editing component
+   * Nadpis komponenty ovládající otočení definované plochy kolem osy X
+   */
+  private JLabel rotaceXLabel;
+  /**
+   * Label for Y rotation editing component
+   * Nadpis komponenty ovládající otočení definované plochy kolem osy Y
+   */
+  private JLabel rotaceYLabel;
+  /**
+   * Label for Z rotation editing component
+   * Nadpis komponenty ovládající otočení definované plochy kolem osy Z
+   */
+  private JLabel rotaceZLabel;
+
+  /**
+   * GL events listener
+   * Objekt reagující na události OpenGL plátna
+   */
+  private GLListener glListener;
+
+  /**
+   * Use lighting checkbox
+   * Checkbox použití nasvícení objektu 
+   */
+  private JCheckBox lightingChBox;
+
+  /**
+   * Constructor, creates GUI
+   * Konstruktor, vytvoří grafické uživatelské rozhraní
+   */
+  public SurfaceApp() {
+    //super( "Tomáš Hráský - ukázková aplikace funkcionality GLU NURBS funkcí - JOGL");
+    super( "Tomáš Hráský - example application of GLU NURBS in JOGL");
+    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+    initGUI();
+  }
+
+  /**
+   * GUI initialization
+   * Inicializace grafického uživatelského rozhraní
+   */
+  private void initGUI() {
+    JPopupMenu.setDefaultLightWeightPopupEnabled(false);
+    ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false);
+
+    this.glCanvas = new GLCanvas();
+    glCanvas.setSize(new Dimension(750, 500));
+    this.glListener=new GLListener(this);
+    glCanvas.addGLEventListener(glListener);
+    mouseListener = new SurfaceMouseListener(this);
+    glCanvas.addMouseListener(mouseListener);
+    glCanvas.addMouseMotionListener(mouseListener);
+    setLayout(new GridBagLayout());
+    GridBagConstraints c = new GridBagConstraints();
+    c.fill = GridBagConstraints.BOTH;
+
+    c.gridy = 0;
+    c.gridwidth = GridBagConstraints.REMAINDER;
+
+    ActListener listener = new ActListener(this);
+
+    JMenuBar menuBar = new JMenuBar();
+    getContentPane().add(menuBar, c);
+
+    JMenu aplikaceMenu = new JMenu("Aplication");
+    menuBar.add(aplikaceMenu);
+
+    JMenuItem aboutMI = new JMenuItem("About");
+    aboutMI.setActionCommand(INFO_AC);
+    aboutMI.addActionListener(listener);
+    aplikaceMenu.add(aboutMI);
+
+    aplikaceMenu.add(new JSeparator());
+
+    JMenuItem konecMI = new JMenuItem("Exit");
+    konecMI.addActionListener(listener);
+    konecMI.setActionCommand(EXIT_AC);
+    aplikaceMenu.add(konecMI);
+
+    JMenu krivkaMenu = new JMenu("Surface");
+    menuBar.add(krivkaMenu);
+
+    JMenu pridatBodyM = new JMenu("Add points");
+    krivkaMenu.add(pridatBodyM);
+    //		pridatBodyM.addActionListener(listener);
+
+    //		pridatBodyM.setActionCommand(PRIDAT_AC);
+		
+    JMenuItem pridatRadkyMI=new JMenuItem("Points row");
+    pridatRadkyMI.setActionCommand(PRIDAT_AC_RADEK);
+    pridatRadkyMI.addActionListener(listener);
+		
+    JMenuItem pridatSloupceMI=new JMenuItem("Points column");
+    pridatSloupceMI.setActionCommand(PRIDAT_AC_SLOUPEC);
+    pridatSloupceMI.addActionListener(listener);
+    pridatBodyM.add(pridatRadkyMI);
+    pridatBodyM.add(pridatSloupceMI);
+		
+		
+    JMenu smazatBodyM = new JMenu("Delete points");
+    krivkaMenu.add(smazatBodyM);
+    //		smazatBodyM.addActionListener(listener);
+
+    //		smazatBodyM.setActionCommand(SMAZAT_AC);
+
+    JMenuItem smazatRadkyMI=new JMenuItem("Points row");
+    smazatRadkyMI.setActionCommand(SMAZAT_AC_RADEK);
+    smazatRadkyMI.addActionListener(listener);
+    smazatBodyM.add(smazatRadkyMI);
+    JMenuItem smazatSloupceMI=new JMenuItem("Points column");
+    smazatSloupceMI.setActionCommand(SMAZAT_AC_SLOUPEC);
+    smazatSloupceMI.addActionListener(listener);
+    smazatBodyM.add(smazatSloupceMI);
+		
+		
+    JMenuItem stupenMI = new JMenuItem("Set surface degree");
+    krivkaMenu.add(stupenMI);
+    stupenMI.addActionListener(listener);
+    stupenMI.setActionCommand(STUPEN_AC);
+
+    JMenu knotVecMenu = new JMenu("Create knotvectors");
+    krivkaMenu.add(knotVecMenu);
+
+    JMenuItem clampedKVMI = new JMenuItem("Clamped");
+    knotVecMenu.add(clampedKVMI);
+    clampedKVMI.setActionCommand(UZAVRENY_AC);
+    clampedKVMI.addActionListener(listener);
+    JMenuItem unclampedKVMI = new JMenuItem("Uniform");
+    knotVecMenu.add(unclampedKVMI);
+    unclampedKVMI.setActionCommand(OTEVRENY_AC);
+    unclampedKVMI.addActionListener(listener);
+
+    JMenuItem moveMI = new JMenuItem("Move points");
+    krivkaMenu.add(moveMI);
+    moveMI.setActionCommand(MOVE_AC);
+    moveMI.addActionListener(listener);
+
+    krivkaMenu.add(new JSeparator());
+
+    krivkaMenu.add(new JSeparator());
+
+    JMenuItem novaMI = new JMenuItem("New surface");
+    krivkaMenu.add(novaMI);
+    novaMI.setActionCommand(NOVA_AC);
+    novaMI.addActionListener(listener);
+
+    JMenuItem ulozitMI = new JMenuItem("Safe surface as...");
+    krivkaMenu.add(ulozitMI);
+    ulozitMI.setActionCommand(ULOZIT_AC);
+    ulozitMI.addActionListener(listener);
+    JMenuItem nacistMI = new JMenuItem("Load surface");
+    krivkaMenu.add(nacistMI);
+    nacistMI.setActionCommand(NACIST_AC);
+    nacistMI.addActionListener(listener);
+
+    c.gridy++;
+    JToolBar toolBar = new JToolBar();
+    getContentPane().add(toolBar, c);
+
+    ButtonGroup bg = new ButtonGroup();
+
+    JButton novaB = new JButton();
+    // novaB.setText("Nová");
+    novaB.setToolTipText("New surface");
+    novaB.setIcon(IconFactory.getIcon("demos/nurbs/icons/folder_new.png"));
+    novaB.setActionCommand(NOVA_AC);
+    novaB.addActionListener(listener);
+    toolBar.add(novaB);
+
+    JButton ulozitB = new JButton();
+    ulozitB.setIcon(IconFactory.getIcon("demos/nurbs/icons/adept_sourceseditor.png"));
+    // ulozitB.setText("Uložit");
+    ulozitB.setToolTipText("Save");
+    ulozitB.setActionCommand(ULOZIT_AC);
+    ulozitB.addActionListener(listener);
+    toolBar.add(ulozitB);
+
+    JButton nahratB = new JButton();
+    // nahratB.setText("Nahrát");
+    nahratB.setToolTipText("Load surface");
+    nahratB.setIcon(IconFactory.getIcon("demos/nurbs/icons/fileimport.png"));
+    nahratB.setActionCommand(NACIST_AC);
+    nahratB.addActionListener(listener);
+    toolBar.add(nahratB);
+
+    toolBar.add(new JToolBar.Separator());
+
+    JToggleButton pridatTB = new JToggleButton();
+    // pridatTB.setText("Přidat body");
+    pridatTB.setToolTipText("Add contol points");
+    toolBar.add(pridatTB);
+    pridatTB.setIcon(IconFactory.getIcon("demos/nurbs/icons/add.png"));
+    //		pridatTB.setActionCommand(PRIDAT_AC);
+    //		pridatTB.addActionListener(listener);
+		
+    bg.add(pridatTB);
+		
+    final JPopupMenu popup2=new JPopupMenu();
+    JMenuItem radkyPopupMI=new JMenuItem("Poits row");
+    radkyPopupMI.setActionCommand(PRIDAT_AC_RADEK);
+    JMenuItem sloupcePopupMI=new JMenuItem("Points column");
+    sloupcePopupMI.setActionCommand(PRIDAT_AC_SLOUPEC);
+    radkyPopupMI.addActionListener(listener);
+    sloupcePopupMI.addActionListener(listener);
+		
+    popup2.add(radkyPopupMI);
+    popup2.add(sloupcePopupMI);
+		
+    pridatTB.addMouseListener(new 
+                              /**
+                               * CLass to add context menu to toolbar button
+                               * Třída pro připojení kontextového menu na
+                               *         tlačítko na liště nástrojů
+                               * @author Tomáš Hráský 
+                               */
+                              MouseAdapter() {
+
+        @Override
+          public void mouseClicked(MouseEvent e) {
+          super.mouseClicked(e);
+          e.isPopupTrigger();
+          popup2.show(e.getComponent(), e.getX(), e.getY());
+        }
+			
+			
+      });
+		
+    JToggleButton smazatTB = new JToggleButton();
+    // smazatTB.setText("Smazat body");
+    smazatTB.setToolTipText("Delete points");
+    toolBar.add(smazatTB);
+    smazatTB.setIcon(IconFactory.getIcon("demos/nurbs/icons/fileclose.png"));
+    //		smazatTB.setActionCommand(SMAZAT_AC);
+    //		smazatTB.addActionListener(listener);
+    bg.add(smazatTB);
+		
+    final JPopupMenu popup3=new JPopupMenu();
+    JMenuItem radky2PopupMI=new JMenuItem("Points row");
+    radky2PopupMI.setActionCommand(SMAZAT_AC_RADEK);
+    JMenuItem sloupce2PopupMI=new JMenuItem("Points column");
+    sloupce2PopupMI.setActionCommand(SMAZAT_AC_SLOUPEC);
+    radky2PopupMI.addActionListener(listener);
+    sloupce2PopupMI.addActionListener(listener);
+		
+    popup3.add(radky2PopupMI);
+    popup3.add(sloupce2PopupMI);
+
+		
+    smazatTB.addMouseListener(new 
+                              /**
+                               * CLass to add context menu to toolbar button
+                               * Třída pro připojení kontextového menu na
+                               *         tlačítko na liště nástrojů
+                               * @author Tomáš Hráský 
+                               */
+                              MouseAdapter() {
+
+        @Override
+          public void mouseClicked(MouseEvent e) {
+          super.mouseClicked(e);
+          e.isPopupTrigger();
+          popup3.show(e.getComponent(), e.getX(), e.getY());
+        }
+			
+			
+      });
+		
+    JToggleButton stupenTB = new JToggleButton();
+    // stupenTB.setText("Smazat body");
+    stupenTB.setToolTipText("Set surface degree");
+    toolBar.add(stupenTB);
+    stupenTB.setIcon(IconFactory.getIcon("demos/nurbs/icons/math_rsup.png"));
+    stupenTB.setActionCommand(STUPEN_AC);
+    stupenTB.addActionListener(listener);
+    bg.add(stupenTB);
+
+    final JPopupMenu popup = new JPopupMenu();
+
+    JMenuItem uzavrenyPopupMI = new JMenuItem("Clamped");
+    popup.add(uzavrenyPopupMI);
+    uzavrenyPopupMI.setActionCommand(UZAVRENY_AC);
+    uzavrenyPopupMI.addActionListener(listener);
+    JMenuItem otevrenyPopupMI = new JMenuItem("Uniform");
+    popup.add(otevrenyPopupMI);
+    otevrenyPopupMI.setActionCommand(OTEVRENY_AC);
+    otevrenyPopupMI.addActionListener(listener);
+
+    JToggleButton vytvoritButton = new JToggleButton();
+    // vytvoritButton.setText("Vytvořit uzlový vektor");
+    vytvoritButton.setToolTipText("Create knotvectors");
+    vytvoritButton.setIcon(IconFactory.getIcon("demos/nurbs/icons/newfunction.png"));
+    bg.add(vytvoritButton);
+
+    vytvoritButton.addMouseListener(new
+                                    /**
+                                     * CLass to add context menu to toolbar button
+                                     * Třída pro připojení kontextového menu na
+                                     *         tlačítko na liště nástrojů
+                                     * @author Tomáš Hráský 
+                                     */
+                                    MouseAdapter() {
+
+        @Override
+          public void mouseClicked(MouseEvent e) {
+          super.mouseClicked(e);
+          e.isPopupTrigger();
+          popup.show(e.getComponent(), e.getX(), e.getY());
+        }
+
+      });
+    popup.setInvoker(vytvoritButton);
+    toolBar.add(vytvoritButton);
+
+    moveTB = new JToggleButton();
+    // moveTB.setText("Hýbat body");
+    moveTB.setToolTipText("Move points");
+    moveTB.setIcon(IconFactory.getIcon("demos/nurbs/icons/mouse.png"));
+    toolBar.add(moveTB);
+    moveTB.setActionCommand(MOVE_AC);
+    moveTB.addActionListener(listener);
+    bg.add(moveTB);
+    toolBar.add(new JToolBar.Separator());
+    JButton infoB = new JButton();
+    // infoB.setText("Ukončit");
+    infoB.setToolTipText("About");
+
+    infoB.setIcon(IconFactory.getIcon("demos/nurbs/icons/info.png"));
+    toolBar.add(infoB);
+    infoB.setActionCommand(INFO_AC);
+    infoB.addActionListener(listener);
+    toolBar.add(new JToolBar.Separator());
+
+    JButton exitB = new JButton();
+    // exitB.setText("Ukončit");
+    exitB.setToolTipText("Exit");
+
+    exitB.setIcon(IconFactory.getIcon("demos/nurbs/icons/exit.png"));
+    toolBar.add(exitB);
+    exitB.setActionCommand(EXIT_AC);
+    exitB.addActionListener(listener);
+
+    c.gridwidth = 1;
+
+    c.gridx = 0;
+    c.gridy = 2;
+
+    c.weightx = 1;
+    c.weighty = 1;
+
+    getContentPane().add(glCanvas, c);
+    c.gridx = 1;
+    JPanel rightPanel = new JPanel(new GridBagLayout());
+    GridBagConstraints cc = new GridBagConstraints();
+    cc.insets = new Insets(5, 5, 5, 5);
+    xSpinner = new JSpinner(new SpinnerNumberModel(0, -10000, 10000.0, 1));
+    ySpinner = new JSpinner(new SpinnerNumberModel(0, -10000.0, 10000.0, 1));
+    zSpinner = new JSpinner(new SpinnerNumberModel(0, -10000.0, 10000.0, 1));
+    wSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 10000.0, .05));
+
+    SpinnerListener spinnerListener = new SpinnerListener(this);
+    SliderListener sliderListener=new SliderListener(this);
+		
+    xSpinner.addChangeListener(spinnerListener);
+    xSpinner.setName(X_SPINNER_NAME);
+    ySpinner.addChangeListener(spinnerListener);
+    ySpinner.setName(Y_SPINNER_NAME);
+    wSpinner.addChangeListener(spinnerListener);
+    wSpinner.setName(W_SPINNER_NAME);
+		
+    zSpinner.setName(Z_SPINNER_NAME);
+    zSpinner.addChangeListener(spinnerListener);
+
+    cc.gridx = 0;
+    cc.gridy = 0;
+    cc.gridwidth = 2;
+    cc.weighty = 0;
+
+    rotaceXLabel = new JLabel();
+    rightPanel.add(rotaceXLabel, cc);
+    cc.gridy++;
+
+    rotaceXSlider = new JSlider(-180, 180, 0);
+    rotaceXSlider.addChangeListener(sliderListener);
+    rightPanel.add(rotaceXSlider, cc);
+    cc.gridy++;
+
+    rotaceYLabel = new JLabel();
+    rightPanel.add(rotaceYLabel, cc);
+    cc.gridy++;
+
+    rotaceYSlider = new JSlider(-180, 180, 0);
+    rotaceYSlider.addChangeListener(sliderListener);
+    rightPanel.add(rotaceYSlider, cc);
+    cc.gridy++;
+
+    rotaceZLabel = new JLabel();
+    rightPanel.add(rotaceZLabel, cc);
+    cc.gridy++;
+
+    rotaceZSlider = new JSlider(-180, 180, 0);
+    rotaceZSlider.addChangeListener(sliderListener);
+    rightPanel.add(rotaceZSlider, cc);
+    cc.gridy++;
+		
+    lightingChBox=new JCheckBox(new 
+                                /**
+                                 * Class for easy reaction to checkbox event
+                                 * Třída pro jendoduché zpracování akce na checkboxu 
+                                 * @author Tomáš Hráský
+                                 */	
+                                AbstractAction("Show Bézier plates"){
+
+        public void actionPerformed(ActionEvent e) {
+          updateGLCanvas();				
+        }
+			
+      });
+    lightingChBox.setSelected(false);
+		
+    rightPanel.add(lightingChBox,cc);
+		
+    cc.gridy++;
+
+    updateRotationLabels();
+		
+    cc.weighty = 1;
+    rightPanel.add(new JPanel(), cc);
+    cc.weighty = 0;
+    cc.gridwidth = 1;
+
+    cc.gridy++;
+    rightPanel.add(new JLabel("X"), cc);
+    cc.gridy++;
+    rightPanel.add(new JLabel("Y"), cc);
+    cc.gridy++;
+    rightPanel.add(new JLabel("Z"), cc);
+    cc.gridy++;
+    rightPanel.add(new JLabel("W"), cc);
+
+    cc.gridx = 1;
+    cc.gridy -= 3;
+    rightPanel.add(xSpinner, cc);
+    cc.gridy++;
+    rightPanel.add(ySpinner, cc);
+    cc.gridy++;
+    rightPanel.add(zSpinner, cc);
+    cc.gridy++;
+    rightPanel.add(wSpinner, cc);
+
+    xSpinner.setEnabled(false);
+    ySpinner.setEnabled(false);
+    zSpinner.setEnabled(false);
+    wSpinner.setEnabled(false);
+
+    c.weightx = 0;
+    c.weighty = 0;
+    getContentPane().add(rightPanel, c);
+
+    c.gridx = 0;
+    c.gridy++;
+
+    knotSlider = new JKnotSlider(Surface.getInstance().getKnotsU());
+    knotSlider.addActionListener(this);
+    knotSlider.setName(U_KNOTSLIDER);
+    getContentPane().add(knotSlider, c);
+
+    c.gridy++;
+    knotSlider2 = new JKnotSlider(Surface.getInstance().getKnotsU());
+    knotSlider2.addActionListener(this);
+    knotSlider2.setName(V_KNOTSLIDER);
+    getContentPane().add(knotSlider2, c);
+
+    pack();
+    invalidate();
+    setVisible(true);
+  }
+
+  /**
+   * Method for running application
+   * Metoda pro spuštění aplikace
+   * 
+   * @param args
+   * 	no arguments from command line
+   * 
+   */
+  public static void main(String[] args) {
+    new SurfaceApp();
+
+  }
+
+  /**
+   * Reaction to reqest for canvas redraw - redraws canvas and sets coords of actually selected control point to editing components
+   * Reakce na požadavek překreslení OpenGL plátna, překreslí plátno a nastaví
+   * souřadnice aktuálního vybraného bodu do editačních komponent
+   */
+  public void updateGLCanvas() {
+    glCanvas.repaint();
+    if (Surface.getInstance().getBodIndex() >= 0) {
+      xSpinner.setEnabled(true);
+      ySpinner.setEnabled(true);
+      zSpinner.setEnabled(true);
+      wSpinner.setEnabled(true);
+
+      xSpinner.setValue(Double.valueOf(Math.round(Surface.getInstance()
+                                                  .getActiveX())));
+      ySpinner.setValue(Double.valueOf(Math.round(Surface.getInstance()
+                                                  .getActiveY())));
+      zSpinner.setValue(Double.valueOf(Math.round(Surface.getInstance()
+                                                  .getActiveZ())));
+      wSpinner.setValue(Double
+                        .valueOf(Surface.getInstance().getActiveW()));
+    } else {
+      xSpinner.setEnabled(false);
+      ySpinner.setEnabled(false);
+      zSpinner.setEnabled(false);
+      wSpinner.setEnabled(false);
+    }
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+   */
+  public void actionPerformed(ActionEvent e) {
+    if (e.getSource() instanceof JKnotSlider) {
+      JKnotSlider src = (JKnotSlider) e.getSource();
+      if (src.getName().equals(U_KNOTSLIDER)) {
+        if(src.checkKnotMulti(Surface.getInstance().getOrderU())){
+          Surface.getInstance().setKnotsU(src.getKnotsFloat());
+        }else{
+          JOptionPane.showMessageDialog(this,"Maximum knot multiplicity exceeded","Error",JOptionPane.ERROR_MESSAGE);
+          src.setKnots(Surface.getInstance().getKnotsU());
+        }
+
+      } else {
+        if(src.checkKnotMulti(Surface.getInstance().getOrderV())){
+          Surface.getInstance().setKnotsV(src.getKnotsFloat());
+        }else{
+          JOptionPane.showMessageDialog(this,"Maximum knot multiplicity exceeded","Error",JOptionPane.ERROR_MESSAGE);
+          //JOptionPane.showMessageDialog(this,"Překročení maximální násobnosti uzlu","Chyba",JOptionPane.ERROR_MESSAGE);
+          src.setKnots(Surface.getInstance().getKnotsV());
+        }
+      }
+      updateGLCanvas();
+    }
+  }
+
+  /**
+   * Returns OpenGL canvas
+   * Vrací OpenGL plátno
+   * 
+   * @return OpenGL canvas
+   */
+  public GLCanvas getGlCanvas() {
+    return glCanvas;
+  }
+
+  /**
+   * Returns mouse listener
+   * Vrací listener událostí myši
+   * 
+   * @return mouse listener
+   */
+  public SurfaceMouseListener getMouseListener() {
+    return mouseListener;
+  }
+
+  /**
+   * Creates NURBS surface with clamped knotvectors
+   * Vytvoří NURBS plochu s okrajovými uzlovými vektory
+   */
+  public void uzavernyKV() {
+    int stupen;
+    int pocetBodu;
+    boolean isOK=true;
+    float[] newKnots = null,newKnotsV = null;
+		
+    stupen= Surface.getInstance().getOrderU();
+    pocetBodu= Surface.getInstance().getPointsInU();
+    if (stupen <= pocetBodu) {
+      int knotCount = stupen + pocetBodu;
+      int middlePartSize = knotCount - 2 * stupen;
+      newKnots = new float[knotCount];
+      int i;
+      int j = 0;
+      float middleStep = 1f / (middlePartSize + 2);
+      float knot = middleStep;
+
+      // knot=.5f;
+
+      for (i = 0; i < stupen; i++)
+        newKnots[j++] = 0;
+      for (i = 0; i < middlePartSize; i++) {
+        newKnots[j++] = knot;
+        knot += middleStep;
+      }
+      for (i = 0; i < stupen; i++)
+        newKnots[j++] = 1;
+
+			
+
+    } else{
+      isOK=false;
+      //errorMessage("Malý počet řídících bodů ve směru paramteru U vzhledem k zadanému stupni plochy");
+      errorMessage("Too few control points as of U degree");
+    }
+		
+    stupen= Surface.getInstance().getOrderV();
+    pocetBodu= Surface.getInstance().getPointsInV();
+    if (stupen <= pocetBodu) {
+      int knotCount = stupen + pocetBodu;
+      int middlePartSize = knotCount - 2 * stupen;
+      newKnotsV = new float[knotCount];
+      int i;
+      int j = 0;
+      float middleStep = 1f / (middlePartSize + 2);
+      float knot = middleStep;
+
+      // knot=.5f;
+
+      for (i = 0; i < stupen; i++)
+        newKnotsV[j++] = 0;
+      for (i = 0; i < middlePartSize; i++) {
+        newKnotsV[j++] = knot;
+        knot += middleStep;
+      }
+      for (i = 0; i < stupen; i++)
+        newKnotsV[j++] = 1;
+
+			
+
+    } else{
+      isOK=false;
+      //errorMessage("Malý počet řídících bodů ve směru paramteru V vzhledem k zadanému stupni plochy");
+      errorMessage("Too few control points as of V degree");
+    }
+		
+    if(isOK)
+      postNewKnot(newKnots,newKnotsV);
+  }
+
+  /**
+   * Shows modal window with error report
+   * Zobrazí modální okno s hlášením chyby
+   * 
+   * @param error
+   * 	error report
+   */
+
+  public void errorMessage(String error) {
+    JOptionPane.showMessageDialog(this, error, "Error!",
+                                  JOptionPane.ERROR_MESSAGE);
+  }
+
+  /**
+   * Creates NURBS surface with uniform knotvectors
+   * Vytvoří NURBS plochu s uniformními uzlovými vektory
+   */
+  public void otevrenyKV() {
+    int stupen,pocetBodu;
+		
+    boolean isOK=true;
+    float[] newKnots = null,newKnotsV = null;
+
+		
+    stupen = Surface.getInstance().getOrderU();
+    pocetBodu = Surface.getInstance().getPointsInU();
+    if (stupen <= pocetBodu) {
+      int knotCount = stupen + pocetBodu;
+      int middlePartSize = knotCount;
+      newKnots = new float[knotCount];
+      int i;
+      int j = 0;
+      float middleStep = 1f / (middlePartSize - 1);
+      float knot = 0;
+
+      for (i = 0; i < middlePartSize; i++) {
+        newKnots[j++] = knot;
+        knot += middleStep;
+      }
+
+			
+    } else{
+      isOK=false;
+      //errorMessage("Malý počet řídících bodů ve směru parametru U vzhledem k zadanému stupni plochy");
+      errorMessage("Too few control points as of U degree");
+    }
+		
+    stupen = Surface.getInstance().getOrderV();
+    pocetBodu = Surface.getInstance().getPointsInV();
+    if (stupen <= pocetBodu) {
+      int knotCount = stupen + pocetBodu;
+      int middlePartSize = knotCount;
+      newKnotsV = new float[knotCount];
+      int i;
+      int j = 0;
+      float middleStep = 1f / (middlePartSize - 1);
+      float knot = 0;
+
+      for (i = 0; i < middlePartSize; i++) {
+        newKnotsV[j++] = knot;
+        knot += middleStep;
+      }
+
+					
+    } else{
+      isOK=false;
+      //errorMessage("Malý počet řídících bodů ve směru parametru V vzhledem k zadanému stupni plochy");
+      errorMessage("Too few control points as of V degree");
+    }
+		
+    if(isOK)
+      postNewKnot(newKnots,newKnotsV);	
+
+  }
+	
+
+  /**
+   * Method called after adding new knot
+   * Metoda volaná po přidání nového uzlu
+   * @param newKnots new U knotvector
+   * @param newKnotsV new V knotvector
+   */
+  private void postNewKnot(float[] newKnots,float[] newKnotsV) {
+    Surface.getInstance().setKnotsU(newKnots);
+    Surface.getInstance().setKnotsV(newKnotsV);
+    knotSlider.setKnots(newKnots);
+    knotSlider2.setKnots(newKnotsV);
+    Surface.getInstance().setIsSurfaceFinished(true);
+    updateGLCanvas();
+    moveTB.setSelected(true);
+  }
+
+  /**
+   * Activates move mode button
+   * Aktivuje tlačítko módu pohybu řícími body
+   */
+  public void selectMoveButt() {
+    moveTB.setSelected(true);
+  }
+
+  /**
+   * Sets datasource for editation of knotvectors from surface definition object
+   * Nastaví zdroje dat komponent pro editaci uzlových vektorů podle uz.
+   * vektorů v objektu plochy
+   */
+  public void updateJKnotSlider() {
+    knotSlider.setKnots(Surface.getInstance().getKnotsU());
+    knotSlider2.setKnots(Surface.getInstance().getKnotsV());
+  }
+
+  /**
+   * Returns value of X axe rotation set in editing component
+   * Vrací hodnotu rotace kolem osy X nastavenou v editační komponentě 
+   * @return X rotation
+   */
+  public float getXrotation() {
+    return rotaceXSlider.getValue();
+  }
+	
+  /**
+   * Returns value of Y axe rotation set in editing component
+   * Vrací hodnotu rotace kolem osy Y nastavenou v editační komponentě 
+   * @return Y rotation
+   */
+  public float getYrotation() {
+    return rotaceYSlider.getValue();
+  }
+	
+  /**
+   * Returns value of Z axe rotation set in editing component
+   * Vrací hodnotu rotace kolem osy Z nastavenou v editační komponentě 
+   * @return Z rotation
+   */
+  public float getZrotation() {
+    return rotaceZSlider.getValue();
+  }
+	
+  /**
+   * Updates labels's text according to their actual state
+   * Upraví text popisků prvků pro ovládání rotace podle jejich aktuálního stavu
+   */
+  public void updateRotationLabels(){
+    String zakladniText = "Rotation by axe ";
+		
+    PrintfFormat format=new PrintfFormat("%0.3d");
+    String add;
+    if(rotaceXSlider.getValue()<0)add="-";
+    else add="+";
+    rotaceXLabel.setText(zakladniText+"X "+add+format.sprintf(Math.abs(rotaceXSlider.getValue()))+"˚");
+    if(rotaceYSlider.getValue()<0)add="-";
+    else add="+";
+    rotaceYLabel.setText(zakladniText+"Y "+add+format.sprintf(Math.abs(rotaceYSlider.getValue()))+"˚");
+    if(rotaceZSlider.getValue()<0)add="-";
+    else add="+";	
+    rotaceZLabel.setText(zakladniText+"Z "+add+format.sprintf(Math.abs(rotaceZSlider.getValue()))+"˚");
+  }
+
+  /**
+   * Return OpenGL canvas listener
+   * Vrací listener OpenGL plátna
+   * @return OpenGL canvas listener
+   */
+  public GLListener getGlListener() {
+    return glListener;
+  }
+
+  /**
+   * Notifies about reqest to light surface
+   * Informuje o požadavku na nasvětlení tělesa
+   * @return true if lighting is enabled
+   */
+  public boolean isLightingEnabled() {
+    return !lightingChBox.isSelected();
+  }
+}
diff --git a/src/demos/nurbs/surfaceapp/SurfaceMouseListener.java b/src/demos/nurbs/surfaceapp/SurfaceMouseListener.java
new file mode 100755
index 0000000..834ae46
--- /dev/null
+++ b/src/demos/nurbs/surfaceapp/SurfaceMouseListener.java
@@ -0,0 +1,277 @@
+package demos.nurbs.surfaceapp;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.util.Vector;
+
+import javax.media.opengl.GL;
+import javax.media.opengl.glu.GLU;
+
+/**
+ * Třída zpracovávající události myši (implementuje rozhraní zpracovávající stisk tlačítek i pohyb a tažení myší)
+ * @author Tomáš Hráský
+ *
+ */
+public class SurfaceMouseListener implements MouseListener, MouseMotionListener {
+  /**
+   * Index aktuálně vybraného řídícího bodu
+   */
+  private int bodIndex;
+
+  /**
+   * Okno k nemuž liustener patří
+   */
+  private SurfaceApp appWindow;
+
+  /**
+   * Typ prováděné činnosti
+   */
+  private String actionType;
+
+  /**
+   * Tolerance pro indikaci kliku na řídící bod
+   */
+  private static final int TOLERANCE=10;
+
+  /**
+   * Vytvoří listener s odkazem na zadané okno
+   * @param app rodičovské okno
+   */
+  public SurfaceMouseListener(SurfaceApp app) {
+    this.appWindow=app;
+  }
+
+  /* (non-Javadoc)
+   * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
+   */
+  public void mouseClicked(MouseEvent e) {
+    if(actionType==SurfaceApp.PRIDAT_AC){
+      //			Surface.getInstance().setIsSurfaceFinished(false);
+      //			float x=e.getX();
+      //			float y=e.getY();
+      //			float z=0;
+      //			float w=1;
+      //			int size;
+      //			float[] newCtrls;
+      //			try{
+      //				size=Surface.getInstance().getCtrlPoints().length;
+      //			}catch (Exception ex) {
+      //				size=0;
+      //			}
+      //			newCtrls=new float[size+4];
+      //			System.arraycopy(Surface.getInstance().getCtrlPoints(),0,newCtrls,0,size);
+      //			
+      //			newCtrls[size]=x;
+      //			newCtrls[size+1]=y;
+      //			newCtrls[size+2]=z;
+      //			newCtrls[size+3]=w;
+      //			Surface.getInstance().setCtrlPoints(newCtrls);
+    }else if(actionType==SurfaceApp.SMAZAT_AC&&bodIndex>=0){
+      //			Surface.getInstance().setIsSurfaceFinished(false);
+      //			int size=Surface.getInstance().getCtrlPoints().length;
+      //			float[] newCtrls=new float[size-4];
+      //			
+      //			int firstPartSize=(bodIndex)*4;
+      //			int secondPartSize=newCtrls.length-firstPartSize;
+      //			System.arraycopy(Surface.getInstance().getCtrlPoints(),0,newCtrls,0,firstPartSize);
+      //			System.arraycopy(Surface.getInstance().getCtrlPoints(),firstPartSize+4,newCtrls,firstPartSize,secondPartSize);
+      //			bodIndex=-1;
+      //			Surface.getInstance().setBodIndex(bodIndex);
+      //			Surface.getInstance().setCtrlPoints(newCtrls);
+    }else if(actionType==SurfaceApp.SMAZAT_AC_RADEK&&bodIndex>=0){
+			
+      Vector<float[]> oldPoints=new Vector<float[]>();
+      Surface srf=Surface.getInstance();
+      for(int i=0;i<srf.getCtrlPoints().length;i+=4){
+        float[] pole={srf.getCtrlPoints()[i],srf.getCtrlPoints()[i+1],srf.getCtrlPoints()[i+2],srf.getCtrlPoints()[i+3]};
+        oldPoints.add(pole);
+      }
+			
+      int index=bodIndex+1;
+      while(!(index%srf.getPointsInV()==0))
+        index--;
+      //			index--;
+      Vector<Integer> indexes=new Vector<Integer>();
+      for(int i=index;i<index+srf.getPointsInV();i++)
+        indexes.add(i);
+      Vector<float[]> newOldPoints=new Vector<float[]>();
+      for(int i=0;i<oldPoints.size();i++){
+        if(!indexes.contains(Integer.valueOf(i)))
+          newOldPoints.add(oldPoints.get(i));
+      }
+      //				oldPoints.remove(i);
+      float[] newPoints=new float[newOldPoints.size()*4];
+      int i=0;
+      for(float[] f:newOldPoints){
+        newPoints[i++]=f[0];
+        newPoints[i++]=f[1];
+        newPoints[i++]=f[2];
+        newPoints[i++]=f[3];
+      }
+      srf.setIsSurfaceFinished(false);
+      srf.setPointsInU(srf.getPointsInU()-1);
+      bodIndex=-1;
+      srf.setBodIndex(-1);
+      srf.setCtrlPoints(newPoints);
+			
+    }else if(actionType==SurfaceApp.SMAZAT_AC_SLOUPEC&&bodIndex>=0){
+      Vector<float[]> oldPoints=new Vector<float[]>();
+      Surface srf=Surface.getInstance();
+      for(int i=0;i<srf.getCtrlPoints().length;i+=4){
+        float[] pole={srf.getCtrlPoints()[i],srf.getCtrlPoints()[i+1],srf.getCtrlPoints()[i+2],srf.getCtrlPoints()[i+3]};
+        oldPoints.add(pole);
+      }
+			
+      int index=bodIndex+1;
+			
+      Vector<Integer> indexes=new Vector<Integer>();
+			
+      for(int i=index;i>=0;i-=srf.getPointsInV()){
+        indexes.add(i-1);
+      }
+      for(int i=index;i<srf.getCtrlPoints().length;i+=srf.getPointsInV()){
+        indexes.add(i-1);
+      }
+			
+      //			index--;
+      //			for(int i=index;i<index+srf.getPointsInV();i++)
+      //				
+			
+      Vector<float[]> newOldPoints=new Vector<float[]>();
+      for(int i=0;i<oldPoints.size();i++){
+        if(!indexes.contains(Integer.valueOf(i)))
+          newOldPoints.add(oldPoints.get(i));
+      }
+      //				oldPoints.remove(i);
+      float[] newPoints=new float[newOldPoints.size()*4];
+      int i=0;
+      for(float[] f:newOldPoints){
+        newPoints[i++]=f[0];
+        newPoints[i++]=f[1];
+        newPoints[i++]=f[2];
+        newPoints[i++]=f[3];
+      }
+      srf.setIsSurfaceFinished(false);
+      srf.setPointsInV(srf.getPointsInV()-1);
+      bodIndex=-1;
+      srf.setBodIndex(-1);
+      srf.setCtrlPoints(newPoints);
+
+    }
+		
+    appWindow.updateGLCanvas();
+  }
+
+  /* (non-Javadoc)
+   * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
+   */
+  public void mousePressed(MouseEvent e) {
+    float[] ctrlpoints=Surface.getInstance().getCtrlPoints();
+    int xE=e.getX();
+    int yE=e.getY();
+    //		System.out.println(xE+" "+yE);
+    double x,y,z=0;
+		
+    this.bodIndex=-1;
+		
+    GL gl=appWindow.getGlCanvas().getGL();
+    GLU glu=appWindow.getGlListener().getGlu();
+		
+    int[] viewport;
+    double[] mvmatrix;
+    double[] projmatrix;
+    int realy;
+    double[] wcoord=new double[4];
+
+    viewport=appWindow.getGlListener().getViewport();
+    mvmatrix=appWindow.getGlListener().getMvmatrix();
+    projmatrix=appWindow.getGlListener().getProjmatrix();
+
+		
+    for(int i=0;i<ctrlpoints.length/4;i++){
+      x = ctrlpoints[i*4]/ctrlpoints[i*4+3];
+      y = ctrlpoints[i*4+1]/ctrlpoints[i*4+3];
+      z=ctrlpoints[i*4+2]/ctrlpoints[i*4+3];			
+      //projekce souřadnic do okna
+      glu.gluProject(x,y,z,mvmatrix,0,projmatrix,0,viewport,0,wcoord,0);
+			
+      x=wcoord[0];
+      y=(viewport[3]-wcoord[1]-1);
+			
+      if(xE>=x-TOLERANCE&&xE<=x+TOLERANCE&&yE>=y-TOLERANCE&&yE<=y+TOLERANCE){
+        this.bodIndex=i;
+      }                    
+    }
+		
+    Surface.getInstance().setBodIndex(bodIndex);
+    appWindow.updateGLCanvas();
+		
+  }
+
+  /* (non-Javadoc)
+   * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
+   */
+  public void mouseReleased(MouseEvent e) {
+    //		this.bodIndex=-1;
+  }
+
+  /* (non-Javadoc)
+   * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
+   */
+  public void mouseEntered(MouseEvent e) {
+
+  }
+
+  /* (non-Javadoc)
+   * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
+   */
+  public void mouseExited(MouseEvent e) {
+
+  }
+
+  /* (non-Javadoc)
+   * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
+   */
+  public void mouseDragged(MouseEvent e) {
+    //		if(this.bodIndex>=0){
+    //			int x=e.getX();
+    //			int y=e.getY();
+    //			
+    //			Surface.getInstance().setActiveX(x);
+    //			Surface.getInstance().setActiveY(y);
+    //		}
+    //		appWindow.updateGLCanvas();
+  }
+
+  /* (non-Javadoc)
+   * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
+   */
+  public void mouseMoved(MouseEvent e) {
+		
+  }
+
+  /**
+   * Nastaví typ prováděné činnosti
+   * @param action typ prováděné činnosti
+   */
+  public void setActionType(String action) {
+    this.actionType=action;
+  }
+
+  /**
+   * Vrací index aktuálně vybraného řídícího bodu
+   * @return index aktuálně vybraného řídícího bodu
+   */
+  public int getBodIndex() {
+    return bodIndex;
+  }
+
+  /**
+   * Vrací index aktuálně vybraného řídícího bodu
+   * @param bodIndex aktuálně vybraného řídícího bodu
+   */
+  public void setBodIndex(int bodIndex) {
+    this.bodIndex = bodIndex;
+  }
+}
-- 
cgit v1.2.3