2009-07-01 8 views
1

Je suis impliqué dans un projet dans lequel nous faisons un éditeur visuel (écrit en Java). Maintenant, j'essaie de faire des courbes qui joignent deux objets différents que je peins dans une classe qui étend JPanel (cette classe est ce que j'utilise pour peindre, dans un JFrame, en surchargeant la méthode paintComponent). Je suis dans les ennuis parce que j'utilise la classe QuadCurve2D pour faire cela, mais je ne peux pas le rendre cliquable (j'utilise la méthode contient, mais ça ne marche pas à chaque fois), le rendre modifiable (par exemple, mettre un carré dans son point central pour modifier sa courbure.Le point qui est utilisé au milieu du QuadCurve2D quand le constructeur est appelé est en dehors de la courbe) ou quelque chose (méthode, variable, itérateur, etc) qui pourrait me dire quels Points sont en le QuadCurve2D.Comment faire une courbe pour joindre deux objets modifiables?

Après avoir cherché tout cela pendant un certain temps, je n'ai pas de réponse, alors j'essaie de l'afficher ici pour trouver une solution. Est-il possible de le faire avec la classe QuadCurve2D, ou dois-je essayer avec une bibliothèque externe?

Répondre

4

Tout d'abord désolé pour la réponse longue. Je publie maintenant une réponse complète à votre question. Je classe la classe QuadCurve2D.Double et avec un peu de maths, vous définissez la courbe avec un début, une fin et un point central au lieu d'un point de contrôle. J'ai aussi créé une nouvelle méthode qui vérifie si un point est sur la courbe. La méthode des intersections vérifie si la coque convexe de la forme croise la forme fournie, donc dans le cas de la courbe concave, cela est fonctionnel mais pas précis. Notez que ma mise en œuvre de la méthode pour vérifier si un point est sur la courbe est plutôt coûteuse et pas précise à 100% puisque je vérifie la longueur de la courbe avec une résolution spécifiée (0 est le début de la courbe, 1 est la fin Donc, dans l'exemple fourni, je vérifie avec une résolution de 0.01 ce qui signifie que 100 contrôles sont effectués le long de la courbe). Pour cela, assurez-vous que l'étape fournie dans la résolution est un diviseur de 0.5 (le point central) de sorte que vous puissiez être en mesure de le sélectionner. Si cela n'a aucun sens, ne faites pas attention, cela n'a pas vraiment d'importance, vous pouvez utiliser mon exemple hors de la boîte. Notez que je fournis également une case à cocher pour basculer entre la méthode intersects et la mienne pour vérifier si la souris est sur la courbe. Et lors de l'utilisation de ma nouvelle méthode, je fournis également un curseur pour spécifier la résolution afin que vous puissiez voir les effets de diverses valeurs. Voici les classes.

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.RenderingHints; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.event.MouseMotionListener; 
import java.awt.geom.Point2D; 

import javax.swing.JCheckBox; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JSlider; 

@SuppressWarnings("serial") 
public class CurvePanel extends JPanel implements MouseListener,MouseMotionListener{ 

    Point2D startPoint = new Point2D.Double(50, 50); 
    Point2D middlePoint = new Point2D.Double(100,80); 
    Point2D endPoint = new Point2D.Double(200, 200); 
    Point2D[] points = new Point2D[] {startPoint,middlePoint,endPoint}; 
    QuadCurveWithMiddlePoint curve; 
    private Point2D movingPoint; 
    private boolean dragIt = false; 
    private boolean showControls = false; 
    JCheckBox useNewMethod; 
    JSlider resolution; 

