3b9255eb66
Reviewed-by: honkar, prr
393 lines
15 KiB
Java
393 lines
15 KiB
Java
/*
|
|
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 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 General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
import java.awt.BorderLayout;
|
|
import java.awt.Canvas;
|
|
import java.awt.Choice;
|
|
import java.awt.Color;
|
|
import java.awt.Dimension;
|
|
import java.awt.EventQueue;
|
|
import java.awt.Frame;
|
|
import java.awt.Graphics;
|
|
import java.awt.Graphics2D;
|
|
import java.awt.Panel;
|
|
import java.awt.Polygon;
|
|
import java.awt.Shape;
|
|
import java.awt.geom.AffineTransform;
|
|
import java.awt.geom.Area;
|
|
import java.awt.geom.Ellipse2D;
|
|
import java.awt.geom.GeneralPath;
|
|
import java.awt.geom.Point2D;
|
|
|
|
/*
|
|
* @test
|
|
* @bug 4210936 4214524
|
|
* @summary Tests the results of the hit test methods on 3 different
|
|
* Shape objects - Polygon, Area, and GeneralPath. Both an
|
|
* automatic test for constraint compliance and a manual
|
|
* test for correctness are included in this one class.
|
|
* @library /java/awt/regtesthelpers
|
|
* @build PassFailJFrame
|
|
* @run main PathHitTest
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @bug 4210936 4214524
|
|
* @summary Tests the results of the hit test methods on 3 different
|
|
* Shape objects - Polygon, Area, and GeneralPath. Both an
|
|
* automatic test for constraint compliance and a manual
|
|
* test for correctness are included in this one class.
|
|
* @library /java/awt/regtesthelpers
|
|
* @build PassFailJFrame
|
|
* @run main/manual PathHitTest manual
|
|
*/
|
|
|
|
public class PathHitTest {
|
|
|
|
public static final int BOXSIZE = 5;
|
|
public static final int BOXCENTER = 2;
|
|
public static final int TESTSIZE = 400;
|
|
public static final int NUMTESTS = (TESTSIZE + BOXSIZE - 1) / BOXSIZE;
|
|
|
|
public static Shape[] testShapes = new Shape[5];
|
|
public static String[] testNames = {
|
|
"Polygon",
|
|
"EvenOdd GeneralPath",
|
|
"NonZero GeneralPath",
|
|
"Area from EO GeneralPath",
|
|
"Area from NZ GeneralPath",
|
|
};
|
|
|
|
static {
|
|
GeneralPath gpeo = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
|
Ellipse2D ell = new Ellipse2D.Float();
|
|
Point2D center = new Point2D.Float();
|
|
AffineTransform at = new AffineTransform();
|
|
for (int i = 0; i < 360; i += 30) {
|
|
center.setLocation(100, 0);
|
|
at.setToTranslation(200, 200);
|
|
at.rotate(i * Math.PI / 180);
|
|
at.transform(center, center);
|
|
ell.setFrame(center.getX() - 50, center.getY() - 50, 100, 100);
|
|
gpeo.append(ell, false);
|
|
}
|
|
GeneralPath side = new GeneralPath();
|
|
side.moveTo(0, 0);
|
|
side.lineTo(15, 10);
|
|
side.lineTo(30, 0);
|
|
side.lineTo(45, -10);
|
|
side.lineTo(60, 0);
|
|
append4sides(gpeo, side, 20, 20);
|
|
side.reset();
|
|
side.moveTo(0, 0);
|
|
side.quadTo(15, 10, 30, 0);
|
|
side.quadTo(45, -10, 60, 0);
|
|
append4sides(gpeo, side, 320, 20);
|
|
side.reset();
|
|
side.moveTo(0, 0);
|
|
side.curveTo(15, 10, 45, -10, 60, 0);
|
|
append4sides(gpeo, side, 20, 320);
|
|
|
|
GeneralPath gpnz = new GeneralPath(GeneralPath.WIND_NON_ZERO);
|
|
gpnz.append(gpeo, false);
|
|
Polygon p = new Polygon();
|
|
p.addPoint( 50, 50);
|
|
p.addPoint( 60, 350);
|
|
p.addPoint(250, 340);
|
|
p.addPoint(260, 150);
|
|
p.addPoint(140, 140);
|
|
p.addPoint(150, 260);
|
|
p.addPoint(340, 250);
|
|
p.addPoint(350, 60);
|
|
testShapes[0] = p;
|
|
testShapes[1] = gpeo;
|
|
testShapes[2] = gpnz;
|
|
testShapes[3] = new Area(gpeo);
|
|
testShapes[3].getPathIterator(null);
|
|
testShapes[4] = new Area(gpnz);
|
|
testShapes[4].getPathIterator(null);
|
|
}
|
|
|
|
private static void append4sides(GeneralPath path, GeneralPath side,
|
|
double xoff, double yoff) {
|
|
AffineTransform at = new AffineTransform();
|
|
at.setToTranslation(xoff, yoff);
|
|
for (int i = 0; i < 4; i++) {
|
|
path.append(side.getPathIterator(at), i != 0);
|
|
at.rotate(Math.toRadians(90), 30, 30);
|
|
}
|
|
}
|
|
|
|
public static void main(String[] argv) throws Exception {
|
|
if (argv.length > 0 && argv[0].equals("manual")) {
|
|
PathHitTestManual.doManual();
|
|
} else {
|
|
int totalerrs = 0;
|
|
for (int i = 0; i < testShapes.length; i++) {
|
|
totalerrs += testshape(testShapes[i], testNames[i]);
|
|
}
|
|
if (totalerrs != 0) {
|
|
throw new RuntimeException(totalerrs +
|
|
" constraint conditions violated!");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static int testshape(Shape s, String name) {
|
|
int numerrs = 0;
|
|
long start = System.currentTimeMillis();
|
|
for (int y = 0; y < TESTSIZE; y += BOXSIZE) {
|
|
for (int x = 0; x < TESTSIZE; x += BOXSIZE) {
|
|
boolean rectintersects = s.intersects(x, y, BOXSIZE, BOXSIZE);
|
|
boolean rectcontains = s.contains(x, y, BOXSIZE, BOXSIZE);
|
|
boolean pointcontains = s.contains(x + BOXCENTER, y + BOXCENTER);
|
|
if (rectcontains && !rectintersects) {
|
|
System.err.println("rect is contained " +
|
|
"but does not intersect!");
|
|
numerrs++;
|
|
}
|
|
if (rectcontains && !pointcontains) {
|
|
System.err.println("rect is contained " +
|
|
"but center is not contained!");
|
|
numerrs++;
|
|
}
|
|
if (pointcontains && !rectintersects) {
|
|
System.err.println("center is contained " +
|
|
"but rect does not intersect!");
|
|
numerrs++;
|
|
}
|
|
}
|
|
}
|
|
long end = System.currentTimeMillis();
|
|
System.out.println(name + " completed in " +
|
|
(end - start) + "ms with " +
|
|
numerrs + " errors");
|
|
return numerrs;
|
|
}
|
|
|
|
static class PathHitTestManual extends Panel {
|
|
private static final String INSTRUCTIONS = """
|
|
This test displays the results of hit testing 5 different Shape
|
|
objects one at a time.
|
|
|
|
You can switch between shapes using the Choice component located
|
|
at the bottom of the window.
|
|
|
|
Each square in the test represents the
|
|
return values of the hit testing operators for that square region:
|
|
|
|
yellow - not yet tested
|
|
translucent blue overlay - the shape being tested
|
|
|
|
black - all outside
|
|
dark gray - rectangle intersects shape
|
|
light gray - rectangle intersects and center point is inside shape
|
|
white - rectangle is entirely contained in shape
|
|
red - some constraint was violated, including:
|
|
rectangle is contained, but center point is not
|
|
rectangle is contained, but rectangle.intersects is false
|
|
centerpoint is contained, but rectangle.intersects is false
|
|
|
|
Visually inspect the results to see if they match the above table.
|
|
Note that it is not a violation for rectangles that are entirely
|
|
inside the path to be light gray instead of white since sometimes
|
|
the path is complex enough to make an exact determination expensive.
|
|
You might see this on the GeneralPath NonZero example where the
|
|
circles that make up the path cross over the interior of the shape
|
|
and cause the hit testing methods to guess that the rectangle is
|
|
not guaranteed to be contained within the shape.
|
|
""";
|
|
|
|
PathHitTestCanvas phtc;
|
|
|
|
public void init() {
|
|
setLayout(new BorderLayout());
|
|
phtc = new PathHitTestCanvas();
|
|
add("Center", phtc);
|
|
final Choice ch = new Choice();
|
|
for (int i = 0; i < PathHitTest.testNames.length; i++) {
|
|
ch.add(PathHitTest.testNames[i]);
|
|
}
|
|
ch.addItemListener(e -> phtc.setShape(ch.getSelectedIndex()));
|
|
ch.select(0);
|
|
phtc.setShape(0);
|
|
add("South", ch);
|
|
}
|
|
|
|
public void start() {
|
|
phtc.start();
|
|
}
|
|
|
|
public void stop() {
|
|
phtc.stop();
|
|
}
|
|
|
|
public static class PathHitTestCanvas extends Canvas implements Runnable {
|
|
public static final Color[] colors = {
|
|
/* contains? point in? intersects? */
|
|
Color.black, /* NO NO NO */
|
|
Color.darkGray, /* NO NO YES */
|
|
Color.red, /* NO YES NO */
|
|
Color.lightGray, /* NO YES YES */
|
|
Color.red, /* YES NO NO */
|
|
Color.red, /* YES NO YES */
|
|
Color.red, /* YES YES NO */
|
|
Color.white, /* YES YES YES */
|
|
Color.yellow, /* used for untested points */
|
|
};
|
|
|
|
public Dimension getPreferredSize() {
|
|
return new Dimension(TESTSIZE, TESTSIZE);
|
|
}
|
|
|
|
public synchronized void start() {
|
|
if (!testdone) {
|
|
renderer = new Thread(this);
|
|
renderer.setPriority(Thread.MIN_PRIORITY);
|
|
renderer.start();
|
|
}
|
|
}
|
|
|
|
public synchronized void stop() {
|
|
renderer = null;
|
|
}
|
|
|
|
private Thread renderer;
|
|
private int shapeIndex = 0;
|
|
private byte[] indices = new byte[NUMTESTS * NUMTESTS];
|
|
boolean testdone = false;
|
|
|
|
private synchronized void setShape(int index) {
|
|
shapeIndex = index;
|
|
testdone = false;
|
|
start();
|
|
}
|
|
|
|
public void run() {
|
|
Thread me = Thread.currentThread();
|
|
Graphics2D g2d = (Graphics2D) getGraphics();
|
|
byte[] indices;
|
|
Shape s = testShapes[shapeIndex];
|
|
synchronized (this) {
|
|
if (renderer != me) {
|
|
return;
|
|
}
|
|
this.indices = new byte[NUMTESTS * NUMTESTS];
|
|
java.util.Arrays.fill(this.indices, (byte) 8);
|
|
indices = this.indices;
|
|
}
|
|
|
|
System.err.printf("%s %s\n", g2d, Color.yellow);
|
|
g2d.setColor(Color.yellow);
|
|
g2d.fillRect(0, 0, TESTSIZE, TESTSIZE);
|
|
int numtests = 0;
|
|
long start = System.currentTimeMillis();
|
|
for (int y = 0; renderer == me && y < TESTSIZE; y += BOXSIZE) {
|
|
for (int x = 0; renderer == me && x < TESTSIZE; x += BOXSIZE) {
|
|
byte index = 0;
|
|
if (s.intersects(x, y, BOXSIZE, BOXSIZE)) {
|
|
index += 1;
|
|
}
|
|
if (s.contains(x + BOXCENTER, y + BOXCENTER)) {
|
|
index += 2;
|
|
}
|
|
if (s.contains(x, y, BOXSIZE, BOXSIZE)) {
|
|
index += 4;
|
|
}
|
|
numtests++;
|
|
int i = (y / BOXSIZE) * NUMTESTS + (x / BOXSIZE);
|
|
indices[i] = index;
|
|
g2d.setColor(colors[index]);
|
|
g2d.fillRect(x, y, BOXSIZE, BOXSIZE);
|
|
}
|
|
}
|
|
synchronized (this) {
|
|
if (renderer != me) {
|
|
return;
|
|
}
|
|
g2d.setColor(new Color(0, 0, 1, .2f));
|
|
g2d.fill(s);
|
|
testdone = true;
|
|
long end = System.currentTimeMillis();
|
|
System.out.println(numtests + " tests took " + (end - start) + "ms");
|
|
}
|
|
}
|
|
|
|
public void paint(Graphics g) {
|
|
g.setColor(Color.yellow);
|
|
g.fillRect(0, 0, TESTSIZE, TESTSIZE);
|
|
byte[] indices = this.indices;
|
|
if (indices != null) {
|
|
for (int y = 0; y < TESTSIZE; y += BOXSIZE) {
|
|
for (int x = 0; x < TESTSIZE; x += BOXSIZE) {
|
|
int i = (y / BOXSIZE) * NUMTESTS + (x / BOXSIZE);
|
|
g.setColor(colors[indices[i]]);
|
|
g.fillRect(x, y, BOXSIZE, BOXSIZE);
|
|
}
|
|
}
|
|
}
|
|
Graphics2D g2d = (Graphics2D) g;
|
|
g2d.setColor(new Color(0, 0, 1, .2f));
|
|
g2d.fill(testShapes[shapeIndex]);
|
|
}
|
|
}
|
|
|
|
static volatile PathHitTestManual pathHitTestManual;
|
|
|
|
private static void createAndShowGUI() {
|
|
pathHitTestManual = new PathHitTestManual();
|
|
Frame frame = new Frame("PathHitTestManual test window");
|
|
|
|
frame.add(pathHitTestManual);
|
|
frame.setSize(400, 450);
|
|
|
|
PassFailJFrame.addTestWindow(frame);
|
|
PassFailJFrame.positionTestWindow(frame, PassFailJFrame.Position.HORIZONTAL);
|
|
|
|
frame.setVisible(true);
|
|
|
|
pathHitTestManual.init();
|
|
pathHitTestManual.start();
|
|
}
|
|
|
|
public static void doManual() throws Exception {
|
|
PassFailJFrame passFailJFrame = PassFailJFrame.builder()
|
|
.title("PathHitTestManual Instructions")
|
|
.instructions(INSTRUCTIONS)
|
|
.testTimeOut(5)
|
|
.rows(30)
|
|
.columns(70)
|
|
.screenCapture()
|
|
.build();
|
|
|
|
EventQueue.invokeAndWait(PathHitTestManual::createAndShowGUI);
|
|
try {
|
|
passFailJFrame.awaitAndCheck();
|
|
} finally {
|
|
pathHitTestManual.stop();
|
|
}
|
|
}
|
|
}
|
|
}
|