diff options
author | Kenneth Russel <[email protected]> | 2007-10-09 07:38:25 +0000 |
---|---|---|
committer | Kenneth Russel <[email protected]> | 2007-10-09 07:38:25 +0000 |
commit | c8e0d487886dbec1e3a994ea36724bbf59f5122a (patch) | |
tree | 0b9588684708a2429b8252ca367777d0b9125bf6 | |
parent | 2bdf0b792c0b89cde549087af1676ee558e37aa6 (diff) |
Integration of Tomas Hrasky's port of basic GLU NURBS functionality
from C++ to Java, plus example applications, done as part of his
Bachelor of Science degree at the University of Hradec Králové,
Faculty of Informatics and Management.
Current state of code is documented in
src/classes/com/sun/opengl/impl/nurbs/README.txt.
Example applications require Java 1.5 and are not currently built by
default. Specify -Djogl.nurbs=1 during jogl-demos build with a 1.5
javac on the PATH to build them. Dependent jars are copied to build
output directory.
Deleted old partially-complete GLU NURBS port.
git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/../svn-server-sync/jogl-demos/trunk@223 3298f667-5e0e-4b4a-8ed4-a3559d26a5f4
34 files changed, 7891 insertions, 1 deletions
diff --git a/lib/simple-xml-1.1.1.jar b/lib/simple-xml-1.1.1.jar Binary files differnew file mode 100644 index 0000000..20c8bb1 --- /dev/null +++ b/lib/simple-xml-1.1.1.jar diff --git a/lib/stax-1.2.0.jar b/lib/stax-1.2.0.jar Binary files differnew file mode 100644 index 0000000..0df112a --- /dev/null +++ b/lib/stax-1.2.0.jar diff --git a/lib/stax-api-1.0.1.jar b/lib/stax-api-1.0.1.jar Binary files differnew file mode 100644 index 0000000..483dc30 --- /dev/null +++ b/lib/stax-api-1.0.1.jar diff --git a/make/build.xml b/make/build.xml index e73061a..1394819 100644 --- a/make/build.xml +++ b/make/build.xml @@ -43,6 +43,7 @@ <property name="jogl.jar" value="../../jogl/build/jogl.jar" /> <property name="joal.jar" value="../lib/joal.jar" /> <property name="joal-demos.jar" value="../lib/joal-demos.jar" /> + <property name="simple-xml.jar" value="../lib/simple-xml-1.1.1.jar" /> <property name="classes" value="../build/classes" /> <property name="src" value="../src" /> <property name="javadoc" value="../javadoc" /> @@ -54,17 +55,37 @@ <property name="jogl.demos.src.zip" value="../build/jogl-demos-src.zip" /> </target> + <target name="nurbs" if="jogl.nurbs"> + <!-- NOTE: this target requires a 1.5 javac to be on the PATH --> + <javac destdir="${classes}" includes="demos/nurbs/**" source="1.5" debug="true" debuglevel="source,lines"> + <src path="${src}" /> + <classpath refid="jogl-demos.classpath" /> + </javac> + <!-- Copy icons to output dir --> + <copy todir="../build"> + <fileset dir="${src}"> + <include name="demos/nurbs/icons/*.png" /> + </fileset> + </copy> + <!-- Copy dependent libraries to output dir --> + <copy file="../lib/simple-xml-1.1.1.jar" todir="../build" /> + <copy file="../lib/stax-api-1.0.1.jar" todir="../build" /> + <copy file="../lib/stax-1.2.0.jar" todir="../build" /> + </target> + <target name="all" depends="init"> <mkdir dir="${classes}" /> <path id="jogl-demos.classpath"> <pathelement location="${jogl.jar}" /> <pathelement location="${joal.jar}" /> <pathelement location="${joal-demos.jar}" /> + <pathelement location="${simple-xml.jar}" /> </path> - <javac destdir="${classes}" excludes="${jogl.cg.excludes}" source="1.4" debug="true" debuglevel="source,lines"> + <javac destdir="${classes}" excludes="${jogl.cg.excludes},demos/nurbs/**" source="1.4" target="1.4" debug="true" debuglevel="source,lines"> <src path="${src}" /> <classpath refid="jogl-demos.classpath" /> </javac> + <antcall target="nurbs" inheritrefs="true" /> <jar destfile="${jogl.demos.jar}"> <fileset dir="${classes}"> <exclude name="gleem/**" /> diff --git a/src/demos/nurbs/curveapp/ActListener.java b/src/demos/nurbs/curveapp/ActListener.java new file mode 100755 index 0000000..52e89b8 --- /dev/null +++ b/src/demos/nurbs/curveapp/ActListener.java @@ -0,0 +1,114 @@ +package demos.nurbs.curveapp; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.ImageIcon; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; + +import demos.nurbs.icons.*; + +/** + * Class reacting to events from 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 CurveApp app; + /** + * File chooser object + * Objekt pro výběr souboru + */ + private JFileChooser fc; + + /** + * Creates instance of object with pointer to parent window + * Vytvoří instanci objektu s odkazem na rodičovské okno + * @param app parent window + */ + public ActListener(CurveApp 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()==CurveApp.PRIDAT_AC){ + + }else if(e.getActionCommand()==CurveApp.SMAZAT_AC){ + + }else if(e.getActionCommand()==CurveApp.UZAVRENY_AC){ + app.uzavernyKV(); + }else if(e.getActionCommand()==CurveApp.OTEVRENY_AC){ + app.otevrenyKV(); + }else if(e.getActionCommand()==CurveApp.ULOZIT_AC){ + if(fc.showSaveDialog(app)==JFileChooser.APPROVE_OPTION){ + Curve.getInstance().persist(fc.getSelectedFile()); + } + }else if(e.getActionCommand()==CurveApp.NACIST_AC){ + if(fc.showOpenDialog(app)==JFileChooser.APPROVE_OPTION){ + try { + Curve.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 loading file","Error",JOptionPane.ERROR_MESSAGE); + } + } + }else if(e.getActionCommand()==CurveApp.NOVA_AC){ + Curve.getInstance().clear(); + app.getMouseListener().setBodIndex(-1); + Curve.getInstance().setBodIndex(-1); + app.updateGLCanvas(); + app.updateJKnotSlider(); + }else if(e.getActionCommand()==CurveApp.EXIT_AC){ + //TODO exit confirmation ? + System.exit(0); + }else if(e.getActionCommand()==CurveApp.STUPEN_AC){ + try{ + //String retval = JOptionPane.showInputDialog(null,"Zadejte stupeň křivky",new Integer(Curve.getInstance().getOrder())); + String retval = JOptionPane.showInputDialog(null,"Curve degree",new Integer(Curve.getInstance().getOrder())); + if(retval!=null){ + int stupen=(new Integer(retval)).intValue(); + Curve.getInstance().setOrder(stupen); + Curve.getInstance().setIsCurveFinished(false); + } + }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); + } + }else if(e.getActionCommand()==CurveApp.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/curveapp/Curve.java b/src/demos/nurbs/curveapp/Curve.java new file mode 100755 index 0000000..b772973 --- /dev/null +++ b/src/demos/nurbs/curveapp/Curve.java @@ -0,0 +1,313 @@ +package demos.nurbs.curveapp; + +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 křivky, vystavěna podle návrhového vzoru Singleton + * @author Tomáš Hráský + * + */ +@Root(name="curve") +public class Curve +{ + /** + * Odkaz na instanci třídy + */ + private static Curve singleton; + /** + * Indikuje, zda je zadání křivky kompletní + */ + @Element(name="finished") + private boolean isCurveFinished; + + /** + * Index aktuálního vybraného řídícího bodu + */ + private int bodIndex = -1; + + /** + * Stupeň křivky + */ + @Element(name="order") + private int order=3; + + /** + * Pole souřadnic řídícíh bodů + * + */ + private float[] ctrlPoints; + + /** + * Pole hodnot uzlového vektoru + */ + private float knots[]; + + /** + * 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 + */ + @ElementList(name="knots",type=MyFloat.class) + private Vector<MyFloat> knotVector; + + /** + * Vytvoří prázdnou definici křivky + */ + public void clear(){ + isCurveFinished=false; + ctrlPoints=new float[0]; + knots=new float[0]; + order=3; + } + + /** + * 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); + knotVector=new Vector<MyFloat>(knots.length); + + for(Float ff:ctrlPoints) + ctrlVector.add(new MyFloat(ff)); + + for(Float ff:knots) + knotVector.add(new MyFloat(ff)); + + Serializer s=new Persister(); + try { + System.out.println("ukládám"); + s.write(Curve.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(); + Curve c=s.read(Curve.class,f); + initFromCurve(c); + } + + /** + * Inicializuje objekt podle jiného objektu typu Curve + * @param c referenční objekt - křivka + */ + private void initFromCurve(Curve c) { + this.order=c.getOrder(); + this.ctrlPoints=new float[c.getCtrlVector().size()]; + this.knots=new float[c.getKnotVector().size()]; + int i=0; + for(MyFloat f:c.getCtrlVector()) + ctrlPoints[i++]=f.getValue(); + i=0; + for(MyFloat f:c.getKnotVector()) + knots[i++]=f.getValue(); + + this.isCurveFinished=c.isCurveFinished(); + } + + /** + * Konstruktor, nastaví prázdné hodnoty polí definujících NURBS křivku + */ + private Curve(){ + ctrlPoints=new float[0]; + knots=new float[0]; + isCurveFinished=false; + } + + /** + * Vrací instanci třídy (podle návrhového vzoru Singleton) + * @return instance třídy Curve + */ + public static Curve getInstance() { + if (singleton == null) + singleton = new Curve(); + return singleton; + + } + + /** + * Vrací pole uzlového vektoru + * @return pole hodnot uzlového vektoru + */ + public float[] getKnots() { + return this.knots; + } + + /** + * 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 křivky + * @return stupeň NURBS křivky + */ + public int getOrder() { + return this.order; + } + + /** + * 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í 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 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; + } + + } + } + + /** + * Nastavuje uzlový vektor + * @param knots nový uzlový vektor + */ + public void setKnots(float[] knots) { + this.knots = knots; + } + + /** + * Vrací informaci o stavu dokončení definice křvky + * @return true pokud je definice křivky kompletní, jinak false + */ + public boolean isCurveFinished() { + return isCurveFinished; + } + + /** + * 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 setIsCurveFinished(boolean b) { + isCurveFinished=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 pro serializaci + * @return vektor prvků uzlového vektoru + */ + private Vector<MyFloat> getKnotVector() { + return knotVector; + } + + /** + * Nastaví stupeň křivky + * @param order požadovaný stupeň + */ + public void setOrder(int order) { + this.order = order; + } +} diff --git a/src/demos/nurbs/curveapp/CurveApp.java b/src/demos/nurbs/curveapp/CurveApp.java new file mode 100755 index 0000000..ff70f68 --- /dev/null +++ b/src/demos/nurbs/curveapp/CurveApp.java @@ -0,0 +1,659 @@ +package demos.nurbs.curveapp; + +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.media.opengl.GLCanvas; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +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.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; + +/** + * Main class of application demostrating capabilities of JOGL when working with NURBS curves + * Hlavní třída aplikace demonstrující shopnosti knihovny JOGL při práci s NURBS křivkami + * @author Tomáš Hráský + * + */ +@SuppressWarnings("serial") +public class CurveApp extends JFrame implements ActionListener +{ + + /** + * Name of X-coord editing component of actually selected control point + * Jméno komponenty pro editaci X-ové souřadnice aktuálního bodu + */ + public static final String X_SPINNER_NAME = "xspinner"; + /** + * Name of Y-coord editing component of actually selected control point + * Jméno komponenty pro editaci Y-ové souřadnice aktuálního bodu + */ + public static final String Y_SPINNER_NAME = "yspinner"; + /** + * Name of weight editing component of actually selected control point + * Jméno komponenty pro editaci váhy aktuálního bodu + */ + public static final String W_SPINNER_NAME = "wspinner"; + + /** + * Name of ADD CONTROL POINT event + * Jméno události přidání řídícího bodu + */ + public static final String PRIDAT_AC = "PRIDAT"; + + /** + * Name of SET CURVE DEGREE event + * Jméno události zadání stupně křivky + */ + public static final String STUPEN_AC="STUPEN"; + /** + * Name of DELETE CONTROL POINT event + * Jméno události smazání řídícího bodu + */ + public static final String SMAZAT_AC = "SMAZAT"; + /** + * Name of MAKE CLOSED KNOTVECTOR event + * Jméno události vytvoření uzavřeného uzlového vektoru + */ + public static final String UZAVRENY_AC = "UZAVRENY"; + /** + * Name of MAKE OPEN (UNIFORM) KNOTVECTOR event + * Jméno události vytvoření otevřeného (uniformního) uzlového vektoru + */ + public static final String OTEVRENY_AC = "OTEVRENY"; + /** + * Name of SAVE CURVE event + * Jméno události uložení křivky + */ + public static final String ULOZIT_AC = "ULOZIT"; + /** + * Name of LOAD CURVE event + * Jméno události načetení uložené definice křivky + */ + public static final String NACIST_AC = "NACIST"; + /** + * Name of MOVE CONTROL POINT event + * Jméno události pohybu řídícího bodu + */ + private static final String MOVE_AC = "MOVE"; + + /** + * Name of CREATE NEW CURVE event + * Jméno události vytvoření nové křivky + */ + static final String NOVA_AC = "NEWCURVE"; + + /** + * Name of EXIT APP event + * Jméno události ukončení aplikace + */ + public static final String EXIT_AC = "EXIT"; + /** + * Name of SHOW ABOUT event + * Jméno události zobrazení okna o aplikaci + */ + public static final String INFO_AC = "INFO"; + + /** + * OpenGL 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; + + /** + * Mouse events listener + * Listener událostí myši + */ + private CurveMouseListener mouseListener; + + /** + * Knot vector editing component + * Komponenta pro editaci uzlového vektoru + */ + private JKnotSlider knotSlider; + + /** + * Start "move control point" mode + * Tlačítko pro zapnutí módu pohybu řídících bodů + */ + private JToggleButton moveTB; + + /** + * Constructor, initializes GUI + * Konstruktor, vytvoří grafické uživatelské rozhraní + */ + public CurveApp() { + super("Tomáš Hráský - example application demonstrating GLU NURBS capabilites - 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)); + glCanvas.addGLEventListener(new GLListener()); + mouseListener = new CurveMouseListener(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("Aplikace"); + JMenu aplikaceMenu = new JMenu("Application"); + menuBar.add(aplikaceMenu); + + //JMenuItem aboutMI=new JMenuItem("O aplikaci"); + JMenuItem aboutMI=new JMenuItem("About"); + aboutMI.setActionCommand(INFO_AC); + aboutMI.addActionListener(listener); + aplikaceMenu.add(aboutMI); + + aplikaceMenu.add(new JSeparator()); + + //JMenuItem konecMI=new JMenuItem("Ukončit"); + JMenuItem konecMI=new JMenuItem("Exit"); + konecMI.addActionListener(listener); + konecMI.setActionCommand(EXIT_AC); + aplikaceMenu.add(konecMI); + + //JMenu krivkaMenu = new JMenu("Křivka"); + JMenu krivkaMenu = new JMenu("Curve"); + menuBar.add(krivkaMenu); + + //JMenuItem pridatBodyMI = new JMenuItem("Přidat body"); + JMenuItem pridatBodyMI = new JMenuItem("Add control points"); + krivkaMenu.add(pridatBodyMI); + pridatBodyMI.addActionListener(listener); + + + pridatBodyMI.setActionCommand(PRIDAT_AC); + JMenuItem smazatBodyMI = new JMenuItem( + //"Smazat body"); + "Delete points"); + krivkaMenu.add(smazatBodyMI); + smazatBodyMI.addActionListener(listener); + + smazatBodyMI.setActionCommand(SMAZAT_AC); + + //JMenuItem stupenMI=new JMenuItem("Zadat stupeň křivky"); + JMenuItem stupenMI=new JMenuItem("Set curve degree"); + krivkaMenu.add(stupenMI); + stupenMI.addActionListener(listener); + stupenMI.setActionCommand(STUPEN_AC); + + //JMenu knotVecMenu = new JMenu("Vytvořit uzlový vektor"); + JMenu knotVecMenu = new JMenu("Create knot vector"); + krivkaMenu.add(knotVecMenu); + + //JMenuItem clampedKVMI = new JMenuItem("Okrajový"); + JMenuItem clampedKVMI = new JMenuItem("Clamped"); + knotVecMenu.add(clampedKVMI); + clampedKVMI.setActionCommand(UZAVRENY_AC); + clampedKVMI.addActionListener(listener); + //JMenuItem unclampedKVMI = new JMenuItem("Uniformní"); + JMenuItem unclampedKVMI = new JMenuItem("Uniform"); + knotVecMenu.add(unclampedKVMI); + unclampedKVMI.setActionCommand(OTEVRENY_AC); + unclampedKVMI.addActionListener(listener); + + //JMenuItem moveMI=new JMenuItem("Hýbat body"); + 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("Nová křivka"); + JMenuItem novaMI=new JMenuItem("New curve"); + krivkaMenu.add(novaMI); + novaMI.setActionCommand(NOVA_AC); + novaMI.addActionListener(listener); + + //JMenuItem ulozitMI = new JMenuItem("Uložit křivku jako..."); + JMenuItem ulozitMI = new JMenuItem("Save curve as..."); + krivkaMenu.add(ulozitMI); + ulozitMI.setActionCommand(ULOZIT_AC); + ulozitMI.addActionListener(listener); + //JMenuItem nacistMI = new JMenuItem("Načíst křivku"); + JMenuItem nacistMI = new JMenuItem("Load curve"); + 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("Nová křivka"); + novaB.setToolTipText("New curve"); + 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("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("Nahrát uloženou křivku"); + nahratB.setToolTipText("Load curve"); + 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("Přidat body"); + pridatTB.setToolTipText("Add points"); + toolBar.add(pridatTB); + pridatTB.setIcon(IconFactory.getIcon("demos/nurbs/icons/add.png")); + pridatTB.setActionCommand(PRIDAT_AC); + pridatTB.addActionListener(listener); + bg.add(pridatTB); + JToggleButton smazatTB = new JToggleButton(); + // smazatTB.setText("Smazat body"); + //smazatTB.setToolTipText("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); + + JToggleButton stupenTB = new JToggleButton(); + // stupenTB.setText("Smazat body"); + //stupenTB.setToolTipText("Zadat stupeň křivky"); + stupenTB.setToolTipText("Set curve 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("Okrajový"); + JMenuItem uzavrenyPopupMI = new JMenuItem("Clamped"); + popup.add(uzavrenyPopupMI); + uzavrenyPopupMI.setActionCommand(UZAVRENY_AC); + uzavrenyPopupMI.addActionListener(listener); + //JMenuItem otevrenyPopupMI = new JMenuItem("Uniformní"); + 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("Vytvořit uzlový vektor"); + vytvoritButton.setToolTipText("Create knot vector"); + vytvoritButton.setIcon(IconFactory.getIcon("demos/nurbs/icons/newfunction.png")); + bg.add(vytvoritButton); + + vytvoritButton.addMouseListener(new + /** + * @author Tomáš Hráský + * Class connecting context menu to button on toolbar + * Třída pro připojení kontextového menu na tlačítko na liště nástrojů + */ + 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("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("O aplikaci"); + 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("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, 0, 10000.0, 1)); + ySpinner = new JSpinner(new SpinnerNumberModel(0, 0, 10000.0, 1)); + wSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 10000.0, .05)); + + SpinnerListener spinnerListener = new SpinnerListener(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); + + cc.gridx = 0; + cc.gridy = 0; + + cc.gridwidth = 2; + 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("W"), cc); + + cc.gridx = 1; + cc.gridy = 1; + rightPanel.add(xSpinner, cc); + cc.gridy++; + rightPanel.add(ySpinner, cc); + cc.gridy++; + rightPanel.add(wSpinner, cc); + + xSpinner.setEnabled(false); + ySpinner.setEnabled(false); + wSpinner.setEnabled(false); + + c.weightx = 0; + c.weighty = 0; + getContentPane().add(rightPanel, c); + + c.gridx = 0; + c.gridy++; + + knotSlider = new JKnotSlider(Curve.getInstance().getKnots()); + knotSlider.addActionListener(this); + getContentPane().add(knotSlider, c); + + pack(); + invalidate(); + setVisible(true); + } + + /** + * Main method starting application + * Metoda pro spuštění aplikace + * @param args no arguments accepted + * + */ + public static void main(String[] args) { + new CurveApp(); + + } + + /** + * Reaction to request for redrive OpenGL canvas - repaints canvas, sets actually selected control points coords 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 (Curve.getInstance().getBodIndex() >= 0) { + xSpinner.setEnabled(true); + ySpinner.setEnabled(true); + wSpinner.setEnabled(true); + + xSpinner.setValue(Double.valueOf(Math.round(Curve.getInstance() + .getActiveX()))); + ySpinner.setValue(Double.valueOf(Math.round(Curve.getInstance() + .getActiveY()))); + wSpinner.setValue(Double.valueOf(Curve.getInstance().getActiveW())); + } else { + xSpinner.setEnabled(false); + ySpinner.setEnabled(false); + wSpinner.setEnabled(false); + } + } + + /* (non-Javadoc) + * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) + */ + public void actionPerformed(ActionEvent e) { + JKnotSlider src = (JKnotSlider) e.getSource(); + if(src.checkKnotMulti(Curve.getInstance().getOrder())){ + Curve.getInstance().setKnots(src.getKnotsFloat()); + }else{ + //JOptionPane.showMessageDialog(this,"Překročení maximální násobnosti uzlu","Chyba",JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this,"Maximum knot multiplicity exceeded","Error",JOptionPane.ERROR_MESSAGE); + src.setKnots(Curve.getInstance().getKnots()); + } + updateGLCanvas(); + } + + /** + * Returns OpenGL canvas + * Vrací OpenGL plátno + * @return OpenGL canvas + */ + public GLCanvas getGlCanvas() { + return glCanvas; + } + + /** + * Returns mouse events listener + * Vrací listener událostí myši + * @return mouse listener + */ + public CurveMouseListener getMouseListener() { + return mouseListener; + } + + /** + * Creates NURBS curve with clamped knot vector + * Vytvoří NURBS křivku s okrajovým uzlovým vektorem + */ + public void uzavernyKV() { + int stupen = Curve.getInstance().getOrder(); + int pocetBodu = Curve.getInstance().getCtrlPoints().length / 4; + if (stupen <= pocetBodu) { + int knotCount = stupen + pocetBodu; + int middlePartSize = knotCount - 2 * stupen; + float[] 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; + + postNewKnot(newKnots); + + } else + //errorMessage("Malý počet řídících bodů vzhledem k zadanému stupni křivky"); + errorMessage("Too few control points regarding set curve degree"); + } + + /** + * Displays modal window with error report + * Zobrazí modální okno s hlášením chyby + * @param error error message + */ + public void errorMessage(String error){ + //JOptionPane.showMessageDialog(this,error,"Chyba!",JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this,error,"Error!",JOptionPane.ERROR_MESSAGE); + } + + /** + * Creates NURBS curves with uniform knot vector + * Vytvoří NURBS křivku s uniformním uzlovým vektorem + */ + public void otevrenyKV() { + int stupen = Curve.getInstance().getOrder(); + int pocetBodu = Curve.getInstance().getCtrlPoints().length / 4; + if (stupen <= pocetBodu) { + int knotCount = stupen + pocetBodu; + int middlePartSize = knotCount; + float[] newKnots = new float[knotCount]; + int i; + int j = 0; + float middleStep = 1f / (middlePartSize - 1); + float knot = 0; + + // 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; + + postNewKnot(newKnots); + } + else + //errorMessage("Malý počet řídících bodů vzhledem k zadanému stupni křivky"); + errorMessage("Too few control points regarding set curve degree"); + } + + /** + * Method called after adding new knot + * Metoda volaná po přidání nového uzlu + * @param newKnots new knot vector + */ + private void postNewKnot(float[] newKnots) { + Curve.getInstance().setKnots(newKnots); + knotSlider.setKnots(newKnots); + Curve.getInstance().setIsCurveFinished(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 data source for knot editing component from knot vector of curve object + * Nastaví zdroj dat komponenty pro editaci uzlového vektoru podle uz. vektoru v objektu křivky + */ + public void updateJKnotSlider() { + knotSlider.setKnots(Curve.getInstance().getKnots()); + } +} diff --git a/src/demos/nurbs/curveapp/CurveMouseListener.java b/src/demos/nurbs/curveapp/CurveMouseListener.java new file mode 100755 index 0000000..b74e1cf --- /dev/null +++ b/src/demos/nurbs/curveapp/CurveMouseListener.java @@ -0,0 +1,180 @@ +package demos.nurbs.curveapp; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; + +/** + * Class reacting to mouse events (implements interface for button press, drag and movement) + * 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 CurveMouseListener implements MouseListener, MouseMotionListener { + /** + * Actually selected control point index + * Index aktuálně vybraného řídícího bodu + */ + private int bodIndex; + + /** + * Window listener is connected to + * Okno k nemuž listener patří + */ + private CurveApp appWindow; + + /** + * Action type + * Typ prováděné činnosti + */ + private String actionType; + + /** + * Pixel tolerance when selecting control point by clicking + * Tolerance pro indikaci kliku na řídící bod + */ + private static final int TOLERANCE=10; + + /** + * Creates new listener with connection to given app window + * Vytvoří listener s odkazem na zadané okno + * @param app rodičovské okno + */ + public CurveMouseListener(CurveApp app) { + this.appWindow=app; + } + + /* (non-Javadoc) + * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) + */ + public void mouseClicked(MouseEvent e) { + if(actionType==CurveApp.PRIDAT_AC){ + Curve.getInstance().setIsCurveFinished(false); + float x=e.getX(); + float y=e.getY(); + float z=0; + float w=1; + int size; + float[] newCtrls; + try{ + size=Curve.getInstance().getCtrlPoints().length; + }catch (Exception ex) { + size=0; + } + newCtrls=new float[size+4]; + System.arraycopy(Curve.getInstance().getCtrlPoints(),0,newCtrls,0,size); + + newCtrls[size]=x; + newCtrls[size+1]=y; + newCtrls[size+2]=z; + newCtrls[size+3]=w; + Curve.getInstance().setCtrlPoints(newCtrls); + }else if(actionType==CurveApp.SMAZAT_AC&&bodIndex>=0){ + Curve.getInstance().setIsCurveFinished(false); + int size=Curve.getInstance().getCtrlPoints().length; + float[] newCtrls=new float[size-4]; + + int firstPartSize=(bodIndex)*4; + int secondPartSize=newCtrls.length-firstPartSize; + System.arraycopy(Curve.getInstance().getCtrlPoints(),0,newCtrls,0,firstPartSize); + System.arraycopy(Curve.getInstance().getCtrlPoints(),firstPartSize+4,newCtrls,firstPartSize,secondPartSize); + bodIndex=-1; + Curve.getInstance().setBodIndex(bodIndex); + Curve.getInstance().setCtrlPoints(newCtrls); + } + appWindow.updateGLCanvas(); + } + + /* (non-Javadoc) + * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent) + */ + public void mousePressed(MouseEvent e) { + // if(actionType==MOVE_AC){ + float[] ctrlpoints=Curve.getInstance().getCtrlPoints(); + int x=e.getX(); + int y=e.getY(); + this.bodIndex=-1; + // System.out.println(ctrlpoints.length); + for(int i=0;i<ctrlpoints.length/4;i++){ + float xS = ctrlpoints[i*4]/ctrlpoints[i*4+3]; + float yS = ctrlpoints[i*4+1]/ctrlpoints[i*4+3]; + if(x>=xS-TOLERANCE&&x<=xS+TOLERANCE&&y>=yS-TOLERANCE&&y<=yS+TOLERANCE){ + this.bodIndex=i; + } + } + + Curve.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(); + + Curve.getInstance().setActiveX(x); + Curve.getInstance().setActiveY(y); + } + appWindow.updateGLCanvas(); + } + + /* (non-Javadoc) + * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent) + */ + public void mouseMoved(MouseEvent e) { + + } + + /** + * Set action type + * Nastaví typ prováděné činnosti + * @param action Action type + */ + public void setActionType(String action) { + this.actionType=action; + } + + /** + * Returns actually selected control point index + * Vrací index aktuálně vybraného řídícího bodu + * @return actually selected control point index + */ + public int getBodIndex() { + return bodIndex; + } + + /** + * Sets actually selected control point index + * Nastavuje index aktuálně vybraného řídícího bodu + * @param bodIndex actually selected control point index + */ + public void setBodIndex(int bodIndex) { + this.bodIndex = bodIndex; + } +} diff --git a/src/demos/nurbs/curveapp/GLListener.java b/src/demos/nurbs/curveapp/GLListener.java new file mode 100755 index 0000000..18bf36f --- /dev/null +++ b/src/demos/nurbs/curveapp/GLListener.java @@ -0,0 +1,141 @@ +package demos.nurbs.curveapp; + +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; + +/** + * Listener raacting to OpenGL canvas events + * Listener reagující na události na OpenGL plátně + * @author Tomáš Hráský + * + */ +public class GLListener implements GLEventListener { + + /** + * OpenGL object + * objekt realizující základní OpenGL funkce + */ + private GL gl; + + /** + * GLU + * Objekt realizující funkce nadstavbové knihovny GLU + */ + private GLU glu; + + /** + * GLUT object + * Objekt realizující funkce nadstavbové knihovny GLUT + */ + private GLUT glut; + + /** + * NURBS curve object + * OpenGL Objekt NURBS křivky + */ + private GLUnurbs nurbs; + + + /* (non-Javadoc) + * @see javax.media.opengl.GLEventListener#init(javax.media.opengl.GLAutoDrawable) + */ + public void init(GLAutoDrawable drawable) { + this.gl = drawable.getGL(); + this.glu = new GLU(); + this.glut=new GLUT(); + + this.nurbs = glu.gluNewNurbsRenderer(); + gl.glClearColor(1, 1, 1, 1); + } + + /* (non-Javadoc) + * @see javax.media.opengl.GLEventListener#display(javax.media.opengl.GLAutoDrawable) + */ + public void display(GLAutoDrawable drawable) { + + gl.glClear(GL.GL_COLOR_BUFFER_BIT); + + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glLoadIdentity(); + + float[] knots = Curve.getInstance().getKnots(); + float[] ctrlpoints = Curve.getInstance().getCtrlPoints(); + + gl.glEnable(GL.GL_LINE_SMOOTH); + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); + gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_DONT_CARE); + + gl.glLineWidth(3); + + if(Curve.getInstance().isCurveFinished()){ + glu.gluBeginCurve(nurbs); + glu.gluNurbsCurve(nurbs, knots.length, knots, 4, ctrlpoints, Curve.getInstance().getOrder(), GL.GL_MAP1_VERTEX_4); + glu.gluEndCurve(nurbs); + } + + gl.glColor3f(0,0,0); + gl.glPointSize(5); + gl.glBegin(GL.GL_POINTS); + for (int i = 0; i < ctrlpoints.length / 4; i++) { + // if(i!=Curve.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(); + + + for (int i = 0; i < ctrlpoints.length / 4; i++) { + // if(i!=Curve.getInstance().getBodIndex()) + // gl.glPushMatrix(); + gl.glRasterPos2f(ctrlpoints[i * 4]/ctrlpoints[i * 4 + 3], ctrlpoints[i * 4 + 1]/ctrlpoints[i * 4 + 3]-5); + glut.glutBitmapString(GLUT.BITMAP_HELVETICA_18,String.valueOf(i+1)); + // gl.glPopMatrix(); + } + + + gl.glLineWidth(1); + gl.glBegin(GL.GL_LINE_STRIP); + for (int i = 0; i < ctrlpoints.length / 4; i++) { + // if(i!=Curve.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(); + gl.glColor3f(0,0,1); + if(Curve.getInstance().getBodIndex()>=0){ + gl.glPointSize(8); + gl.glBegin(GL.GL_POINTS); + int i=Curve.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.glMatrixMode(GL.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrtho(0, drawable.getWidth(), 0, drawable.getHeight(), -1, 1); + 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) { + } +} diff --git a/src/demos/nurbs/curveapp/MyFloat.java b/src/demos/nurbs/curveapp/MyFloat.java new file mode 100755 index 0000000..6adc2ef --- /dev/null +++ b/src/demos/nurbs/curveapp/MyFloat.java @@ -0,0 +1,55 @@ +package demos.nurbs.curveapp; + +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/curveapp/SpinnerListener.java b/src/demos/nurbs/curveapp/SpinnerListener.java new file mode 100755 index 0000000..ccc5ac4 --- /dev/null +++ b/src/demos/nurbs/curveapp/SpinnerListener.java @@ -0,0 +1,47 @@ +package demos.nurbs.curveapp; + +import javax.swing.JSpinner; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * Listener reacting to events on GUI components setting coords and 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 { + + /** + * Application window + * Okno aplikace, k němuž listener patří + */ + private CurveApp appWindow; + + /** + * Creates new instance with link to parent window + * Vytvoří instanci objektu s odkazem na rodičovské okno + * @param app app window + */ + public SpinnerListener(CurveApp 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(); + 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()==CurveApp.X_SPINNER_NAME) + Curve.getInstance().setActiveX(val); + if(src.getName()==CurveApp.Y_SPINNER_NAME) + Curve.getInstance().setActiveY(val); + if(src.getName()==CurveApp.W_SPINNER_NAME) + Curve.getInstance().setActiveW(val); + appWindow.updateGLCanvas(); + } +} diff --git a/src/demos/nurbs/icons/IconFactory.java b/src/demos/nurbs/icons/IconFactory.java new file mode 100755 index 0000000..88d8539 --- /dev/null +++ b/src/demos/nurbs/icons/IconFactory.java @@ -0,0 +1,19 @@ +package demos.nurbs.icons; + +import java.io.*; +import javax.swing.ImageIcon; +import com.sun.opengl.util.StreamUtil; + +public class IconFactory { + private IconFactory() {} + + public static ImageIcon getIcon(String resourceName) { + try { + InputStream input = IconFactory.class.getClassLoader().getResourceAsStream(resourceName); + byte[] data = StreamUtil.readAll(input); + return new ImageIcon(data, resourceName); + } catch (IOException e) { + return new ImageIcon(); + } + } +} diff --git a/src/demos/nurbs/icons/add.png b/src/demos/nurbs/icons/add.png Binary files differnew file mode 100644 index 0000000..ade93b9 --- /dev/null +++ b/src/demos/nurbs/icons/add.png diff --git a/src/demos/nurbs/icons/adept_sourceseditor.png b/src/demos/nurbs/icons/adept_sourceseditor.png Binary files differnew file mode 100644 index 0000000..c69015a --- /dev/null +++ b/src/demos/nurbs/icons/adept_sourceseditor.png diff --git a/src/demos/nurbs/icons/exit.png b/src/demos/nurbs/icons/exit.png Binary files differnew file mode 100644 index 0000000..5313b7a --- /dev/null +++ b/src/demos/nurbs/icons/exit.png diff --git a/src/demos/nurbs/icons/fileclose.png b/src/demos/nurbs/icons/fileclose.png Binary files differnew file mode 100644 index 0000000..84813bc --- /dev/null +++ b/src/demos/nurbs/icons/fileclose.png diff --git a/src/demos/nurbs/icons/fileimport.png b/src/demos/nurbs/icons/fileimport.png Binary files differnew file mode 100644 index 0000000..3bf3b99 --- /dev/null +++ b/src/demos/nurbs/icons/fileimport.png diff --git a/src/demos/nurbs/icons/filenew.png b/src/demos/nurbs/icons/filenew.png Binary files differnew file mode 100644 index 0000000..1f7f88e --- /dev/null +++ b/src/demos/nurbs/icons/filenew.png diff --git a/src/demos/nurbs/icons/folder_new.png b/src/demos/nurbs/icons/folder_new.png Binary files differnew file mode 100644 index 0000000..1ea3951 --- /dev/null +++ b/src/demos/nurbs/icons/folder_new.png diff --git a/src/demos/nurbs/icons/info.png b/src/demos/nurbs/icons/info.png Binary files differnew file mode 100644 index 0000000..e3c8adb --- /dev/null +++ b/src/demos/nurbs/icons/info.png diff --git a/src/demos/nurbs/icons/math_rsup.png b/src/demos/nurbs/icons/math_rsup.png Binary files differnew file mode 100644 index 0000000..d689192 --- /dev/null +++ b/src/demos/nurbs/icons/math_rsup.png diff --git a/src/demos/nurbs/icons/mouse.png b/src/demos/nurbs/icons/mouse.png Binary files differnew file mode 100644 index 0000000..fd57fac --- /dev/null +++ b/src/demos/nurbs/icons/mouse.png diff --git a/src/demos/nurbs/icons/newfunction.png b/src/demos/nurbs/icons/newfunction.png Binary files differnew file mode 100644 index 0000000..f890ef2 --- /dev/null +++ b/src/demos/nurbs/icons/newfunction.png diff --git a/src/demos/nurbs/knotslidercomponent/JKnotSlider.java b/src/demos/nurbs/knotslidercomponent/JKnotSlider.java new file mode 100755 index 0000000..02da402 --- /dev/null +++ b/src/demos/nurbs/knotslidercomponent/JKnotSlider.java @@ -0,0 +1,468 @@ +package demos.nurbs.knotslidercomponent; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Vector; + +import javax.swing.JComponent; + +/** + * GUI component for editing NURBS curve/surface knotvector + * Komponenta grafického uživatelského rozhraní pro editaci uzlového vektoru NURBS křivky + * @author Tomáš Hráský + * + */ +@SuppressWarnings("serial") +public class JKnotSlider extends JComponent implements ComponentListener, + MouseMotionListener, MouseListener { + + /** + * Knot value change event + * Událost změny hodoty prvku uzlového vektoru + */ + private static final String KNOT_MOVED = "KnotMoved"; + + /** + * Vector representing knots + * Vektor objektů reprezentujících prvky uzlového vektoru + */ + private Vector<KnotPolygon> knots; + + /** + * Previous knot vector (for recovery after user wrong setting) + * Předchozí vektor objektů reprezentujících prvky uzlového vektoru - pro obnovení při chybě uživatele + */ + private Vector<KnotPolygon> previousState; + + /** + * List of listeners attached to component + * Seznam ActionListenerů navázaných na komponentu + */ + private LinkedList<ActionListener> actionListeners; + + /** + * Value of one pixel movement + * Hodnota posunu o jeden pixel + */ + private double oneStep; + + /** + * Side space + * Mezera na straně osy + */ + private int side; + + /** + * Top space + * Mezera nad osou + */ + private int top; + + /** + * Actually selected knot index + * Index aktuálně vybraného prvku uzlového vektoru + */ + private int activeKnot; + +// private Vector<Integer> xVector; + + + /** + * Creates component + * Vytvoří komponentu + */ + public JKnotSlider() { + oneStep = 0; + top = 0; + side = 0; + knots = new Vector<KnotPolygon>(); + previousState=new Vector<KnotPolygon>(); +// xVector=new Vector<Integer>(); + + actionListeners = new LinkedList<ActionListener>(); + this.addComponentListener(this); + this.addMouseMotionListener(this); + this.addMouseListener(this); + } + + /** + * Adds listener to notified list. list + * Přidá zadaný listener do seznamu naslouchajících objektů + * @param listener added listener + */ + public void addActionListener(ActionListener listener) { + actionListeners.add(listener); + } + + /** + * Creates component with given knotvector knots + * Vytvoří komponentu se zadanými hodnotami uzlového vektoru + * @param knots knot vector + */ + public JKnotSlider(double[] knots) { + this(); + for (double d : knots) { + addKnot(new Double(d)); + } + } + + /** + * Creates component with given knotvector knots + * Vytvoří komponentu se zadanými hodnotami uzlového vektoru + * @param knots knot vector + */ + public JKnotSlider(Vector<Double> knots) { + this(); + for (Double d : knots) { + addKnot(d); + } + } + /** + * Creates component with given knotvector knots + * Vytvoří komponentu se zadanými hodnotami uzlového vektoru + * @param knots uzlový vektor + */ + public JKnotSlider(float[] knots) { + this(); + if(knots!=null) + for(double d:knots) + addKnot(new Double(d)); + } + + /** + * Adds a knot + * Přidá uzel do uzlového vektoru + * @param d knot + */ + public void addKnot(Double d) { +// preserveState(); + knots.add(new KnotPolygon(d, oneStep, top, side)); + } + + + /** + * Saves actual knotvector for later recovery + * Uloží aktuální uzlový vektor pro pozdější obnovení + */ + private void preserveState(){ + previousState.clear(); + previousState.addAll(knots); + } + +// /** +// * Přidá uzel do uzlového vektoru +// * @param d hodnota přidávaného uzlu +// */ +// public void addKnot(double d) { +// addKnot(new Double(d)); +// } + + /** + * Returns knotvector + * Vrací uzlový vektor + * @return knotvector array + */ + @SuppressWarnings("unchecked") + public double[] getKnots() { + double[] output = new double[knots.size()]; + int i = 0; + if (activeKnot >= 0) { + Double d = knots.get(activeKnot).getValue(); + Collections.sort(knots); + for (int j = 0; j < knots.size(); j++) + if (knots.get(j).getValue().equals(d)) { + activeKnot = j; + break; + } + } else + Collections.sort(knots); + for (KnotPolygon p : knots) + output[i++] = p.getValue().doubleValue(); + return output; + } + /** + * Returns knotvector + * Vrací uzlový vektor + * @return knotvector array + */ + public float[] getKnotsFloat(){ + float[] output=new float[knots.size()]; + int i=0; + for(double d:getKnots()){ + output[i++]=(float) d; + } + return output; + } + + /* (non-Javadoc) + * @see javax.swing.JComponent#paintComponent(java.awt.Graphics) + */ + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + drawBaseLine((Graphics2D) g); + updateMultis((Graphics2D)g); + drawKnots((Graphics2D) g); + } + + /** + * Draws info about knot multiplicity + * Vykreslí informaci o násobnosti uzlů + * @param graphics2D object to draw to + */ + private void updateMultis(Graphics2D graphics2D) { + Vector<Integer> vrcholy=new Vector<Integer>(); + for(int i=0;i<knots.size();i++){ + knots.get(i).setMulti(0); + vrcholy.add(knots.get(i).xpoints[0]); + } + + for(int i=0;i<knots.size();i++){ + //k.xpoints[0] //hodnota na X -> počet stejných hodnot nám zjistí multiplicitu + for(Integer ii:vrcholy) + if(ii.intValue()==knots.get(i).xpoints[0]) + knots.get(i).setMulti(knots.get(i).getMulti()+1); + } + } + + /** + * Sends event to all notified listeners + * Pošle všem navešeným listenerům událost + * @param ae event being sent + */ + private void notificateActionListeners(ActionEvent ae) { + for (ActionListener a : actionListeners) + a.actionPerformed(ae); + } + + /** + * "Draws" knotvector + * Vykreslí reprezentaci uzlového vektoru + * @param g object to draw to + */ + private void drawKnots(Graphics2D g) { + String txt; +// int freq; + for (KnotPolygon p : knots) { + g.drawPolygon(p); + g.drawString(p.getMulti()+"x",p.xpoints[1],top); +// freq=Collections.frequency(xVector,Integer.valueOf(p.xpoints[0])); +// g.drawString(freq+"x",p.xpoints[1],top-8); + } + g.rotate(Math.PI / 2); + for (KnotPolygon p : knots) { + txt = p.getValue().toString(); + if (txt.length() > 5) + txt = txt.substring(0, 4); + g.translate(top + 15, -p.xpoints[1]); + g.drawString(txt, 0, 0); + g.translate(-(top + 15), p.xpoints[1]); + } + } + + + /** + * Draws baseline + * Vykreslí základní linku + * @param g object to draw to + */ + private void drawBaseLine(Graphics2D g) { + g.drawLine(side, top, (int) (side + (this.getWidth() * .8)), top); + } + + /* (non-Javadoc) + * @see java.awt.Component#getMinimumSize() + */ + @Override + public Dimension getMinimumSize() { + return new Dimension(100, 60); + } + + /* (non-Javadoc) + * @see java.awt.Component#getPreferredSize() + */ + @Override + public Dimension getPreferredSize() { + return new Dimension(250, 60); + } + + /* (non-Javadoc) + * @see java.awt.event.ComponentListener#componentResized(java.awt.event.ComponentEvent) + */ + public void componentResized(ComponentEvent e) { + int width = this.getWidth(); + int height = this.getHeight(); + + side = (int) (width * .1); + top = (int) (height * .3); + + width *= .8; + height *= .8; + + oneStep = 1d / (width); + + updateKnotPolygons(); + repaint(); + } + + /** + * Updates all objects representing knots + * Aktualizuje nastavení všech objektů reprezentujících uzly + */ + private void updateKnotPolygons() { + for (KnotPolygon p : knots) { + p.update(oneStep, top, side); + } + + } + + /* (non-Javadoc) + * @see java.awt.event.ComponentListener#componentMoved(java.awt.event.ComponentEvent) + */ + public void componentMoved(ComponentEvent e) { + } + + /* (non-Javadoc) + * @see java.awt.event.ComponentListener#componentShown(java.awt.event.ComponentEvent) + */ + public void componentShown(ComponentEvent e) { + } + + /* (non-Javadoc) + * @see java.awt.event.ComponentListener#componentHidden(java.awt.event.ComponentEvent) + */ + public void componentHidden(ComponentEvent e) { + } + + /* (non-Javadoc) + * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent) + */ + public void mouseDragged(MouseEvent e) { + + if (activeKnot >= 0) { +// preserveState(); + + if (e.getX() >= side && e.getX() <= (getWidth() * .8 + side)) { + knots.get(activeKnot).updateByX(e.getX()); + } else if (e.getX() < side) + knots.get(activeKnot).updateByValue(new Double(0)); + else + knots.get(activeKnot).updateByValue(new Double(1)); + + notificateActionListeners(new ActionEvent(this, + ActionEvent.ACTION_PERFORMED, KNOT_MOVED)); + repaint(); + } + } + +// private void updateXVector() { +// xVector.clear(); +// for(KnotPolygon p:knots){ +// xVector.add(Integer.valueOf(p.xpoints[0])); +// } +// } + + /* (non-Javadoc) + * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent) + */ + public void mouseMoved(MouseEvent e) { + } + + /* (non-Javadoc) + * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) + */ + public void mouseClicked(MouseEvent e) { + } + + /* (non-Javadoc) + * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent) + */ + public void mousePressed(MouseEvent e) { + int x = e.getX(); + int y = e.getY(); + KnotPolygon p; + this.activeKnot = -1; + for (int i = 0; i < knots.size(); i++) { + p = knots.get(i); + if (p.contains(x, y)) { + activeKnot = i; + break; + } + } + } + + /* (non-Javadoc) + * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent) + */ + public void mouseReleased(MouseEvent e) { + this.activeKnot = -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) { + } + +// /** +// * Nastaví uzlový vektor +// * @param knots nový uzlový vektor +// */ +// public void setKnots(double[] knots) { +// preserveState(); +// for(double d:knots) +// addKnot(new Double(d)); +// repaint(); +// } + /** + * Sets knotvector + * Nastaví uzlový vektor + * @param knots new knotvector + */ + public void setKnots(float[] knots) { +// preserveState(); + this.knots.clear(); + for(double d:knots) + addKnot(new Double(d)); + repaint(); + } + + /** + * Checks whether knot multiplicity is not bigger than given value + * Zkontroluje, zda násobnost uzlů nepřekročila zadanou hodnotu + * @param maxMulti maximum multiplicity + * @return true if multiplicity is NOT bigger + */ + public boolean checkKnotMulti(int maxMulti) { + updateMultis((Graphics2D) this.getGraphics()); + for(KnotPolygon p:knots) + if(p.getMulti()>maxMulti) + return false; + return true; + } + +// /** +// * Obnosví poslední uložený stav uzlového vektoru +// */ +// public void restoreState() { +// knots.clear(); +// knots.addAll(previousState); +// repaint(); +// } +} diff --git a/src/demos/nurbs/knotslidercomponent/KnotPolygon.java b/src/demos/nurbs/knotslidercomponent/KnotPolygon.java new file mode 100755 index 0000000..5665392 --- /dev/null +++ b/src/demos/nurbs/knotslidercomponent/KnotPolygon.java @@ -0,0 +1,159 @@ +package demos.nurbs.knotslidercomponent; + +import java.awt.Polygon; + +/** + * Object representing knot + * Objekt reprezentující uzel v uzlovém vektoru + * @author Tomáš Hráský + * + */ +@SuppressWarnings("serial") +class KnotPolygon extends Polygon implements Comparable { + /** + * Knot value + * Hodnota uzlu + */ + private Double value; + + /** + * Size of change when moved by one pixel + * Velikost změny při posunu o jeden pixel + */ + private double oneStep; + + /** + * Top space + * Horní mezera osy + */ + private int top; + + /** + * Side space + * Boční mezera osy + */ + private int side; + + /** + * Knot multiplicity + * Násobnost uzlu + */ + private int multi; + + /** + * Creates new instance with given values + * Vytvoří instanci se zadanými hodnotami + * @param d knot value + * @param oneStep change of one pixel movement + * @param top top space + * @param side side space + */ + public KnotPolygon(Double d, double oneStep, int top, int side) { + this.value = d; + xpoints = new int[3]; + ypoints = new int[3]; + npoints = 3; + multi=1; + makeCoords(oneStep, top, side); + } + + /** + * Computes coords of polygon representing knot + * Vypočte souřadnice polygonu reprezentujícího uzel + * @param oneStep change of one pixel movement + * @param top top space + * @param side side space + */ + private void makeCoords(double oneStep, int top, int side) { + this.oneStep = oneStep; + this.top = top; + this.side = side; + + int x = (int) (value / oneStep); + x += side; + + xpoints[0] = x; + xpoints[1] = x - 4; + xpoints[2] = x + 4; + ypoints[0] = top + 2; + ypoints[1] = top + 12; + ypoints[2] = top + 12; + + invalidate(); + } + + /** + * Computes coords from set values + * Vypočte souřadnice podle nastavených hodont + */ + private void makeCoords() { + makeCoords(oneStep, top, side); + } + + /** + * Computes coords from given values + * Vypočte souřadnice podle zadaných hodont + * @param oneStep step of one pixel movement + * @param top top space + * @param side side space + */ + public void update(double oneStep, int top, int side) { + makeCoords(oneStep, top, side); + } + + /** + * Updates coords from given coord of polygon top + * Upraví souřadnice podle nové zadané souřadnice vrcholu polygonu + * @param x nová souřadnice vrcholu + */ + public void updateByX(int x) { + value = oneStep * (x - side); + makeCoords(); + } + + /** + * Updates coords from given value of knot + * Upraví souřadnice polygonu podle nové hodnoty + * @param d nová hodnota + */ + public void updateByValue(Double d){ + value=d; + makeCoords(); + } + + public int compareTo(Object o) { + if (o instanceof KnotPolygon) { + KnotPolygon kp = (KnotPolygon) o; + return getValue().compareTo(kp.getValue()); + } else + return 0; + } + + /** + * Returns knot value + * Vrací hodnotu uzlu + * @return knot value + */ + public Double getValue() { + return value; + } + + /** + * Returns knot multiplicity + * Vrací násobnost uzlu + * @return knot multiplicity + */ + public int getMulti() { + return multi; + } + + /** + * Sets knot multiplicity + * Nastavuje násobnost uzlu + * @param multi knot multiplicity + */ + public void setMulti(int multi) { + this.multi = multi; + } + +} 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..fe0918f --- /dev/null +++ b/src/demos/nurbs/surfaceapp/GLListener.java @@ -0,0 +1,336 @@ +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; + +/** + * 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 GL 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 GLUT 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(); + this.glu = new GLU(); + this.glut = new GLUT(); + + + this.nurbs = glu.gluNewNurbsRenderer(); + // gl.glClearColor(0, 0, 0, 0); + gl.glClearColor(1, 1, 1, 0); + gl.glEnable(GL.GL_DEPTH_TEST); + gl.glDepthFunc(GL.GL_LESS); + gl.glClearDepth(1000.0f); + gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); + } + + /* (non-Javadoc) + * @see javax.media.opengl.GLEventListener#display(javax.media.opengl.GLAutoDrawable) + */ + public void display(GLAutoDrawable drawable) { + + gl.glClear(GL.GL_COLOR_BUFFER_BIT| GL.GL_DEPTH_BUFFER_BIT); + + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glLoadIdentity(); + + glu.gluLookAt(0,0,400, 0,0,0, 0,1,0); + + + + // gl.glPushMatrix(); + gl.glShadeModel(GL.GL_SMOOTH); + gl.glPolygonMode(GL.GL_FRONT, GL.GL_FILL); + gl.glPolygonMode(GL.GL_BACK, GL.GL_FILL); + gl.glDisable(GL.GL_CULL_FACE); + + gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, materialDiffuse,0); + gl.glMaterialfv(GL.GL_BACK, GL.GL_DIFFUSE, materialDiffuse,0); + + gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, lightPosition,0); + gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, materialDiffuse,0); + gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, lightAmbient,0); + gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, lightAmbient,0); + + gl.glLightfv(GL.GL_LIGHT2, GL.GL_POSITION, lightPosition3,0); + gl.glLightfv(GL.GL_LIGHT2, GL.GL_DIFFUSE, lightDiffuse,0); + + if(app.isLightingEnabled())gl.glEnable(GL.GL_LIGHTING); + else gl.glDisable(GL.GL_LIGHTING); + gl.glEnable(GL.GL_LIGHT0); + gl.glEnable(GL.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(GL.GL_VIEWPORT,viewport,0); + gl.glGetDoublev(GL.GL_MODELVIEW_MATRIX,mvmatrix,0); + gl.glGetDoublev(GL.GL_PROJECTION_MATRIX,projmatrix,0); + + gl.glEnable(GL.GL_LINE_SMOOTH); + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); + gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.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(), + GL.GL_MAP2_VERTEX_4); + glu.gluEndSurface(nurbs); + } + + gl.glDisable(GL.GL_LIGHTING); + + // gl.glColor3f(1,1,1); + gl.glColor3f(0,0,0); + gl.glPointSize(5); + gl.glBegin(GL.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(GLUT.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(GL.GL_LINE_STRIP); + int baseIndex=0 ; + + + + + //"příčná žebra" + //"cross ribs" + for(int i=0;i<Surface.getInstance().getPointsInU();i++){ + gl.glBegin(GL.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(GL.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(GL.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(GL.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><space><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..7f2a546 --- /dev/null +++ b/src/demos/nurbs/surfaceapp/SurfaceApp.java @@ -0,0 +1,1066 @@ +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.media.opengl.GLCanvas; +import javax.swing.AbstractAction; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +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; + +/** + * 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; + } +} |