Java/2D Graphics GUI/Area Calculation

Материал из Java эксперт
Перейти к: навигация, поиск

A Java implementation of the X11 region

/*
 * (C) 2004 - Geotechnical Software Services
 * 
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
 * MA  02111-1307, USA.
 */
//package no.geosoft.cc.geometry;

/**
 * A <em>Region</em> is simply an area, as the name implies, and is
 * implemented as a so called "y-x-banded" array of rectangles; Each Region
 * is made up of a certain number of rectangles sorted by y coordinate first,
 * and then by x coordinate.
 * <p>
 * Furthermore, the rectangles are banded such that every rectangle with a
 * given upper-left y coordinate (y1) will have the same lower-right y
 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band,
 * it will span the entire vertical distance of the band. This means that
 * some areas that could be merged into a taller rectangle will be represented
 * as several shorter rectangles to account for shorter rectangles to its
 * left or right but within its "vertical scope".
 * <p>
 * An added constraint on the rectangles is that they must cover as much
 * horizontal area as possible. E.g. no two rectangles in a band are allowed
 * to touch. Whenever possible, bands will be merged together to cover a
 * greater vertical distance (and thus reduce the number of rectangles).
 * Two bands can be merged only if the bottom of one touches the top of the
 * other and they have rectangles in the same places (of the same width, of
 * course). This maintains the y-x-banding.
 * <p>
 * Region operations includes add (union), subtract, intersect, and
 * exclusive-or.
 * <p>
 * This class corresponds to Region.c of the X11 distribution and the
 * implemntation is based on it.
 * <p>
 * The <em>Region</em> is essentially equivalent to an AWT <em>Area</em>
 * but with different back-end implementation. Becnhmarking proves it more
 * than 100 times faster than AWT Area for binary CAG operations,
 * <p>
 * Thanks to:
 * <ul>
 * <li>Bryan Lin @ China Minmetals Corporation - for identifying
 *     synchronization errors when run on the MS WindowsXP platform.
 * <li>Maxim Butov @ Belhard - for identifying error in the
 *     isInside(Rect) method.
 * </ul>
 *
 * @author 
 */   
//public 
class Rect
{
  public int  x;
  public int  y;
  public int  height;
  public int  width;

  
  /**
   * Create a rectangle.
   * 
   * @param x       X coordinate of upper left corner.
   * @param y       Y coordinate of upper left corner.
   * @param width   Width of rectangle.
   * @param height  Height of rectangle.
   */
  public Rect (int x, int y, int width, int height)
  {
    set (x, y, width, height);
  }

  /**
   * Create a default rectangle.
   */
  public Rect()
  {
    this (0, 0, 0, 0);
  }
  
  
  /**
   * Create a rectangle as a copy of the specified rectangle.
   * 
   * @param rectangle
   */
  public Rect (Rect rectangle)
  {
    this (rectangle.x, rectangle.y, rectangle.width, rectangle.height);
  }
  
  /**
   * Create a rectnagle based on specified box.
   * 
   * @param box  Box to create rectangle from.
   */
  public Rect (Box box)
  {
    this (box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1);
  }
  
  /**
   * Copy the specified rectangle.
   * 
   * @param rectangle  Rectangle to copy.
   */
  public void copy (Rect rectangle)
  {
    this.x      = rectangle.x;
    this.y      = rectangle.y;
    this.width  = rectangle.width;
    this.height = rectangle.height;
  }
  
  /**
   * Clone this rectangle
   * 
   * @return  Clone of this rectangle.
   */
  public Object clone()
  {
    return new Rect (x, y, width, height);
  }

  
  /**
   * Check if this rectangle equals the specified object.
   * 
   * @param object  Object to chack.
   * @return        True if the two equals, false otherwise.
   */
  public boolean equals (Object object)
  {
    Rect rectangle = (Rect) object;
    return this.x      == rectangle.x     &&
           this.y      == rectangle.y     &&
           this.width  == rectangle.width &&
           this.height == rectangle.height;
  }
  

  /**
   * Return true if this rectangle is empty.
   * 
   * @return  True if this rectangle is empty, false otherwise.
   */
  public boolean isEmpty()
  {
    return width <= 0 || height <= 0;
  }
  
  /**
   * Expand this rectangle the specified amount in each direction.
   * 
   * @param dx  Amount to expand to left and right.
   * @param dy  Amount to expand on top and botton.
   */
  public void expand (int dx, int dy)
  {
    x      -= dx;
    y      -= dy;
    width  += dx + dx;
    height += dy + dy;
  }
  
  
  /**
   * Set the parameters for this rectangle.
   * 
   * @param x       X coordinate of upper left corner.
   * @param y       Y coordinate of upper left corner.
   * @param width   Width of rectangle.
   * @param height  Height of rectangle.
   */
  public void set (int x, int y, int width, int height)
  {
    this.x      = x;
    this.y      = y;
    this.width  = width;
    this.height = height;
  }
  
