Trying to draw a metro graph using JUNG.
Is it possible to draw an edge of straight line that uses 2 or more colors in parallel, using the transformer?
You could do something like this....
Copy and modify some code from the EdgeShape class (where it makes the Bowtie shape) modified to a rectangle, then set the edge fill paint, draw paint, and stroke to look like what you want
public class RectangleEdge<V,E> extends AbstractEdgeShapeTransformer<V,E> {
private static GeneralPath rectangle;
public RectangleEdge(int width) {
rectangle = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
rectangle.moveTo(0, width/2); // change points to make rectangle
rectangle.lineTo(1, width/2);
rectangle.lineTo(1, -width/2);
rectangle.lineTo(0, -width/2);
rectangle.closePath();
}
public Shape transform(Context<Graph<V,E>,E> context) {
return rectangle;
}
}
vv.getRenderContext().setEdgeShapeTransformer(new RectangleEdge<>(4));
vv.getRenderContext().setEdgeFillPaintTransformer(new ConstantTransformer(Color.red));
vv.getRenderContext().setEdgeStrokeTransformer(new ConstantTransformer(new BasicStroke(2)));
vv.getRenderContext().setEdgeDrawPaintTransformer(new ConstantTransformer(Color.blue));
Here's a picture:
ok, if you want a lot of colors, try this:
vv.getRenderer().setEdgeRenderer(
new NotSimpleEdgeRenderer(new Color[]{
Color.red, Color.blue, Color.pink, Color.green, Color.magenta,
Color.cyan, Color.black, Color.orange, Color.yellow
}));
vv.getRenderContext().setEdgeStrokeTransformer(new ConstantTransformer(new BasicStroke(2)));
public class NotSimpleEdgeRenderer<V,E> extends BasicEdgeRenderer<V,E> {
Color[] colors;
Shape[] shapes;
public NotSimpleEdgeRenderer(Color... colors) {
int count = colors.length;
this.colors = colors;
shapes = new Shape[count];
for (int i=0; i<count; i++) {
shapes[i] = new Line2D.Double(0, -count/2+(2*i), 1, -count/2+(2*i));
}
}
protected void drawSimpleEdge(RenderContext<V,E> rc, Layout<V,E> layout, E e) {
GraphicsDecorator g = rc.getGraphicsContext();
Graph<V,E> graph = layout.getGraph();
Pair<V> endpoints = graph.getEndpoints(e);
V v1 = endpoints.getFirst();
V v2 = endpoints.getSecond();
Point2D p1 = layout.transform(v1);
Point2D p2 = layout.transform(v2);
p1 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p1);
p2 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p2);
float x1 = (float) p1.getX();
float y1 = (float) p1.getY();
float x2 = (float) p2.getX();
float y2 = (float) p2.getY();
boolean edgeHit;
Rectangle deviceRectangle = null;
JComponent vv = rc.getScreenDevice();
if(vv != null) {
Dimension d = vv.getSize();
deviceRectangle = new Rectangle(0,0,d.width,d.height);
}
AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1);
float dx = x2-x1;
float dy = y2-y1;
float thetaRadians = (float) Math.atan2(dy, dx);
xform.rotate(thetaRadians);
float dist = (float) Math.sqrt(dx*dx + dy*dy);
xform.scale(dist, 1.0);
Paint oldPaint = g.getPaint();
for (int i=0; i<shapes.length; i++) {
Shape edgeShape = shapes[i];
edgeShape = xform.createTransformedShape(edgeShape);
MutableTransformer vt = rc.getMultiLayerTransformer().getTransformer(Layer.VIEW);
edgeHit = vt.transform(edgeShape).intersects(deviceRectangle);
if(edgeHit == true) {
Paint fill_paint = colors[i];
if (fill_paint != null) {
g.setPaint(fill_paint);
g.fill(edgeShape);
}
Paint draw_paint = colors[i];
if (draw_paint != null) {
g.setPaint(draw_paint);
g.draw(edgeShape);
}
}
// restore old paint
g.setPaint(oldPaint);
}
}
}
and it looks like this:
I think you can do this with small modifications to the code samples I posted in previous answers.
I assume that you know what colors you want for each edge. Make a Map that has the mappings from each edge to the colors you want for that edge. Then modify the code I answered with to look like this:
public class NotSimpleEdgeRenderer<V,E> extends BasicEdgeRenderer<V,E> {
Map<E,Color[]> colorMap;
public NotSimpleEdgeRenderer(Map<E,Color[]> colorMap) {
this.colorMap = colorMap;
}
protected void drawSimpleEdge(RenderContext<V,E> rc, Layout<V,E> layout, E e) {
GraphicsDecorator g = rc.getGraphicsContext();
Graph<V,E> graph = layout.getGraph();
Pair<V> endpoints = graph.getEndpoints(e);
V v1 = endpoints.getFirst();
V v2 = endpoints.getSecond();
Point2D p1 = layout.transform(v1);
Point2D p2 = layout.transform(v2);
p1 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p1);
p2 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p2);
float x1 = (float) p1.getX();
float y1 = (float) p1.getY();
float x2 = (float) p2.getX();
float y2 = (float) p2.getY();
boolean edgeHit;
Rectangle deviceRectangle = null;
JComponent vv = rc.getScreenDevice();
if(vv != null) {
Dimension d = vv.getSize();
deviceRectangle = new Rectangle(0,0,d.width,d.height);
}
AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1);
float dx = x2-x1;
float dy = y2-y1;
float thetaRadians = (float) Math.atan2(dy, dx);
xform.rotate(thetaRadians);
float dist = (float) Math.sqrt(dx*dx + dy*dy);
xform.scale(dist, 1.0);
Paint oldPaint = g.getPaint();
// get the colors for this edge from the map
Color[] colors = colorMap.get(e);
int count = colors.length;
// make the Shapes for this edge here
Shape[] shapes = new Shape[count];
for (int i=0; i<count; i++) {
// this code offsets the lines enough to see the colors
shapes[i] = new Line2D.Double(0, -count/2+(2*i), 1, -count/2+(2*i));
}
// iterate over the edge shapes and draw them with the corresponding colors
for (int i=0; i<shapes.length; i++) {
Shape edgeShape = shapes[i];
edgeShape = xform.createTransformedShape(edgeShape);
MutableTransformer vt = rc.getMultiLayerTransformer().getTransformer(Layer.VIEW);
edgeHit = vt.transform(edgeShape).intersects(deviceRectangle);
if(edgeHit == true) {
Paint fill_paint = colors[i];
if (fill_paint != null) {
g.setPaint(fill_paint);
g.fill(edgeShape);
}
Paint draw_paint = colors[i];
if (draw_paint != null) {
g.setPaint(draw_paint);
g.draw(edgeShape);
}
}
// restore old paint
g.setPaint(oldPaint);
}
}
}
Related
Example Picture: 1
I try to draw a driving path. For this I draw one line behind the other and rotate the lines depending on the actual rotation by using affine transformation.
Everything works fine and I can draw the path but the path is not displayed very nice. There are a lot of white gaps. I think this is caused by the rotation of the lines.
How can I manage it to have a path without these white gaps?
public class Stage extends JPanel
{
/**
*
*/
private static final long serialVersionUID = 1L;
int pW = 2000;
int pH = 2000;
double _x = 400,_y=300;
double centerX, centerY;
double x,y,angle, angleLine, xpic,ypic;
double w = 100;
double x1,y1,x2,y2,x3,y3;
double xCnt = 0;
BufferedImage bi;
private boolean gf_bgSet;
Point2D p1 = new Point2D.Double(0,0);
Point2D p2 = new Point2D.Double(0,0);;
Point2D lastRotP = new Point2D.Double(0,0);
double degrees;
boolean gf_up = false;
boolean gf_down = false;
private double angleOld;
private double ArcAngleOld;
public Stage()
{
setLayout(null);
setBounds(0,0,pW,pH);
setBackground(Color.WHITE);
centerX = _x;
centerY = _y;
x = centerX;
y = centerY;
xpic = centerX;
ypic = centerY;
bi = new BufferedImage(pW, pH, BufferedImage.TYPE_INT_ARGB);
}
public void moveLine(KeyEvent e)
{
switch(e.getKeyCode())
{
case KeyEvent.VK_PLUS:
angle--;
angleLine--;
degrees++;
if (degrees == -360 || degrees == 360)
{
degrees = 0;
}
repaint();
break;
case KeyEvent.VK_MINUS:
angle++;
angleLine++;
degrees--;
if (degrees == -360 || degrees == 360)
{
degrees = 0;
}
repaint();
break;
case KeyEvent.VK_LEFT:
break;
case KeyEvent.VK_RIGHT:
break;
case KeyEvent.VK_UP:
gf_down = false;
gf_up = true;
xpic++;
repaint();
break;
case KeyEvent.VK_DOWN:
ypic--;
gf_down = true;
gf_up = false;
repaint();
break;
case KeyEvent.VK_Z:
repaint();
break;
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
double _w = 100;
//get Graphic2D objects
Graphics2D g2d = (Graphics2D) bi.createGraphics();
Graphics2D g2dMain = (Graphics2D) g;
//set Background and first blue line
if (gf_bgSet==false)
{
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, pW, pH);
g2d.setColor(Color.BLUE);
Line2D line = new Line2D.Double(_x, _y, _x+_w/2,_y);
g2d.draw(line);
gf_bgSet=true;
}
else
{
double _angle = 0;
_angle = Math.toRadians(degrees);
//Set color red transparent
g2d.setColor(Color.BLUE);
//Draw line
Line2D line = null;
line = new Line2D.Double(_x, _y, _x+(_w/2), _y);
if(gf_up==true)
{
_x=_x+(Math.sin(_angle));
_y=_y-(Math.cos(_angle));
}
else
{
_x=_x-(Math.sin(_angle));
_y=_y+(Math.cos(_angle));
}
angleOld = _angle;
double xDiff = centerX-_x;
double yDiff = centerY-_y;
AffineTransform old = g2d.getTransform();
AffineTransform atLine = AffineTransform.getRotateInstance(_angle,_x+w/2,_y);
g2d.draw(atLine.createTransformedShape(line));
g2d.setTransform(old);
//Rotate picture
AffineTransform oldtrans = g2dMain.getTransform();
AffineTransform trans = AffineTransform.getRotateInstance(-_angle, centerX-xDiff, centerY-yDiff);
g2dMain.translate(xDiff, yDiff);
g2dMain.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2dMain.drawImage(bi, trans , null);
g2dMain.setTransform(oldtrans);
}
}
}
Instead of drawing the lines one by one, try using a GeneralPath and set the BasicStroke to the size of the lines you are using at the moment. With every new point you just update your path.
Here is a good example:
https://kodejava.org/how-do-i-draw-a-generalpath-in-java-2d/
this is a really weird bug and I can't seem to figure it out. To simplify this, I have three classes: A Canvas class (which draws the object with Graphics2D.draw()) then lots ob furniture classes, which can be viewed as one because all they do is return different shapes. Last I have a CustomShape class, which lets me create a new Shape based on my existing other shapes. But the shapes are being drawn at weird places. The x any y coordinates do not match where the shape is being drawn.
Closet.java:
public class Closet {
double x, y, width, height, rotation;
Color color;
Closet() {
this.x = X;
this.y = Y;
this.width = 40;
this.height = 40;
this.rotation = 0;
this.color = Color.blue;
}
public Shape getShape() {
GeneralPath closetShape = new GeneralPath();
closetShape.append(new Rectangle2D.Double(0, 0, width, height), false);
closetShape.moveTo(0 , 0);
closetShape.lineTo(width, height);
closetShape.moveTo(0, height);
closetShape.lineTo(width, 0);
// transform:
AffineTransform t = new AffineTransform();
t.translate(x, y);
Rectangle2D umriss = closetShape.getBounds2D();
t.rotate(Math.toRadians(rotation),umriss.getX()+umriss.getWidth()/2,umriss.getY()+umriss.getHeight()/2);
return t.createTransformedShape(closetShape);
}
}
CustomShape.java
public class CustomShape {
double x, y, width, height, rotation;
Color color;
private Closet[] c;
CustomShape(Closet... elements) {
this.m = elements;
GeneralPath path = new GeneralPath();
Closet[] newC = elements.clone();
Arrays.stream(newC).forEach(e -> path.append(e.getShape(), false));
this.x = path.getBounds2D().getX();
this.y = path.getBounds2D().getY();
this.width = path.getBounds2D().getWidth();
this.height = path.getBounds2D().getHeight();
this.rotation = 0;
this.color = Color.blue;
}
public Shape getShape() {
GeneralPath path = new GeneralPath();
Arrays.stream(c).forEach(e -> path.append(e.getShape(), false));
AffineTransform t = new AffineTransform();
t.translate(x, y);
Rectangle2D umriss = path.getBounds2D();
t.rotate(Math.toRadians(rotation),umriss.getX()+umriss.getWidth()/2,umriss.getY()+umriss.getHeight()/2);
return t.createTransformedShape(path);
}
}
I found the solution: I had transformed these Shapes before adding them to the CustomShape. That is why the x and y coordinates were wrong.
I am writing a ray tracer.I am currently working on reflections.But the seem not to be reflecting correctly.I keep on getting StackOverflowError.I increased the memory and it runs now but the reflections are not like I thought the would be this.
(source: ageofarmour.com)
I thought it would Reflect the reflections!But it just ends up like this.
Note:This is after moved the normal off the object and changed the color calculations!Check the Cal_Reflection for new color calculation!
Here is my code for my tracer!
public class Tracer {
public boolean Tracing;
public Camera Cam;
public int Width, Height;
public BufferedImage Image;
public Color BackGroundColor;
public int StartX, StartY, EndX, EndY, RowCount, ColCount;
public double AmbientLight;
public double DiffuseLight;
public int MaxReflectionCount;
public ArrayList<GeometricObject> GeoObjects;
public ArrayList<LightObject> LightObjects;
public Tracer(Camera cam, int width, int height, BufferedImage image, Color backGroundColor, int startX, int startY, int endX, int endY, int rowCount, int colCount, double ambientLight, double diffuseLight, int maxReflectionCount, ArrayList<GeometricObject> geoObjects, ArrayList<LightObject> lightObjects) {
super();
Cam = cam;
Width = width;
Height = height;
Image = image;
BackGroundColor = backGroundColor;
StartX = startX;
StartY = startY;
EndX = endX;
EndY = endY;
RowCount = rowCount;
ColCount = colCount;
AmbientLight = ambientLight;
DiffuseLight = diffuseLight;
MaxReflectionCount = maxReflectionCount;
GeoObjects = geoObjects;
LightObjects = lightObjects;
}
public void TracePixelFast(int x, int y) {
Color color = new Color(BackGroundColor.r, BackGroundColor.g, BackGroundColor.b);
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject GO = GeoObjects.get(o);
Ray r = new Ray(Cam.GetRayPos(Width, Height, x, y, 1, 1, RowCount, ColCount), Cam.GetRayDir(Width, Height, x, y, 1, 1, RowCount, ColCount));
double hit = GO.hit(r);
if (hit != 0.0) {
color = Cal_Pixel(x, y);
Image.setRGB(x, y, color.toInt());
break;
}
}
}
public void TracePixelSmooth(int x, int y) {
Image.setRGB(x, y, Cal_Pixel(x, y).toInt());
}
public Color Cal_Pixel(int x, int y) {
Color color = new Color(BackGroundColor);
Color colorh = new Color(BackGroundColor);
Color bgc = new Color(BackGroundColor);
int HIT = 0;
int MISS = 0;
for (int row = 0; row < RowCount; row++) {
for (int col = 0; col < ColCount; col++) {
double min = Double.MAX_VALUE;
Ray r = new Ray(Cam.GetRayPos(Width, Height, x, y, row, col, RowCount, ColCount), Cam.GetRayDir(Width, Height, x, y, row, col, RowCount, ColCount));
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject GO = GeoObjects.get(o);
double hit = GO.hit(r);
if (hit != 0.0 && hit < min) {
min = hit;
colorh = ShadePixel(0, GO, r, hit);
HIT++;
} else {
double min2 = Double.MAX_VALUE;
for (int o2 = 0; o2 < GeoObjects.size(); o2++) {
if (o != o2) {
GeometricObject GO2 = GeoObjects.get(o2);
double hit2 = GO2.hit(r);
if (hit2 != 0.0 && hit2 < min2) {
min2 = hit2;
bgc = ShadePixel(0, GO2, r, hit2);
}
}
}
MISS++;
}
}
}
}
for (int h = 0; h < HIT; h++) {
color.Add(colorh);
}
for (int m = 0; m < MISS; m++) {
color.Add(bgc);
}
color.Divide(RowCount * ColCount);
return color;
}
public Color ShadePixel(int ReflectionDepthCount, GeometricObject GO, Ray ray, double t) {
Normal normal = GO.Cal_Normal(ray, t);
if (GO.Reflectivity > 0) {
Color GoColor = new Color(Cal_Reflection(GO, ReflectionDepthCount, ray, normal));
Color finalcolor = new Color(Cal_Light(GoColor, normal));
return finalcolor;
} else {
;
Color finalcolor = new Color(Cal_Light(GO.Color, normal));
return finalcolor;
}
}
public Color Cal_Light(Color color, Normal normal) {
ArrayList<Color> PixelShade = new ArrayList<Color>();
Color Final = new Color();
for (int l = 0; l < LightObjects.size(); l++) {
LightObject light = LightObjects.get(l);
Vector3D r_Dir = light.Pos.Sub(normal.Origin);
r_Dir.normalize();
Ray raytolight = new Ray(normal.Origin, r_Dir);
int WAS_HIT = 0;
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject NGO = GeoObjects.get(o);
double hit = NGO.hit(raytolight);
if (hit != 0.0) {
WAS_HIT = 1;
}
}
PixelShade.add(light.ShadePixel(WAS_HIT, normal, r_Dir, color, AmbientLight, DiffuseLight));
}
for (int s = 0; s < PixelShade.size(); s++) {
Final.Add(PixelShade.get(s));
}
Final.Divide(PixelShade.size());
return Final;
}
public Color Cal_Reflection(GeometricObject OriginalObject, int ReflectionDepthCount, Ray InRay, Normal normal) {
if (ReflectionDepthCount <= MaxReflectionCount) {
GeometricObject LastGO = null;
Ray LastRay = null;
double LastT = 0.0;
double min = Double.MAX_VALUE;
Vector3D Origin = normal.Origin.Add(normal.Direction.Mul(1E-100));
Vector3D Direction = normal.Direction;
Direction.normalize();
Ray r = new Ray(Origin, Direction);
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject GO = GeoObjects.get(o);
double hit = GO.hit(r);
if (hit != 0.0 && hit < min) {
min = hit;
LastGO = GO;
LastRay = r;
LastT = hit;
}
}
if (LastGO != null) {
System.out.println(ReflectionDepthCount);
Color Reflected = new Color(ShadePixel(ReflectionDepthCount++, LastGO, LastRay, LastT));
Color HitColor = new Color(LastGO.Color);
Color FinalColor = new Color(OriginalObject.Color);
Reflected.Mul(OriginalObject.Reflectivity);
HitColor.Mul(OriginalObject.Reflectivity);
FinalColor.Add(HitColor);
FinalColor.Add(Reflected);
FinalColor.Divide(2);
return FinalColor;
}
} else {
return BackGroundColor;
}
return OriginalObject.Color;
}
public void TraceArea(boolean SmoothTracing) {
Tracing = true;
if (SmoothTracing) {
for (int x = StartX; x < EndX; x++) {
for (int y = StartY; y < EndY; y++) {
TracePixelSmooth(x, y);
}
}
} else {
for (int x = StartX; x < EndX; x++) {
for (int y = StartY; y < EndY; y++) {
TracePixelFast(x, y);
}
}
}
}}
Here is my code for my sphere!
public class Sphere extends GeometricObject{
public Vector3D Center;
public double Radius;
public Sphere(Vector3D Center,Color Color,double Radius,double Reflectivity){
this.Center = Center;
this.Radius = Radius;
this.Color = Color;
this.Reflectivity = Reflectivity;
}
public double hit(Ray ray) {
double a = ray.Direction.Dot(ray.Direction);
double b = 2 * ray.Origin.Sub(Center).Dot(ray.Direction);
double c = ray.Origin.Sub(Center).Dot(ray.Origin.Sub(Center))-Radius*Radius;
double discreminant = b*b-4*a*c;
if(discreminant < 0.0f){
return 0.0;
}else{
double t = (-b - Math.sqrt(discreminant))/(2*a);
if(t > 10E-9){
return t;
}else{
return 0.0;
}
}
}
public Normal Cal_Normal(Ray ray,double t) {
Vector3D NPos = new Vector3D(ray.Origin.x + ray.Direction.x*t,ray.Origin.y + ray.Direction.y*t,ray.Origin.z + ray.Direction.z*t);
Vector3D NDir = NPos.Sub(Center).Div(Radius);
NDir.normalize();
return new Normal(NPos,NDir);
}}
And here is my launcher that controls the scene and tracer!
public class Launcher {
public static int Width = 600;
public static int Height = 600;
public static void main(String[] args) {
Scene scene = new Scene(Width, Height, 8, 8, new Color(0, 0, 0), new Camera(new Vector3D(0, 0, 30), new Vector3D(0.3, 0, -1), 1, true, Width, Height, 40), 0.1, 0.2, 2);
// scene.AddObject(new Sphere(new Vector3D(0,0,0),new
// Color(0,255,0),5,1));
// scene.AddObject(new Sphere(new Vector3D(-30,0,0),new
// Color(0,0,255),10,0.5));
// scene.AddObject(new Sphere(new Vector3D(30,0,0),new
// Color(255,0,0),10,0.5));
scene.AddObject(new Sphere(new Vector3D(15, 0, 0), new Color(255, 0, 0), 15, 1));
scene.AddObject(new Sphere(new Vector3D(-15, 0, 0), new Color(0, 0, 255), 15, 1));
scene.AddLight(new NonColoredLight(new Vector3D(0, 0, 20), 0.1));
long Start = System.currentTimeMillis();
BufferedImage Image = scene.Trace(false, 2);
long End = System.currentTimeMillis();
System.out.println("Milli Seconds To Render " + (End - Start));
File ImageFile = new File("TracedImage.png");
try {
ImageIO.write(Image, "PNG", ImageFile);
} catch (IOException e) {
e.printStackTrace();
}
}}
And here is the scene code!
public Scene(int width, int height, int row, int col, Color backGroundColor, Camera cam, double ambientLight, double diffuseLight, int maxReflectionCount) {
super();
Width = width;
Height = height;
Row = row;
Col = col;
BackGroundColor = backGroundColor;
Cam = cam;
AmbientLight = ambientLight;
DiffuseLight = diffuseLight;
MaxReflectionCount = maxReflectionCount;
GeoObjects = new ArrayList<GeometricObject>();
LightObjects = new ArrayList<LightObject>();
if (ambientLight > 1) {
AmbientLight = 1;
} else if (ambientLight < 0) {
AmbientLight = 0;
} else {
AmbientLight = ambientLight;
}
if (diffuseLight > 1) {
DiffuseLight = 1;
} else if (diffuseLight < 0) {
DiffuseLight = 0;
} else {
DiffuseLight = ambientLight;
}
}
public void AddObject(GeometricObject GO) {
GeoObjects.add(GO);
}
public void AddLight(LightObject Light) {
LightObjects.add(Light);
}
public BufferedImage Trace(boolean SmoothTracing, int ThreadCount) {
Image = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_RGB);
Tracer tracer = new Tracer(Cam, Width, Height, Image, BackGroundColor, 0, 0, Width, Height, Row, Col, AmbientLight, DiffuseLight, MaxReflectionCount, GeoObjects, LightObjects);
tracer.TraceArea(SmoothTracing);
return Image;
}}
If you need me to post any more of my code just let me know!
Thanks in advance!
Variables for first reflection!
AmbientLight 0.1
BackGroundColor Color (id=39)
b 0.0
g 0.0
r 0.0
Cam Camera (id=41)
Direction Vector3D (id=104)
x 0.3
y 0.0
z -1.0
Distance 357.526077778263
FOV 40.0
Height 600
Perspective true
PixelSize 1.0
Pos Vector3D (id=115)
x 0.0
y 0.0
z 30.0
u Vector3D (id=116)
x -0.9578262852211514
y 0.0
z -0.28734788556634544
v Vector3D (id=117)
x 0.0
y 1.0
z 0.0
w Vector3D (id=118)
x 0.2873478855663454
y 0.0
z -0.9578262852211513
Width 600
ColCount 8
DiffuseLight 0.1
EndX 600
EndY 600
GeoObjects ArrayList<E> (id=43)
[0] Sphere (id=26)
[1] Sphere (id=34)
Height 600
Image BufferedImage (id=50)
accelerationPriority 0.5
colorModel DirectColorModel (id=120)
imageType 1
osis null
properties null
raster IntegerInterleavedRaster (id=124)
surfaceManager null
LightObjects ArrayList<E> (id=59)
[0] NonColoredLight (id=88)
MaxReflectionCount 2
RowCount 8
StartX 0
StartY 0
Tracing true
Width 600
OriginalObject Sphere (id=26)
Center Vector3D (id=60)
Color Color (id=61)
Radius 15.0
Reflectivity 1.0
ReflectionDepthCount 0
InRay Ray (id=29)
Direction Vector3D (id=62)
Origin Vector3D (id=63)
normal Normal (id=31)
Direction Vector3D (id=38)
Origin Vector3D (id=36)
LastGO Sphere (id=34)
Center Vector3D (id=64)
Color Color (id=65)
Radius 15.0
Reflectivity 1.0
LastRay Ray (id=35)
Direction Vector3D (id=38)
Origin Vector3D (id=36)
LastT 4.2468950498166125
min 4.2468950498166125
Origin Vector3D (id=36)
x 0.5809721247344103
y -0.023188722729640822
z 0.8135930637731328
Direction Vector3D (id=38)
x -0.32494278056317
y -0.02694400302823308
z 0.9453497818589108
r Ray (id=35)
Direction Vector3D (id=38)
Origin Vector3D (id=36)
Project Zip File
You are reflecting your ray exactly on the surface of the object. When checking intersections with the reflected ray, you hit on the same object again. You assume that checking if the distance is equal to 0.0 is enough to avoid this, but FP numbers are trickier than you think...
I'm trying to build a User Interface for the RGBike POV:
http://www.instructables.com/id/RGBike-POV-Open-project/
The program will display a bike wheel in form of a grid. The user can click
onto the single squares and changes the colour of these.
I want to build this applet in java. I'm stuck at drawing the wheel in the right way.
I need to have a sort of array of every rectangle, to export the colour later.
The best thing would be to draw a sort of circular table. Drawing each shape
With graphics2D to have each as a single object would be an idea, too. But that would
be around 860 single shapes, little bit too much to update them every time by paint().
Spoke POV has done such a user Interface for their project already:
http://www.ladyada.net/make/spokepov/software.html
But only their old python script is open source.
Be VERY grateful that I have previously generate a "segment" shape in the past ;)
This basically generates each segment individually (does some funky translation into real space) and maintains a cache of shapes which can be checked to see if the mouse falls within there bounds.
This is rather inefficient, but I think you get the idea.
I should also be noted, that I didn't bother with a backing buffer. Not to say it could use one, I just got away without it...
public class TestSpoke {
public static void main(String[] args) {
new TestSpoke();
}
public TestSpoke() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public static final int CIRCLE_COUNT = 16;
public static final int SEGMENT_COUNT = 80;
private Map<Integer, List<Shape>> mapWheel;
private Map<Point, Color> mapColors;
public TestPane() {
mapColors = new HashMap<>(CIRCLE_COUNT * SEGMENT_COUNT);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Map<Integer, List<Shape>> mapWheel = getWheel();
for (Integer circle : mapWheel.keySet()) {
List<Shape> segments = mapWheel.get(circle);
for (int index = 0; index < segments.size(); index++) {
Shape segment = segments.get(index);
if (segment.contains(e.getPoint())) {
mapColors.put(new Point(circle, index), Color.RED);
repaint();
break;
}
}
}
}
});
}
#Override
public void invalidate() {
mapWheel = null;
super.invalidate();
}
protected float getRadius() {
return Math.min(getWidth(), getHeight());
}
/**
* This builds a wheel (if required) made of segments.
* #return
*/
protected Map<Integer, List<Shape>> getWheel() {
if (mapWheel == null) {
mapWheel = new HashMap<>(CIRCLE_COUNT);
// The current radius
float radius = getRadius();
// The radius of each individual circle...
float circleRadius = radius / CIRCLE_COUNT;
// The range of each segment
float extent = 360f / SEGMENT_COUNT;
for (int circle = 0; circle < CIRCLE_COUNT; circle++) {
float startAngle = 0;
List<Shape> segments = new ArrayList<>(SEGMENT_COUNT);
mapWheel.put(circle, segments);
// Calculate the "translation" to place each segement in the
// center of the screen
float innerRadius = circleRadius * circle;
float x = (getWidth() - innerRadius) / 2;
float y = (getHeight() - innerRadius) / 2;
for (int seg = 0; seg < SEGMENT_COUNT; seg++) {
// Generate a Segment shape
Segment segment = new Segment(circleRadius * circle, circleRadius, startAngle, extent);
startAngle += extent;
// We translate the segment to the screen space
// This will make it faster to paint and check for mouse clicks
PathIterator pi = segment.getPathIterator(AffineTransform.getTranslateInstance(x, y));
Path2D path = new Path2D.Float();
path.append(pi, true);
segments.add(path);
}
}
}
return mapWheel;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Map<Integer, List<Shape>> mapWheel = getWheel();
for (Integer circle : mapWheel.keySet()) {
List<Shape> segments = mapWheel.get(circle);
for (int index = 0; index < segments.size(); index++) {
Shape segment = segments.get(index);
Color color = mapColors.get(new Point(circle, index));
if (color != null) {
g2d.setColor(color);
g2d.fill(segment);
}
g2d.setColor(Color.BLACK);
g2d.draw(segment);
}
}
g2d.dispose();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public static class Segment extends Path2D.Float {
public Segment(float radius, float thickness, float extent) {
this(radius, thickness, 0f, extent);
}
public Segment(float radius, float thickness, float startAngle, float extent) {
// Basically, we want to draw the outter edge from a to b angle,
// draw the connecting line from the outter to the inner,
// draw the inner from b to a angel and
// draw the connecting line from the inner to out the outter
// We want to span about 30 degrees, with a small gap...
// I want the gap to be a factor of the radius
Arc2D.Float outter = new Arc2D.Float(0, 0, radius, radius, startAngle, extent, Arc2D.OPEN);
Arc2D.Float inner = new Arc2D.Float(thickness / 2f, thickness / 2f, radius - thickness, radius - thickness, startAngle + extent, -extent, Arc2D.OPEN);
append(outter, true);
float angel = startAngle + extent;
Point2D p1 = getPointOnEdge(angel, radius);
Point2D p2 = getPointOnEdge(angel, radius - thickness);
// We need to adjust in for the change in the radius
p2.setLocation(p2.getX() + (thickness / 2f), p2.getY() + (thickness / 2f));
lineTo(p2.getX(), p2.getY());
append(inner, true);
angel = startAngle;
p1 = getPointOnEdge(angel, radius);
p2 = getPointOnEdge(angel, radius - thickness);
p2.setLocation(p2.getX() + (thickness / 2f), p2.getY() + (thickness / 2f));
lineTo(p1.getX(), p1.getY());
closePath();
}
public Point2D getPointOnEdge(float angel, float radius) {
angel -= 90;
float x = radius / 2f;
float y = radius / 2f;
double rads = Math.toRadians((angel + 90));
// This determins the length of tick as calculate from the center of
// the circle. The original code from which this derived allowed
// for a varible length line from the center of the cirlce, we
// actually want the opposite, so we calculate the outter limit first
float fullLength = (radius / 2f);
// Calculate the outter point of the line
float xPosy = (float) (x + Math.cos(rads) * fullLength);
float yPosy = (float) (y - Math.sin(rads) * fullLength);
return new Point2D.Float(xPosy, yPosy);
}
}
}
Is there a simple library that can parse a True Type font file and give me a list of vectors/points for me to render it myself? I'm talking about something similar to freetype, but for Java.
Java can do that out of the box. Some time ago, I explored this field.
I used Processing for easy/fast graphic rendering, but the code to access font information is pure Java. So maybe my little program can be of interest for you. I just pasted it in Pastebin
/**
Get a font and display glyphs with additional information
(bounding boxes, anchor & control points...)
by Philippe Lhoste <PhiLho(a)GMX.net> http://Phi.Lho.free.fr & http://PhiLho.deviantART.com
*/
/* File/Project history:
1.01.000 -- 2008/08/28 (PL) -- Some improvements.
1.00.000 -- 2008/08/28 (PL) -- Creation.
*/
/* Copyright notice: For details, see the following file:
http://Phi.Lho.free.fr/softwares/PhiLhoSoft/PhiLhoSoftLicence.txt
This program is distributed under the zlib/libpng license.
Copyright (c) 2008 Philippe Lhoste / PhiLhoSoft
*/
import java.awt.font.*;
import java.awt.geom.*;
final static int DEMO_ID = 2;
final static String FONT_NAME = "Times New Roman";
final static String STRING = "p#";
int FONT_SIZE, SCALE;
Graphics2D g2;
AffineTransform transform;
HashMap colorList = new HashMap();
void setup()
{
size(1000, 700);
smooth();
noLoop();
background(150);
noFill();
g2 = ((PGraphicsJava2D) g).g2;
// Move drawing to a convenient place
transform = new AffineTransform();
if (DEMO_ID == 1)
{
FONT_SIZE = 1;
SCALE = 300;
transform.translate(40, 320);
transform.scale(SCALE, SCALE);
}
else if (DEMO_ID == 2)
{
FONT_SIZE = 2;
SCALE = 300;
transform.translate(50, 500);
transform.scale(SCALE, SCALE);
}
else if (DEMO_ID == 3)
{
FONT_SIZE = 2;
SCALE = 300;
transform.translate(50, 500);
transform.scale(SCALE, SCALE);
}
// g2.setTransform(transform);
// And show the origin (and scale) of drawing
SetColor(#FF0000); // Update g2
strokeWeight(5);
Line2D.Double line = new Line2D.Double(-0.1, -0.1, 0.1, 0.1);
g2.draw(transform.createTransformedShape(line));
line = new Line2D.Double(-0.1, 0.1, 0.1, -0.1);
g2.draw(transform.createTransformedShape(line));
strokeWeight(2);
// Now, we get the font
Font font = new Font(FONT_NAME, Font.PLAIN, FONT_SIZE);
g2.setFont(font);
FontRenderContext frc = g2.getFontRenderContext();
if (DEMO_ID == 1)
{
//~ String str = "%&#";
GlyphVector glyphVector = font.createGlyphVector(frc, STRING);
for (int i = 0; i < STRING.length(); i++)
{
strokeWeight(2);
SetColor(#00FF00);
Shape vbs = glyphVector.getGlyphVisualBounds(i);
g2.draw(transform.createTransformedShape(vbs));
SetColor(#005000);
Rectangle r = glyphVector.getGlyphPixelBounds(i, null, 0.0f, 0.0f);
g2.draw(transform.createTransformedShape(r));
SetColor(#A0A000);
Shape lbs = glyphVector.getGlyphLogicalBounds(i);
g2.draw(transform.createTransformedShape(lbs));
SetColor(#0050F0);
strokeWeight(5);
Shape shape = glyphVector.getGlyphOutline(i);
g2.draw(transform.createTransformedShape(shape));
}
// Draw the whole string at once
SetColor(#F0A000);
strokeWeight(1);
Shape shape = glyphVector.getOutline();
g2.draw(transform.createTransformedShape(shape));
}
else if (DEMO_ID == 2)
{
GlyphVector glyphVector = font.createGlyphVector(frc, STRING);
for (int i = 0; i < STRING.length(); i++)
{
SetColor(#0050F0);
strokeWeight(5);
Shape shape = glyphVector.getGlyphOutline(i);
g2.draw(transform.createTransformedShape(shape));
HighlightPoints(shape);
}
}
else if (DEMO_ID == 3) // To test the SEG_CUBICTO case!
{
SetColor(#0050F0);
strokeWeight(5);
GeneralPath shape = new GeneralPath();
shape.moveTo(1.5, 0.0);
shape.lineTo(1.8, 0.0);
shape.quadTo(2.0, 0.3, 1.5, 0.3);
shape.lineTo(1.1, 0.3);
shape.curveTo(0.7, 0.3, 0.5, -0.5, 1.2, -0.2);
shape.lineTo(1.5, -0.3);
shape.closePath();
g2.draw(transform.createTransformedShape(shape));
HighlightPoints(shape);
}
}
void HighlightPoints(Shape shape)
{
strokeWeight(1);
PathIterator iterator = shape.getPathIterator(null);
float[] coords = new float[6];
float px = 0, py = 0;
while (!iterator.isDone())
{
int type = iterator.currentSegment(coords);
switch (type)
{
case PathIterator.SEG_MOVETO: // One point
print("Move ");
px = coords[0];
py = coords[1];
DrawMovePoint(px, py);
break;
case PathIterator.SEG_LINETO:
print("Line ");
px = coords[0];
py = coords[1];
DrawLinePoint(px, py);
break;
case PathIterator.SEG_QUADTO: // Two points
print("Quad ");
DrawControlLine(coords[0], coords[1], px, py);
px = coords[2];
py = coords[3];
DrawControlLine(coords[0], coords[1], px, py);
DrawControlPoint(coords[0], coords[1]);
DrawQuadPoint(px, py);
break;
case PathIterator.SEG_CUBICTO: // Three points
print("Cubic "); // Not seen yet...
DrawControlLine(coords[0], coords[1], px, py); // Connect to last point
px = coords[4];
py = coords[5];
DrawControlLine(coords[2], coords[3], px, py);
DrawControlPoint(coords[0], coords[1]);
DrawControlPoint(coords[2], coords[3]);
DrawCubicPoint(px, py);
break;
case PathIterator.SEG_CLOSE: // No points
}
iterator.next();
}
}
void DrawMovePoint(float x, float y)
{
float radius = 2.5 / SCALE;
Ellipse2D.Float e = new Ellipse2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
SetColor(#FF00A0);
g2.fill(transform.createTransformedShape(e));
SetColor(#FF00FF);
g2.draw(transform.createTransformedShape(e));
}
void DrawLinePoint(float x, float y)
{
float radius = 4.5 / SCALE;
Ellipse2D.Float e = new Ellipse2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
SetColor(#FF00FF);
g2.draw(transform.createTransformedShape(e));
}
void DrawQuadPoint(float x, float y)
{
SetColor(#80FF00);
float radius = 3.0 / SCALE;
Rectangle2D.Float r = new Rectangle2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
g2.draw(transform.createTransformedShape(r));
}
void DrawCubicPoint(float x, float y)
{
SetColor(#FF8000);
float radius = 5.0 / SCALE;
Rectangle2D.Float r = new Rectangle2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
g2.draw(transform.createTransformedShape(r));
}
void DrawControlPoint(float x, float y)
{
SetColor(#D0E000);
float radius = 3.0 / SCALE;
Ellipse2D.Float e = new Ellipse2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
g2.draw(transform.createTransformedShape(e));
}
void DrawControlLine(float x1, float y1, float x2, float y2)
{
SetColor(#FFA000);
Line2D.Float l = new Line2D.Float(x1, y1, x2, y2);
g2.draw(transform.createTransformedShape(l));
}
Color GetColor(color c)
{
Integer ic = Integer.valueOf(c); // New to 1.5! Cache values
Color k = (Color) colorList.get(ic);
if (k == null)
{
k = new Color(ic);
colorList.put(ic, k);
}
return k;
}
void SetColor(color c)
{
Color k = GetColor(c);
g2.setPaint(k);
}
You can use Batik to parse a TrueType Font and generate an SVG file (SVGFont). So I bet that you could use the libraries to get your vectors out.