Java/2D Graphics GUI/Area Calculation
Содержание
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();
}
}