Graphics2D transformation result does not match manual transformation - java

I am using Java's Graphics2D to draw on a component using AffineTransform's to manipulate my drawing.
Graphics2D offers an method transform for this, which takes an AffineTransform.
Sometimes I need to manipulate a point manually without using the builtin-transformation.
But when I try to transform a point using the same transformation I gave to Graphics2D.transform sometimes the resulting point is not the same.
The following code reproduces the problem (It's Scala code, but I think you can imagine the Java code.):
var transformationMatrix = new AffineTransform()
/*
* transformationMatrix is modified throughout the program
* ...
*/
override def paintComponent(g: Graphics2D) = {
super.paintComponent(g)
/* 1. transform using graphics transform */
g.transform(transformationMatrix)
g.setColor(Color.RED)
g.fill(new Rectangle(0, 0, 1, 1))
/* 2. transform point manually */
g.setTransform(new AffineTransform) // reset transformation to standard
val p0 = new Point(0, 0)
val pDest = new Point()
transformationMatrix.transform(p0, pDest)
g.setColor(Color.BLUE)
g.fill(new Rectangle(pDest.x, pDest.y, 1, 1)
}
Expected behaviour
The blue rectangle (manually calculated) overdraws the red one (calculated by transform).
Experienced behaviour
I admit that my transformationMatrix is not really integer, but that should'nt be the problem, should it?
affineTransform = 1.1, 0.0, 520.55
0.0, 1.1, 182.54999999999995
0.0, 0.0, 1.0
Is this a bug or am I missing some deep insight?
Edit: You can reproduce the bug, if you set transformationMatrix to
transformationMatrix = new AffineTransform(1.1, 0.0, 0.0, 1.1, 521.55, 183.54999999999995)
at the beginning of paintComponent. Please note, that g is of type Graphics2D.

Your transform is basically just a translation by (520.55, 182.55). And because it has fractional pixel values it is in fact sensitive to choice of roundoff. If you have anti-aliasing on, you'll actually get a 4-pixel red blob covering the pixels that are overlapped. Otherwise, the behavior (disagreement) you're seeing is reasonable given the ambiguity between rounding to integer and truncating to integer.

Well, you are doing two different things.
In (1) you are subjecting a shape (and it is irrelevant that it is Rectangle and not Rectangle2D.Double) to a transform that yields fractional coordinates. It only is painted aliased, because you haven't set specific rendering hints (RenderingHints.KEY_ANTIALIASING -> RenderingHints.VALUE_ANTIALIAS_ON, and RenderingHints.KEY_STROKE_CONTROL -> RenderingHints.VALUE_STROKE_PURE).
In (2) you are subjecting a point to the transform, and coerce it into aliased coordinates (Point instead of Point2D.Double). Then successively construct a rectangle from that point.
Clearly there may be very different things happening under the hood, and I wouldn't expect at all that transforming into an integer point versus painting floating point shapes in an aliasing graphics context yield the same results.
(Without testing) I would guess that a valid equivalent statement for (1) would be
g.fill(transformationMatrix.createTransformedShape(new Rectangle(0, 0, 1, 1)))

When you are performing the first step, g.transform(transformationMatrix), the Graphics composes that with the already present transformations. On the second step you are overrinding it with, g.setTransform(new AffineTransform), thus losing the previous transformation if any. You are assuming you are back to the start but it might not be true.
Make a getTransform() before step 1 and another after step 2 to verify those are the same.

Whenever you work with floating point coordinates, you should use the '2D' version of graphical objects if you want correct results. I didn't read that from 'book', so I can't quote, it is just experience with it.
Here is my ugly java code that produces result that you are expecting.
AffineTransform transformationMatrix = AffineTransform.getTranslateInstance(520.55, 182.54999999999995);
transformationMatrix.scale(1.1, 1.1);
((Graphics2D) previewGraphics).transform(transformationMatrix);
previewGraphics.setColor(Color.RED);
((Graphics2D) previewGraphics).fill(new Rectangle(0,0,1,1));
((Graphics2D) previewGraphics).setTransform(new AffineTransform());
Point2D p0 = new Point2D.Double(0, 0);
Point2D pDest = new Point2D.Double();
transformationMatrix.transform(p0, pDest);
previewGraphics.setColor(Color.BLUE);
((Graphics2D) previewGraphics).fill((Shape) new Rectangle2D.Double(pDest.getX(), pDest.getY(), 1, 1));

Related

How to draw star in java swing using fillPolygon

I'm having trouble setting the coordinate of the star are there any better solution for this. I cannot get the the correct shape. Can someone help me on this?
public void star(Graphics shapes)
{
shapes.setColor(color);
int[] x = {42,52,72,52,60,40,15,28,9,32,42};
int [] y = {38,62,68,80,105,85,102,75,58,20,38};
shapes.fillPolygon(x, y, 5);
}
Sun's implementation provides some custom Java 2D shapes like Rectangle, Oval, Polygon etc. but it's not enough. There are GUIs which require more custom shapes like Regular Polygon, Star and Regular polygon with rounded corners. The project provides some more shapes often used. All the classes implements Shape interface which allows user to use all the usual methods of Graphics2D like fill(), draw(), and create own shapes by combining them.
Regular Polygon Star
Edit:
Link
Honestly, I'd use the 2D Graphics shapes API, they allow you to "draw" a shape, which is simpler (IMHO) then using polygon. The advantage is, they are easy to paint and transform
Having said that, the problem you're actually having is the fact that you're not passing the right information to the fillPolygon method.
If you take a look at the JavaDocs for Graphics#fillPolygon, you'll note that the last parameter is the number of points:
nPoints - a the total number of points.
But you're passing 5, where there are actually 11 points in your array
Something like...
shapes.setColor(color);
int[] x = {42,52,72,52,60,40,15,28,9,32,42};
int [] y = {38,62,68,80,105,85,102,75,58,20,38};
shapes.fillPolygon(x, y, 11);
should now draw all the points, but some of your coordinates are slightly off, so you might want to check that
The second to last number of your Y should be 60 not 20
g2.setColor(color);
int[] x = {42,52,72,52,60,40,15,28,9,32,42};
int[] y = {38,62,68,80,105,85,102,75,58,60,38};
g2.fillPolygon(x , y, 11);
I'm having trouble setting the coordinate of the star are there any better solution for this
Check out Playing With Shapes. You should be able to use the ShapeUtils class to generate your shape.
This class will generate the points for you so you don't need to manage every pixel.
a star has 10 points ppl mind that not 11
setBackground(Color.black);
int[]x={250,150,0,150,100,250,400,350,500,350};
int[]y={100,200,200,300,400,300,400,300,200,200};
g.fillPolygon( (x),(y),10);
setForeground(Color.cyan);
this will help to draw a star with black bg and cyan foreground

Small bug in Koch's Snowflake Implementation

So I'm programming a recursive program that is supposed to draw Koch's snowflake using OpenGL, and I've got the program basically working except one tiny issue. The deeper the recursion, the weirder 2 particular vertices get. Pictures at the bottom.
EDIT: I don't really care about the OpenGL aspect, I've got that part down. If you don't know OpenGL, all that the glVertex does is draw a line between the two vertices specified in the 2 method calls. Pretend its drawLine(v1,v2). Same difference.
I suspect that my method for finding points is to blame, but I can't find anything that looks incorrect.
I'm following the basically standard drawing method, here are the relevant code snips
(V is for vertex V1 is the bottom left corner, v2 is the bottom right corner, v3 is the top corner):
double dir = Math.PI;
recurse(V2,V1,n);
dir=Math.PI/3;
recurse(V1,V3,n);
dir= (5./3.)* Math.PI ;
recurse(V3,V2,n);
Recursive method:
public void recurse(Point2D v1, Point2D v2, int n){
double newLength = v1.distance(v2)/3.;
if(n == 0){
gl.glVertex2d(v1.getX(),v1.getY());
gl.glVertex2d(v2.getX(),v2.getY());
}else{
Point2D p1 = getPointViaRotation(v1, dir, newLength);
recurse(v1,p1,n-1);
dir+=(Math.PI/3.);
Point2D p2 = getPointViaRotation(p1,dir,newLength);
recurse(p1,p2,n-1);
dir-=(Math.PI*(2./3.));
Point2D p3 = getPointViaRotation(p2, dir, newLength);
recurse(p2,p3,n-1);
dir+=(Math.PI/3.);
recurse(p3,v2,n-1);
}
}
I really suspect my math is the problem, but this looks correct to me:
public static Point2D getPointViaRotation(Point2D p1, double rotation, double length){
double xLength = length * Math.cos(rotation);
double yLength = length * Math.sin(rotation);
return new Point2D.Double(xLength + p1.getX(), yLength + p1.getY());
}
N = 0 (All is well):
N = 1 (Perhaps a little bendy, maybe)
N = 5 (WAT)
I can't see any obvious problem code-wise. I do however have a theory about what happens.
It seems like all points in the graph are based on the locations of the points that came before it. As such, any rounding errors that occurs during this process eventually start accumulating, eventually ending with it going haywire and being way off.
What I would do for starters is calculating the start and end points of each segment before recursing, as to limit the impact of the rounding errors of the inner calls.
One thing about Koch's snowflake is, that the algorithm will lead to a rounding issue one time (it is recursive and all rounding errors add up). The trick is, to keep it going as long as possible. There're three things you can do:
If you want to get more detailed, the only way is to expand the possibilities of Double. You will need to use your own range of coordinates and transform them, every time you actually paint on the screen, to screen coordinates. Your own coordinates should zoom and show the last recursion step (the last triangle) in a coordination system of e.g. 100x100. Then calculate the three new triangles on top of that, transform into screen coordinates and paint.
The line dir=Math.PI/3; divides by 3 instead of (double) 3. Add the . after the 3
Make sure you use Point2D.Double anywhere. Your code should do so, but I would explicitely write it everywhere.
You won the game, when you still have a nice snowflake but get a Stackoverflow.
So, it turns out I am the dumbest man alive.
Thanks everyone for trying, I appreciate the help.
This code is meant to handle an equilateral triangle, its very specific about that (You can tell by the angles).
I put in a triangle with the height equal to the base (not equilateral). When I fixed the input triangle, everything works great.

Following and Rotating about a path in OpenGL

I'm attempting an exercise where a vehicle is following the Lemniscate of Bernoulli (or more simply, a figure-8 track). I want to use glTranslatef and glRotatef to achieve this. So far, I have been able to successfully get the vehicle to follow/translate along this path by using the parametric form as follows:
X = (width * cos(t)) / (1+sin^2(t))
Y = (width * cos(t) * sin(t)) / (1+sin^2(t))
Where t is in -pi, pi
In the code, this is as follows:
carX = (float) ((Math.cos(t) / (1 + Math.sin(t)*Math.sin(t))));
carY = 0.0f;
carZ = (float) ((Math.cos(t) * (Math.sin(t))) / (1 + Math.sin(t)*Math.sin(t)));
gl.glTranslatef(carX,carY,carZ);
So that works well enough. My problem now is rotating the vehicle so that it follows the path defined by the Lemniscate of Bernoulli. I want to achieve this by using glRotatef to rotate around the Y axis, but I am not sure how to proceed in regards to finding the angle to input in glRotatef. The rotate is currently in place so that it only manipulates the vehicle, and appears to just need the correct mathematics to follow the path.
Things I have tried:
Using the derivative of the X and Y forms listed above. I used them independently of each other, because I'm not sure how to/if they need to be combined to be used for the angle. With some
minor manipulation they follow the straight areas near the origin,
but broke down around the curves.
Directly finding the tangent of the
t value and converting to degrees. Erratic spinning resulted.
If anyone has any suggestions that may be better than the glRotatef method, that would be appreciated as well. I've seen that gluLookAt may be helpful, and I may attempt to find a solution using that.
(Note: I'm working in JOGL using Java and the FFP, but I'm comfortable with C/C++ code snippets.)
assuming camera view is the driver's view, gluLookAt is exactly what you need! based on your carX,carY,carZ computations (assuming that the math is good), you can store previous values and use it:
//globals & imports:
import javax.vecmath.*;
Vector3f current = new Vector3f();
Vector3f prev = new Vector3f();
computation is as followed:
//on drawing:
prev.set(current);
current.x = (float) ((Math.cos(t) / (1 + Math.sin(t)*Math.sin(t))));
current.z = (float) ((Math.cos(t) * (Math.sin(t))) / (1 + Math.sin(t)*Math.sin(t)));
glu.gluLookAt(current.x, 0f, current.z,
current.x - prev.x, 0f, current.z - prev.z,
0f, 1f, 0f);
i'll test it when i get back home, to make sure it's working, but as far as i can tell, this should do the trick.

Determining if a point lies within an ellipse, including the edge

I am trying to test if a point lies within a circle and if the point is on the perimeter, it should be included in the results. However, Java's contains() implementation uses less than instead of less than or equal to. For example consider this snippet:
Ellipse2D.Double circle = new Ellipse2D.Double(0, 0, 100, 100);
System.out.println(circle.contains(50, 0));
System.out.println(circle.contains(50, 100));
System.out.println(circle.contains(0, 50));
System.out.println(circle.contains(100, 50));
System.out.println(circle.contains(50, 50));
This prints the following:
false
false
false
false
true
How can I achieve a value of true for all of those cases?
You have to decide what kind of tolerance your method will use. While your example uses points that are expressible in floating point, there are many points along the border of the ellipse which will not be, and so deciding whether a point is "on the border" isn't clear-cut. If you don't much care, then I would suggest making the ellipse slightly "bigger" than you actually want and using the built-in contains() method.
If you want to write your own method, it's as simple as taking the formula for an ellipse, plugging in the X and Y values of the point you wish to test, and observing the result:
bool isInsideOfOrOnBorderOfEllipse = ((x*x)/(a*a) + (y*y)/(b*b)) <= 1;
Note that this still runs into the problem of non-representable points, so some points that you think should be "on the border" won't be.
Update: Given that you're just using the built-in ellipse object (and thus specifying height/width rather than the general ellipse parameters) it would be worthwhile to have a look at the source for contains() here: http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/ffa98eed5766/src/share/classes/java/awt/geom/Ellipse2D.java
Derive a new class, and then override contains(). In the overridden version, just copy the code, except use <= instead of < and you should be good.
You could use the method intersects. As javadoc says: Tests if the interior of this Ellipse2D intersects the interior of a specified rectangular area. Although it is not a circle (best representation of a tolerance around a point) works pretty well
This snippet should work for any x, y you want to check:
int size = 2;
...
ellipse.intersects(x - (size/2), y - (size/2), size, size);
It is just a rectangle around the point of interest. More size, nore tolerance
Maybe getDistance() can help you here? Points on the prerimeter should return 0.

Graphics2D - Math plot - Ploygon - how to get all plot points

I've just tried to write "line" code to visualize a simple math;
Here it is
Ploygon polygon=new Ploygon();
int x,y;
ploygon.addPoint(0,0);
polygon.addPoint(width,height);
g.drawPolygon(polygon);
The code gives y=x effect;
OK... it is quite simple code; But the thing I am interested to get is points each N pixels during the statement period as {x0,y0}{0,0} and {x1,y1} {width,height} and that is the problem :(
The polygon xpoints array is not handy because it may contain just the same points which were added when addPoint(x,y) method was invoked; so in my case there just two added points which are connected by Polygon but what about all the rest points which stay between these points {x0,y0}{0,0} and {x1,y1} {width,height} ? How to get them?
For example. Coming back to the previous snippet how to find out what point x,y value is when (height%N)=0 etc?
Is there the most optimal way?
Thanks
What you have to realise here is that you are no longer working with pixels/coordinates per se, but you are working with vectors. You'd get much the same image from a polygon contained the coordinates (-500,-500) and (500,500) which is drawn onto a Graphics object which represents the (clipped) area from (0,0) in the bottom left to (100,100) in the bottom right. (ignoring for now that the actual coordinate system of Graphics has an inverted y-axis).
Therefore you have to solve this in a more back-to-basic's Math way rather than a “read the pixels” way. Unless you just want to determine if a given point is in the shape (for which the Shape interface offers a built-in method), you would be looking at calculating the slope of a line and determining functions which represent your line. For instance continuing from the example you have two points (-500,-500) and (500,500) which gives a slope of 1000/1000 = 1. So you could rewrite that function in terms of your x-coordinates as f(x) = -500 + (x + 500). Then if you want to know if the point (100,200) is on that line all you need to do is calculate f(100) and see that it isn't.
Getting back to your example, finding points which match a predicate (height%N =0), we'd be looking for f(x) == 0 mod N and so 'all' you'd need to do is solve the equation for x.

Categories

Resources