  /**
   * Set this rectangle as extent of specified polyline.
   * 
   * @param xArray  X coordinates of polyline.
   * @param yArray  Y coordinates of polyline.
   */
  public void set (int xArray[], int yArray[])
  {
    int  minX = Integer.MAX_VALUE;
    int  maxX = Integer.MIN_VALUE;
    
    int  minY = Integer.MAX_VALUE;
    int  maxY = Integer.MIN_VALUE;
    for (int i = 0; i < xArray.length; i++) {
      if (xArray[i] < minX) minX = xArray[i];
      if (xArray[i] > maxX) maxX = xArray[i];
      if (yArray[i] < minY) minY = yArray[i];
      if (yArray[i] > maxY) maxY = yArray[i];
    }
    x = minX;
    y = minY;
    width  = maxX - minX + 1;
    height = maxY - minY + 1;    
  }

  
  /**
   * Return X coordinate of center of this rectangle.
   * 
   * @return  X coordinate of center of this rectangle.
   */
  public int getCenterX()
  {
    return x + (int) Math.floor (width / 2.0);
  }
  
  
  /**
   * Return Y coordinate of center of this rectangle.
   * 
   * @return  Y coordinate of center of this rectangle.
   */
  public int getCenterY()
  {
    return y + (int) Math.floor (height / 2.0);
  }
  
  
  /**
   * Return a string representation of this rectangle.
   * 
   * @return  String representation of this rectangle.
   */
  public String toString()
  {
    return new String ("Rectangle: x= " + x + " y=" + y +
                       " width=" + width + " height=" + height);
  }
  
}



Area Add

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import javax.swing.JApplet;
import javax.swing.JFrame;
public class AreaAdd extends JApplet {
    Ellipse2D.Double circle = new Ellipse2D.Double();
    Ellipse2D.Double oval = new Ellipse2D.Double();
    Area circ = new Area(circle); 
    Area ov = new Area(oval);
    public void init() {
        setBackground(Color.white);
    }
    public void paint (Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        double halfWdith = getSize().width/2;
        double halfHeight = getSize().height/2;
        circle.setFrame(halfWdith-25, halfHeight, 50.0, 50.0);
        oval.setFrame(halfWdith-19, halfHeight-20, 40.0, 70.0);
        circ = new Area(circle);
        ov = new Area(oval);
        circ.add(ov);
        g2.fill(circ);
    }
    public static void main(String s[]) {
        JFrame f = new JFrame("Pear");
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {System.exit(0);}
        });
        JApplet applet = new AreaAdd();
        f.getContentPane().add("Center", applet);
        applet.init();
        f.pack();
        f.setSize(new Dimension(150,200));
        f.show();
    }
}



