Graphics2D question

R

RichT

Hello,

I want to build my application from three main classes.
1. A frame class to handle file operations.
2. A Drawing panel to render images to.
3. An engine to perform the actual rendering.

All the examples I have read so far for Image stuff in Java have
everything stuffed into one monolithic class, and the crux of the
rendering seems to be done in paint(Graphics g) or
paintComponent(Graphics g) using a g2d object cast againg the parameter g.

If I wanted to do calculations for rotations translations etc I can do
this with the AffineTransform class setX methods, but the drawImage
method of g2d seems to be handled in the paint/paintComponent methods.

This is fine until I want to specify x, y arguments in the drawImage
Method as neither Paint or PaintComponent take these parameters in their
method signature how can I do this, is there another way to draw apart
from the paint/paint component methods?

Any help appreciated.
Rich
 
P

Peter Duniho

[...]
If I wanted to do calculations for rotations translations etc I can do
this with the AffineTransform class setX methods, but the drawImage
method of g2d seems to be handled in the paint/paintComponent methods.

This is fine until I want to specify x, y arguments in the drawImage
Method as neither Paint or PaintComponent take these parameters in their
method signature how can I do this, is there another way to draw apart
from the paint/paint component methods?

Can you rephrase the question? As stated it's a little confusing.

You write "the drawImage method of g2d seems to be handled in...". I
assume you're actually talking about Graphics2D.drawImage(), but that's
not _handled_ in paintComponent(). It might be _used_ there, but it
wouldn't be handled there.

As far as specifying coordinates when using drawImage(), since you
shouldn't call paintComponent() yourself, the parameter list for the
method isn't relevant. Instead, your component overrides the
paintComponent() and draws what it needs to when called. If an image you
want to draw using drawImage() requires a specific location to be drawn
(and it's natural that it would), then that location needs to be set prior
to when paintComponent() is called.

A typical sequence of events would be for the location to be updated by
some internal calculation in the component, or by some method called by a
client of the component, and when the location is changed, the component
would call repaint() to signal that it needs to be repainted. Then the
Java run-time will eventually call the paintComponent() method to have the
actual painting done, at which time the location that was previously set
would then be used to actually draw the image in question.

If that information doesn't clarify for you how to manage what you're
trying to do, you should try to be more specific about what problem you're
having. A concise-but-complete code sample would be very helpful. Try to
show what it is you're trying to do and be clear about why the code
doesn't work as you want it to.

Pete
 
K

Knute Johnson

RichT said:
Hello,

I want to build my application from three main classes.
1. A frame class to handle file operations.
2. A Drawing panel to render images to.
3. An engine to perform the actual rendering.

All the examples I have read so far for Image stuff in Java have
everything stuffed into one monolithic class, and the crux of the
rendering seems to be done in paint(Graphics g) or
paintComponent(Graphics g) using a g2d object cast againg the parameter g.

If I wanted to do calculations for rotations translations etc I can do
this with the AffineTransform class setX methods, but the drawImage
method of g2d seems to be handled in the paint/paintComponent methods.

This is fine until I want to specify x, y arguments in the drawImage
Method as neither Paint or PaintComponent take these parameters in their
method signature how can I do this, is there another way to draw apart
from the paint/paint component methods?

Any help appreciated.
Rich

See my response in comp.lang.gui.
 
R

RichT

Can you rephrase the question? As stated it's a little confusing.

Sorry that is me all over type faster than I think :)
You write "the drawImage method of g2d seems to be handled in...". I
assume you're actually talking about Graphics2D.drawImage(), but that's
not _handled_ in paintComponent(). It might be _used_ there, but it
wouldn't be handled there.

Ok to simplify my question ;) I have nothing to show code wise, still
thinking about a/the design.

Yours and Knutes answers have clarified some things for me thanks :) but
I still have a problem getting my head around this.

My thoughts were to have something that extends a JPanel for rendering
images to, but I want the logic for doing this in another non gui class.

If for instance I have an image that I wanted to scale and centre in the
JPanel, I would want to calculate if enlarged images is bigger than the
JPanel and if so crop it to fit.

This would require as far as I know an AffineTransform.setSclale(x, y)
and also as far as I know a bufferedImage.getSubImages(x1,y1,x2,y2)
and would require some code to calculate all of this.

I would prefer to have all the above in my non gui logic class and
somehow have the JPanel class repaint all of this after.

While I could call repaint() in the JPanel, how would it know about the
changes to the image and what if I wanted to specify a different x, y
location for the image if it is smaller than the JPanel and needed to be
centred? how would I get this information to the g2d object in the
PaintComponent method?
 
P

Peter Duniho

[...]
Yours and Knutes answers have clarified some things for me thanks :) but
I still have a problem getting my head around this.

Please keep in mind that apparently you've multi-posted your question. I
haven't looked at his answer in the other newsgroup, so I don't know
what's already been suggested, nor do I know anything you've posted
there. (This is a good example of why you shouldn't multi-post, by the
way).
My thoughts were to have something that extends a JPanel for rendering
images to, but I want the logic for doing this in another non gui class.

Any particular reason for extending JPanel? Does your custom component
actually need to act as a simple container for other components? If not,
perhaps you should simply be extending JComponent.
If for instance I have an image that I wanted to scale and centre in the
JPanel, I would want to calculate if enlarged images is bigger than the
JPanel and if so crop it to fit.

Why would an image be "enlarged"? How does that happen? Hint: it's at
whatever point the image display, including scaling, is configured that
you'd set the necessary data that will be used later for drawing the image.
This would require as far as I know an AffineTransform.setSclale(x, y)
and also as far as I know a bufferedImage.getSubImages(x1,y1,x2,y2)
and would require some code to calculate all of this.

I doubt you need to use getSubImage(). Your component's drawing will be
clipped to the boundary of the image, so all you really need to do is
transform the Graphics2D correctly (scale and translate, if I understand
your post correctly) and the image will be placed and draw appropriately.

The transformation will be based on whatever configuration was performed
prior to the painting (as I mention above).

It's possible that calling getSubImage() might improve performance, by
avoiding having the graphics subsystem drawing outside the clip area. But
since clipping is usually a highly optimized operation, and because most
of the cost of drawing is in the actual moving of bits that are drawn (the
area that's clipped won't be processed), I would be surprised if it's a
necessary optimization, and it may not improve performance much, if at all
(depending on the exact implementation, the cost of creating the new
object and then collecting it later could in fact cause reduced
performance).
I would prefer to have all the above in my non gui logic class and
somehow have the JPanel class repaint all of this after.

I'm not really clear on what the "non gui logic class" is. Anything that
needs to know the size of your custom component and involves itself in the
drawing of the component is, to my perception, a GUI class. If you have
in mind a class that doesn't know anything about the GUI, then perhaps you
could be more explicit about that. What interface does that class
implement? I don't mean what's the name of the interface; I mean, what
methods, properties, and/or fields are exposed by that interface? How
does your custom component use this hypothetical "non gui logic class"?
While I could call repaint() in the JPanel, how would it know about the
changes to the image

