Java: line-stroke with filled shape - java

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

Related

Java how to draw and fill a Polygon which has holes

I am currently trying to draw and fill a Polygon which has a hole in it in Java. Normally this would not be a big problem, since I would draw the exterior ring and then draw the interior ring with the color of the background.
But the problem is, that the polygon is displayed above a image which should be "seen" through the hole.
I am writing the code in Java and am using JTS Topology Suite for my geometry data.
This is my current code, which just paints the border and fills the polygon with a color.
private void drawPolygon(com.vividsolutions.jts.geom.Polygon gpoly, Color color, Graphics2D g2d){
java.awt.Polygon poly = (java.awt.Polygon)gpoly;
for(Coordinate co : gpoly.getExteriorRing().getCoordinates() {
poly.addPoint(co.x, co.y);
}
g2d.setColor(col);
g2d.fill(poly);
g2d.setColor(Color.BLACK);
g2d.draw(poly);
}
Sadly java.awt.Polygon does not support Polygons with holes.
Use the Polygon as the basis for an Area (e.g. called polygonShape).
Create an Ellipse2D for the 'hole', then establish an Area for it (ellipseShape).
Use Area.subtract(Area) something like:
Area polygonWithHole = polygonShape.subtract(ellipseShape);
There are some ways to draw shapes or areas that are more complex than a simple polygon (another answer already mentioned Area).
Besides those, you could try to tessellate your final polygon. There are lots of algorithms to do this. For more complex shapes, the algorithms get a bit more complex as well. Basically, you're dividing your final shape into little polygons (usually triangles, but it also can be something else) and then draw those polygons.
You can take a look at your possibilities by searching for "Tessellation Algorithm", there are also some already implemented libraries for Java.
You can use java.awt.geom.Path2D to render a "compound shape" with a hole in it:
If you have java.awt.Shape objects defining the outside & inside edges of the shape, use append(shape, false) to add each shape.
If you have a set of path points for the outside edge and a set of path points for the inside edge, use lineTo() to add the first set of points - creating a closed loop by either ending with the same point you started with, or calling closePath() to automatically close the loop. Then use moveTo() to create a break before adding the inner set of points via more lineTo() calls.
In either case, you must create the path passing Path.WIND_NON_ZERO to the constructor - otherwise the hole won't be left unfilled.
See How to create shape with a hole? for a longer code example.
You could fill the polygon first, and then draw the holes on top, giving the illusion that it filled everything but the holes.

fixing blurrieness with Graphics2D in JAVA

I was wondering why displaying text in my frame has to be so blury, and i came across this piece of code, which is working by the way
public void paint(Graphics graphicsObject){
if(graphicsObject instanceof Graphics2D){
Graphics2D g2D = (Graphics2D) graphicsObject;
g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
graphicsObject.drawString("not blurry text display", 200, 200);
}
having dificulties trying to understand how this work out.
how come g2D.setRenderingHint is fixing my blurry problem, but i dont use it to paint my string?
First, what this fixed was not blurring. It was aliasing.
Aliasing occurs when drawing functions try to create curvy shapes using a raster image - a matrix of pixels, which are squares. If the lines are not vertical or horizontal, you end up with "stairs" - edges that are jaggy.
Antialiasing is a way to make this effect less visible to the eye, by using additional pixels around the drawn line, which are painted in different tones between the foreground and the background. This cheats our eyes to see the line as "smooth". If you zoom an image drawn with antialiasing, you may notice those pixels around the actual line.
So, actually, antialiasing blurs the line, and this makes it seem smoother to our eyes.
As to your actual question - you are using the graphics object to draw the line. You set the hint in the graphics object by accessing the object in its form as a Graphics2D. Even though you then go on and use the graphicsObject using its regular Graphics reference, the method drawString() is overridden. This means that it will be activated in the concrete object that implements it, which sees - and uses - the RenderingHint hash map where your hint is stored.
but i dont use it to paint my string?
Why do you think so? Because, you are actually using it.
Nowadays, Graphics is always a Graphics2D object, so your if-statement will be executed.
g2D and graphicsObject both point to the same object. For the compiler they are two variables of different types, allowing different methods to be called, but at execution time, these two variables actually point to the same object.
So by setting the rendering hint on g2D, and this being the same object as graphicsObject, drawing the string respects that hint and fixes the blurryness by using anti-aliasing as specified in the hint.

Draw diagonal lines with Java

I have the following code:
private _x,_y,_w,_h;
protected void paintComponent( Graphics g_ ) {
g_.setStroke( new BasicStroke(2) );
g_.drawLine(_x, _y, _x+_w, _y+_h);
g_.drawLine(_x, _y+_h, _x+_w, _y);
}
In my case I'm drawing the diagonals of a square so: _w==_h.
My problem the two line don't have the same apparent thikness: the first line look thicker than the second. When checking the actual pixels drawn here's the difference of rendering:
I don't really care which one should be considered "correct" (though I'd like to understand the reasons of this result), but I'd like some coherence here, that both lines have the same rendering: how can I do that?
(when I use a 1px stroke, there's no difference between the two lines).
Followup to Olavi's ansewer:
Using an odd number of pixel for the stroke doesn't solve the problem:
Enabling anti-aliasing leads to another problem: the stroke of the square in which the cross is drawn gets blurred:
Basically you have two options:
Use an odd-number thickness
Use antialiasing
Personally I prefer antialiasing, because it's pretty and I like pretty things. However to explain why a stroke width of 2 behaves like that: Java does not know whether or not draw the line with the first way or the second way (I can't really explain exactly why it does what it does, but this is what I see). To further elaborate this answer, try starting the other line one pixel to the left or to the right: this should result in two lines with the same thickness.
To use antialiasing, do the following (untested code!). Code picked up from here:
Graphics2D graphics2D = (Graphics2D) g_;
graphics2D.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.drawLine(_x, _y, _x+_w, _y+_h);
graphics2D.drawLine(_x, _y+_h, _x+_w, _y);

Can we tilt a JPanel at an angle?

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...)

Good architecture for drawing shapes

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.

Categories