Area Calculation: Add, Subtract, XOR

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import javax.swing.ButtonGroup;
import javax.swing.JApplet;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.TitledBorder;
public class TArea extends JApplet implements ActionListener {
  DrawingCanvas canvas;
  JRadioButton addButton, subtractButton, intersectButton, exclusiveORButton,
      resetButton;
  public static void main(String[] a) {
    JFrame f = new JFrame();
    TArea area = new TArea();
    area.init();
    f.getContentPane().add(area);
    f.setDefaultCloseOperation(1);
    f.setSize(650, 450);
    f.setVisible(true);
  }
  public void init() {
    Container container = getContentPane();
    JPanel panel = new JPanel();
    panel.setLayout(new GridLayout(2, 2));
    resetButton = new JRadioButton("Reset", true);
    addButton = new JRadioButton("Add");
    subtractButton = new JRadioButton("Subtract");
    intersectButton = new JRadioButton("Intersect");
    exclusiveORButton = new JRadioButton("ExclusiveOR");
    ButtonGroup group = new ButtonGroup();
    group.add(resetButton);
    group.add(addButton);
    group.add(subtractButton);
    group.add(intersectButton);
    group.add(exclusiveORButton);
    group.add(resetButton);
    resetButton.addActionListener(this);
    addButton.addActionListener(this);
    subtractButton.addActionListener(this);
    intersectButton.addActionListener(this);
    exclusiveORButton.addActionListener(this);
    panel.add(addButton);
    panel.add(subtractButton);
    panel.add(intersectButton);
    panel.add(exclusiveORButton);
    container.add(panel, BorderLayout.NORTH);
    container.add(resetButton, BorderLayout.SOUTH);
    canvas = new DrawingCanvas();
    container.add(canvas);
  }
  class DrawingCanvas extends Canvas {
    GeneralPath gp;
    Ellipse2D ellipse;
    Area area1, area2;
    boolean drawFlag = true;
    boolean fillFlag = false;
    public DrawingCanvas() {
      setBackground(Color.white);
      setSize(350, 250);
      int w = getWidth();
      int h = getHeight();
      gp = new GeneralPath();
      gp.moveTo(w / 8, h / 2);
      gp.lineTo(w / 2, h / 4);
      gp.lineTo(7 * w / 8, h / 2);
      gp.lineTo(w / 2, 3 * h / 4);
      gp.closePath();
      area1 = new Area(gp);
      ellipse = new Ellipse2D.Double(w / 4, h / 4, w / 2, h / 2);
      area2 = new Area(ellipse); // Ellipse area object
    }
    public void paint(Graphics g) {
      Graphics2D g2D = (Graphics2D) g;
      g2D.setStroke(new BasicStroke(2.0f));
      if (drawFlag) {
        g2D.draw(area1);
        g2D.draw(area2);
      }
      if (fillFlag)
        g2D.fill(area1);
    }
  }
  public void actionPerformed(ActionEvent e) {
    JRadioButton temp = (JRadioButton) e.getSource();
    if (temp.equals(addButton)) {
      canvas.area1 = new Area(canvas.gp);
      canvas.area1.add(canvas.area2);
      canvas.drawFlag = false;
      canvas.fillFlag = true;
      canvas.repaint();
    } else if (temp.equals(subtractButton)) {
      canvas.area1 = new Area(canvas.gp);
      canvas.area1.subtract(canvas.area2);
      canvas.drawFlag = false;
      canvas.fillFlag = true;
      canvas.repaint();
    } else if (temp.equals(intersectButton)) {
      canvas.area1 = new Area(canvas.gp);
      canvas.area1.intersect(canvas.area2);
      canvas.drawFlag = false;
      canvas.fillFlag = true;
      canvas.repaint();
    } else if (temp.equals(exclusiveORButton)) {
      canvas.area1 = new Area(canvas.gp);
      canvas.area1.exclusiveOr(canvas.area2);
      canvas.drawFlag = false;
      canvas.fillFlag = true;
      canvas.repaint();
    } else if (temp.equals(resetButton)) {
      if (canvas.drawFlag == false) {
        canvas.area1 = new Area(canvas.gp);
        canvas.drawFlag = true;
        canvas.fillFlag = false;
        canvas.repaint();
      }
    }
  }
}



Area Intersect

import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
public class AreaIntersect extends JApplet {
    Ellipse2D.Double leaf = new Ellipse2D.Double(); 
    Ellipse2D.Double stem = new Ellipse2D.Double();
    Area leaf1 = new Area(leaf); 
    Area leaf2 = new Area(leaf); 
    public void init() {
        setBackground(Color.white);
    }
    public void paint (Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        double halfWdith = getSize().width/2;
        double halfHeight = getSize().height/2;
        g2.setColor(Color.green);
        leaf.setFrame(halfWdith-16, halfHeight-29, 15.0, 15.0);
        leaf1 = new Area(leaf);
        leaf.setFrame(halfWdith-14, halfHeight-47, 30.0, 30.0);
        leaf2 = new Area(leaf); 
        leaf1.intersect(leaf2);   
        g2.fill(leaf1);   
    }
    public static void main(String s[]) {
        JFrame f = new JFrame("Pear");
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {System.exit(0);}
        });
        JApplet applet = new AreaIntersect();
        f.getContentPane().add("Center", applet);
        applet.init();
        f.pack();
        f.setSize(new Dimension(150,200));
        f.show();
    }
}



Area Subtract

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import javax.swing.JApplet;
import javax.swing.JFrame;
public class AreaSubtract extends JApplet {
    Ellipse2D.Double stem = new Ellipse2D.Double();
    Area st1 = new Area(stem); 
    Area st2 = new Area(stem);
    public void init() {
        setBackground(Color.white);
    }
    public void paint (Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        double halfWdith = getSize().width/2;
        double halfHeight = getSize().height/2;
        stem.setFrame(halfWdith, halfHeight-42, 40.0, 40.0);
        st1 = new Area(stem);
        stem.setFrame(halfWdith+3, halfHeight-47, 50.0, 50.0);
        st2 = new Area(stem);
        st1.subtract(st2);
        g2.fill(st1);
        g2.setColor(Color.yellow);
    }
    public static void main(String s[]) {
        JFrame f = new JFrame("Pear");
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {System.exit(0);}
        });
        JApplet applet = new AreaSubtract();
        f.getContentPane().add("Center", applet);
        applet.init();
        f.pack();
        f.setSize(new Dimension(150,200));
        f.show();
    }
}