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 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 previousState; /** * List of listeners attached to component * Seznam ActionListenerů navázaných na komponentu */ private LinkedList 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 xVector; /** * Creates component * Vytvoří komponentu */ public JKnotSlider() { oneStep = 0; top = 0; side = 0; knots = new Vector(); previousState=new Vector(); // xVector=new Vector(); actionListeners = new LinkedList(); 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 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 vrcholy=new Vector(); for(int i=0;i 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(); // } }