Well, typically the custom component would either itself be directly
informed of changes to the image, or it would refer to some data structure
that itself knows about the changes. By the time repaint() is called,
everything should already be set up to draw the custom component
correctly. The paintComponent() method itself would then simply use the
current state of the data structures involved to draw the correct thing.
and what if I wanted to specify a different x, y location for the image
if it is smaller than the JPanel and needed to be centred?

It seems to me that if you want the image centered in the custom
component, the you're not "specifying a different x, y location".
Instead, that location is implied by the size of the image. You could
either compute the location in the paintComponent() method, or you could
pre-compute and cache that information any time the image or component
size changes (since either would affect the location of the image).
how would I get this information to the g2d object in the PaintComponent
method?

Again, I'm not really sure what you mean here. The Graphics2D instance
doesn't have any state that specifically would know about your image.
However, you can certainly set the transformation for the Graphics2D based
on whatever scaling and translation is necessary. In fact, creating this
transformation could be part of the "pre-compute and cache" operation I
mentioned earlier. Then all that the paintComponent() method would have
to do is call Graphics2D.transform() with the AffineTransform that you'd
computed earlier.

Alternatively, you could just compute the necessary parameters that would
be used later to call one of the many drawImage() overloads defined in
Graphics and Graphics2D and rather than setting the transform, just call
the appropriate overload directly.

Pete
 
R

RichT

Please keep in mind that apparently you've multi-posted your question.
Yes sorry about that, I posted to comp.lang.java.gui first and then here
as I was unsure if this question belonged to the gui NG as it is more a
design thing?
I haven't looked at his answer in the other newsgroup, so I don't know
what's already been suggested, nor do I know anything you've posted
there. (This is a good example of why you shouldn't multi-post, by the
way).
Yes sorry again I really didn't give it a thought at the time, what
Knute suggested has been extremely helpful and gives me part of my
answer, and that is that as long as I have a reference to the graphic of
the component I can draw to this and send a repaint call to the
component after?
Any particular reason for extending JPanel? Does your custom component
actually need to act as a simple container for other components? If
not, perhaps you should simply be extending JComponent.
No particular reason, I chose JPanel as most of the examples I have seen
use this.
Why would an image be "enlarged"? How does that happen? Hint: it's at
whatever point the image display, including scaling, is configured that
you'd set the necessary data that will be used later for drawing the image.

Int the graphic object and then call a repaint on the component to
refresh display?
I doubt you need to use getSubImage(). Your component's drawing will be
clipped to the boundary of the image, so all you really need to do is
transform the Graphics2D correctly (scale and translate, if I understand
your post correctly) and the image will be placed and draw appropriately.
Thanks, the more I learn about Java the more I realise how much it can
do for you.
I'm not really clear on what the "non gui logic class" is. Anything
that needs to know the size of your custom component and involves itself
in the drawing of the component is, to my perception, a GUI class.
Ah my idea of a gui class is an extended JPanel stuffed with code :)

If you have in mind a class that doesn't know anything about the GUI, then
perhaps you could be more explicit about that.
This is exactly what I had in mind, if my understanding is correct then
passin a reference of tthe components graphic object will allow me to
draw to the graphic and then fire an event for the component to redraw
itself.
What interface does that class implement?
> I don't mean what's the name of the interface; I mean,
what methods, properties, and/or fields are exposed by that interface?
I would imagine methods something like
scale, translate, draw, update, origin, rotate, drawText, to name a few
How does your custom component use this hypothetical "non gui logic class"?

I am guessing that the component class would create an instance of the
logic class and pass it's graphic object and an image in the constructor
and would implement property change listener.

Any buttons or menu item events would call the relevant method in the
logic class.

The logic class would calculate positions, scales and translations etc
and then using the graphic reference draw to this and then call repaint
on the component, possibly via an event, not 100% sure though
Well, typically the custom component would either itself be directly
informed of changes to the image, or it would refer to some data
structure that itself knows about the changes. By the time repaint() is
called, everything should already be set up to draw the custom component
correctly. The paintComponent() method itself would then simply use the
current state of the data structures involved to draw the correct thing.
This sounds interesting, how would this work? this sounds similar to the
Swing Model View pattern?
Again, I'm not really sure what you mean here. The Graphics2D instance
doesn't have any state that specifically would know about your image.
However, you can certainly set the transformation for the Graphics2D
based on whatever scaling and translation is necessary. In fact,
creating this transformation could be part of the "pre-compute and
cache" operation I mentioned earlier. Then all that the
paintComponent() method would have to do is call Graphics2D.transform()
with the AffineTransform that you'd computed earlier.
By using the reference to the components graphic object ?
Alternatively, you could just compute the necessary parameters that
would be used later to call one of the many drawImage() overloads
defined in Graphics and Graphics2D and rather than setting the
transform, just call the appropriate overload directly.

Do you mean Graphics2D.DrawImage, DrawLine etc?

Thanks for you help and paitience
Rich
 
P

Peter Duniho

[...]
Yes sorry again I really didn't give it a thought at the time, what
Knute suggested has been extremely helpful and gives me part of my
answer, and that is that as long as I have a reference to the graphic of
the component I can draw to this and send a repaint call to the
component after?

The way I read the above: you've got a GUI component that only redraws
itself using a referenced "graphic" (there's no such thing as a "graphic"
in Java...do you mean an instance of Image, such as BufferedImage?), while
some other code draws into the "graphic" and then tells the component that
the "graphic" has changed.

Is that a correct understanding of what you wrote? If so, then that seems
fine to me.
No particular reason, I chose JPanel as most of the examples I have seen
use this.

Well, unless you have a specific reason for using JPanel, then don't.
Extend JComponent instead, as it's the actual base class for Swing
components.
Int the graphic object and then call a repaint on the component to
refresh display?

You're answering a question with a question (and one I don't really
understand either...what does the word "int" mean here? The usual meaning
in this context, "integer", doesn't seem to apply).
[...]
I'm not really clear on what the "non gui logic class" is. Anything
that needs to know the size of your custom component and involves
itself in the drawing of the component is, to my perception, a GUI
class.

Ah my idea of a gui class is an extended JPanel stuffed with code :)

Well, fine. But that's not an explanation as to what a "non gui logic
class" is.
If you have in mind a class that doesn't know anything about the GUI,
then

This is exactly what I had in mind, if my understanding is correct then
passin a reference of tthe components graphic object will allow me to
draw to the graphic and then fire an event for the component to redraw
itself.

See above. If I understand this statement correctly, then it seems like a
fine approach. If I don't, then it might or might not be. :)
I would imagine methods something like
scale, translate, draw, update, origin, rotate, drawText, to name a few

