/******************************************************************************* * Copyright (c) 2013, Daniel Murphy All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted * provided that the following conditions are met: * Redistributions of source code must retain the * above copyright notice, this list of conditions and the following disclaimer. * Redistributions * in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ package org.jbox2d.testbed.framework.j2d; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import org.jbox2d.callbacks.DebugDraw; import org.jbox2d.collision.AABB; import org.jbox2d.common.Color3f; import org.jbox2d.common.IViewportTransform; import org.jbox2d.common.Mat22; import org.jbox2d.common.MathUtils; import org.jbox2d.common.Transform; import org.jbox2d.common.Vec2; import org.jbox2d.particle.ParticleColor; import org.jbox2d.pooling.arrays.IntArray; import org.jbox2d.pooling.arrays.Vec2Array; import org.jbox2d.testbed.pooling.ColorPool; // pooling local, not thread-safe /** * Implementation of {@link DebugDraw} that uses Java2D! Hooray!</br> * * @author Daniel Murphy */ public class DebugDrawJ2D extends DebugDraw { public static int circlePoints = 13; public static final float edgeWidth = 0.02f; private final TestPanelJ2D panel; private final ColorPool<Color> cpool = new ColorPool<Color>() { protected Color newColor(float r, float g, float b, float alpha) { return new Color(r, g, b, alpha); } }; private final boolean yFlip; private final BasicStroke stroke; private final Shape circle; public DebugDrawJ2D(TestPanelJ2D argTestPanel, boolean yFlip) { panel = argTestPanel; this.yFlip = yFlip; stroke = new BasicStroke(0); circle = new Ellipse2D.Float(-1, -1, 2, 2); } @Override public void setViewportTransform(IViewportTransform viewportTransform) { super.setViewportTransform(viewportTransform); viewportTransform.setYFlip(yFlip); } private final Vec2Array vec2Array = new Vec2Array(); @Override public void drawPoint(Vec2 argPoint, float argRadiusOnScreen, Color3f argColor) { getWorldToScreenToOut(argPoint, sp1); Graphics2D g = getGraphics(); Color c = cpool.getColor(argColor.x, argColor.y, argColor.z); g.setColor(c); sp1.x -= argRadiusOnScreen; sp1.y -= argRadiusOnScreen; g.fillOval((int) sp1.x, (int) sp1.y, (int) argRadiusOnScreen * 2, (int) argRadiusOnScreen * 2); } private final Vec2 sp1 = new Vec2(); private final Vec2 sp2 = new Vec2(); @Override public void drawSegment(Vec2 p1, Vec2 p2, Color3f color) { getWorldToScreenToOut(p1, sp1); getWorldToScreenToOut(p2, sp2); Color c = cpool.getColor(color.x, color.y, color.z); Graphics2D g = getGraphics(); g.setColor(c); g.setStroke(stroke); g.drawLine((int) sp1.x, (int) sp1.y, (int) sp2.x, (int) sp2.y); } public void drawAABB(AABB argAABB, Color3f color) { Vec2 vecs[] = vec2Array.get(4); argAABB.getVertices(vecs); drawPolygon(vecs, 4, color); } private final AffineTransform tr = new AffineTransform(); private AffineTransform oldTrans = new AffineTransform(); private Stroke oldStroke; private void saveState(Graphics2D g) { oldTrans = g.getTransform(); oldStroke = g.getStroke(); } private void restoreState(Graphics2D g) { g.setTransform(oldTrans); g.setStroke(oldStroke); } private void transformGraphics(Graphics2D g, Vec2 center) { Vec2 e = viewportTransform.getExtents(); Vec2 vc = viewportTransform.getCenter(); Mat22 vt = viewportTransform.getMat22Representation(); int flip = yFlip ? -1 : 1; tr.setTransform(vt.ex.x, flip * vt.ex.y, vt.ey.x, flip * vt.ey.y, e.x, e.y); tr.translate(-vc.x, -vc.y); tr.translate(center.x, center.y); g.transform(tr); } @Override public void drawCircle(Vec2 center, float radius, Color3f color) { Graphics2D g = getGraphics(); Color s = cpool.getColor(color.x, color.y, color.z, 1f); saveState(g); transformGraphics(g, center); g.setStroke(stroke); g.scale(radius, radius); g.setColor(s); g.drawOval(-1, -1, 2, 2); restoreState(g); } @Override public void drawCircle(Vec2 center, float radius, Vec2 axis, Color3f color) { Graphics2D g = getGraphics(); saveState(g); transformGraphics(g, center); g.setStroke(stroke); Color s = cpool.getColor(color.x, color.y, color.z, 1f); g.scale(radius, radius); g.setColor(s); g.draw(circle); if (axis != null) { g.rotate(MathUtils.atan2(axis.y, axis.x)); g.drawLine(0, 0, 1, 0); } restoreState(g); } @Override public void drawSolidCircle(Vec2 center, float radius, Vec2 axis, Color3f color) { Graphics2D g = getGraphics(); saveState(g); transformGraphics(g, center); g.setStroke(stroke); Color f = cpool.getColor(color.x, color.y, color.z, .4f); Color s = cpool.getColor(color.x, color.y, color.z, 1f); g.scale(radius, radius); g.setColor(f); g.fill(circle); g.setColor(s); g.draw(circle); if (axis != null) { g.rotate(MathUtils.atan2(axis.y, axis.x)); g.drawLine(0, 0, 1, 0); } restoreState(g); } private final Vec2 zero = new Vec2(); private final Color pcolorA = new Color(1f, 1f, 1f, .4f); @Override public void drawParticles(Vec2[] centers, float radius, ParticleColor[] colors, int count) { Graphics2D g = getGraphics(); saveState(g); transformGraphics(g, zero); g.setStroke(stroke); for (int i = 0; i < count; i++) { Vec2 center = centers[i]; Color color; if (colors == null) { color = pcolorA; } else { ParticleColor c = colors[i]; color = cpool.getColor(c.r * 1f / 127, c.g * 1f / 127, c.b * 1f / 127, c.a * 1f / 127); } AffineTransform old = g.getTransform(); g.translate(center.x, center.y); g.scale(radius, radius); g.setColor(color); g.fill(circle); g.setTransform(old); } restoreState(g); } private final Color pcolor = new Color(1f, 1f, 1f, 1f); @Override public void drawParticlesWireframe(Vec2[] centers, float radius, ParticleColor[] colors, int count) { Graphics2D g = getGraphics(); saveState(g); transformGraphics(g, zero); g.setStroke(stroke); for (int i = 0; i < count; i++) { Vec2 center = centers[i]; Color color; // No alpha channel, it slows everything down way too much. if (colors == null) { color = pcolor; } else { ParticleColor c = colors[i]; color = new Color(c.r * 1f / 127, c.g * 1f / 127, c.b * 1f / 127, 1); } AffineTransform old = g.getTransform(); g.translate(center.x, center.y); g.scale(radius, radius); g.setColor(color); g.draw(circle); g.setTransform(old); } restoreState(g); } private final Vec2 temp = new Vec2(); private final static IntArray xIntsPool = new IntArray(); private final static IntArray yIntsPool = new IntArray(); @Override public void drawSolidPolygon(Vec2[] vertices, int vertexCount, Color3f color) { Color f = cpool.getColor(color.x, color.y, color.z, .4f); Color s = cpool.getColor(color.x, color.y, color.z, 1f); Graphics2D g = getGraphics(); saveState(g); int[] xInts = xIntsPool.get(vertexCount); int[] yInts = yIntsPool.get(vertexCount); for (int i = 0; i < vertexCount; i++) { getWorldToScreenToOut(vertices[i], temp); xInts[i] = (int) temp.x; yInts[i] = (int) temp.y; } g.setStroke(stroke); g.setColor(f); g.fillPolygon(xInts, yInts, vertexCount); g.setColor(s); g.drawPolygon(xInts, yInts, vertexCount); restoreState(g); } @Override public void drawPolygon(Vec2[] vertices, int vertexCount, Color3f color) { Color s = cpool.getColor(color.x, color.y, color.z, 1f); Graphics2D g = getGraphics(); saveState(g); int[] xInts = xIntsPool.get(vertexCount); int[] yInts = yIntsPool.get(vertexCount); for (int i = 0; i < vertexCount; i++) { getWorldToScreenToOut(vertices[i], temp); xInts[i] = (int) temp.x; yInts[i] = (int) temp.y; } g.setStroke(stroke); g.setColor(s); g.drawPolygon(xInts, yInts, vertexCount); restoreState(g); } @Override public void drawString(float x, float y, String s, Color3f color) { Graphics2D g = getGraphics(); if (g == null) { return; } Color c = cpool.getColor(color.x, color.y, color.z); g.setColor(c); g.drawString(s, x, y); } private Graphics2D getGraphics() { return panel.getDBGraphics(); } private final Vec2 temp2 = new Vec2(); @Override public void drawTransform(Transform xf) { Graphics2D g = getGraphics(); getWorldToScreenToOut(xf.p, temp); temp2.setZero(); float k_axisScale = 0.4f; Color c = cpool.getColor(1, 0, 0); g.setColor(c); temp2.x = xf.p.x + k_axisScale * xf.q.c; temp2.y = xf.p.y + k_axisScale * xf.q.s; getWorldToScreenToOut(temp2, temp2); g.drawLine((int) temp.x, (int) temp.y, (int) temp2.x, (int) temp2.y); c = cpool.getColor(0, 1, 0); g.setColor(c); temp2.x = xf.p.x + -k_axisScale * xf.q.s; temp2.y = xf.p.y + k_axisScale * xf.q.c; getWorldToScreenToOut(temp2, temp2); g.drawLine((int) temp.x, (int) temp.y, (int) temp2.x, (int) temp2.y); } }