I need to create a simple Java program, that draws a bezier curve pixel by pixel through any amount of points. At the moment, everything seems to be ok except that the curve always ends at x=0 y=0 coordinates.
Screenshot 1
Screenshot 2
I need it to end at the last point. My brain is not quite working today, so I'm looking for some help.
Here is what I have:
private void drawScene(){
precision = Float.parseFloat(this.jTextField4.getText());
//Clears the screen and draws X and Y lines
g.setColor(Color.white);
g.fillRect(0, 0, pWidth, pHeight);
g.setColor(Color.gray);
g.drawLine(0, offsetY, pWidth, offsetY);
g.drawLine(offsetX, 0, offsetX, pHeight);
//Drawing the points
if(pointCount > 0){
for(int i = 0;i<pointCount;i++){
g.setColor(Color.red);
g.drawString(String.valueOf(i+1), points[i].x + offsetX, points[i].y - 6 + offsetY);
g.drawOval(points[i].x + offsetX, points[i].y - 6 + offsetY, 3, 3);
}
}
//Drawing the curve
if(pointCount > 1){
float t = 0;
while(t <= 1){
g.setColor(Color.gray);
this.besierCurvePixel(t);
t += precision;
}
}
}
//Factorial
private static int fact(int n) {
int fact = 1;
for (int i = 1; i <= n; i++) {
fact *= i;
}
return fact;
}
//Bernstein polynomial
private static double bernstein(float t, int n, int i){
return (fact(n) / (fact(i) * fact(n-i))) * Math.pow(1-t, n-i) * Math.pow(t, i);
}
private void besierCurvePixel(float t){
double bPoly[] = new double[pointCount];
for(int i = 0; i < pointCount; i++){
bPoly[i] = bernstein(t, pointCount, i+1);
}
double sumX = 0;
double sumY = 0;
for(int i = 0; i < pointCount; i++){
sumX += bPoly[i] * points[i].x;
sumY += bPoly[i] * points[i].y;
}
int x, y;
x = (int) Math.round(sumX);
y = (int) Math.round(sumY);
g.drawLine(x + offsetX, y + offsetY, x + offsetX, y + offsetY);
}
This is the method for adding the points (pointCount is 0 initially):
points[pointCount] = new Point();
points[pointCount].x = evt.getX() - this.offsetX;
points[pointCount].y = evt.getY() - this.offsetY;
pointCount++;
this.drawScene();
The problem was here
for(int i = 0; i < pointCount; i++){
bPoly[i] = bernstein(t, pointCount, i+1);
}
The second parameter in the bernstein method was incorrect. Basically If I have 3 points, it should be 2 not 3;
bPoly[i] = bernstein(t, pointCount-1, i+1);
Where does "pointcount" get set (and to what)?
Have you tried stepping through your code to see why it continues after reaching the last point?
Is it possible that you are stepping through a loop 1 extra time, which is why the last point would have a destination set to (0,0)?
Could you set the number of steps for the app to make to each point?
Hopefully I am bringing up points to help you find your answer
*Edit: If I had to guess- you are accidentally adding an additional point of (0,0) to points[]; Here is where I am seeing it go to (0,0) after the last point:
for(int i = 0; i < pointCount; i++){
sumX += bPoly[i] * **points[i]**.x;
sumY += bPoly[i] * **points[i]**.y;
}
Edit: Glad you were able to fix it, and hopefully i helped with finding that issue. Best of luck in the future!
I have a matrix double[][] with arbitrary dimensions but bigger than 300 (maybe in one or maybe on both dimensions). I want to scale it to double[300][300].
My main approach is to interpolate the matrix and bring it up to double[600][600] and then take four elements and find their average, i.e. the elements 0,0, 0,1, 1,0 and 1,1 will be the 0,0 of the final 300x300 matrix.
I have found the interpolation library in JAVA but I cannot figure out how to use it. Can anyone provide some examples or info?
The library is: http://docs.oracle.com/cd/E17802_01/products/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/Interpolation.html
Thnx.
What about writing a simple method that maps source cells to destination, then averages out?
public static boolean matrixReduce(double[][] dst, double[][] src) {
double dstMaxX = dst.length - 1, dstMaxY = dst[0].length - 1;
double srcMaxX = src.length - 1, srcMaxY = src[0].length - 1;
int count[][] = new int[dst.length][dst[0].length];
for (int x = 0; x < src.length; x++) {
for (int y = 0; y < src[0].length; y++) {
int xx = (int) Math.round((double) x * dstMaxX / srcMaxX);
int yy = (int) Math.round((double) y * dstMaxY / srcMaxY);
dst[xx][yy] += src[x][y];
count[xx][yy]++;
}
}
for (int x = 0; x < dst.length; x++) {
for (int y = 0; y < dst[0].length; y++) {
dst[x][y] /= count[x][y];
}
}
return true;
}
I have been trying to figure out how to create infinite terrain in 3D using perlin noise. I have so far got a terrain that is made of chunks, and when the player moves out of a chunk, new chunks are generated. Chunks out of range are unloaded. So I have the illusion of an infinite world.
I am using perlin noise to create height maps for the chunks (each chunk has its own height map)
My question is how do I seamlessly tile the height map of each chunk so that there aren't horrible gaps in the world between chunks, and without having the same height map for every chunk.
By "gaps in the world" I mean this.
And I followed this to implement perlin noise
Here is my perlin noise code:
private float[][] perlinNoise(int width, int height, int octave, float[][] whiteNoise)
{
float[][] result = new float[width][height];
int samplePeriod = 1 << octave;
float sampleFrequency = 1.0f / samplePeriod;
for (int i = 0; i < width; i++)
{
int x1 = (i / samplePeriod) * samplePeriod;
int x2 = (x1 + samplePeriod) % width;
float xBlend = (i - x1) * sampleFrequency;
for (int j = 0; j < height; j++)
{
int y1 = (j / samplePeriod) * samplePeriod;
int y2 = (y1 + samplePeriod) % height;
float yBlend = (j - y1) * sampleFrequency;
float top = (float) MathHelper.interpolateLinear(whiteNoise[x1][y1], whiteNoise[x2][y1], xBlend);
float bottom = (float) MathHelper.interpolateLinear(whiteNoise[x1][y2], whiteNoise[x2][y2], xBlend);
result[i][j] = (float) MathHelper.interpolateLinear(top, bottom, yBlend);
}
}
return result;
}
public float[][] generatePerlinNoise(int width, int height, Random random, int octaveCount)
{
float[][] whiteNoise = new float[width][height];
float[][][] totalNoise = new float[octaveCount][][];
float[][] perlinNoise = new float[width][height];
float amplitude = 1.0f;
float totalAmplitude = 0.0f;
float persistance = 0.5f;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
whiteNoise[i][j] = random.nextFloat() % 1;
}
}
for (int i = 0; i < octaveCount; i++)
{
totalNoise[i] = perlinNoise(width, height, i, whiteNoise);
}
for (int o = octaveCount - 1; o >= 0; o--)
{
amplitude *= persistance;
totalAmplitude += amplitude;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
perlinNoise[i][j] += totalNoise[o][i][j] * amplitude;
}
}
}
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
perlinNoise[i][j] /= totalAmplitude;
}
}
return perlinNoise;
}
Edit: I have just asked about this on GameDev my question there might be more detailed and helpful. Please try and answer there if possible, because I will be looking at this post less and that one more.
Edit: I have realised that my noise code is NOT PERLIN NOISE. It is actually something called VALUE NOISE, which actually doesnt look as good, and wont work for what I need anyway. I cant find any good java implementation of perlin noise. And I dont just want a link to code I can use, I would like to have a tutorial, where I can understand how the algorithm actually works.
when I rotate a model matrix in opengl es with the function I created, the model matrix makes the model smaller while rotating, and I don't understand why.
Here is the code of the rotating function(Rotating around the z axis only).
public void rotateZ(float angle){
float cos = (float) (Math.cos(Math.toRadians(angle)));
float sin = (float) (Math.sin(Math.toRadians(angle)));
Matrix4x4 ret = IdentityM();
ret.setValue(cos, 0, 0);
ret.setValue(-sin, 0, 1);
ret.setValue(sin, 1, 0);
ret.setValue(cos, 1, 1);
Multiply(ret);
}
And here is the code of the multiplication function:
public void Multiply(Matrix4x4 m){
float[][] m1 = m.toFloatMat();
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
float value = 0f;
for(int t = 0; t < 4; t++){
value += matrix[i][t] * m1[t][j];
}
matrix[i][j] = value;
}
}
}
And the setValue function:
public void setValue(float v, int i, int j){
matrix[i][j] = v;
}
The object is only getting smaller and I don't understand why ><
In your Multiply function, you are overwriting the original matrix while calculating the product. Make a temporary matrix to store the result, and then write it back to the class member matrix.
(Sorry for the long post... at least it has pictures?)
I have written an algorithm that creates a mosaic from an image by statistically generating N convex polygons that cover the image with no overlap. These polygons have anywhere between 3-8 sides, and each side has an angle that is a multiple of 45 degrees. These polygons are stored internally as a rectangle with displacements for each corner. Below is an image that explains how this works:
getRight() returns x + width - 1, and getBottom() returns y + height - 1. The class is designed to maintain a tight bounding box around filled pixels so the coordinates shown in this image are correct. Note that width >= ul + ur + 1, width >= ll + lr + 1, height >= ul + ll + 1, and height >= ur + ul + 1, or there would be empty pixels on a side. Note also that it is possible for a corner's displacement to be 0, thus indicating all pixels are filled in that corner. This enables this representation to store 3-8 sided convex polygons, each of whose sides are at least one pixel in length.
While it's nice to mathematically represent these regions, I want to draw them so I can see them. Using a simple lambda and a method that iterates over each pixel in the polygon, I can render the image perfectly. As an example, below is Claude Monet's Woman with a Parasol using 99 polygons allowing all split directions.
The code that renders this image looks like this:
public void drawOnto(Graphics graphics) {
graphics.setColor(getColor());
forEach(
(i, j) -> {
graphics.fillRect(x + i, y + j, 1, 1);
}
);
}
private void forEach(PerPixel algorithm) {
for (int j = 0; j < height; ++j) {
int nj = height - 1 - j;
int minX;
if (j < ul) {
minX = ul - j;
} else if (nj < ll) {
minX = ll - nj;
} else {
minX = 0;
}
int maxX = width;
if (j < ur) {
maxX -= ur - j;
} else if (nj < lr) {
maxX -= lr - nj;
}
for (int i = minX; i < maxX; ++i) {
algorithm.perform(i, j);
}
}
}
However, this is not ideal for many reasons. First, the concept of graphically representing a polygon is now part of the class itself; it is better to allow other classes whose focus is to represent these polygons. Second, this entails many, many calls to fillRect() to draw a single pixel. Finally, I want to be able to develop other methods of rendering these polygons than drawing them as-is (for example, performing weighted interpolation over the Voronoi tessellation represented by the polygons' centers).
All of these point to generating a java.awt.Polygon that represents the vertices of the polygon (which I named Region to differentiate from the Polygon class). No problem; I wrote a method to generate a Polygon that has the corners above with no duplicates to handle the cases that a displacement is 0 or that a side has only one pixel on it:
public Polygon getPolygon() {
int[] xes = {
x + ul,
getRight() - ur,
getRight(),
getRight(),
getRight() - lr,
x + ll,
x,
x
};
int[] yes = {
y,
y,
y + ur,
getBottom() - lr,
getBottom(),
getBottom(),
getBottom() - ll,
y + ul
};
int[] keptXes = new int[8];
int[] keptYes = new int[8];
int length = 0;
for (int i = 0; i < 8; ++i) {
if (
length == 0 ||
keptXes[length - 1] != xes[i] ||
keptYes[length - 1] != yes[i]
) {
keptXes[length] = xes[i];
keptYes[length] = yes[i];
length++;
}
}
return new Polygon(keptXes, keptYes, length);
}
The problem is that, when I try to use such a Polygon with the Graphics.fillPolygon() method, it does not fill all of the pixels! Below is the same mosaic rendered with this different method:
So I have a few related questions about this behavior:
Why does the Polygon class not fill in all these pixels, even though the angles are simple multiples of 45 degrees?
How can I consistently code around this defect (as far as my application is concerned) in my renderers so that I can use my getPolygon() method as-is? I do not want to change the vertices it outputs because I need them to be precise for center-of-mass calculations.
MCE
If the above code snippets and pictures are not enough to help explain the problem, I have added a Minimal, Complete, and Verifiable Example that demonstrates the behavior I described above.
package com.sadakatsu.mce;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Main {
#FunctionalInterface
private static interface PerPixel {
void perform(int x, int y);
}
private static class Region {
private int height;
private int ll;
private int lr;
private int width;
private int ul;
private int ur;
private int x;
private int y;
public Region(
int x,
int y,
int width,
int height,
int ul,
int ur,
int ll,
int lr
) {
if (
width < 0 || width <= ll + lr || width <= ul + ur ||
height < 0 || height <= ul + ll || height <= ur + lr ||
ul < 0 ||
ur < 0 ||
ll < 0 ||
lr < 0
) {
throw new IllegalArgumentException();
}
this.height = height;
this.ll = ll;
this.lr = lr;
this.width = width;
this.ul = ul;
this.ur = ur;
this.x = x;
this.y = y;
}
public Color getColor() {
return Color.BLACK;
}
public int getBottom() {
return y + height - 1;
}
public int getRight() {
return x + width - 1;
}
public Polygon getPolygon() {
int[] xes = {
x + ul,
getRight() - ur,
getRight(),
getRight(),
getRight() - lr,
x + ll,
x,
x
};
int[] yes = {
y,
y,
y + ur,
getBottom() - lr,
getBottom(),
getBottom(),
getBottom() - ll,
y + ul
};
int[] keptXes = new int[8];
int[] keptYes = new int[8];
int length = 0;
for (int i = 0; i < 8; ++i) {
if (
length == 0 ||
keptXes[length - 1] != xes[i] ||
keptYes[length - 1] != yes[i]
) {
keptXes[length] = xes[i];
keptYes[length] = yes[i];
length++;
}
}
return new Polygon(keptXes, keptYes, length);
}
public void drawOnto(Graphics graphics) {
graphics.setColor(getColor());
forEach(
(i, j) -> {
graphics.fillRect(x + i, y + j, 1, 1);
}
);
}
private void forEach(PerPixel algorithm) {
for (int j = 0; j < height; ++j) {
int nj = height - 1 - j;
int minX;
if (j < ul) {
minX = ul - j;
} else if (nj < ll) {
minX = ll - nj;
} else {
minX = 0;
}
int maxX = width;
if (j < ur) {
maxX -= ur - j;
} else if (nj < lr) {
maxX -= lr - nj;
}
for (int i = minX; i < maxX; ++i) {
algorithm.perform(i, j);
}
}
}
}
public static void main(String[] args) throws IOException {
int width = 10;
int height = 8;
Region region = new Region(0, 0, 10, 8, 2, 3, 4, 1);
BufferedImage image = new BufferedImage(
width,
height,
BufferedImage.TYPE_3BYTE_BGR
);
Graphics graphics = image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
region.drawOnto(graphics);
ImageIO.write(image, "PNG", new File("expected.png"));
image = new BufferedImage(
width,
height,
BufferedImage.TYPE_3BYTE_BGR
);
graphics = image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
graphics.setColor(Color.BLACK);
graphics.fillPolygon(region.getPolygon());
ImageIO.write(image, "PNG", new File("got.png"));
}
}
I spent all day working on it, and I seem to have a fix for this. The clue was found in the documentation for the Shape class, which reads:
Definition of insideness: A point is considered to lie inside a Shape if and only if:
it lies completely inside theShape boundary or
it lies exactly on the Shape boundary and the space immediately adjacent to the point in the increasing X direction is entirely inside the boundary or
it lies exactly on a horizontal boundary segment and the space immediately adjacent to the point in the increasing Y direction is inside the boundary.
Actually, this text is a bit misleading; the third case overrides second (i.e., even if a pixel in a horizontal boundary segment on the bottom of a Shape has a filled point to its right, it still will not be filled). Represented pictorially, the Polygon below will not draw the x'ed out pixels:
The red, green, and blue pixels are part of the Polygon; the rest are not. The blue pixels fall under the first case, the green pixels fall under the second case, and the red pixels fall under the third case. Note that all of the rightmost and lowest pixels along the convex hull are NOT drawn. To get them to be drawn, you have to move the vertices to the orange pixels as shown to make a new rightmost/bottom-most portion of the convex hull.
The easiest way to do this is to use camickr's method: use both fillPolygon() and drawPolygon(). At least in the case of my 45-degree-multiple-edged convex hulls, drawPolygon() draws the lines to the vertices exactly (and probably for other cases as well), and thus will fill the pixels that fillPolygon() misses. However, neither fillPolygon() nor drawPolygon() will draw a single-pixel Polygon, so one has to code a special case to handle that.
The actual solution I developed in trying to understand the insideness definition above was to create a different Polygon with the modified corners as shown in the picture. It has the benefit (?) of calling the drawing library only once and automatically handles the special case. It probably is not actually optimal, but here is the code I used for anyone's consideration:
package com.sadakatsu.mosaic.renderer;
import java.awt.Polygon;
import java.util.Arrays;
import com.sadakatsu.mosaic.Region;
public class RegionPolygon extends Polygon {
public RegionPolygon(Region region) {
int bottom = region.getBottom();
int ll = region.getLL();
int lr = region.getLR();
int right = region.getRight();
int ul = region.getUL();
int ur = region.getUR();
int x = region.getX();
int y = region.getY();
int[] xes = {
x + ul,
right - ur + 1,
right + 1,
right + 1,
right - lr,
x + ll + 1,
x,
x
};
int[] yes = {
y,
y,
y + ur,
bottom - lr,
bottom + 1,
bottom + 1,
bottom - ll,
y + ul
};
npoints = 0;
xpoints = new int[xes.length];
ypoints = new int[xes.length];
for (int i = 0; i < xes.length; ++i) {
if (
i == 0 ||
xpoints[npoints - 1] != xes[i] ||
ypoints[npoints - 1] != yes[i]
) {
addPoint(xes[i], yes[i]);
}
}
}
}