Are the transformation operations (scale, translate, rotate...and what's
the difference between "translate" and "origin"?) independent of the
similar operations that would be set for the custom Swing component? That
is, from your previous description, I have the impression that you want to
be able to scale and center the image in the custom component, and now
from this most recent description I have the impression that you also want
to be able to do things like that and other operations on the non-GUI
class.
I am guessing that the component class would create an instance of the
logic class and pass it's graphic object and an image in the constructor
and would implement property change listener.

What's the difference between "its graphic object" and "an image"? At the
outset of this message, I made the assumption that your ambiguous term
"graphic" referred to some sort of Image instance. But if you can have
both a "graphic" and an "image", then I'm not so sure.
Any buttons or menu item events would call the relevant method in the
logic class.

The logic class would calculate positions, scales and translations etc
and then using the graphic reference draw to this and then call repaint
on the component, possibly via an event, not 100% sure though

I think it makes more sense to follow the "listener" idiom, where your
"non-GUI" class defines a listener interface that the Swing component can
implement and respond to. Then when changes are made, the listener is
notified. In this case, the Swing component would call repaint() itself,
but this allows for a more general-purpose "non-GUI" class. After all, if
it's "non-GUI" then why should it know about a JComponent that needs to
have repaint() called?

Other than that, your description sounds fine. Of course, that assumes I
understand the rest of the model correctly, which may or may not be the
case.
This sounds interesting, how would this work? this sounds similar to the
Swing Model View pattern?

Well, it's more like being "similar" to the event-driven GUI updating
paradigm that practically all mainstream GUI's use.

Model/View is about separating the data from the model. And it works well
in an event-driven paradigm. But it's not mandatory...you can easily have
views (i.e. Swing components) that incorporate their own model, and that's
not in line with the Model/View pattern. Yet, they would still use this
"change some data, call repaint() to ask to be repainted" paradigm.

In other words, what I'm describing really isn't optional, the way that
Model/View is. A correctly-written GUI application will always use this
event-driven paradigm, whether or not it separates the model from the view.
By using the reference to the components graphic object ?

Again, that depends on what you mean by "graphic object". But sure, if
that "graphic" object keeps track of the transform needed for drawing,
that would be a natural place to get the AffineTransform instance.

Note that setting the Graphics2D transform is not the only approach. You
can also pass a transform to some of the Graphics2D.drawImage() overloads,
and it would have the same effect as changing the Graphics2D's own
transform. This is what I'm talking about here:
Do you mean Graphics2D.DrawImage, DrawLine etc?

Those are not the names of any methods in the Graphics2D class. But if
you mean Graphics2D.drawImage(), yes. For that method, you could just
pass a precomputed transform. For drawLine(), there's no such
overload...you've have to explicitly transform integer coordinates
yourself and then use the Graphics.drawLine() method, or create a Shape
representing the line and either transform the Shape before drawing it
(some Shape implementations include a transform() method) or set the
Graphics2D transform itself.

It all depends on exactly what you want to do.

In the hopes that it might help you out a little, I've attached below an
example of a simple custom Swing component that has image and scale
properties that can be set, and which always draws the image centered in
the component at the scale that's been set. It doesn't demonstrate the
additional layer that I think you've been talking about, where there's an
intermediate class that actually manages things like holding on to the
image, storing settings like scale factor, and implementing notification
for listeners, but hopefully it gives you a better idea of how the Swing
component side of things would work.

The custom Swing class is first, followed by some simple sample code that
demonstrates the use of it. (The demo code just opens image files, but
the custom component can also just be assigned some arbitrary image; it
would be simple enough to add a public method to notify the component that
the image's changed so that the component knows to call repaint(), but as
I mentioned before I think it would be better to implement a full-fledged
"listener" API that the component can subscribe to if you're going to have
anything more elaborate than just a simple reference to an Image instance
as shown here).

Pete


import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

public class JImageBox extends JComponent
{
private Image _image;
private double _scale = 1.0;
AffineTransform _transform;

public JImageBox()
{
this.addComponentListener(new ComponentAdapter()
{
public void componentResized(ComponentEvent arg0)
{
_UpdateImage();
}
});
}

public void setImage(BufferedImage image)
{
_image = image;
_UpdateImage();
}

public void setImage(File file)
{
try
{
_image = ImageIO.read(file);
}
catch (IOException e)
{
e.printStackTrace();
_image = null;
}
_UpdateImage();
}

public void setScale(double scale)
{
_scale = scale;
_UpdateImage();
}

protected void paintComponent(Graphics gfx)
{
if (_image != null)
{
Graphics2D gfx2 = (Graphics2D)gfx;

gfx2.drawImage(_image, _transform, null);
}
}

private void _UpdateImage()
{
_transform = null;

if (_image != null)
{
Dimension size = getSize();
double cxImage, cyImage;

cxImage = _image.getWidth(null) * _scale;
cyImage = _image.getHeight(null) * _scale;

_transform = AffineTransform.getTranslateInstance(
(size.getWidth() - cxImage) / 2,
(size.getHeight() - cyImage) / 2);
_transform.scale(_scale, _scale);
}

repaint();
}
}



import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;

public class TestImageComponentFrame extends JFrame
{
private JImageBox _imagebox = new JImageBox();

public TestImageComponentFrame(String arg0)
{
super(arg0);

setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));

Box box = Box.createHorizontalBox();

JButton button = new JButton("Open File...");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
_ChooseImage();
}
});
box.add(button);

final JTextField text = new JTextField("1.0", 8);
text.setMaximumSize(text.getPreferredSize());
button = new JButton("Set Scale");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
try
{
_imagebox.setScale(Double.parseDouble(text.getText()));
}
catch (NumberFormatException e)
{
e.printStackTrace();
}
}
});
box.add(button);
box.add(text);

add(box);
add(_imagebox);
}

private void _ChooseImage()
{
FileDialog filedlg = new FileDialog(this, "Please choose an image
file to load:", FileDialog.LOAD);

filedlg.setVisible(true);

if (filedlg.getFile() != null)
{
_imagebox.setImage(new File(filedlg.getDirectory()
+ filedlg.getFile()));
}
}
}



import java.awt.*;

