jdk-24/test/jdk/java/awt/geom/HitTest/PathHitTest.java
Alexey Ivanov 3b9255eb66 8325851: Hide PassFailJFrame.Builder constructor
Reviewed-by: honkar, prr
2024-03-14 10:57:30 +00:00

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