    public CurvePanel() { 
     setPreferredSize(new Dimension(300,300)); 
     addMouseListener(this); 
     addMouseMotionListener(this); 
     curve = new QuadCurveWithMiddlePoint(); 
     useNewMethod = new JCheckBox("Use new \"contains\" method"); 
     resolution = new JSlider(JSlider.HORIZONTAL,1,10,1); 
     useNewMethod.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       resolution.setEnabled(useNewMethod.isSelected()); 
      } 
     }); 
     useNewMethod.setSelected(false); 
     resolution.setEnabled(false); 
     setCurve(); 
    } 

    private void setCurve() { 
     curve.setCurveWithMiddlePoint(startPoint, middlePoint, endPoint); 
    } 

    public static void main(String[] args) { 
     JFrame f = new JFrame("Test"); 
     CurvePanel panel = new CurvePanel(); 
     f.getContentPane().setLayout(new BorderLayout()); 
     f.getContentPane().add(panel.useNewMethod,BorderLayout.NORTH); 
     f.getContentPane().add(panel,BorderLayout.CENTER); 
     f.getContentPane().add(panel.resolution,BorderLayout.SOUTH); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.pack(); 
     f.setVisible(true); 
    } 

    @Override 
    public void mouseClicked(MouseEvent e) {} 
    @Override 
    public void mouseEntered(MouseEvent e) {} 
    @Override 
    public void mouseExited(MouseEvent e) {} 
    @Override 
    public void mousePressed(MouseEvent e) { 
     for (Point2D point : points) { 
      if (e.getPoint().distance(point) <= 2) { 
       movingPoint = point; 
       dragIt = true; 
      } 
     } 
    } 
    @Override 
    public void mouseReleased(MouseEvent e) { 
     dragIt = false; 
    } 
    @Override 
    public void mouseDragged(MouseEvent e) { 
     if (dragIt) { 
      movingPoint.setLocation(e.getPoint()); 
      setCurve(); 
      repaint(); 
     } 
    } 
    @Override 
    public void mouseMoved(MouseEvent e) { 
     if (useNewMethod.isSelected()) 
      showControls = curve.pointOnCurve(e.getPoint(), 2, resolution.getValue()/100.0); 
     else 
      showControls = curve.intersects(e.getX()-2, e.getY()-2, 4, 4); 
     repaint(); 
    } 
    @Override 
    public void paintComponent(Graphics g) { 
     Graphics2D g2 = (Graphics2D)g; 
     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
     g2.setPaint(Color.white); 
     g2.fillRect(0, 0, getWidth(), getHeight()); 
     g2.setPaint(Color.black); 
     g2.draw(curve); 
     if (showControls) 
      for (Point2D point : points) { 
       g2.setPaint(Color.black); 
       g2.drawOval((int)point.getX()-2, (int)point.getY()-2, 4, 4); 
       g2.setPaint(Color.red); 
       g2.fillOval((int)point.getX()-2, (int)point.getY()-2, 4, 4); 
      } 
    } 
} 

Et aussi:

import java.awt.geom.Point2D; 
import java.awt.geom.QuadCurve2D.Double; 

@SuppressWarnings("serial") 
public class QuadCurveWithMiddlePoint extends Double { 

    private Point2D middlePoint = new Point2D.Double(); 
    private final double L = 0.5; 

    public QuadCurveWithMiddlePoint(double x1,double y1, double xm, double ym, double x2, double y2) { 
     super(x1,y1,xm,ym,x2,y2); 
     setMiddlePoint(xm, ym); 
    } 

    public QuadCurveWithMiddlePoint() { 
     this(0,0,0,0,0,0); 
    } 

    public Point2D getMiddlePoint() { 
     calculateMiddlePoint(); 
     return middlePoint; 
    } 

    public void setMiddlePoint(double middleX, double middleY) { 
     setCurve(getP1(), getControlPointByMiddle(middleX, middleY), getP2()); 
     calculateMiddlePoint(); 
    } 

    public void setMiddlePoint(Point2D middle) { 
     setMiddlePoint(middle.getX(),middle.getY()); 
    } 

    private Point2D getControlPointByMiddle(double middleX,double middleY) { 
     double cpx = (middleX-(L*L-2*L+1)*x1-(L*L)*x2)/(-2*L*L+2*L); 
     double cpy = (middleY-(L*L-2*L+1)*y1-(L*L)*y2)/(-2*L*L+2*L); 
     return new Point2D.Double(cpx,cpy); 
    } 

    private Point2D calculatePoint(double position) { 
     if (position<0 || position>1) 
      return null; 
     double middlex = (position*position-2*position+1)*x1+(-2*position*position+2*position)*ctrlx+(position*position)*x2; 
     double middley = (position*position-2*position+1)*y1+(-2*position*position+2*position)*ctrly+(position*position)*y2; 
     return new Point2D.Double(middlex,middley); 
    } 

    public void calculateMiddlePoint() { 
     middlePoint.setLocation(calculatePoint(L)); 
    } 

    public void setCurveWithMiddlePoint(double xx1,double yy1, double xxm, double yym, double xx2, double yy2) { 
     setCurve(xx1, yy1, xxm, yym, xx2, yy2); 
     setMiddlePoint(xxm,yym); 
    } 

    public void setCurveWithMiddlePoint(Point2D start, Point2D middle, Point2D end) { 
     setCurveWithMiddlePoint(start.getX(),start.getY(),middle.getX(),middle.getY(),end.getX(),end.getY()); 
    } 

    public boolean pointOnCurve(Point2D point, double accuracy, double step) { 
     if (accuracy<=0) 
      return false; 
     if (step<=0 || step >1) 
      return false; 
     boolean oncurve = false; 
     double current = 0; 
     while (!oncurve && current <= 1) { 
      if (calculatePoint(current).distance(point)<accuracy) 
       oncurve = true; 
      current += step; 
     } 
     return oncurve; 
    } 

} 

Si vous voulez savoir comment je l'ai fait la classe, faire une recherche pour l'algèbre linéaire de base et la recherche aussi Wikipedia pour Bézier curves.

+0

Nous vous remercions de votre réponse. C'est exactement ce que j'ai fait, mais ce dont j'ai besoin, c'est la même chose mais en plaçant l'Ellipse2D juste au milieu de la courbe. Comment puis-je obtenir ce point? –

+0

S'il vous plaît voir la réponse complète j'ai posté. Il vous fournira une classe ouvrière avec un point de contrôle au milieu de la courbe. –