public class TestImageComponent
{
/**
* @param args
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
TestImageComponentFrame frame = new
TestImageComponentFrame("TestImageComponentFrame");

frame.setSize(640, 480);
frame.setVisible(true);
}
});
}
}
 
R

RichT

The way I read the above: you've got a GUI component that only redraws
itself using a referenced "graphic" (there's no such thing as a
"graphic" in Java...do you mean an instance of Image, such as
BufferedImage?), while some other code draws into the "graphic" and then
tells the component that the "graphic" has changed.

I am not sure, I was referring to Graphics2D or Graphics as the graphic
object, which appears to be an abstract class in the API, so I am
guessing rightly or wrongly that this would be the image/buffered image?
Is that a correct understanding of what you wrote? If so, then that
seems fine to me.

If my understanding of your understanding (see above) is correct then yes :)
Well, unless you have a specific reason for using JPanel, then don't.
Extend JComponent instead, as it's the actual base class for Swing
components. Ok thanks for the tip :)
You're answering a question with a question (and one I don't really
understand either...what does the word "int" mean here? The usual
meaning in this context, "integer", doesn't seem to apply).
Oh dear that was a mistake it seems that half the text vanished (more
likely I deleted it by mistake when editing ;))

I meant This would most likely be triggered by an event from the user,
in the gui logic class the calculations would be made in an
AffineTransform passed to the Grahics2D.DrawImage(img, affineTransform,
null) and then an event sent to the component to redraw itself.

Well, fine. But that's not an explanation as to what a "non gui logic
class" is.

Ok I think I mean a class that handles the logic for the gui component :)

See above. If I understand this statement correctly, then it seems like
a fine approach. If I don't, then it might or might not be. :)
Excellent :)

Are the transformation operations (scale, translate, rotate...and what's
the difference between "translate" and "origin"?)

Nothing :) they mean the same thing
That is, from your previous description, I have the impression that you
want to be able to scale and center the image in the custom component,

correct :)
and now from this most recent description I have the impression that you
also want to be able to do things like that and other operations on the
non-GUI class.

Correct again :) including drawing test into the image (perhaps later
much later when I understand this better)
What's the difference between "its graphic object" and "an image"?

I am not entirely sure I thought Graphics2D was a reference to the
components graphical context?

At
the outset of this message, I made the assumption that your ambiguous
term "graphic" referred to some sort of Image instance. But if you can
have both a "graphic" and an "image", then I'm not so sure.

I am unsure now too :(

Thanks for all your help so far, I will answer the rest of this post
later :)

Rich
 
P

Peter Duniho

I am not sure, I was referring to Graphics2D or Graphics as the graphic
object, which appears to be an abstract class in the API, so I am
guessing rightly or wrongly that this would be the image/buffered image?

Not exactly. There is a BufferedImage class. That would be the actual
image. You can call BufferedImage.createGraphics() to get a Graphics2D
that will allow you to draw into that image. That's a "Graphics2D"
instance, not a "graphic".

I apologize if it seems like I'm being picky, but the compiler will be at
least as picky :) and I feel that since there are precise terms that refer
to specific classes, those terms should be used. Otherwise, it's
impossible to know that we're really talking about the same thing. It
will be much more productive if you are careful to use the exact names for
things, where those things have exact names.

So, anyway, the Graphics2D isn't the actual image per se, but it is a sort
of portal that lets you get at the image with various drawing operations,
allowing you to modify it according to your needs.
If my understanding of your understanding (see above) is correct then
yes :)

See above. I think I _almost_ understood what you were talking about. :)

I don't know how expensive it is in Java to create a Graphics2D from a
BufferedImage instance. But assuming it's not overly expensive, then it
seems to me that the object you really want to hang on to would be the
BufferedImage instance only, creating a Graphics2D as necessary whenever
you want to update the image.

You haven't been very specific about what sorts of updates you might do,
but again there seems to be the assumption that there's some higher-level,
user-defined class (where "user" is you) that encapsulates the behavior of
the BufferedImage and Graphics2D, exposing it in a more general way. If
that's the correct assumption, then I think the "listener" mechanism I
mentioned earlier is still appropriate for dealing with updates to the
Swing component that displays this image.
[...]
I meant This would most likely be triggered by an event from the user,
in the gui logic class the calculations would be made in an
AffineTransform passed to the Grahics2D.DrawImage(img, affineTransform,
null) and then an event sent to the component to redraw itself.

That doesn't make much sense as I understand the previous discussion. The
only image that you've mentioned so far is the one that represents your
non-GUI data structure. The only time you'd draw that image would be in
the paintComponent() method itself.

Are you saying that one of the operations you might have on this non-GUI
data structure is to draw yet another image into it? How does the "gui
logic class" come into play here? By "gui logic class", are you talking
about your custom Swing component that's displaying your non-GUI data
structure? Or something else?
Ok I think I mean a class that handles the logic for the gui component :)

Why are you using the phrase "non gui logic class" to description
something that "handles the logic for the gui component"? To me, "non
gui" implies no connection with the GUI. Conversely, "handles the logic
for the gui component" implies a direct connection with the GUI.

The two seem mutually exclusive to me, and yet you seem to be using the
phrases as being directly related to each other. I'm obviously not
understanding what you mean, and I hope that my explanation here makes it
clear why so that you can clarify.
[...]
That is, from your previous description, I have the impression that you
want to be able to scale and center the image in the custom component,

correct :)

Then hopefully the code I posted can help with that part of the problem.
Correct again :) including drawing test into the image (perhaps later
much later when I understand this better)

As I noted at the beginning of this message, you can draw into a
BufferedImage by getting a Graphics2D from it (by calling
createGraphics()). With that Graphics2D instance you can draw whatever
you like onto the BufferedImage, including text (I assume that's what you
meant instead of "test" :) ).
I am not entirely sure I thought Graphics2D was a reference to the
components graphical context?

The Graphics2D instance passed to paintComponent() is such a reference.
It's used for drawing the representation of the custom Swing component.
As such it has very little to do with a Graphics2D that you might obtain
from an image data structure, such as a BufferedImage. I mean, it's the
same class, and so of course you can do all the same drawing operations
regardless. But in the former, those operations wind up shown on the
screen, whereas in the latter, they wind up modifying the BufferedImage.

It kind of sounds like you really have at least two parts to this design
question:

* how to display an image in a custom Swing component
* how to maintain that image in a class that isn't specific to the GUI

I think that mostly I've been answering the first question, though
hopefully some of the things I've written above help address the second.
If you disagree with the "two parts" assertion, please explain why so that
I (and anyone else reading) can understand better what the actual question
or questions are.

Pete
 
R

RichT

Hi Peter,
I think it makes more sense to follow the "listener" idiom, where your
"non-GUI" class defines a listener interface that the Swing component
can implement and respond to.

Yes this was indeed my initial thought :) the two classes which I shall
now refer to as the 'Gui Component class' and the 'Gui Component Engine
class'

When the Gui Component Engine makes a change to the image in some may
perhaps to scale the image, this will send the Gui Component class an
event which will call repaint().

I understand now that the overridden paintComponent(Graphics g) method
is not called directly but will be called by the repaint() method of the
component indirectly ? :)

Now if my understanding of the rest of the post is correct I would place
in the overridden paintComponent method something like
g2d.drawImage(bufferedImage, affineTransform, null) as per your code?


Then when changes are made, the listener
is notified. In this case, the Swing component would call repaint()
itself, but this allows for a more general-purpose "non-GUI" class.
After all, if it's "non-GUI" then why should it know about a JComponent
that needs to have repaint() called?

Yes this is true :)
Model/View is about separating the data from the model. And it works
well in an event-driven paradigm. But it's not mandatory...you can
easily have views (i.e. Swing components) that incorporate their own
model, and that's not in line with the Model/View pattern. Yet, they
would still use this "change some data, call repaint() to ask to be
repainted" paradigm.

I see, I think this is what I want for my design ?
In other words, what I'm describing really isn't optional, the way that
Model/View is. A correctly-written GUI application will always use this
event-driven paradigm, whether or not it separates the model from the view.

I think I understand now :)
Again, that depends on what you mean by "graphic object". But sure, if
that "graphic" object keeps track of the transform needed for drawing,
that would be a natural place to get the AffineTransform instance.

Note that setting the Graphics2D transform is not the only approach.
You can also pass a transform to some of the Graphics2D.drawImage()
overloads, and it would have the same effect as changing the
Graphics2D's own transform. This is what I'm talking about here:

Ok I understand now I think, from your other post to reply part I, I can
get the graphic object of the Image and do calculations on this and then
send an event to the listener i.e. the Gui Component Class which will
then call its repaint() and anything else it wants to method?

It all depends on exactly what you want to do.

In the hopes that it might help you out a little, I've attached below an
example of a simple custom Swing component that has image and scale
properties that can be set, and which always draws the image centered in
the component at the scale that's been set.

Thank you for this code it has been very helpful, I possibly would not
always want the image scaled up or down but certainly want it centred.

It doesn't demonstrate the
additional layer that I think you've been talking about, where there's
an intermediate class that actually manages things like holding on to
the image, storing settings like scale factor, and implementing

Yes I believe this is the additional layer I am looking for, but if my
understanding is correct, just having a reference to the image will
allow me to do all this by getting a reference to its graphic and apply
a transform to this and then send an event to the component informing of
a change to the image, where more than likely the component would call
its own repaint() method?
notification for listeners, but hopefully it gives you a better idea of
how the Swing component side of things would work.

Indeed it does thanks :)
The custom Swing class is first, followed by some simple sample code
that demonstrates the use of it. (The demo code just opens image files,
but the custom component can also just be assigned some arbitrary image;
it would be simple enough to add a public method to notify the component
that the image's changed so that the component knows to call repaint(),
but as I mentioned before I think it would be better to implement a
full-fledged "listener" API that the component can subscribe to if
you're going to have anything more elaborate than just a simple
reference to an Image instance as shown here).

Thank you for all your patience, your answers have been clear and
concise, unlike some of my questions, sorry about that, a mix of not
understanding certain concepts myself and a wireless keyboard which
intermittently misses key stroke (or perhaps I just type too fast ;)) :)

Rich
 
L

Lew

RichT said:
a wireless keyboard which
intermittently misses key stroke (or perhaps I just type too fast ;)) :)

I have a wireless keyboard/mouse combination that feeds through an A/B USB
switch, and it used to miss keystrokes abominably.

When I changed the batteries in the mouse, the keyboard behaved better, in
fact, flawlessly since then. Even at speeds in the vicinity of 50 wpm, at times.

It is true that the kb and the mouse share a USB port with this model
(Microsoft Wireless Optical Desktop 4000). It may also be that weak or
scrambled radio transmissions from the mouse were harming the receiver's
ability to decode the keyboard's transmissions.

I use rechargeable batteries, incidentally. The life of the charge in these
units seems to be months, three to sixish.
 
P

Peter Duniho

[...]
I understand now that the overridden paintComponent(Graphics g) method
is not called directly but will be called by the repaint() method of the
component indirectly ? :)

Yes. And by "indirectly", this means that the paintComponent() method
does not generally get called until some time _after_ repaint() has
returned to its caller. That is, it's not even that repaint() itself
winds up calling paintComponent() or something that calls it. Instead, it
simply signals to the Java system that the component needs to be redrawn
and returns. Later on, Java then gets around to calling paintComponent()
to have the component redrawn (in fact, if you look you'll notice that
there are some overloads of repaint() that give you some degree of control
regarding the definition of "later on").
Now if my understanding of the rest of the post is correct I would place
in the overridden paintComponent method something like
g2d.drawImage(bufferedImage, affineTransform, null) as per your code?

Well, I obviously think so. :) That's why I wrote the code that way.
[...]
Again, that depends on what you mean by "graphic object". But sure, if
that "graphic" object keeps track of the transform needed for drawing,
that would be a natural place to get the AffineTransform instance.
Note that setting the Graphics2D transform is not the only approach.
You can also pass a transform to some of the Graphics2D.drawImage()
overloads, and it would have the same effect as changing the
Graphics2D's own transform. This is what I'm talking about here:

Ok I understand now I think, from your other post to reply part I, I can
get the graphic object of the Image and do calculations on this and then
send an event to the listener i.e. the Gui Component Class which will
then call its repaint() and anything else it wants to method?

I'll ask you again to be more specific about your terminology. You've
already written that you're going to describe your Swing component as "Gui
Component class" and "Gui Component Engine class", neither of which are
"graphic". So do you mean something else by "graphic"? Perhaps you mean
to write "Graphics2D"?

It's difficult to make sure that your question is answered correctly and
precisely if you yourself aren't using correct and/or precise terminology.

Now, assuming that you mean "Graphics2D", I would say that the answer is
"no". You don't "do calculations" on a Graphics2D instance, at least not
as I am in the habit of using the phrase "do calculations". You use it to
draw into some image presentation object (for example, an Image object, or
the on-screen representation of a JComponent).

You can do two things with a Graphics2D: you can change its own state, and
you can perform a drawing operation (which changes the state of something
the Graphics2D refers to, like a BufferedImage). Only the drawing
operations persist and affect later users of the underyling image object.
Changing state of the Graphics2D object, such as setting a transformation,
setting a stroke, a color, etc. these things only affect subsequent
drawing operations using _that_ Graphics2D object.

So, for example, you can't have a BufferedImage, get a Graphics2D object
from it, set the scaling for that Graphics2D, and then expect that to
change how the BufferedImage is drawn when used later by being drawn into
a _different_ Graphics2D object passed to a control's paintComponent()
method.

Again, because your question is somewhat vague it's hard for me to know
whether this is what you expect or not. But it sort of sounds like you
expect to be able to set the transformation for the Graphics2D, doing
nothing else, and then have that have some effect when the underlying
image object is drawn later. If you do, then that expectation isn't going
to work.
[...]
It doesn't demonstrate the
additional layer that I think you've been talking about, where there's
an intermediate class that actually manages things like holding on to
the image, storing settings like scale factor, and implementing

Yes I believe this is the additional layer I am looking for, but if my
understanding is correct, just having a reference to the image will
allow me to do all this by getting a reference to its graphic and apply
a transform to this and then send an event to the component informing of
a change to the image, where more than likely the component would call
its own repaint() method?

See above. Most of what you wrote seems fine, except for the part where
you write "apply a transform to this and then send an event to the
component informing of a change to the image". Applying a transform to a
Graphics2D that you've obtained from an Image object doesn't change the
image itself. It only affects drawing operations _to_ that Image done
later through the Graphics2D object that you set a new transform for.

Again, I don't know whether this is what you meant, because you're not
using precise terminology. But previously in this thread you seem to be
using the word "graphic" (incorrectly) to refer to a Graphics2D object.
If that's the case here, then my reply is accurate.

It seems to me that if you want to be able to apply scaling, translation,
or rotation changes to the image itself (as opposed to things being drawn
into the image), then those are things that should be stored as state in
your "Gui Component Engine class".

You might even just maintain a single AffineTransform instance, updated
any time those things are changed, and then used to draw the image later.
You could do this by having your "Gui Component Engine class" have a
property that returns this transform as well as the Image that the class
stores. Or you could have your "Gui Component Engine class" have a method
that takes as a parameter a Graphics2D instance, and then itself draws the
Image into that Graphics2D using the transform that's been set.

In either case, note that the transform in this situation is used for
_presenting_ the underlying image object that your "Gui Component Engine
class" is storing. It doesn't affect the image directly, but rather
affects how the image is displayed at some later point.

At least based on what you've written so far, it seems to me that you
should be careful to keep in mind that you have two different places where
"drawing" happens. There is drawing into your "Gui Component Engine
class"'s image object, and then there is the drawing _of_ the "Gui
Component Engine class"'s image object. Each will use its own Graphics2D
object to handle drawing, and you'll need to be careful to make sure
you're changing the state you care about in the appropriate place
depending on what effect you want it to have.

Pete
 
R

RichT

Hi again Peter :)
I apologize if it seems like I'm being picky, but the compiler will be
at least as picky :) and I feel that since there are precise terms that
refer to specific classes, those terms should be used.

No problem you are not being picky ;) I am just not being as clear and
concise with my questions as you have been with you answers :)

Otherwise, it's
impossible to know that we're really talking about the same thing. It
will be much more productive if you are careful to use the exact names
for things, where those things have exact names.

This makes sense and reduces the possibility of any ambiguity :)
So, anyway, the Graphics2D isn't the actual image per se, but it is a
sort of portal that lets you get at the image with various drawing
operations, allowing you to modify it according to your needs.

This was my understanding too I think :)
See above. I think I _almost_ understood what you were talking about. :)

Yes you did :)
I don't know how expensive it is in Java to create a Graphics2D from a
BufferedImage instance. But assuming it's not overly expensive, then it
seems to me that the object you really want to hang on to would be the
BufferedImage instance only, creating a Graphics2D as necessary whenever
you want to update the image.

Ok to the 'Gui Componet class' = the swing component and 'Gui Component
Engine class' = the non Swing class which will hold a reference to the
image and perform changes to it.

Yes this is what I want :) So the 'Gui Component class' will create the
the 'Gui Component Engine class' and pass it the buffered image that
itself will display, itself being the 'Gui Component class'.
You haven't been very specific about what sorts of updates you might do,
but again there seems to be the assumption that there's some
higher-level, user-defined class (where "user" is you) that encapsulates
the behavior of the BufferedImage and Graphics2D, exposing it in a more
general way. If that's the correct assumption, then I think the
"listener" mechanism I mentioned earlier is still appropriate for
dealing with updates to the Swing component that displays this image.

Spot on :)

That doesn't make much sense as I understand the previous discussion.
The only image that you've mentioned so far is the one that represents
your non-GUI data structure. The only time you'd draw that image would
be in the paintComponent() method itself.

No I am not sure it does either :), no my 'non-GUI' data structure as I
understand it is not the 'Gui Component class' but the 'Gui Component
Engine class' 'BufferedImage' reference.
Are you saying that one of the operations you might have on this non-GUI
data structure is to draw yet another image into it? How does the "gui
logic class" come into play here? By "gui logic class", are you talking
about your custom Swing component that's displaying your non-GUI data
structure? Or something else?

No :) not custome Swing component, the 'Gui Component Engine class'
Why are you using the phrase "non gui logic class" to description
something that "handles the logic for the gui component"? To me, "non
gui" implies no connection with the GUI. Conversely, "handles the logic
for the gui component" implies a direct connection with the GUI.

Yes sorry again ;) this is very ambiguous, I am not referring to the
'Gui Component class' but the 'Gui Component Engine class.
The two seem mutually exclusive to me, and yet you seem to be using the
phrases as being directly related to each other. I'm obviously not
understanding what you mean, and I hope that my explanation here makes
it clear why so that you can clarify.
Then hopefully the code I posted can help with that part of the problem.
It has :)
As I noted at the beginning of this message, you can draw into a
BufferedImage by getting a Graphics2D from it (by calling
createGraphics()). With that Graphics2D instance you can draw whatever
you like onto the BufferedImage, including text (I assume that's what
you meant instead of "test" :) ).

Yes I did mean text :) and of course spell check did not pick this up
because test is a legitimate word in the English Dictionary as is text :)
The Graphics2D instance passed to paintComponent() is such a reference.
It's used for drawing the representation of the custom Swing component.
As such it has very little to do with a Graphics2D that you might obtain
from an image data structure, such as a BufferedImage. I mean, it's the
same class, and so of course you can do all the same drawing operations
regardless. But in the former, those operations wind up shown on the
screen, whereas in the latter, they wind up modifying the BufferedImage.

I understand this now, Graphics2D is more an interface than a class, so
that any class which implements it is able to implement its methods in a
way that makes sense to that particular class, but anything that accepts
a Graphics2D can call this interfaces methods and they will just work?
have I understood this bit correctly :)
It kind of sounds like you really have at least two parts to this design
question:

* how to display an image in a custom Swing component
* how to maintain that image in a class that isn't specific to the GUI

Yes this is correct :) I know how to display the image in the component
now, and I also believe I now understand how to maintain and manipulate
this image in the 'Gui Component Engine class' using the 'BufferedImage'
Graphics2D object applying an AffineTransform to it and then sending an
event to the 'Gui Component class' informing it that the image has been
modified and this class could then call its repaint() method which would
then call its paintComponent(Graphics g) method :)
I think that mostly I've been answering the first question, though
hopefully some of the things I've written above help address the
second.

Indeed they have :)

If you disagree with the "two parts" assertion, please explain
why so that I (and anyone else reading) can understand better what the
actual question or questions are.

I don't ;)

Rich
 
P

Peter Duniho

[...]
The Graphics2D instance passed to paintComponent() is such a
reference. It's used for drawing the representation of the custom
Swing component. As such it has very little to do with a Graphics2D
that you might obtain from an image data structure, such as a
BufferedImage. I mean, it's the same class, and so of course you can
do all the same drawing operations regardless. But in the former,
those operations wind up shown on the screen, whereas in the latter,
they wind up modifying the BufferedImage.

I understand this now, Graphics2D is more an interface than a class, so
that any class which implements it is able to implement its methods in a
way that makes sense to that particular class, but anything that accepts
a Graphics2D can call this interfaces methods and they will just work?
have I understood this bit correctly :)

Yes, basically. I mean, technically the Graphics2D class is a class, but
since it's an abstract class with pretty much all abstract methods, it
operates very much like an interface. I think there are good reasons for
it being an abstract class rather than an interface, but from the client
point of view, it is basically just an interface, in the sense that it's a
contract that is implemented by various graphical objects to allow a
uniform way to draw to those graphical objects.
Yes this is correct :) I know how to display the image in the component
now, and I also believe I now understand how to maintain and manipulate
this image in the 'Gui Component Engine class' using the 'BufferedImage'
Graphics2D object applying an AffineTransform to it and then sending an
event to the 'Gui Component class' informing it that the image has been
modified and this class could then call its repaint() method which would
then call its paintComponent(Graphics g) method :)

Please see my other post. In particular, where you write "using the
'BufferedImage' Graphics2D object applying an AffineTransform to it", I
believe you've misunderstood what setting a transform on that particular
Graphics2D object will do.

I have the impression that this spur of the thread is basically dealt
with, and so for simplicity's sake I'll try to keep my replies in the
other main trunk of the thread.

Pete
 
R

RichT

Please see my other post. In particular, where you write "using the
'BufferedImage' Graphics2D object applying an AffineTransform to it", I
believe you've misunderstood what setting a transform on that particular
Graphics2D object will do.

I have the impression that this spur of the thread is basically dealt
with, and so for simplicity's sake I'll try to keep my replies in the
other main trunk of the thread.

Pete

Thank you for your help :)
 
R

RichT

Hi Peter,

Well, I obviously think so. :) That's why I wrote the code that way.

Doh ;)
I'll ask you again to be more specific about your terminology. You've
already written that you're going to describe your Swing component as
"Gui Component class" and "Gui Component Engine class", neither of which
are "graphic". So do you mean something else by "graphic"? Perhaps you
mean to write "Graphics2D"?

Yes I do mean Graphic2D :)
It's difficult to make sure that your question is answered correctly and
precisely if you yourself aren't using correct and/or precise terminology. Sorry :(

Now, assuming that you mean "Graphics2D",
I do :)
I would say that the answer is
"no". You don't "do calculations" on a Graphics2D instance, at least
not as I am in the habit of using the phrase "do calculations". You use
it to draw into some image presentation object (for example, an Image
object, or the on-screen representation of a JComponent).

You can do two things with a Graphics2D: you can change its own state,
and you can perform a drawing operation (which changes the state of
something the Graphics2D refers to, like a BufferedImage). Only the
drawing operations persist and affect later users of the underyling
image object. Changing state of the Graphics2D object, such as setting
a transformation, setting a stroke, a color, etc. these things only
affect subsequent drawing operations using _that_ Graphics2D object.

So, for example, you can't have a BufferedImage, get a Graphics2D object
from it, set the scaling for that Graphics2D, and then expect that to
change how the BufferedImage is drawn when used later by being drawn
into a _different_ Graphics2D object passed to a control's
paintComponent() method.
Ah Now I really do think I understand now :)
Again, because your question is somewhat vague it's hard for me to know
whether this is what you expect or not. But it sort of sounds like you
expect to be able to set the transformation for the Graphics2D, doing
nothing else, and then have that have some effect when the underlying
image object is drawn later. If you do, then that expectation isn't
going to work.

The Grpahics2D of the Image affects drawing to the image, the Graphics2D
in the 'Gui Component class' affects drawing of the component, so to
display the image correctly in the component will require that the 'Gui
Components class' Grpahics2D to be updated too ?

Again, I don't know whether this is what you meant, because you're not
using precise terminology. But previously in this thread you seem to be
using the word "graphic" (incorrectly) to refer to a Graphics2D object.
If that's the case here, then my reply is accurate. Yes it is what I meant :)

It seems to me that if you want to be able to apply scaling,
translation, or rotation changes to the image itself (as opposed to
things being drawn into the image), then those are things that should be
stored as state in your "Gui Component Engine class".
using fields perhaps an AffineTransform?
You might even just maintain a single AffineTransform instance, updated
any time those things are changed, and then used to draw the image
later. You could do this by having your "Gui Component Engine class"
have a property that returns this transform as well as the Image that
the class stores. Or you could have your "Gui Component Engine class"
have a method that takes as a parameter a Graphics2D instance, and then
itself draws the Image into that Graphics2D using the transform that's
been set.

Which way would you suggest? I guess the 'Gui Component class' should
draw its own Graphics2D?
In either case, note that the transform in this situation is used for
_presenting_ the underlying image object that your "Gui Component Engine
class" is storing. It doesn't affect the image directly, but rather
affects how the image is displayed at some later point.

I think I get this now :) I hope
At least based on what you've written so far, it seems to me that you
should be careful to keep in mind that you have two different places
where "drawing" happens. There is drawing into your "Gui Component
Engine class"'s image object, and then there is the drawing _of_ the
"Gui Component Engine class"'s image object. Each will use its own
Graphics2D object to handle drawing, and you'll need to be careful to
make sure you're changing the state you care about in the appropriate
place depending on what effect you want it to have.

Thank you for all your help, I really do believe I understand this now
thanks to your patience.

I think it makes more sense for the 'Gui Component class' to handle its
own drawing so providing a method in the 'Gui Component Engine class' to
return the AffineTransform is possibly better?

I may not have acces to internet for a couple of days, but I am going to
prototype some of these ideas and will post back the results to check
my understanding

Thanks for everything
Rich
 
P

Peter Duniho

[...]
Again, because your question is somewhat vague it's hard for me to
know whether this is what you expect or not. But it sort of sounds
like you expect to be able to set the transformation for the
Graphics2D, doing nothing else, and then have that have some effect
when the underlying image object is drawn later. If you do, then that
expectation isn't going to work.

The Grpahics2D of the Image affects drawing to the image, the Graphics2D
in the 'Gui Component class' affects drawing of the component, so to
display the image correctly in the component will require that the 'Gui
Components class' Grpahics2D to be updated too ?

Sort of. At the time that you want to adjust how the image is displayed,
there is no Graphics2D to update. You only get one of those when the
component actually needs to be repainted and that Graphics2D is only
around long enough for you to repaint the component.

But you can (and should) store data that can be used at that point in
time, applying the data (an AffineTransform, for example) to the
Graphics2D passed to you when the paintComponent() method is called by
Java.
[...]
You might even just maintain a single AffineTransform instance,
updated any time those things are changed, and then used to draw the
image later. You could do this by having your "Gui Component Engine
class" have a property that returns this transform as well as the Image
that the class stores. Or you could have your "Gui Component Engine
class" have a method that takes as a parameter a Graphics2D instance,
and then itself draws the Image into that Graphics2D using the
transform that's been set.

Which way would you suggest?

It depends on how exactly you're using the "engine".

Part of the lack of a clearly superior choice is that it seems as though
you want to include in your engine properties that affect how the image is
drawn. You mentioned scaling, translation, and rotation for example.
These things can all be defined relative to the image itself, independent
of wherever it might actually be drawn.

At the same time, you've also said that the image should be drawn centered
in the component, which implies a contributor to the translation that's
relative to the component itself. This cannot be defined relative to the
image; it only would make sense in the context of a component.

So, on the one hand, it'd be nice to have the "engine" draw itself into
some arbitrary Graphics2D. That way if you want to reuse the "engine" in
a different context, it doesn't require you duplicating a bunch of code
that gets the properties from the "engine" and uses them to draw the image.

On the other hand, doing it that way means you necessarily have to pass
_some_ kind of contextual information in addition to the Graphics2D
instance. That's not terrible -- after all, the Graphics2D is itself
contextual information -- but it does complicate things a bit.

I guess I sort of lean toward the latter suggestion I offered. As I
describe here, the fact is that there's a good argument for doing it the
other way. But I think in the long run, the more reusable technique is
preferable, even if it does mean that your "engine" winds up a little more
complicated.
I guess the 'Gui Component class' should draw its own Graphics2D?

Not only should it, it has to. That's the only way that the Swing
component can repaint itself. And as I mentioned, it will only have that
Graphics2D long enough to repaint itself once. When it's done, so is that
Graphics2D. You'll get (effectively) a fresh one the next time it needs
to be repainted.
[...]
Thank you for all your help, I really do believe I understand this now
thanks to your patience.

You're welcome. :)
I think it makes more sense for the 'Gui Component class' to handle its
own drawing so providing a method in the 'Gui Component Engine class' to
return the AffineTransform is possibly better?

As I mentioned above, I think the other way is marginally more desirable.
But if you feel differently, I have to say that it's not a terrible choice
either way. IMHO, having the Swing component handle all of the drawing
itself definitely has the edge in simplicity, even if it would require
more code later on if you want to reuse the "engine" class.

Pete
 
R

RichT

Hi Peter :)
Internet online once again
Sort of. At the time that you want to adjust how the image is
displayed, there is no Graphics2D to update. You only get one of those
when the component actually needs to be repainted and that Graphics2D is
only around long enough for you to repaint the component.

But you can (and should) store data that can be used at that point in
time, applying the data (an AffineTransform, for example) to the
Graphics2D passed to you when the paintComponent() method is called by
Java.
I do believe the penny has dropped, must have been the aroma of coffee
or was that Java ;)
It depends on how exactly you're using the "engine".

Part of the lack of a clearly superior choice is that it seems as though
you want to include in your engine properties that affect how the image
is drawn. You mentioned scaling, translation, and rotation for
example.
True :)
These things can all be defined relative to the image itself,
independent of wherever it might actually be drawn. With you so far...

At the same time, you've also said that the image should be drawn
centered in the component, which implies a contributor to the
translation that's relative to the component itself. This cannot be
defined relative to the image; it only would make sense in the context
of a component.
This is the tricky part :) if the engine will do the drawing, then to
centre the image it will require the dimensions of the component it is
drawing the image on to.
So, on the one hand, it'd be nice to have the "engine" draw itself into
some arbitrary Graphics2D. That way if you want to reuse the "engine"
in a different context, it doesn't require you duplicating a bunch of
code that gets the properties from the "engine" and uses them to draw
the image. True...

On the other hand, doing it that way means you necessarily have to pass
_some_ kind of contextual information in addition to the Graphics2D
instance. That's not terrible -- after all, the Graphics2D is itself
contextual information -- but it does complicate things a bit.
When you say contextual are you referring to the dimension of the 'Gui
Component class' object? If so I guess it will be easy enough to store
this info either directly in the 'Gui Component Engine class' object as
fields and have the 'Gui Component class' set the dimensions when it
resizes, or store this in another class which the 'Gui Component class'
can update as it resizes and the 'Gui Component Engine class' can read
when it needs the dimension information of the 'Gui Component class'?
I guess I sort of lean toward the latter suggestion I offered. As I
describe here, the fact is that there's a good argument for doing it the
other way. But I think in the long run, the more reusable technique is
preferable, even if it does mean that your "engine" winds up a little
more complicated.

Yes I am leaning that way too, I do believe this was my original
intention, which at the time I did not fully understand enough to
realise this :)
As I mentioned above, I think the other way is marginally more
desirable.

I also agree, after reading your explanation it is true that the
centring of the image is a minor complication, but I also understand
that having the 'Gui Component Engine class' handle the drawing
operations will enable better reuse.

But if you feel differently,

I do not :)


IMHO, having the Swing component handle all
of the drawing itself definitely has the edge in simplicity, even if it
would require more code later on if you want to reuse the "engine" class.

I guess it is the choice of more work now or more work later, I have
always been taught 'Don't put off till tomorrow what you can do today!' :)

Thanks again for you help :)
Rich
 
P

Peter Duniho

[...]
On the other hand, doing it that way means you necessarily have to
pass _some_ kind of contextual information in addition to the
Graphics2D instance. That's not terrible -- after all, the Graphics2D
is itself contextual information -- but it does complicate things a bit.

When you say contextual are you referring to the dimension of the 'Gui
Component class' object?

Yes. And any other information that might be required (that's all I'm
aware of, but you never know what else you might want to add later).
If so I guess it will be easy enough to store this info either directly
in the 'Gui Component Engine class' object as fields and have the 'Gui
Component class' set the dimensions when it resizes, or store this in
another class which the 'Gui Component class' can update as it resizes
and the 'Gui Component Engine class' can read when it needs the
dimension information of the 'Gui Component class'?

I think that actually, the easiest thing to do is just pass it when you
ask the "engine" to draw itself. You have to pass the component's
Graphics2D then anyway, since that's the only time you'll have it, so you
might as well include the other information needed as well.

There's nothing wrong per se with the other options you describe, and
there are even other alternatives beyond those (for example, implementing
some sort of "listener" connection between the two classes). But since
the "engine" isn't going to need the information except when drawing, why
add all that overhead?

I have the impression that otherwise you feel you've got a handle on the
question. Have fun! :)

Pete
 
R

RichT

I have the impression that otherwise you feel you've got a handle on the
question. Have fun! :)

Pete

I sure hope so :)
Thanks for everything Peter, you really are a star
Rich
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,189
Members
46,735
Latest member
HikmatRamazanov

Latest Threads

Top