I'm creating some some shapes and everything seems to be blurred, like anti-aliased despite no effects applied.
For example, a white line drawn on a black backgroud with 1 pixel width, is rendered grey! Changing the width to 2px results in white, but not well-defined.
When searching, the method setSmooth(false) on shapes returned, but calling it makes no difference.
What should I change or disable on Stage or Scene?
See the Shape documentation:
Most nodes tend to have only integer translations applied to them and
quite often they are defined using integer coordinates as well. For
this common case, fills of shapes with straight line edges tend to be
crisp since they line up with the cracks between pixels that fall on
integer device coordinates and thus tend to naturally cover entire
pixels.
On the other hand, stroking those same shapes can often lead to fuzzy
outlines because the default stroking attributes specify both that the
default stroke width is 1.0 coordinates which often maps to exactly 1
device pixel and also that the stroke should straddle the border of
the shape, falling half on either side of the border. Since the
borders in many common shapes tend to fall directly on integer
coordinates and those integer coordinates often map precisely to
integer device locations, the borders tend to result in 50% coverage
over the pixel rows and columns on either side of the border of the
shape rather than 100% coverage on one or the other. Thus, fills may
typically be crisp, but strokes are often fuzzy.
Two common solutions to avoid these fuzzy outlines are to use wider
strokes that cover more pixels completely - typically a stroke width
of 2.0 will achieve this if there are no scale transforms in effect -
or to specify either the StrokeType.INSIDE or StrokeType.OUTSIDE
stroke styles - which will bias the default single unit stroke onto
one of the full pixel rows or columns just inside or outside the
border of the shape.
And see also the documentation of Node:
At the device pixel level, integer coordinates map onto the corners
and cracks between the pixels and the centers of the pixels appear at
the midpoints between integer pixel locations. Because all coordinate
values are specified with floating point numbers, coordinates can
precisely point to these corners (when the floating point values have
exact integer values) or to any location on the pixel. For example, a
coordinate of (0.5, 0.5) would point to the center of the upper left
pixel on the Stage. Similarly, a rectangle at (0, 0) with dimensions
of 10 by 10 would span from the upper left corner of the upper left
pixel on the Stage to the lower right corner of the 10th pixel on the
10th scanline. The pixel center of the last pixel inside that
rectangle would be at the coordinates (9.5, 9.5).
So your options for clean lines when you have an odd stroke width are:
Use a StrokeType.INSIDE or StrokeType.OUTSIDE stroke style.
Offset the co-ordinates of shapes by 0.5 of a pixel so that the strokes line up on the lines rather than the cracks between lines.
Just use the next even number up as the stroke width, e.g. 1 => 2, 3 => 4, etc.
As to why setSmooth(false) does not work, I don't know exactly, my guess is that the antialiasing it refers to is independent of the antialiasing styles performed when strokes are centered on the cracks between pixels, but I would not know why that would be.
Related
The OpenCV documentation states:
dp: Inverse ratio of the accumulator resolution to the image resolution. For example, if dp=1 , the accumulator has the same resolution as the input image. If dp=2 , the accumulator has half as big width and height.
But it gives no indication how the size of this value affects circle detection. I thought the accumulator was just a collection of maxima, how does it have a resolution?
During hough transformation you transform your input image into so called hough space. It is 3-dimensional while trying to find circles (the three dimensions are the coordinates of the circle center and the radius). During the transformation each edge pixel in your input image votes for all possible circles on which the pixel could lie.
You can think of the voting as increasing multiple values within a 3-dimensional matrix (hough space). After voting you search for the highest value within this matrix and read of the circle center and its radius.
The bigger the matrix (compared to your input image) (the smaller your dp), the higher the resolution of your voting. The higher the resolution, the more accurate the circle detection.
However, the more accurate the detection, the more likely it is to e.g. miss slightly degenerated circles or detect multiple circles instead of one with a big edge.
I'm trying to write a in-app joystick axis calibration tool.
The joystick axis area should be a rectangle, but in the reality it's a non-linear closed curve and I want to increase the accuracy.
The calibration should work this way:
we have a measured value, and this way we get the correct value:
Correct value = [(measured value)/range] * wantedrange
where range is the difference between the maximum and minimum value measured for that axis.
But there is also an offset to move the center point to the right position, how to calculate it?
EDIT: I also made an image: green rectangle is the expected area, red shape is the "real" inaccurate measured area, finally blue is the wanted calibrated area that I shift to (0,0) so that I can use the ratio to convert coordinates to the bigger green rectangle.
EDIT2:
This image explains how calibration can be even more accurate, thanks to zapl answer:
If we find the blue rectangle center, we can divide the rectangle in 4 rectangles and calculate a ratio between that range and the green rectangle's range.
And the code should be something like this:
if(value<axiscenter) correctedvalue = ((value-axismin)/(axiscenter-axismin)) * wantedaxisrange;
else correctedvalue = wantedaxisrange + ((value-offset-axiscenter)/(axismax-axiscenter-axismin)) * wantedaxisrange;
You can get the position of the blue rectangle by instructing the user to move the joystick along the edges so that the values you see are the red curve. You should also instruct user to leave joystick in centered position since you usually need to know the center. Calcuated center is not always the real center position.
For each axis separate those values by the side of the center they are on and find those that are the closest to the center point. That would work with calculated center. Now you have the blue rectangle.
E.g. on X axis you see values ranging from 0-20 and 80-100, center is ~50 > blue rectangle is 20 - 80.
Assuming you want to calibrate it so that values are 0-100 (green) you calculate correction for the x axis as
calibratedX = (uncalibrated - 20) * 100 / 60
Values are shifted by 20 to the right (-20 to normalize them to 0-60) and their range is 60 (80 - 20) which you want to upscale to 0-100. After that clip values to 0-100 since they will be outside for every point on the red line that was outside the blue rectangle.
Result looks like
where pink are the values after transformation, and the pink area outside the green rectangle is cut away.
Regarding the center point: just run it through those calculations as well.
What is the best way to draw a crisp, opaque hairline in JavaFX 2.2?
The documentation says, that with a strokeWidth of 0.0d it'll be a hairline, but it's just not visible at all. Values > 0.0d and <1.0d show lines that are pretty fine, but also don't show an opaque behaviour. When one line cuts another, the intersecting points are lighter than the rest of the line (i'd expect this behaviour from a line with some transparency). Finally, 1.0d draws a white line with several pixels width.
That's my test code:
LineBuilder.create().startX(i*gridSize).startY(0).endX(i*gridSize).endY(height).smooth(false).stroke(Color.WHITE).strokeWidth(0.5d).fill(Color.WHITE).build();
You can use a Region subclass, such as a Pane for your Parent, with snapToPixel set to true.
Additionally, refer to the Node documentation on the co-ordinate system.
At the device pixel level, integer coordinates map onto the corners
and cracks between the pixels and the centers of the pixels appear at
the midpoints between integer pixel locations. Because all coordinate
values are specified with floating point numbers, coordinates can
precisely point to these corners (when the floating point values have
exact integer values) or to any location on the pixel. For example, a
coordinate of (0.5, 0.5) would point to the center of the upper left
pixel on the Stage. Similarly, a rectangle at (0, 0) with dimensions
of 10 by 10 would span from the upper left corner of the upper left
pixel on the Stage to the lower right corner of the 10th pixel on the
10th scanline. The pixel center of the last pixel inside that
rectangle would be at the coordinates (9.5, 9.5).
Also see the Shape documentation:
Most nodes tend to have only integer translations applied to them and
quite often they are defined using integer coordinates as well. For
this common case, fills of shapes with straight line edges tend to be
crisp since they line up with the cracks between pixels that fall on
integer device coordinates and thus tend to naturally cover entire
pixels. On the other hand, stroking those same shapes can often lead
to fuzzy outlines because the default stroking attributes specify both
that the default stroke width is 1.0 coordinates which often maps to
exactly 1 device pixel and also that the stroke should straddle the
border of the shape, falling half on either side of the border. Since
the borders in many common shapes tend to fall directly on integer
coordinates and those integer coordinates often map precisely to
integer device locations, the borders tend to result in 50% coverage
over the pixel rows and columns on either side of the border of the
shape rather than 100% coverage on one or the other. Thus, fills may
typically be crisp, but strokes are often fuzzy.
Two common solutions to avoid these fuzzy outlines are to use wider
strokes that cover more pixels completely - typically a stroke width
of 2.0 will achieve this if there are no scale transforms in effect -
or to specify either the StrokeType.INSIDE or StrokeType.OUTSIDE
stroke styles - which will bias the default single unit stroke onto
one of the full pixel rows or columns just inside or outside the
border of the shape.
So, if you leave your Nodes in a Group or Region which does not snapToPixel, you can follow the above instructions from the Shape documentation.
Here is some sample code:
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineBuilder;
import javafx.scene.shape.StrokeType;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/** http://stackoverflow.com/questions/11886230/how-to-draw-a-crisp-opaque-hairline-in-javafx-2-2 */
public class LineWidths extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
Line fuzzyline = LineBuilder.create()
.startX(5).startY(50)
.endX(90).endY(50)
.stroke(Color.BLACK).strokeWidth(1)
.build();
Line hairline = LineBuilder.create()
.startX(4.5).startY(99.5)
.endX(89.5).endY(99.5)
.stroke(Color.BLACK).strokeWidth(1)
.build();
Line fatline = LineBuilder.create()
.startX(5).startY(150)
.endX(90).endY(150)
.stroke(Color.BLACK).strokeWidth(1).strokeType(StrokeType.OUTSIDE)
.build();
Pane snappedPane = new Pane();
Line insideline = LineBuilder.create()
.startX(5).startY(25)
.endX(90).endY(25)
.stroke(Color.BLACK).strokeWidth(1)
.build();
snappedPane.setSnapToPixel(true);
snappedPane.getChildren().add(insideline);
snappedPane.setPrefSize(100, 50);
snappedPane.relocate(-0.5, 174.5);
stage.setScene(
new Scene(
new Group(
fuzzyline, hairline, fatline, snappedPane,
new Text(10, 40, "fuzzyline"),
new Text(10, 90, "hairline"),
new Text(10, 140, "fatline"),
new Text(10, 190, "snappedPane")
), 100, 250
)
);
stage.show();
}
}
So I've got an assignment that takes two inputs, males and females, and outputs matingPairs, the product of the two.
In addition to that, the instructions ask to draw a shape using one of those variables.
I've decided to draw circles for each value.
I first draw matingPairs, followed by the smaller male and female circles on top of the original, larger matingPairs circle.
The problem I'm running in to is obviously representing the graphic in the applet. If the numbers go higher than say 100, the graphic becomes too large for the applet.
I'm looking for a way to basically have the matingPairs circle always fill the applet, then have males and females dynamically adjust so their size is scaled relative to the matingPairs circle size. I'm using JApplet.
Thank you very much for any guidance. I'm really looking for a solution, rather a push in the right direction.
May be you should provide more instruction about how are you drawing the circles in the Graphics object.
The idea is to manage two bi-dimensional spaces with different scales; the first one is the input data and the second one represents the available area to draw such data. The first one can have data on any location, such (5, 5), (0.2, 0.3)or (1200, 3400). The key is to map the original coordinates of the first space into the second, using the proper transformation: scale + translation.
This transformation must be calculated prior to start drawing and applies to any point drawn.
The idea is to map the rectangle where input data resides to the available area in the graphics. If the graphics area is 200x200 pixels and the data could be from (0, 0) to (400, 400), just divide by 2 the coordinates of the points to draw. If the original data is not centered in (0, 0), use a translation.
So, do you need to know how to get the size of the applets canvas or how to scale the male/female circles accordingly?
Edit:
Drawing a circle to fill the 600x600 area should be easy. Just keep in mind that you often specify the top left corner of the circle and the width and height (i.e. the diameter) when calling drawOval() / fillOval() or similar methods.
The next question is: what does represent the size of the input (males/females) and output (pairs), the area or the radius of the circles? Whatever it is, it should be easy to calculate the input/output ratio and then multiply the fixed size of the output circle with it in order to get the size of the input circle.
I am currently trying to show a series of images that slightly differ from each other in a 3D view, and which contain lots of transparent areas (for example, points that move in time inside a rectangle, and I would provide a 3D view with all their positions over time).
What I'm doing now is generate an image with the points drawn in it, create one Boxes of 40x40x1 per frame (or rectangular shape of 40x40), apply the image as a texture to the FRONT side of the box, and add the boxes to my scenes at positions (0, 0, z) where z is the frame number.
It works quite well, but of course their is discontinuities (of 1 "meter") between the images.
I would like to know if their is a way to create an "extrusion" object based on that image so as to fill the space between the planes. This would be equivalent of creating one 1x1x1 box for each point, placing them at (x, y, z) where x/y are the point's coordinate and z the frame number. The actual problem is that I have lots of points (several hundreds, if not thousands in some cases), and what was relatively easy to handle and render with an image would, I think, become quite heavy to render if I have to create thousands boxes.
Thanks in advance for your help,
Frederic.
You could use 3d textue with your data (40 x 40 x N) pixels, N=number of frames.
But you still has to draw something with this texture enabled.
I would do what you are doing currently - draw quads, but no only along Z axis, but along X and Y too.
Each of N quads along Z axis would have 40x40 size, each of 40 quads along X axis would be 40xN size, and each of 40 quads along Y axis would be Nx40 size.
So for 2x2x2 textue we will draw 2+2+2 = 6 quads, and it will look like regular cube, for 3x3x3 points in texture we will draw 3+3+3 quads, and it will look like 8 cubes stacked into one big cube (so instead of 8 cubes 6 quads each we just draw 9 quads, but the effect is the same).
For 40x40x1000 it would be 1080 quads (reasonable to draw in real time imho) instead of 40*40*1000*6 quads.
I only don't know, if the graphical effect would be exactly what you wanted to achieve.