⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 curveapplet.java

📁 Beginning Java 2, SDK 1.4 Edition Exercise Code samples for this book
💻 JAVA
字号:
// Chapter 18 Exercise 5 
/*
  I anticipated that exercises 5 & 6 would only appeal to the mathematically-inclined
  graphics enthusiast because there is significant maths involved in their solution.

  The exercises essentially involve finding the right algorithm for joining cubic curve
  segments. This bnoils down to determining the control points, given the set of points
  that define the curve. The programming is easy (relatively!) once you have the method.
  
  This applet creates a smooth curve through a set of points where the curve
  segment between each pair of points is a cubic Bezier curve( CubicCurve2D.Double).
  Once the curve is complete, you can modify the curve by dragging either a control
  point or a defining point. Dragging a control point will result in the segments
  still being joind but the tangent at the intersection will be discontinuous. This
  is an advantage if you want a curve with corners in it!
  
  The method used here for determining the control points is as described in the book
  'Graphics Gems' edited by Andrew S. Glassner(ISBN: 0-12-286166-3) on P579-583 in the
  section entitled Explicit Cubic Spline Interpolation Formulas.
  
  As explained in 'Graphics Gems', the vectors determining the control points are only
  affected by a limited number of points either side of the position of the point to
  which the control applies. The coefficients -  the array, a, in the applet - can be 
  precalculated and stored as constants, and this would make the code faster. However,
  I chose to calculate them each time to show how this can be done. 
*/
import javax.swing.JApplet;
import javax.swing.JComponent;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.FlowLayout;
import java.awt.BorderLayout;
import java.awt.Point;

import java.awt.geom.Point2D;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;

import javax.swing.event.MouseInputAdapter;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.BorderFactory;
import javax.swing.border.EtchedBorder;

import java.awt.event.MouseEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import java.util.Vector;

