This week's giveaway is in the Android forum.
We're giving away four copies of Android Security Essentials Live Lessons and have Godfrey Nolan on-line!
See this thread for details.
The moose likes Swing / AWT / SWT and the fly likes Shape.getPathIterator() called w/ null AffineTransform Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Android Security Essentials Live Lessons this week in the Android forum!
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Shape.getPathIterator() called w/ null AffineTransform" Watch "Shape.getPathIterator() called w/ null AffineTransform" New topic
Author

Shape.getPathIterator() called w/ null AffineTransform

Gabe Johnson
Greenhorn

Joined: Nov 23, 2005
Posts: 5
Hi. I posted this to the 'other APIs' group a few days ago (because it listed Java2D) and didn't garner any responses, so I thought i would try in the GUI forum.

Hello. I've written an object that implements Shape. This object has some crazy coordinates that it uses internally that must be mapped to the screen using a transformation. Consider the following code:

Sequence s; // this is my Shape implementation
Graphics2D g; // using this context
g.scale(0.5, 0.5); // scale by 50%
g.draw(s);

I have been under the impression that any transformations I do to the Graphics context (the scale statement) will be passed on to my shape when it draws by providing a non-null AffineTransform object in the Shape's getPathIterator(..) method. But when Sequence.getPathIterator(AffineTransform) is called, the transform object is null.

My hacky 'solution' to this problem is to externally tell the Sequence object about the transformation it should use, and then use that whenever the getPathIterator() method is called. But that is a bad solution because it requires too much work. I should be able to tell the Graphics context that there's a transformation I want it to use, and be done with it.

Anybody who helps will be visited with a series of fortuitous events and great karma.
Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
There is some room for wonder and curiosity in your question. The resolution may depend
on what you have done with your Sequence class. As far as the classes in the
java.awt.geom package are concerned I can offer a few observations. The Shape interface
and the classes that implement it and the AffineTransform class all seem to operate
independent of any graphics context. We can create, manipulate and transform primitives
without regard to any graphics context. The 'getPathIterator' methods in the Shape
interface take an optional (see api) AffineTransform which is used to transform the (x,y)
point(s) in the 'coords' array which is returned by the iterator. The PathIterator is
unaware of the graphics context that may render its shape. It has no way of knowing what
transform may exist in any graphics context that may be asked to render its shape. So we
must always pass in an AffineTransform (or null) to the PathIterator.

In the app below I tried to explore this for you by showing the difference between
working with AffineTransform and altering the graphics context transform. A PathIterator
only has access to a Shape. The graphics context of the Component that renders it can do
whatever it wants with the shape and the 'coords' array returned by its PathIterator.

One benefit of this design arrangement seems to be flexibility. We can create,
manipulate/transform and render multiple shapes without disturbuing/altering the graphics
context. Ths goes along with the idea of doing painting in the paint code and
manipulating primitives/state in the event code. So I would conclude that explicitly
setting the AffineTransform is a good thing. If this is objectionable you can always
ignore it (setting the AffineTransform in a PathIterator) and do everything by
transforming the graphics context.
Gabe Johnson
Greenhorn

Joined: Nov 23, 2005
Posts: 5
Thanks for your comprehensive reply. I think what is confusing me is that I thought the entire point of the AffineTransform passed in to getPathIterator was to tell the Shape what transformations the graphics context has at the moment. I was under the impression that:

g.scale(5, 5);
g.draw(myShape);

... would call myShape with an affine tranform of

5 0 0
0 5 0
0 0 1

but instead it is sending in null. It seems that what really goes on is that if I scale the graphics context, myShape doesn't need to know at all, and that the graphics context just interprets each point that the shape reports and does the transformation on it's end.

Your code does vindicate the approach that I ended up taking to get around the perceived bug, which was to have a number of different drawing contexts, each of which knew the tranformations they needed to use, so I can take my raw data, throw them at the drawing components, and just let them figure out how to transform themselves.
Brian Cole
Author
Ranch Hand

Joined: Sep 20, 2005
Posts: 862
Originally posted by Gabe Johnson:
I think what is confusing me is that I thought the entire point of the AffineTransform passed in to getPathIterator was to tell the Shape what transformations the graphics context has at the moment. I was under the impression that:

g.scale(5, 5);
g.draw(myShape);

... would call myShape with an affine tranform of

5 0 0
0 5 0
0 0 1

but instead it is sending in null.


But you agree that it does actually draw your shape
five times larger, right? It's just that they way
they implemented this is not what you expected.

When you call g.scale() it does use that transform
internally, but it never exposes it to your shape.
It applies at a more fundamental step of the rendering
process. That way scaling works for simple things
like g.drawLine(x1, y1, x2, y2) too. [In fact,
g.draw(Shape) is probably implemented such that the
straight edges of your shape are drawn with calls
to g.drawLine().]



So you may be wondering why Shape.getPathIterator()
even takes a transform. Well it comes in handy if you
ever have to write something like this:


bitguru blog
Gabe Johnson
Greenhorn

Joined: Nov 23, 2005
Posts: 5
I agree that it is scaling the shape correctly. I originally thought that all the scaling was done inside the shape by receiving an affine transform, but it looks like the transformation at drawing time is done internally in the graphics implementation. Your example of the drawLine(double, double, double, double) really made that point well. I think I understand all of this much better now. And, banging my head against the wall for a while had the unintended consequence of me learning some linear algebra, too. Thanks for your help!
Gabe Johnson
Greenhorn

Joined: Nov 23, 2005
Posts: 5
OK, I'm posting just to add something new that I've discovered.

If I want to scale and transform a graphics context by some amount, and if I use the g.scale(..) and g.transform(..) methods, the order that you do this matters. E.g.

g.scale(s,s);
g.translate(dx, dy);
g.draw(thing);

Gives you a different result from

g.translate(dx, dy);
g.scale(s,s);
g.draw(thing);

I understand why this happens, now that I'm aware of it, but I was initially unaware. Once you've scaled a graphics context, translations are also scaled. So, there you go.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Shape.getPathIterator() called w/ null AffineTransform
 
Similar Threads
How do I scale a shape?
How to Rotate with AffineTransform and keep the orignal coordinates ?
Moving and resizing a rectangle around
Problems moving shapes when set up own Shapes class extending Path2D.Double
Java2D: my Shape's getPathIterator is called with a null AffineTransform