|
Replies:
24
-
Last Post:
Jul 27, 2007 1:58 PM
by: Jim Graham
|
Threads:
[
Previous
|
Next
]
|
|
|
|
|
|
AffineTransformation
Posted:
Jul 23, 2007 11:49 AM
|
|
|
So not getting the essential idea here. I change the AT on the G2D to paint "elsewhere" on the screen, let's just say I have
public void paint() (Graphics g) { yakkity yak yak yak....
g2d.translate(100,0); }
That translates the device space 100 pixels over. Great. But the next time I invoke paint, the device space is back to 0,0, as if I never told the graphics to move over 100 pixels.
So I thought if I want to permanently change the device space on the g2d I use setTranslation(100,0) but the same thing happens...
So it seems that no matter what I do, the device space of the g2d will always be 0,0 when paint is invoked again- I can't permanently move the device space of the g2d. But then the question arises- how do I know when the device space is reset? At the end of paint? Can this be an accurate description of how this all works?
I know I must have it all wrong, but what am I not understanding? Feel free to hold forth with anything useful.
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 23, 2007 11:55 AM
in response to: swv
|
|
|
You can't rely on "persisting" the changes that you do to the Graphics object. Moreover, it's a very dangerous practice to change the state of Graphics and not restore it when your painting method is done, since this Graphics may (or may not) be reused in other places during the painting process.
Why not set this transformation every time on repaint (on a copy Graphics)?
|
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 23, 2007 1:07 PM
in response to: kirillcool
|
|
|
Good questions. I am not trying to "do" anything, especially I am not trying to change the graphics without changing them back, though I can see how one would infer that !
I am really just trying to understand the processing model. For instance, you said- don't use setTransform because other methods (scopes) may depend on the transform you just changed. Fair enough. But that begs the question- when is the graphics transform state reset, by what method and what logic is used to arrive at what that state should be? That's the kind of information I am looking for. All I know is that. somehow, mysteriously, the transform gets changed when I thought I had changed it for good, and what's more, it changes so as to be totally reset.
Is that documented behavior? Because it seems to me that I have to know what state the graphics transform is in before I transform it with my own code. So if mehtods are resetting it and that's known and expected, I need to be clued in.
After all, what good is it to define an translation that is 50 pixels right if I can't count on the state of the graphics at an given instant? 50 pixels right of WHERE? I can query the graphics, but that presupposes a certain programing model- one where the information needed to create an AT is synchronous and co-located (scope wise) , both with the graphics object
Let's say my immediate real-world goal is to place shapes where the user drags them. Or again to layout a screen of shapes. Practical stuff. I have no idea what AT to assoc with a Shape (making the AT the Shape's stateful member field) so it gets drawn somewhere I want it because, well, who knows what state the graphics is going to be in next time I see it? All painting is relative to the graphics device space coordinate system, but unless I have some guarantees about what that might be at some particular time... see what I am saying/
Don't tell me I'm wrong , because I KNOW I am wrong !! The question is, what have I got all wrong.
I am hoping there's an elephant in this room somewhere and someone will tell me where...
|
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 23, 2007 1:21 PM
in response to: swv
|
|
|
Are you trying to store the model information in the view (Graphics)? When the user drags the Shape, you process the mouse event and store the current X / Y offsets somewhere. Don't store them as an AT on the Graphics - store it in the model, and use that information during the rendering.
As to your question about Graphics reset - you can debug the painting pipeline to see exactly when the Graphic object is created / changes its state, but i'd venture to say that it's not necessary in your case. Store the offsets in model, and use them during the painting.
Kirill
|
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 23, 2007 2:18 PM
in response to: kirillcool
|
|
|
ya that is what I am thinking also.. thanks, kirillcool...!!
|
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 23, 2007 7:30 PM
in response to: kirillcool
|
|
|
The thing is, the position of the Shape, after its moment of creation seems to never change (if you ask the shape i.e. shape.getBoundingBox() )
So you might as well just make all your shapes at UpperLeftHandCorner 0,0 and keep some sort of running total about how the graphics object has been transformed with respect to each shape before it drew that shape.
Because if you AT the graphics over here, there or anywhere and draw the shape, the shape will always report that it lives where ever you created it originally, no matter where it's painted on the screen.
If you want it to appear at some point, then 100 pixels to the left of that point later on, and so on and so on then, finally, you want to know if the mouse is over a particular shape, you have to somehow keep a record of the cumulative total of all the AT changes which were applied to the graphics as THAT PARTICULAR shape was painted and then calculate some transform on the mouse event point that places it back to where the shape thinks its home sweet home is, was and always will be, then ask the shape if perhaps shape.contains(mouseEvent.getPoint())
I think I got that right without exaggerating.
Yucky Ducky.
Can this be how Duke meant for things to be?
|
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 23, 2007 8:00 PM
in response to: swv
|
|
|
And that is exactly what a model is for, to store the current state of your shape. So, instead of applying a transform on the Graphics (view), update the model. Then, during the paint of the view, query the model on its current state.
|
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 24, 2007 4:59 AM
in response to: kirillcool
|
|
|
The state of the shape should be reflected by its methods, not contradicted by them.
The position of a Shape is a property of a Shape given to it by its designers and it represents its position in user space. It didn't have to have that property at all, as we can define a Rectangle's width and height without referencing it's position in any space as we do in geometry.
Having defined that property , its incumbent upon the API to now either keep the state current or declare that Shape is immutable. If a Shapes position is immutable, then say so. I have never seen anything to indicate that this was a design decision, but then again all that means for sure is I've never seen it.
I use Rectangles in headless applications, quadtrees and the like, where their immutable position is of no consequence to me, just because of the quirks of my application. That's not the normative case. The normative case is Shapes appear on a screen and as such are inevitably going to be subject to positional mutation.
There's always a way to program what you want, granted, but that's not to the point. We understand the model view controller paradigm. The point is, shapes, because of their screen-based environment, should have an API that let's you put them at point x, y by assigning that point as their ULHC as you can components. Who cares how it's implemented underneath that layer?
Either the API developers are going to program that functionality or developers are going to do it over and over again for themselves, because somehow the model has to be brought into sync with the view. It's not just some application-specific specialized functionality we're talking about here- it goes to the heart of what it means to be a Shape on a screen before user's eyes.
None of the above should be taken as an insult to anyone who worked hard on Shape et. al. The purpose here is to confirm other developer's suspicions who may be encountering these issues themselves, to give clear and detailed feedback on the API from a consumer's point of view and to open myself up to being corrected where I am off.
Cheers!
|
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 24, 2007 10:22 AM
in response to: swv
|
|
|
Just to continue on this meditation for any future forum browsers who find it useful....
Merely recording in a 3rd party object the "actual" position of the Shape for the purpose of rendering isn't going to cure Shape's woes when it's used on screen in a position-mutable capacity.
Shape has several methods which depend upon accurate representation its origin (ULHC) to perform their function. contains() and intersects() are two of these.
If anyone were to query any of these methods, they'd get a "wrong" answer also, just as they would if they asked it for its bounding box. So it's not just about rendering in the proper place via translation or translating mouse events.
Creating a custom Shape implementation which overrides contains and intersects to report accurately is fine, but existing implementors of Shape all have the uncorrected problem. Are we to override them also?
If composition is used, then any any access to the internal Shape has to be forbidden, since it provides uncontrolled access to the defective contains and intersects. But Shape is what we want to draw, so , really forbidding access to it is self-defeating in the extreme.
Now we have quite a little design problem on our hands and it looks like no really clean options. This is because Shape is advertising functionality that it doesn't really keep up with under likely usages. It's not like we're translating into polar coordinates or Riemann space.
|
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 24, 2007 10:43 AM
in response to: swv
|
|
|
Sorry to be obtuse, but why do you store the model information in a Shape object which is a view? If you need to store location, bounds etc, create your own view-independent model implementation, and have model2view and view2model transformations. Using java.awt.Shape to store this information creates coupling between the model and the view because you're restricted by Shape API and unnecessarily "swayed" into the view geometry.
Kirill
|
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 24, 2007 12:02 PM
in response to: kirillcool
|
|
|
kirillcool.. not being obtuse. When I refer to a shape I mean the implementations of Shape... not the interface.. just talking generically I am not looking to put model info into the view.
But still,. my mistake ! Implementations of Rectangle don't have this problem, actually their contains() and intersects() methods work if you don't try to set their position by
rectangle.getBounds().setLocation(shapeLocation) which doesn't work
but use
rectangle.setLocation(shapeLocation)
which is where my misunderstanding started from, also on this post- http://forums.java.net/jive/thread.jspa?messageID=228037𷫅
Rectangle and other Shape implementations will draw at the location specified if you do as above. So also with the other implementations of Shape. So actually, I think, the whole problem goes away... Shapes don't have the problems I thought they did.
Well, there you go! I was wrong - woo hoo!
|
|
|
|
|
|
|
|
Re: AffineTransformation
Posted:
Jul 24, 2007 2:50 PM
in response to: swv
|
|
|
> just talking generically I am not looking > to put model info into the view.
No, you're using core view classes in your model. The Shape and its derived classes are for painting on the screen. Although they might seem like a good option to use in the model, they aren't in most cases (as you're starting to see), since they're targeting the screen (view).
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 24, 2007 4:43 PM
in response to: swv
|
|
|
> rectangle.getBounds().setLocation(shapeLocation) which doesn't work
I think this is the source of your misunderstanding (that snippet of code was never intended to work) which you found out later in the thread you linked in:
> which is where my misunderstanding started from, also on this post- > http://forums.java.net/jive/thread.jspa?messageID=228037
Basically, to modify the Shape you need to invoke a method on the original object. The Shape interface only includes methods to ask for information about the Shape, not to modify the Shape. Even methods like getBounds which return an object are not meant to provide an indirect means to modify the object - the returned object is only an encapsulation of an answer that could not be expressed using a simple java type like an int or a boolean.
The fact that some of the objects used as return values for these methods have methods of their own that make them mutable is a by-product of the fact that those objects have a life other than to be used as a return value. In other words, a Rectangle object is useful in many circumstances in which you might want to move them around - but in the case of the return value of getBounds(), it is only being used to house 4 numbers in a convenient and recognizable way...
...jim
=========================================================================== To unsubscribe, send email to listserv@java.sun.com and include in the body of the message "signoff JAVA2D-INTEREST". For general help, send email to listserv@java.sun.com and include in the body of the message "help".
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 24, 2007 5:30 PM
in response to: Jim Graham
|
|
|
Jim- right on and well said- that was exactly my confusion....
Kirillcool said: you're using core view classes in your model. The Shape and its derived classes are for painting on the screen. Although they might seem like a good option to use in the model, they aren't in most cases (as you're starting to see), since they're targeting the screen (view).
Well Rectangle keeps state in just the way you say it shouldn't, if you mean by state enough information to draw it to the screen in a specific place. It has an x and a y and you can mutate those through it's methods and as a result of that mutation it will draw itself elsewhere. That's state. .
So I am not understanding what you're saying. In what sense is Rectangle not keeping the state you think I want to keep, because I want to keep the same "state" it has.
It's not that views shouldn't hold state, it's that they shouldn't hold non-view state. Renderers etc have state to them, colors Components etc etc.... so also does rectangle have a state- the x and y coordinates of it's upper left hand corner (ULHC going forward). Are you saying Rectangle is wrong for holding onto this state?
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 24, 2007 5:44 PM
in response to: swv
|
|
|
Sorry, i'm tired to repeat the same thing over and over, so this will be the last time.
If you're storing Rectangle objects in your model, and then during the painting of the view you're passing the same exact Rectangles, then you're creating coupling between the model and the view. The Rectangle object doesn't draw itself - you pass it to the draw* methods of Graphics which draws it. That is indeed state, but for that specific view. Not only that, but just calling setLocation on your rectangle doesn't magically redraw it on the screen in the new location (like, say, changing a node in the default implementation of tree would).
And if your renderers hold colors in them, then it's not the best renderer implementation. You get the model object in the get*Renderer, and you should set the text, icon and colors according to that model object. After the renderer has been rubber-stamped, it should be treated as "gone" (even though you'll return the same exact object for the next cell / row).
Kirill
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 24, 2007 6:16 PM
in response to: kirillcool
|
|
|
Jim I wasn't saying you said that.. kirillcool said that, unless you're the same person LOL...the "you" is K not Jim...
To Kirillcool- For the example which I put out, Rectangle, I observed that it keeps state as to its position and wondered why that was wrong. So I am not sure about that still. There is only one instance of a renderer that's reset with different state over and over again... I understand that, but it has state defined within it because x and y positions are the sine qua non of a rectangle.
I get that Rectangle is the is like Renderer- one Rectangle, reset it's state before rendering, render, repeat. Only one instance of Rectangle is necessary to draw any Rectangle. I understand that. What I don't understand is that Rectangle is *wrong* for having x and y coordinates as members.
I just think we are talking past each other. Maybe it's a language thing somehow.
Well anyway thanks to all for the input and cheers!
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 26, 2007 8:19 AM
in response to: swv
|
|
|
This thread was long and somewhat confusing. For anyone following it, I have cleared up some issues to myself and perhaps others who find this thread.
My goal was to position arbitrary shapes at will upon the screen and then change their location (but not shape). That's it.
Here's what I concluded.
1) Some Shape (implementations) have this built in. Rectangle is one of those- it as a setLocation method.
However, generally, there is no Shape (the interface) method for doing this, so you cannot define your methods to take a Shape and then reliably invoke any method, even if you cast to say Rectangle or Arc2D.
This is because having no method defined in the interface, what location-mutation methods are to be had in the various implementations are all named differently and work differently, and also many Shape implementations don't even provide anything like a location-mutator, as is their right.
2) Despite there not being any promise of having a location to report or mutate, Shapes in fact DO live in an x-y coordinate space, called a user space, and they are definitively located in that user space. More precisely, by the a judgment of a winding rule, some x-y points are contained within a Shape and some are not; without this they wouldn't be a Shape.
All of the methods defined in Shape - contains() intersects() getPathIterator() and getBounds() - all refer implicitly to their Shape's "location" (not to say ULHC) in user-space All these methods consider each point of the Path which makes up the boundary of the Shape to be a specific, definite point and will answer your question ( contains() intersects() ) or return some value (getBounds() getPathiterator() ) based on those points.
3) Remembering that the goal as to shuffle these around the screen, and having them at definite places which are not so easily mutated, what's a developer to do?
I chose to pile them all up in the same "place", by which I mean, their location, if they have one, is at user-space 0,0 and their getBounds has an ULHC of 0,0.
Now if I drew them where they "think" they are, it would look like a bunch of Shapes piled up on top of each other in the ULHC of my screen.
In order to draw them where I want them, it's a matter of remembering where they were drawn last, getting the Graphics into device space 0,0 (which coincides with user-space 0, 0 ) and now that device and user spaces are in agreement, using one of the methods of graphics to relocate where the "tip" of the graphics pen is located and hence where it will draw. IF I want the Shape to be at 100,500, then I relocate the graphics there and so on.
Now my shapes appear where I want them. But their other methods, contains() intersects() et.al. are all liars, or at least, out of sync with where the Shape has been drawn.
To bring these other methods into line, you have to, in the case of intersects and contains, first displace the point you're asking about so that it is relocated back into that ULHC where all the Shapes think they live, piled up one on top of the other.
This is done by translating the point by just the amount the Shape has been translated when it was drawn, and just before invoking Shape(implementation) contains().
To make getBoundingBox() and getPathIterator() behave as hoped, an AT should be applied to their result, again using the displacement the Shape was subject to when it was drawn.
So here are some lessons, at lest for me-
Shape and it's implementations are not like Components and neither are their bounding boxes. Some have methods to relocate themselves via setLocation() or setFrame()- but for your purposes- ignore these, they're red herrings.
By my measure, Shape and its implementations have a built in contradiction with respect to the separation between Model and View, which could be a source of confusion.
They do, in fact, live at some definite place. This is manifested by the points reported by their getBounds() and PathIterator(), both of which are implemented by Shape implementors. Yet these implementors are counted as Views, not Models.
What the Model includes, oddly enough, is the offset at which the Shape is to be drawn- the place where it appears to the user and the place where it acts "as if" it is. That "place" we'll call it's location going forward. One possible implementation of this "place" (and not th most efficient perhaps) is an AT. We were advised to NOT let this AT be part of the Shape implementation- because the Shape implementation is the view and the AT is model-stuff.
But that AT, at least this is how it seems to me , is more View than anything else, while the Shape itself, which is to say the Model, lives unchanging elsewhere.
It's not clear to me that the changing location is a model-point and the points of the path iterator are "view" points. That seems solidly backwards to me.
Moreover, it just seems to me that Shapes are willy-nilly going to live SOMEWHERE, "we" might as well just fess up to the fact that not only are they located in space, but that space is going to be presented to the user on a device, probably, a screen.
Because we have no, efficient, non- list-o'-points way to define Shapes, they have to be somewhere. Why not just accept that and provide a way to mutate that "somewhere" along with consistent contains() intersects() getBounds() and getPathIterator()?
Anyway... I hope this clears up some things for someone.
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 26, 2007 1:49 PM
in response to: swv
|
|
|
Hi swv,
I'm definitely not comfortable with my own Java2D capabilities but recently I've been working with the Area class alot and I think you should take alook at it. It is a mutable shape implementation as far as I know - take alook at Area.transform(AffineTransform at). With this you wouldn't have to translate your Graphics object at all, and your areas will still be where you left them the next time paint is called. Also, Area can take any Shape in its constructor and it will try to construct a suitable Area interpretation of the given shape. Just remember to translate the new Area as necessary. I hope this may be of some assistance.
Regards,
Pierre
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 26, 2007 2:10 PM
in response to: irond13
|
|
|
Hi irond13,
Thanks ! Area is indeed one of the Shape implementors that provide a way to mutate position, just in the way you describe.
I cleared everything up for myself and hopefully others who want to do the same thing and posted my conclusions at the post on this thread with the time:
Posted: Jul 26, 2007 8:19 AM (cut, paste and find)
I have a project in my IDE called "other peoplez problems" and I entered your Component issue code in there, but I don't have the dependencies (substance and org.desktop. etc etc)...
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 27, 2007 8:30 AM
in response to: swv
|
|
|
Hi swv,
what is it that you're looking for without dependancies on SwingX and Substance? If it's the whole test app then I can't help since the whole point of the app was to show the difference between Substance and Metal. If it's my RoundedPanel you want, I can make a plan - removing its dependancy on Substance is easy enough but since it extends JXPanel and makes use of ShapePainter to paint itself, this will take a bit a longer. In any case I would really recommend getting the SwingX library, especially if you're doing alot of custom painting. Just let me know what you want & I'll see what I can do.
Regards, Pierre
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 27, 2007 1:58 PM
in response to: irond13
|
|
|
I wanted to follow up this suggestion with a warning.
The Area object is not a generalized Shape. It has some very specific properties which make it inappropriate for some Shapes. Most of these should be documented in the latest javadoc for the Area class.
1. Area cannot represent shapes which enclose no space, such as a Line. Line2D is a Shape since it has a path and can be stroked, but it cannot be filled. An Area constructed from a Line2D is an empty Area containing no geometry since an Area only represents enclosed regions of space.
2. Area will implicitly close a Shape upon construction. If you define an open triangle consisting of 2 lines and you draw that Shape then only 2 lines will be drawn. If you construct an Area from that Shape and then draw the Area, 3 lines will get drawn to represent the closed triangle.
3. Area performs a *lot* of calculations on a Shape when you construct it. The purpose of these calculations is to internalize a representation of the regions of space that are enclosed to make the CAG operations (union, intersect, subtract, xor) easy to implement. Those calculations are pretty fast if your goal is to perform CAG with the Shape, but they are unnecessary if you are not using it to perform any CAG. The CAG operations go much faster because of these precalculations, but if your only goal is to use Area to make a Shape relocatable then the Area class (and its corresponding caveats and precalculations) is overkill.
4. When you use the Area.transform(AT) method, the precalculations that were done when the Area was constructed must be reexecuted and that takes additional time as well.
As a result, I wouldn't use the Area class unless you are planning to perform CAG operations on the Shapes.
However, the GeneralPath class (and now Path2D in JDK 6) offers very similar capabilities. There is a GeneralPath.transform(AT) method just as on the Area class. The even better news is that GeneralPath is simply a repository of geometry, unlike Area, so it is very fast to create one from an arbitrary Shape and it is also fast to transform it.
In the end, though, if you are having to implement any kind of mechanism to manage your Shape objects, I would put translation (and other transformation information) into that architecture and go for a full Model/View approach rather than look to transform the Shapes themselves...
...jim
java2d@JAVADESKTOP.ORG wrote: > Hi swv, > > I'm definitely not comfortable with my own Java2D capabilities but recently I've been working with the Area class alot and I think you should take alook at it. It is a mutable shape implementation as far as I know - take alook at Area.transform(AffineTransform at). With this you wouldn't have to translate your Graphics object at all, and your areas will still be where you left them the next time paint is called. Also, Area can take any Shape in its constructor and it will try to construct a suitable Area interpretation of the given shape. Just remember to translate the new Area as necessary. I hope this may be of some assistance. > > Regards, > > Pierre > [Message sent by forum member 'irond13' (irond13)] > > http://forums.java.net/jive/thread.jspa?messageID=228441 > > =========================================================================== > To unsubscribe, send email to listserv@java.sun.com and include in the body > of the message "signoff JAVA2D-INTEREST". For general help, send email to > listserv@java.sun.com and include in the body of the message "help".
=========================================================================== To unsubscribe, send email to listserv@java.sun.com and include in the body of the message "signoff JAVA2D-INTEREST". For general help, send email to listserv@java.sun.com and include in the body of the message "help".
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 27, 2007 1:49 PM
in response to: swv
|
|
|
I wanted to clarify some points here...
java2d@JAVADESKTOP.ORG wrote: [Points 1 and 2 omitted - all true]
> 3) Remembering that the goal as to shuffle these around the screen, and having them at definite places which are not so easily mutated, what's a developer to do?
Generally if a bunch of arbitrary shapes are to be manipulated, a "model/view" system is constructed which manages mapping between the coordinate space of the shape and the coordinate space of the view.
> In order to draw them where I want them, it's a matter of remembering where they wer drawn last, getting the Graphics into device space 0,0 (which coincides with user-space 0, 0 ) and now that device and user spaces are in agreement, using one of the methods of graphics to relocate where the "tip" of the graphics pen is located and hence where it will draw. IF I want the Shape to be at 100,500, then I relocate the graphics there and so on.
That is the beginnings of a model/view system. Your terminology is confusing, I would say that one should "translate the coordinate system of the graphics so that 0,0 in user space refers to the appropriate location of the object in the original coordinate system".
I'm not sure why you refer to "the tip of the pen" since the Graphics object doesn't have a pen that has a location per se. It does have an attribute that defines how to stroke paths, and the default implementation of that attribute (the BasicStroke) does operate as if it were dragging a "pen" along the shape, but that "pen" is relative to the path being drawn, it doesn't have an independent concept of "its location".
Also note that I said "original coordinate system" above, rather than "device coordinate system" because when the paint() method is called (or the Swing paintComponent() method), there may be some translation or scaling that was applied by the system which means that the default coordinate system when you start the paint() method is not "device space". At the very least, the 0,0 origin of the incoming graphics object points to the upper left corner of your component, but your component is likely not located at 0,0 on the screen, or even at 0,0 on the window it is on. Going beyond that, if you are printing then the scale factor may not be 1:1 when the method is called to generate output for the printer. Perhaps "Component coordinate system" would be a better term there.
> Now my shapes appear where I want them. But their other methods, contains() intersects() et.al. are all liars, or at least, out of sync with where the Shape has been drawn.
I wouldn't call them "liars". Those methods are defined relative to the coordinate space in which they are drawn. If you draw them in a modified coordinate space, then you need to perform hit testing relative to that coordinate space as well.
> To bring these other methods into line, you have to, in the case of intersects and contains, first displace the point you're asking about so that it is relocated back into that ULHC where all the Shapes think they live, piled up one on top of the other.
This is where a good model/view system comes into play. Not only does it manage modifying the coordinate system on the Graphics during rendering operations, it should also provide mechanisms to modify coordinates being hit tested so that they are relative to the same coordinate system in which the object is rendered within the view.
> By my measure, Shape and its implementations have a built in contradiction with respect to the separation between Model and View, which could be a source of confusion.
I think it is better to consider that all of their methods are defined within a single consistent coordinate system. If your model/view system translates the graphics to render them at a specific location and orientation, then it needs to also perform equivalent transformations on any other coordinates that are compared or executed against any other Shape methods.
In this manner I consider the Shape interface to be Model and View agnostic. It doesn't provide any mechanisms to adjust for either a Model transform or a View transform. It is your Model/View architecture which must maintain consistency in its interactions with the Shape interface.
Beyond that, all is left up to your model/view implementation so the ensuing paragraphs about how the Shape agrees or conflicts with either the "Model" or the "View" concept are really just the fact that such concepts are beyond the scope of the Shape interface as I have described above.
> But that AT, at least this is how it seems to me , is more View than anything else, while the Shape itself, which is to say the Model, lives unchanging elsewhere.
Again, I consider the AT to be a tool. Model and View architectures are free to use that tool to implement their own concepts and designs, but the AT is not a View and it is not a Model, it is just a 6 element affine transformation.
> Because we have no, efficient, non- list-o'-points way to define Shapes, they have to be somewhere. Why not just accept that and provide a way to mutate that "somewhere" along with consistent contains() intersects() getBounds() and getPathIterator()?
If you are asking for a method on Shape to be able to relocate the geometry then there are a couple of problems with that. First, some Shape objects may be immutable by design so moving them would force them to have a new property that they can't very well support. Second, location is one attribute that it might be nice to manipulate, but more sophisticated Model/View systems may allow a user to edit not just the location of the objects, but size and rotation as well. The kinds of manipulation that may be needed would grow if we want to deal with all of that. Given that by the time any of these capabilities are needed the type of application has grown in sophistication enough that it would be well served to implement its own Model/View architecture, it is easier to put these kinds of properties into that Model/View architecture rather than to try to deal with them in a generic mutation interface...
...jim
=========================================================================== To unsubscribe, send email to listserv@java.sun.com and include in the body of the message "signoff JAVA2D-INTEREST". For general help, send email to listserv@java.sun.com and include in the body of the message "help".
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 24, 2007 6:06 PM
in response to: swv
|
|
|
java2d@JAVADESKTOP.ORG wrote: > Jim- right on and well said- that was exactly my confusion.... > > Kirillcool said: > you're using core view classes in your model. The Shape and its derived classes are for painting on the screen. Although they might seem like a good option to use in the model, they aren't in most cases (as you're starting to see), since they're targeting the screen (view). > > Well Rectangle keeps state in just the way you say it shouldn't, if you mean by state enough information to draw it to the screen in a specific place. It has an x and a y and you can mutate those through it's methods and as a result of that mutation it will draw itself elsewhere. That's state. .
I never said that Rectangle does not keep state. It does keep state. Nearly all Shape objects keep state and provide methods for you to modify that state in various ways (whatever was deemed useful for that Shape).
What I said is that you cannot use the return value of the getBounds() method to manipulate the state of a Shape object, even if the original object was a Rectangle. You can modify the location of a Rectangle by calling the setLocation() method on *that* Rectangle object, but you cannot modify its location by calling the setLocation() on the object that is returned from its getBounds() method - just like any other Shape.
The return value of Rectangle.getBounds() may be an instance of the Rectangle class, but it is a new instance that is not the same instance as the original. It stores the same x,y,w,h as the original, but it is a brand new object completely isolated from the original.
For example:
Rectangle r = new Rectangle(0, 0, 10, 10); // the following statement modifies r directly r.setLocation(5, 5); // setLocation was called on "r" directly // r now is located at 5,5
as compared to:
Rectangle r = new Rectangle(0, 0, 10, 10); // the following statement modifies a temporary object // which is then dropped on the floor r.getBounds().setLocation(5, 5); // setLocation was called on the return value of setBounds() // *not* on "r" directly // r is still located at 0,0
as compared to:
Shape s = // some shape // the following statement modifies a temporary object // which is then dropped on the floor s.getBounds().setLocation(5, 5); // original s is unaffected just like r in previous example
Does that clear things up?
...jim
=========================================================================== To unsubscribe, send email to listserv@java.sun.com and include in the body of the message "signoff JAVA2D-INTEREST". For general help, send email to listserv@java.sun.com and include in the body of the message "help".
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 24, 2007 5:16 AM
in response to: swv
|
|
|
[I sent this earlier, but it doesn't appear to have gone anywhere so I am resending it in hopes that it makes it through to the forum and mailing list to clarify things. Apologies if this causes duplicate messages for some...jim]
> That translates the device space 100 pixels over. Great. But the next time I invoke paint, the device space is back to 0,0, as if I never told the graphics to move over 100 pixels.
The documentation for the Component.paint(Graphics g) method:
<http://
has a pointer to a technical article which describes the paint model in detail:
<http://
It includes the following text:
When AWT invokes this method, the Graphics object parameter is pre-configured with the appropriate state for drawing on this particular component:
* The Graphics object's color is set to the component's foreground property. * The Graphics object's font is set to the component's font property. * The Graphics object's translation is set such that the coordinate (0,0) represents the upper left corner of the component. * The Graphics object's clip rectangle is set to the area of the component that is in need of repainting.
Basically, the paint() method is always called with a brand new Graphics object. The Graphics object you use during one call to paint() is disposed soon after your paint() method returns so any remaining state on it is simply lost. The system was not designed to persist state from one call to the next, but to always start your paint() method with a consistent inherited state - any state requirements beyond the above conditions should be maintained separately by your application and applied each time the method is called.
The article goes into much more detail about the architecture and is a recommended reading for anyone trying to do more than some basic rendering and management of graphics state...
...jim
=========================================================================== To unsubscribe, send email to listserv@java.sun.com and include in the body of the message "signoff JAVA2D-INTEREST". For general help, send email to listserv@java.sun.com and include in the body of the message "help".
|
|
|
|
|
|
|
|
Re: [JAVA2D] AffineTransformation
Posted:
Jul 24, 2007 5:24 AM
in response to: Jim Graham
|
|
|
awesome great find. Thanks Jim!!
|
|
|
|
|