For my software engineering object-oriented design class, we are tasked with implementing a simple "Paint" application that can draw simple things like Ellipses, Rectangles, Lines, Free-form curves, and Text.
I am really trying to go for the gold here, and come up with a really clean and elegant architecture, because we need to support things like printing, saving, and exporting to an image, and I want to be able to do so with minimal effort.
So far, I have three "tiers" of classes:
Shape classes: Ellipse, Rectangle, Line, Textbox and Freeform, all inheriting from my custom Shape class
Style classes, each of which implements GraphicsModifier, which allows the class to change the Graphics2D object
StrokeStyle describes how the shape's stroke should be drawn, with Stroke and Paint objects
FillStyle describes the fill , with a Paint object
TextStyle is basically a wrapper for AWT's font classes, allowing easy modification of font sizes, styles, and faces.
Drawing classes
Right now, I am stuck on how my drawing classes should be implemented.
My initial thoughts were to have something like
class DrawingObject {
StrokeStyle stroke;
FillStyle fill;
TextStyle text;
Shape shape;
void draw(Graphics2D g) {
//????
}
}
The issue is that, if shape is a Freeform, I need to apply only stroke and loop through each point in the curve and draw it separately. If shape is a Rectangle, Line, or Ellipse, I need to apply only stroke and fill, and use g.draw(Shape). If shape is a Textbox, I need to apply all three stroke, fill, and text, and use g.drawString().
It occurred to me that this might be a good place to apply a Strategy pattern, switched on the subclass of shape:
if (shape instanceof Freeform) {
strategy = new FreeformDrawer(shape);
} else if (shape instanceof Rectangle || ... ) {
strategy = new NormalDrawer(shape);
} else if (shape instanceof Textbox) {
strategy = new TextboxDrawer(shape);
}
strategy.draw(g);
(I guess I could use a little reflection magic to switch on the class name too, but that's beside the point)
But somehow this feels a little dirty.
How would you tackle this problem? Am I going about this wrong? Is Strategy a good idea here?
If it matters, I'm using Java and Swing to implement this, but in theory it should be applicable to any OO language/framework.
tl;dr:
Given a bunch of objects to be drawn in different ways, and styles that get applied to them in differing capacities, how would you draw them, using good object-oriented design?
You're right - it does feel a little dirty :)
The reason is that if you were to really use the strategy pattern you would need to replace your existing subclasses. So there would be no Rectangle class anymore, there would just be a Shape with its strategy set to RectangleDrawer. Then there's no need for that ugly set of ifs, because there's no need to map anything.
In this case though, I don't think strategy would be your best option. Strategy is great for when you need to change behaviors dynamically, and I'm guessing in your program once a square is drawn it's never going to change to a circle.
If you're going for an inheritance heirarchy, it's too flat.
You really want something like:
Drawable
+-Shape
| +-Rectangle
| +-etc.
+-FreeForm
+-etc.
Alternatively, keep the flat heirarchy, and use a virtual render() method. Render should just return a bitmap, and then your draw method can be something like:
void draw(Shape s) {
graphics.Draw(s.render(), s.x, s.y);
}
You can then override render in each of your classes, so that Rectangle produces a solid-colour bitmap, Ellipse returns a bitmap that is transparent around the outside but solid-colour in an elliptical shape in the center, etc.
Community Wiki so that if anyone feels they have something to add, they can go right ahead and add it.
Related
This is the code
player = createEntity(400, 600, 40, 60, Color.BLUE);
private Node createEntity(int x, int y, int w, int h, Color color) {
Rectangle entity = new Rectangle(w, h);
entity.setTranslateX(x);
entity.setTranslateY(y);
entity.setFill(color);
gameRoot.getChildren().add(entity);
return entity;
}
I'm not sure how to put an image to my player, any thoughts?
Java How can I put an image on a rectangle
My suggestion (if this is Swing) is not to use a Rectangle, or else use Rectangle but as part of a larger solution.
Instead I'd create a logical class, perhaps called Entity, that has a position, an image, and a draw method that accepts a Graphics parameter draws its image at whatever position needed, and then create Entity objects. Then within my JPanel's paintComponent method, I'd iterate through all the Entities created, calling their draw method.
First of all, you'll want to use active rendering. This'll prevent you from having all sorts of problems related to refreshing the display, and Java's BufferStrategy automatically handles multi-buffering for you, assuming you request more than one buffer.
Now that you're sure the screen gets updated, when rendering, you can draw a rectangle with a java.awt.Graphics object using the drawRect method. If you would like to fill this rectangle, use fillRect. The Graphics object draws everything in a pre-defined colour. If you would like to change colour, you can call setColor. This method requires a java.awt.Color object as an argument, which allows you to define all possible 64-bit transparent colours using one of the constructors. It also has a few predefined colours, like Color.RED, which is 0xFF0000.
I recommend you explore all the methods Graphics has to offer, and maybe even look into Graphics2D, which supports a few extra things. Note that most Graphics objects can be cast to Graphics2D, but if you're not sure, you might either want to look into Java's source code (there is an src.zip inside the JDK installation) or use an instanceof check. As a matter of fact, Graphics2D supports drawing shapes like your Rectangle using the drawShape or fillShape method.
For those of you who are curious, 0xFF0000 is a way of formatting colours. It is RGB encoded into hexadecimal (0xRRGGBB). It's also commonly represented #FF0000 in other languages, like CSS (Cascading Style Sheet, used for easily formatting HTML documents)
In a component I must display several lines of colored pixels. Each pixel of the line is given a color. What kind of component is suitable to build the line or what kind of component is suitable to hold pixels?
Just extend JComponent and paint the lines/pixels in the paintComponent() method.
To me the best (but maybe not easiest) way would be implementing a custom Paint class that allows for setting colour regions - a bit like the GradientPaint classes but more flexible.
Then you would call Graphics2D.setPaint(myPaint) just before you draw the line.
The Paint implementation could offer a method setColorForRegion(double start, double end, Color color) with start and end taking values between 0.0 and 1.0 to mark a region on the line.
Might be a bit complicated to implement the Paint class, but the benefit is, that you can resize the lines and draw them in any direction while preserving the color pattern.
This is not really important, but it was bothering me for a little while.
Problem description:
Given: a line (Line2D)
Wanted: drawing the line as a wedge (Filled GeneralPath)
Surely this can be done by creating the wedge as General Path and then filling it by the Graphics (my solution).
My first approach would have been a wedge-stroked line, because I didn't want to change the line object for some reason, also I just wanted to draw the line object and not think about it any more.
Creating the wedge-stroke was no problem (Some calculations and then creating the General Path) - but I was not able to fill it (easily)
Apparently it seems the fill of Graphics2D only fills the shape it gets - and does not handle the filling of the stroke (that behavior makes sense if one thinks about it).
Question: Is there any way to fill a shape of a Stroke (filling a shape - more specifically: a GeneralPath - somehow before drawing it)?
May be BasicStroke.public Shape createStrokedShape(Shape s) can help if you pass the Line2D there?
Once you use createStrokedShape(), note that draw() "strokes the outline of a Shape," while fill() "fills the interior of a Shape."
import java.awt.*;
public static Shape strokeToShape(Shape path, float strokeWidth)
return new BasicStroke(strokeWidth).createStrokedShape(path);
}
You may also specify cap and join parameters of BasicStroke
I have image inside the JPanel. I would like to rotate the image. Is it possible to rotate the JPanel using Graphics, Image is rotatable, Just out of curiosity is it possible to rotate JPanel ?
Yes! This is possible and fairly straightforward too. I haven't done rotations but I have done other affine transformations (scaling the entire GUI up and down) very successfully on a project. I cannot see why rotations should be any different.
Instead of trying to scale each component use the fact that you can set a transformation on the Graphics object. Since this is shared between all components being rendered you get all things transformed at once "for free". It is important to realize that the transformation is only a rendering-process-step ... i.e. all components still believe they have the bounds (locations+sizes) which you gave them in the untransformed world. This leaves us with the challenge to deal with mouse-events correctly. To do this you simply add a glass-pane in front of your main-panel. This pane collects all mouse-events and apply a reverse of the transform on the event and then sends the event onward towards all other components.
Conceptually very simple! Still, I remember it took some tweaking to get it all crisp though. Especially the fact that rendered texts (fonts) in java are not correctly linearly scaled (it scales in discrete steps corresponding to font-sizes) imposed a final challenge in my scale-affine-transformation-case. Maybe you don't have to worry about that if you only rotate.
I got my inspiration from JXTransformer: http://www.java.net/blog/alexfromsun/archive/2006/07/jxtransformer_t.html
As far as I know you can't rotate a JPanel itself but you might be able to rotate the image inside the JPanel using Java2D. Here's an article that might help.
Edit:
There might actually be a way to rotate JComponents (such as JPanel) if you override their paintXxx methods and use AffineTransform.
It's not possible to rotate JPanel itself, but it's certainly possible to rotate any image inside. There are quite a few ways to do that, you can - for example - override JPanel's public void paint(Graphics g) and then cast Graphics to Graphics2D. It's very useful class, does rotation and much more ;) Check api docs for more info about this one.
Yes, it is possible. But you won't rotate the panel, but the image:
public void paintComponent(Graphics gg)
{
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTI_ALIAS, RenderingHints.VALUE_ANTI_ALIAS_ON);
AfflineTransform matrix = g.getTransform(); // Backup
float angle = Math.PI / 4.0f; // 45°
g.rotate(angle);
/* Begin */
g.drawImage(yourImage, [your coordinates], null);
/* End */
g.setTranform(matrix); // Restore
}
Everything between /* Begin */ and /* End */ will be drawn rotated.
(I didn't test the code, so, they may be some syntax errors...)
I have a set of icon images of various sizes (16x16, 24x24, 32x32) and base colours (red, green, blue, cyan, magenta, yellow). The images are pretty basic geometric patterns + drop shadow, so my gut feeling is that it should be pretty straightforward to replace the files with an icon factory that can generate images given a base colour.
However, subclassing the Image class seems to be a lot of work - is there a better way? Just to clarify - I'm not interested in generating image files, I intend to use the Image objects directly.
What’s wrong with BufferedImage? It will give you a WritableRaster if you ask nicely. :)
If you intend to paint the images to the screen, there is a better way. Write your class to extend the Icon interface, and use the paint method to actually use the Graphics2D APIs to draw the icon. You can pass the color to the constructor. I've done this before, and it works beautifully.