public class CurveApplet extends JApplet {
  // Initialize the applet
  public void init() {
    pane = new CurvePane();                       // Create pane containing curves
    Container content = getContentPane();         // Get the content pane

    // Add the pane displaying the curves to the content pane for the applet
    content.add(pane);                  // BorderLayout.CENTER is default position
    
    MouseHandler handler = new MouseHandler();    // Create the listener
    pane.addMouseListener(handler);               // Monitor mouse button presses
    pane.addMouseMotionListener(handler);         // as well as movement

    // Create the panel to hold the buttons
    JPanel buttonPane = new JPanel();
    buttonPane.setLayout(new FlowLayout());
    buttonPane.setBackground(Color.DARK_GRAY);
    buttonPane.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED,
                                                      Color.BLUE,
                                                      Color.CYAN));
    
    // Create and add the button to clear the display
    JButton button = new JButton("Clear Pane");
    button.setBorder(BorderFactory.createRaisedBevelBorder());
    button.addActionListener(new ActionListener(){    // Action listener for the button
      public void actionPerformed(ActionEvent e) {
        clearPane();
      }
    });
    buttonPane.add(button);
    
    // Create and add the button to end the definition of the curve
    button  = new JButton("End Curve");
    button.setBorder(BorderFactory.createRaisedBevelBorder());
    button.addActionListener(new ActionListener(){   // Action listener for the button
      public void actionPerformed(ActionEvent e) {
      curveComplete = true;
      pane.repaint();
      }
    });
    buttonPane.add(button);
    
    content.add(buttonPane, BorderLayout.SOUTH);
    
  }

  // Method to clear the screen ready for a new set of points
  private void clearPane() {
    pointMarkers = new Vector();   // Remove the point markers
    curveSegments = null;          // and the array of curve segments
    curveComplete = false;         // Reset curve complete flag
    repaint();
  }

  private void createCurves() {
    // Create the curves
    int n = pointMarkers.size();
    Point2D.Double[] points = new Point2D.Double[n];

    // Extract the points from the vector of Marker objects
    for(int i = 0 ; i<n ; i++)
      points[i] = ((Marker)(pointMarkers.elementAt(i))).getCenter();

    int m =  (n - 2);             // Number of coeffients for calculating control vectors     
    double[] f = new double[m+1]; // Array used to calculate coefficients, a
    f[0] = 1.0;
    f[1] = -4 + n%2 ;            // -3 when n is odd and -4 when n is even
    for(int j = 2 ; j<f.length ; j++)
      f[j] = -4.0*f[j-1] - f[j-2];

    // Calculate the coefficients. This array could be limited to 5 elements
    // since the values of elements of a beyond a[4] are very small.
    double[] a = new double[m];
    double factor = 1.0/f[f.length-1];
    for(int j = 0 ; j<a.length ; j++)
      a[j] = -f[m-j-1]*factor;
       
    // Calculate control vectors, one for each point. The control points
    // for the curve are calculated by adding and/or subtracting the control
    // vector from the corresponding curve point.
    controlVec = new Point2D.Double[n];
    controlVec[0] = new Point2D.Double(0.0, 0.0);
    controlVec[n-1] = controlVec[0];
    for(int i = 1 ; i<n-1; i++) {
      double x = 0.0;
      double y = 0.0;
      int n1 = 0;
      int n2 = 0;
      for(int k = 0 ; k<a.length ; k++) {
        n1 = (i+k+1)%n;
        n2 = Math.abs(i-k-1);
        x += a[k]*(points[n1].x - points[n2].x); 
        y += a[k]*(points[n1].y - points[n2].y); 
      }
      controlVec[i] = new Point2D.Double(x, y);
    }

    // Define the curve segments using the defining points and control points
    // created using the vectors in the controlVec array
    curveSegments = new CubicCurve2D.Double[n-1];
    controlMarkers = new Marker[2*curveSegments.length];
    Point2D.Double controlPt1 = null;
    Point2D.Double controlPt2 = null;
    int j = 0;
    for(int i = 0 ; i<curveSegments.length; i++) {
      curveSegments[i] = new CubicCurve2D.Double();
      controlPt1 = new Point2D.Double(points[i].x+controlVec[i].x,
                                      points[i].y+controlVec[i].y);
      controlPt2 = new Point2D.Double(points[i+1].x-controlVec[i+1].x,
                                      points[i+1].y-controlVec[i+1].y);
      curveSegments[i].setCurve(points[i], controlPt1, controlPt2, points[i+1]);
      controlMarkers[j++] = new Marker(controlPt1, Color.RED);
      controlMarkers[j++] = new Marker(controlPt2, Color.RED);
    }
  }

  // Class defining a pane on which to draw
  class CurvePane extends JComponent {
    // Constructor
    public void paint(Graphics g) {
      Graphics2D g2D = (Graphics2D)g;                   // Get a 2D device context
 
      // If there are any curves, draw them in green     
      if(curveSegments != null) {
        g2D.setColor(Color.GREEN);
        for (int i = 0; i<curveSegments.length ; i++)
          g2D.draw(curveSegments[i]);
      }

      // Draw the point markers
      for(int i = 0 ; i<pointMarkers.size() ; i++) {
        ((Marker)(pointMarkers.elementAt(i))).draw(g2D);
      }
    
      // If the curve is finished, draw the control markers and tangents
      if(curveComplete) {
        for(int i = 0 ; i<controlMarkers.length ; i++) 
          controlMarkers[i].draw(g2D);
  
        // Draw the tangent lines
        Line2D.Double tangent = null;
        g2D.setColor(Color.RED);
        for(int i = 0 ; i<curveSegments.length ; i++) {
          tangent = new Line2D.Double(curveSegments[i].getP1(),curveSegments[i].getCtrlP1());
          g2D.draw(tangent);
          tangent = new Line2D.Double(curveSegments[i].getP2(),curveSegments[i].getCtrlP2());
          g2D.draw(tangent);
        }
      }
    }
  }
  
  // Inner class defining a point marker.
  class Marker {
    public Marker(Point2D.Double point, Color color)  {
      center = point;                   // Save point as circle center
      this.color = color;

      // Create circle around control point
      circle = new Ellipse2D.Double(center.x-radius, center.y-radius,
                                    2.0*radius, 2.0*radius); 
    }

      // Draw the marker
      public void draw(Graphics2D g2D) {
        g2D.setColor(color);
        g2D.draw(circle);
      }

     // Get center of marker - the control point position
      Point2D.Double getCenter() {
        return center;
    }

    // Test if a point x,y is inside the marker
    public boolean contains(double x, double y) {
      return circle.contains(x,y); 
    }

    // Sets a new control point location
    public void setLocation(double x, double y) {
      center.x = x;                      // Update control point
      center.y = y;                      // coordinates
      circle.x = x-radius;               // Change circle position
      circle.y = y-radius;               // correspondingly
    }

    Ellipse2D.Double circle;               // Circle around control point
    Point2D.Double center;                 // Circle center - the control point
    Color color;                           // Color of this marker, controls red, curve pts blue
    static final double radius = 3;        // Radius of circle
  }

  class MouseHandler extends MouseInputAdapter {
    // In this method we are defining a new curve point when curveComplete is false
    // or potentially selecting a control or defining point when curveComplete is true.
    public void mousePressed(MouseEvent e) {
      Point p = e.getPoint();
      if(curveComplete) {
        // Check if the cursor is inside any marker
        Marker marker = null;
        // First check the point markers
        for(int i = 0 ; i<pointMarkers.size() ;i++) {
          marker = (Marker)(pointMarkers.elementAt(i));
          if(marker.contains(p.x,p.y)) {
            selected = marker;
            selectedIndex = i;
            return;
          }
        }
        // It was not a point marker so check the control markers
        for(int i = 0 ; i<controlMarkers.length ;i++) {
          if(controlMarkers[i].contains(p.x,p.y)) {
            selected = controlMarkers[i];
            selectedIndex = i;
            return;
          }
        }
      } else {
        // We are adding a new defining point
        pointMarkers.add(new Marker(new Point2D.Double(p.x,p.y), Color.BLUE)); 
        if(pointMarkers.size()>3)
          createCurves();
        pane.repaint();
      }     
    }

    public void mouseReleased(MouseEvent e) {
      selected = null;                             // Deselect any selected marker
    }
    
    // If selected is not null, this method moves the selected marker
    // and modifies the curve segments that are affected.
    public void mouseDragged(MouseEvent e) {
      if(selected != null) {                       // If a marker is selected
        // Set the marker to current cursor position
        selected.setLocation(e.getX(), e.getY());
        if(selected == controlMarkers[selectedIndex]){
          // It must be a control marker so update it in the curve
          if(selectedIndex%2 == 0) 
            // It is the 1st control point that was changed
            curveSegments[selectedIndex/2].setCurve(curveSegments[selectedIndex/2].getP1(),
                                             selected.getCenter(),
                                             curveSegments[selectedIndex/2].getCtrlP2(),
                                             curveSegments[selectedIndex/2].getP2());
          else
            // It is the 2nd control point that was changed
            curveSegments[selectedIndex/2].setCurve(curveSegments[selectedIndex/2].getP1(),
                                             curveSegments[selectedIndex/2].getCtrlP1(),
                                             selected.getCenter(),
                                             curveSegments[selectedIndex/2].getP2());
        }  else {
          // It must be a point marker. If the index is other than the first or
          // the last, two curve segments have to be updated
          if(selectedIndex>0)
            // Change the segment where this is the second point
            curveSegments[selectedIndex-1].setCurve(curveSegments[selectedIndex-1].getP1(),
                                             curveSegments[selectedIndex-1].getCtrlP1(),
                                             curveSegments[selectedIndex-1].getCtrlP2(),
                                             selected.getCenter());
          if(selectedIndex<curveSegments.length)
            // Change the segment where this is the first point
            curveSegments[selectedIndex].setCurve(selected.getCenter(),
                                             curveSegments[selectedIndex].getCtrlP1(),
                                             curveSegments[selectedIndex].getCtrlP2(),
                                             curveSegments[selectedIndex].getP2());
        }
        pane.repaint();                             // Redraw pane contents
      }
    }

    Marker selected = null;                 // Stores reference to selected marker
    int selectedIndex = 0;                  // Index position of selected Marker
  }

  Vector pointMarkers = new Vector();           // Vector of Marker objects
  CubicCurve2D.Double[] curveSegments = null;   // Array of cubic curve segments
  Point2D.Double[] controlVec = null;           // Tngent vectors for control points  
  Marker[] controlMarkers = null;               // Array of control point markers
  CurvePane pane = new CurvePane();             // Pane to contain curves
  boolean curveComplete = false;                // Indicator for complete curve
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -