/**
* *****************************************************************************
* 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 nars.lab.narclear.jbox2d.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 java.util.ArrayList;
import java.util.List;
import org.jbox2d.callbacks.DebugDraw;
import org.jbox2d.collision.AABB;
import org.jbox2d.collision.shapes.ChainShape;
import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.EdgeShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Color3f;
import org.jbox2d.common.IViewportTransform;
import org.jbox2d.common.Mat22;
import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Settings;
import org.jbox2d.common.Transform;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.Fixture;
import org.jbox2d.dynamics.World;
import org.jbox2d.dynamics.joints.Joint;
import org.jbox2d.dynamics.joints.PulleyJoint;
import org.jbox2d.particle.ParticleColor;
import org.jbox2d.pooling.arrays.IntArray;
import org.jbox2d.pooling.arrays.Vec2Array;
public class DrawPhy2D extends DebugDraw {
public static int circlePoints = 5;
public static final float edgeWidth = 0.02f;
private final TestPanelJ2D panel;
private final boolean yFlip;
private final Shape circle;
Transform xf = new Transform();
Color3f color = new Color3f();
public final List<LayerDraw> layers = new ArrayList();
private Graphics2D graphics;
public DrawPhy2D(TestPanelJ2D argTestPanel, boolean yFlip) {
panel = argTestPanel;
this.yFlip = yFlip;
circle = new Ellipse2D.Float(-1, -1, 2, 2);
}
public interface LayerDraw {
public void drawGround(DrawPhy2D draw, World w);
public void drawSky(DrawPhy2D draw, World w);
}
public void addLayer(LayerDraw l) {
layers.add(l);
}
public void removeLayer(LayerDraw l) {
layers.remove(l);
}
public void draw(World w) {
graphics = panel.getDBGraphics();
for (LayerDraw l : layers) l.drawGround(this, w);
int flags = getFlags();
//boolean wireframe = (flags & DebugDraw.e_wireframeDrawingBit) != 0;
if ((flags & DebugDraw.e_shapeBit) != 0) {
for (Body b = w.getBodyList(); b != null; b = b.getNext()) {
drawBody(b);
}
//drawParticleSystem(m_particleSystem);
}
if ((flags & DebugDraw.e_jointBit) != 0) {
for (Joint j = w.getJointList(); j != null; j = j.getNext()) {
drawJoint(j);
}
}
// if ((flags & DebugDraw.e_pairBit) != 0) {
// color.set(0.3f, 0.9f, 0.9f);
// for (Contact c = m_contactManager.m_contactList; c != null; c = c.getNext()) {
// Fixture fixtureA = c.getFixtureA();
// Fixture fixtureB = c.getFixtureB();
// fixtureA.getAABB(c.getChildIndexA()).getCenterToOut(cA);
// fixtureB.getAABB(c.getChildIndexB()).getCenterToOut(cB);
// drawSegment(cA, cB, color);
// }
// }
//
// if ((flags & DebugDraw.e_aabbBit) != 0) {
// color.set(0.9f, 0.3f, 0.9f);
//
// for (Body b = m_bodyList; b != null; b = b.getNext()) {
// if (b.isActive() == false) {
// continue;
// }
//
// for (Fixture f = b.getFixtureList(); f != null; f = f.getNext()) {
// for (int i = 0; i < f.m_proxyCount; ++i) {
// FixtureProxy proxy = f.m_proxies[i];
// AABB aabb = m_contactManager.m_broadPhase.getFatAABB(proxy.proxyId);
// if (aabb != null) {
// Vec2[] vs = avs.get(4);
// vs[0].set(aabb.lowerBound.x, aabb.lowerBound.y);
// vs[1].set(aabb.upperBound.x, aabb.lowerBound.y);
// vs[2].set(aabb.upperBound.x, aabb.upperBound.y);
// vs[3].set(aabb.lowerBound.x, aabb.upperBound.y);
// drawPolygon(vs, 4, color);
// }
// }
// }
// }
// }
//
// if ((flags & DebugDraw.e_centerOfMassBit) != 0) {
// for (Body b = m_bodyList; b != null; b = b.getNext()) {
// xf.set(b.getTransform());
// xf.p.set(b.getWorldCenter());
// drawTransform(xf);
// }
// }
//
// if ((flags & DebugDraw.e_dynamicTreeBit) != 0) {
// m_contactManager.m_broadPhase.drawTree(m_debugDraw);
// }
for (LayerDraw l : layers) l.drawSky(this, w);
flush();
}
public interface DrawProperty {
public void before(Body b, DrawPhy2D d);
}
Color defaultFillColor = new Color(0.75f, 0.75f, 0.75f);
Color defaultStrokeColor = new Color(1,1,1);
Stroke defaultStroke = new BasicStroke(2);
Stroke stroke = defaultStroke;
Color fillColor = defaultFillColor;
Color strokeColor = defaultStrokeColor;
public void setStrokeColor(Color strokeColor) {
this.strokeColor = strokeColor;
}
public void setFillColor(Color fillColor) {
this.fillColor = fillColor;
}
public void setStroke(Stroke stroke) {
this.stroke = stroke;
}
void drawBody(Body b) {
boolean wireframe = false;
Object o = b.getUserData();
if (o instanceof DrawProperty) {
DrawProperty d = (DrawProperty)o;
d.before(b, this);
if ((fillColor == null) && (stroke == null))
return;
}
else {
strokeColor = defaultStrokeColor;
fillColor = defaultFillColor;
stroke = defaultStroke;
}
xf.set(b.getTransform());
for (Fixture f = b.getFixtureList(); f != null; f = f.getNext()) {
if (b.isActive() == false) {
color.set(0.5f, 0.5f, 0.3f);
drawShape(f, xf, color, wireframe);
} else if (b.getType() == BodyType.STATIC) {
color.set(0.5f, 0.9f, 0.3f);
drawShape(f, xf, color, wireframe);
} else if (b.getType() == BodyType.KINEMATIC) {
color.set(0.5f, 0.5f, 0.9f);
drawShape(f, xf, color, wireframe);
} else if (b.isAwake() == false) {
color.set(0.5f, 0.5f, 0.5f);
drawShape(f, xf, color, wireframe);
} else {
color.set(0.9f, 0.7f, 0.7f);
drawShape(f, xf, color, wireframe);
}
}
}
private final Vec2 center = new Vec2();
private final Vec2 axis = new Vec2();
private final Vec2 v1 = new Vec2();
private final Vec2 v2 = new Vec2();
private final Vec2Array tlvertices = new Vec2Array();
private void drawShape(Fixture fixture, Transform xf, Color3f color, boolean wireframe) {
switch (fixture.getType()) {
case CIRCLE: {
CircleShape circle = (CircleShape) fixture.getShape();
// Vec2 center = Mul(xf, circle.m_p);
Transform.mulToOutUnsafe(xf, circle.m_p, center);
float radius = circle.m_radius;
xf.q.getXAxis(axis);
/*
if (fixture.getUserData() != null && fixture.getUserData().equals(LIQUID_INT)) {
Body b = fixture.getBody();
liquidOffset.set(b.m_linearVelocity);
float linVelLength = b.m_linearVelocity.length();
if (averageLinearVel == -1) {
averageLinearVel = linVelLength;
} else {
averageLinearVel = .98f * averageLinearVel + .02f * linVelLength;
}
liquidOffset.mulLocal(liquidLength / averageLinearVel / 2);
circCenterMoved.set(center).addLocal(liquidOffset);
center.subLocal(liquidOffset);
drawSegment(center, circCenterMoved, liquidColor);
return;
}
*/
if (wireframe) {
drawCircle(center, radius, axis, color);
} else {
drawSolidCircle(center, radius, axis, color);
}
}
break;
case POLYGON: {
PolygonShape poly = (PolygonShape) fixture.getShape();
int vertexCount = poly.m_count;
assert (vertexCount <= Settings.maxPolygonVertices);
Vec2[] vertices = tlvertices.get(Settings.maxPolygonVertices);
for (int i = 0; i < vertexCount; ++i) {
// vertices[i] = Mul(xf, poly.m_vertices[i]);
Transform.mulToOutUnsafe(xf, poly.m_vertices[i], vertices[i]);
}
if (wireframe) {
drawPolygon(vertices, vertexCount, color);
} else {
drawSolidPolygon(vertices, vertexCount, color);
}
}
break;
case EDGE: {
EdgeShape edge = (EdgeShape) fixture.getShape();
Transform.mulToOutUnsafe(xf, edge.m_vertex1, v1);
Transform.mulToOutUnsafe(xf, edge.m_vertex2, v2);
drawSegment(v1, v2, color);
}
break;
case CHAIN: {
ChainShape chain = (ChainShape) fixture.getShape();
int count = chain.m_count;
Vec2[] vertices = chain.m_vertices;
Transform.mulToOutUnsafe(xf, vertices[0], v1);
for (int i = 1; i < count; ++i) {
Transform.mulToOutUnsafe(xf, vertices[i], v2);
drawSegment(v1, v2, color);
drawCircle(v1, 0.05f, color);
v1.set(v2);
}
}
break;
default:
break;
}
}
Vec2 p1 = new Vec2(); //pool.popVec2();
Vec2 p2 = new Vec2(); //pool.popVec2();
private void drawJoint(Joint joint) {
Body bodyA = joint.getBodyA();
Body bodyB = joint.getBodyB();
Transform xf1 = bodyA.getTransform();
Transform xf2 = bodyB.getTransform();
Vec2 x1 = xf1.p;
Vec2 x2 = xf2.p;
joint.getAnchorA(p1);
joint.getAnchorB(p2);
color.set(0.5f, 0.8f, 0.8f);
switch (joint.getType()) {
// TODO djm write after writing joints
case DISTANCE:
drawSegment(p1, p2, color);
break;
case PULLEY: {
PulleyJoint pulley = (PulleyJoint) joint;
Vec2 s1 = pulley.getGroundAnchorA();
Vec2 s2 = pulley.getGroundAnchorB();
drawSegment(s1, p1, color);
drawSegment(s2, p2, color);
drawSegment(s1, s2, color);
}
break;
case CONSTANT_VOLUME:
case MOUSE:
// don't draw this
break;
default:
drawSegment(x1, p1, color);
drawSegment(p1, p2, color);
drawSegment(x2, p2, color);
}
//pool.pushVec2(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 = new Color(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 = new Color(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 drawSegment(Vec2 p1, Vec2 p2, Color3f color, float alpha) {
getWorldToScreenToOut(p1, sp1);
getWorldToScreenToOut(p2, sp2);
Color c = new Color(color.x, color.y, color.z, alpha);
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 = new Color(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 = new Color(color.x, color.y, color.z, 1f);
g.scale(radius, radius);
g.setColor(s);
g.draw(circle);
g.rotate(MathUtils.atan2(axis.y, axis.x));
if (axis != null) {
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 = new Color(color.x, color.y, color.z, .4f);
Color s = new Color(color.x, color.y, color.z, 1f);
g.scale(radius, radius);
g.setColor(f);
g.fill(circle);
g.setColor(s);
g.draw(circle);
g.rotate(MathUtils.atan2(axis.y, axis.x));
if (axis != null) {
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(final Vec2[] centers, final float radius, ParticleColor[] colors, final 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 = new Color(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();
public void drawSolidRect(float px, float py, float w, float h, float r, float G, float b) {
Graphics2D g = getGraphics();
//saveState(g);
getWorldToScreenToOut(px, py, temp);
int ipx = (int)temp.x; int ipy = (int)temp.y;
getWorldToScreenToOut(px+w, py+h, temp);
int jpx = (int)temp.x; int jpy = (int)temp.y;
int iw = jpx - ipx;
int ih = -(jpy - ipy);
// if ((ipy/2 > g.getDeviceConfiguration().getBounds().getHeight()) ||
// (ipx/2 > g.getDeviceConfiguration().getBounds().getWidth()))
// return;
g.setColor(new Color(r, G, b));
g.fillRect(ipx-iw/2, ipy-ih/2, iw, ih);
//if (g.getDeviceConfiguration().getBounds().intersects(ipx-iw/2, ipy-ih/2, iw, ih)) {
//}
//restoreState(g);
}
@Override
public void drawSolidPolygon(Vec2[] vertices, int vertexCount, Color3f color) {
Color s = strokeColor;
Color f = fillColor;
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 = strokeColor; //new Color(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 = new Color(color.x, color.y, color.z);
g.setColor(c);
g.drawString(s, x, y);
}
public Graphics2D getGraphics() {
return graphics;
}
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 = new Color(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 = new Color(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);
}
}