2009-07-01 4 views
1

Ich bin an einem Projekt beteiligt, in dem wir einen visuellen Editor (in Java geschrieben) machen. Jetzt versuche ich Kurven zu erstellen, die zwei verschiedene Objekte verbinden, die ich male, in einer Klasse, die JPanel erweitert (diese Klasse verwende ich zum Zeichnen, in einem JFrame, überschreibe die Methode paintComponent). Ich bin in Schwierigkeiten, weil ich die Klasse QuadCurve2D verwende, um dies zu machen, aber ich kann es nicht anklickbar machen (ich benutze die Methode enthält, aber es funktioniert nicht jedes Mal), mach es editierbar (zum Beispiel, ein Quadrat in seinem Mittelpunkt, um seine Krümmung zu verändern.Der Punkt, der in der Mitte des QuadCurve2D verwendet wird, wenn der Konstruktor aufgerufen wird, liegt außerhalb der Kurve) oder etwas (Methode, Variable, Iterator usw.), das mir sagen könnte, welche Punkte sich befinden die QuadCurve2D.Wie macht man eine Kurve, um zwei Objekte editierbar zu verbinden?

Nachdem ich nach all dem etwas Zeit gesucht habe, habe ich keine Antwort, also versuche ich es hier zu posten, um eine Lösung zu finden. Gibt es überhaupt eine QuadCurve2D Klasse oder muss ich es mit einer externen Bibliothek versuchen?

Antwort

4

Vor allem Entschuldigung für die lange Antwort. Ich poste jetzt eine vollständige Antwort auf Ihre Frage. Ich klassifiziere die QuadCurve2D.Double-Klasse und mit ein wenig Mathe definieren Sie nun die Kurve mit einem Anfang, Ende und einem Mittelpunkt anstelle eines Kontrollpunkts. Außerdem habe ich eine neue Methode erstellt, die überprüft, ob ein Punkt auf der Kurve ist. Die Schnittpunktmethode prüft, ob sich die konvexe Hülle der Form mit der bereitgestellten Form schneidet, so dass dies im Fall der konkaven Kurve funktional, aber nicht genau ist. Beachten Sie, dass meine Implementierung der Methode, um zu überprüfen, ob ein Punkt auf der Kurve ist, eher rechenintensiv und nicht 100% genau ist, da ich die Kurvenlänge mit einer bestimmten Auflösung überprüfe (0 ist der Anfang der Kurve, 1 ist das Ende) Englisch: www.doc-o-matic.com/webhelp/Tdlg.html Im Beispiel wird also mit einer Auflösung von 0,01 geprüft, dass 100 Checks entlang der Kurve gemacht werden. Stellen Sie daher sicher, dass der angegebene Schritt in der Auflösung ein Teiler von 0,5 (der Mittelpunkt) ist, damit Sie ihn auswählen können. Wenn das keinen Sinn macht passe nicht auf, ist es nicht wirklich wichtig, du kannst mein Beispiel out of the box benutzen. Beachten Sie, dass ich auch ein Kontrollkästchen zur Verfügung stelle, um zwischen der Schnittpunktmethode und meiner eigenen zu wechseln, um zu überprüfen, ob sich die Maus auf der Kurve befindet. Und wenn ich meine neue Methode verwende, stelle ich auch einen Schieberegler zur Verfügung, um die Auflösung anzugeben, so dass Sie die Auswirkungen verschiedener Werte sehen können. Hier sind die Klassen.

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); 
      } 
    } 
} 

Und auch:

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; 
    } 

} 

Wenn Sie wissen möchten, wie ich die Klasse gemacht, eine Suche nach grundlegenden linearen Algebra und auch Wikipedia für Bézier curves suchen.

+0

Vielen Dank für Ihre Antwort. Genau das habe ich getan, aber das, was ich brauche, ist dasselbe, aber ich setze das Ellipse2D nur auf den mittleren Punkt innerhalb der Kurve. Wie kann ich diesen Punkt bekommen? –

+0

Bitte lesen Sie die vollständige Antwort, die ich gepostet habe. Sie erhalten eine Arbeiterklasse mit einem Kontrollpunkt in der Mitte der Kurve. –