I have a simple 2D polygon with 4 points.
int[] x = {38, 100, 80, 18};
int[] y = {50, 50, 100, 100};
Polygon poly = new Polygon(x, y, 4);
The above polygon is just an example. The polygon above could really be anything as long as the polygon is always convex, always has 4 points, and is a parallelogram. I need to split it into any number of even parts, all proportional to the bigger polygon, as long as the number is a square number. Is there any simple way I can do this? I am using Graphics on a Jframe if that's important at all.
The code below works for any convex 4-sided polygon. When the initial polygon is a parallelogram, the resultant sub-polygons are by nature all parallelograms too, all with the same size, i.e. they are even-sized.
Since the desired number of parts must be a square number, it means we can simply split the 4-sided polygon horizontally and vertically into partsPerSide = sqrt(parts).
When we split a 4-sided polygon into multiple parts, we may end up with coordinates that are not exact integers. We can simply round the value to an integer, but then the pieces wouldn't be exactly even in size. Whether that is acceptable is a matter of choice. Visually, the rounding can be noticed, since the lines won't be 100% straight.
In the code below, we assume that rounding is not acceptable, i.e. we want exact even sizes. If rounding is ok, simply comment out the if (rounded != delta) throw new ArithmeticException() code at the end, then call splitFourSided() with the desired number of partsPerSide.
Enough talk, here is the code:
private static Polygon[][] splitFourSided(Polygon poly, int partsPerSide) {
if (poly.npoints != 4)
throw new IllegalArgumentException("Polygon must be 4-sided");
if (partsPerSide <= 0)
throw new IllegalArgumentException("There must be a positive number of parts per side");
int[][] x = splitFourSided(poly.xpoints, partsPerSide);
int[][] y = splitFourSided(poly.ypoints, partsPerSide);
Polygon[][] pieces = new Polygon[partsPerSide][partsPerSide];
for (int row = 0; row < partsPerSide; row++) {
for (int col = 0; col < partsPerSide; col++) {
pieces[row][col] = new Polygon(
new int[] { x[row][col], x[row][col+1], x[row+1][col+1], x[row+1][col] },
new int[] { y[row][col], y[row][col+1], y[row+1][col+1], y[row+1][col] },
4);
}
}
return pieces;
}
private static int[][] splitFourSided(int[] xy, int parts) {
// To visualize, assume values are [topLeft, topRight, bottomRight, bottomLeft].
// The 'xy' array is either the x-coordinates or the y-coordinates.
// First we split left and right sides, e.g. for 3 parts:
// From: ┌ To: ┐
// ├ ┤
// ├ ┤
// └ ┘
// Then we split between those:
// ┌─┬─┬─┐
// ├─┼─┼─┤
// ├─┼─┼─┤
// └─┴─┴─┘
int[] from = splitRange(xy[0], xy[3], parts);
int[] to = splitRange(xy[1], xy[2], parts);
int[][] grid = new int[parts + 1][];
for (int i = 0; i <= parts; i++)
grid[i] = splitRange(from[i], to[i], parts);
return grid;
}
private static int[] splitRange(int from, int to, int parts) {
int[] prorated = new int[parts + 1];
for (int i = 0; i <= parts; i++)
prorated[i] = prorate(from, to, i, parts);
return prorated;
}
private static int prorate(int from, int to, int index, int parts) {
if (index == 0)
return from;
if (index == parts)
return to;
double delta = (to - (double) from) * index / parts;
int rounded = (int) Math.round(delta);
if (rounded != delta)
throw new ArithmeticException("Cannot prorate to integer value");
return from + rounded;
}
Test
int[] x = {38, 100, 80, 18};
int[] y = {50, 50, 100, 100};
Polygon poly = new Polygon(x, y, 4);
splitAndDrawFourSided(g, poly, 2);
private static void splitAndDrawFourSided(Graphics g, Polygon poly, int partsPerSide) {
Polygon[][] pieces = splitFourSided(poly, partsPerSide);
for (int row = 0; row < partsPerSide; row++)
for (int col = 0; col < partsPerSide; col++)
g.drawPolygon(pieces[row][col]);
Graphics gMain = g.create();
try {
gMain.setColor(Color.RED);
gMain.drawPolygon(poly);
} finally {
gMain.dispose();
}
}
Result
To search for a valid number of parts, we can add a search loop, and change the coordinates so they are only divisible by 7.
int[] x = {37, 100, 79, 16};
int[] y = {50, 50, 99, 99};
Polygon poly = new Polygon(x, y, 4);
for (int partsPerSide : new int[] { 2, 3, 5, 7, 11, 13, 17, 19 }) {
try {
splitAndDrawFourSided(g, poly, partsPerSide);
break; // stop when successful
} catch (#SuppressWarnings("unused") ArithmeticException ignored) {
continue; // try next number of parts
}
}
Result
If we remove the rounding check, that code will of course always just split by 2 parts per side, i.e. into 4 parts. This shows the effect of rounding, e.g. in this case the center row coordinates ended up a bit to the right, causing the black and red lines to not match up. Even without the red line depicting the input parallelogram, the rounding can be noticed. Anti-aliasing helps, but it can still be noticed that the vertical lines aren't 100% straight.
Related
I made a 3D-renderer that parses .obj files (ASCII) and projects them on to a 2d plane.
At first glance the projection model seems to be fine except one thing.
I noticed that the projection model looks a bit odd:
[1]: https://i.stack.imgur.com/iaLOu.png
All polygons are being drawn including the ones in the back of the model, which I
should definitely not be able to see.
I made a quick recherche in Wikipedia to see what this is about and I think I found something called "Sichtbarkeitsproblem" (Hidden-surface determination).
(DE): https://de.wikipedia.org/wiki/Sichtbarkeitsproblem
(EN):
https://en.wikipedia.org/wiki/Hidden-surface_determination
The article mentions that this is a common thing in computer graphics and that there are many different ways to perform a "Verdeckungsberechnung" (cover up calculation).
It mentions things like using a z-Buffer and Raytracing.
Now I don't really know a lot about Raytracing but It seems to be quite applicable as I later want to add a light source.
I am not sure how Raytracing works but If I just send out rays in an angle that matches the slope from the camera to every pixel on screen and check which polygon hits it first I would only end up having some polygons completely missing only due to one vertex being potentially covered.
How do other Raytracers work? Do they remove the entire polygon when not getting a hit? Remove only one or more vertecies? (which I belief would cause massive distortion in shape) or do they just render all the Polygons and arrange them in a way that they are sorted by the minimum distance to the camera? (I guess this would made it very bad at performance)
Please help me implement this into my code or give me a hint, it would mean a lot to me.
My code is as followed, and the link for the projection model (see Image no. 1) I put here:
https://drive.google.com/file/d/10dpjcL2d2QB15qqTSu5p6kQ534hNOzCz/view?usp=sharing
(Note that the 3d-model and code must be in same folder in order to work)
// 12.11.2022
// Siehe Rotation Matrix in Wikipedia
// View Space: The world space vertex positions relative to the view of the camera
/* Die Verdeckungsberechnung ist zum korrekten Rendern einer 3D-Szene notwendig, weil Oberflächen,
die für den Betrachter nicht sichtbar sind, auch nicht dargestellt werden sollten
*/
// -> https://de.wikipedia.org/wiki/Sichtbarkeitsproblem
// TODO: Raytracing/Verdeckungsberechnung
// TODO: Texture Mapping
import java.util.Arrays;
import java.awt.Robot;
import java.nio.ByteBuffer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.ArrayList;
byte b[];
int amount = 0;
String lines[];
PVector[][] vertices;
int[] faces;
float a = 0;
PVector cam, cam_angle, cam_move, cam_speed;
float angle = 0.0;
void setup() {
size(800,600);
frameRate(60);
noCursor();
cam = new PVector(0, 100, -500);
cam_angle = new PVector(0, 0, 0);
cam_move = new PVector(0, 0, 0);
cam_speed = new PVector(50, 50, 50);
lines = loadStrings("UM2_SkullPile28mm.obj");
println("File loaded. Now scanning contents...");
println();
Pattern numbers = Pattern.compile("(-?\\d+)");
ArrayList<PVector> vertices_ = new ArrayList<PVector>();
ArrayList<ArrayList> faces_ = new ArrayList<ArrayList>();
int parsed_lines = 0;
for(String i:lines) {
switch(i.charAt(0)) {
// Find faces
case 'f':
ArrayList<Integer> values = new ArrayList<Integer>();
for(Matcher m = numbers.matcher(i); m.find(); values.add(Integer.parseInt(m.group())));
faces_.add(values);
break;
// Find Vectors
case 'v':
String s[] = i.trim().split("\\s+");
vertices_.add(new PVector(Float.parseFloat(s[1])*20, Float.parseFloat(s[2])*20, Float.parseFloat(s[3])*20));
break;
};
if(++parsed_lines % (lines.length/6) == 0 || parsed_lines == lines.length) println((int)(map(parsed_lines, 0, lines.length, 0, 100)), "%");
}
println();
println("Done. Found", vertices_.size(), "Vertices and", faces_.size(), "faces");
int i=0;
vertices = new PVector[faces_.size()][];
for(ArrayList<Integer> f_:faces_) {
vertices[i] = new PVector[f_.size()];
int j = 0;
for(int f: f_) {
PVector v = vertices_.get(f-1);
vertices[i][j] = Rotate3d_x(v, -90);
j++;
}
i++;
}
}
PVector Rotate2d(PVector p, float a) {
// a = angle
float[][] m2 = {
{cos(a), -sin(a)},
{sin(a), cos(a)}
};
float[][] rotated = matmul(m2, new float[][] {
{ p.x },
{ p.y }
});
return new PVector(rotated[0][0], rotated[1][0]);
}
PVector Rotate3d(PVector p, float[][] m2) {
float[][] rotated = matmul(m2, new float[][] {
{ p.x },
{ p.y },
{ p.z }
});
return new PVector(rotated[0][0], rotated[1][0], rotated[2][0]);
}
PVector Rotate3d_x(PVector p, float a) {
return Rotate3d(p,
new float[][] {
{1, 0, 0},
{0, cos(a), -sin(a)},
{0, sin(a), cos(a)}
});
};
PVector Rotate3d_y(PVector p, float a) {
return Rotate3d(p,
new float[][] {
{cos(a), 0, sin(a)},
{0, 1, 0},
{-sin(a), 0, cos(a)}
});
}
PVector Rotate3d_z(PVector p, float a) {
return Rotate3d(p,
new float[][] {
{cos(a), -sin(a), 0},
{sin(a), cos(a), 0},
{0, 0, 1}
});
}
PVector Rotate3d(PVector p, PVector a) {
return Rotate3d_z( Rotate3d_y(Rotate3d_x(p, a.x), a.y), a.z );
}
// Matrixmultiplikation
float[][] matmul(float[][] m1, float[][] m2) {
int cols_m1 = m1.length,
rows_m1 = m1[0].length;
int cols_m2 = m2.length,
rows_m2 = m2[0].length;
try {
if (rows_m1 != cols_m2) throw new Exception("Rows of m1 must match Columns of m2!");
}
catch(Exception e) {
println(e);
}
float[][] res = new float[cols_m2][rows_m2];
for (int c=0; c < cols_m1; c++) {
for (int r2=0; r2 < rows_m2; r2++) {
float sum = 0;
float[] buf = new float[rows_m1];
// Multiply rows of m1 with columns of m2 and store in buf
for (int r=0; r < rows_m1; r++) {
buf[r] = m1[c][r]* m2[r][r2];
}
// Add up all entries into sum
for (float entry : buf) {
sum += entry;
}
res[c][r2] = sum;
}
}
return res;
}
PVector applyPerspective(PVector p) {
PVector d = applyViewTransform(p);
return applyPerspectiveTransform(d);
}
PVector applyViewTransform(PVector p) {
// c = camera position
// co = camera orientation / camera rotation
PVector c = cam;
PVector co = cam_angle;
// dx, dy, dz https://en.wikipedia.org/wiki/3D_projection : Mathematical Formula
float[][] dxyz = matmul(
matmul(new float[][]{
{1, 0, 0},
{0, cos(co.x), sin(co.x)},
{0, -sin(co.x), cos(co.x)}
}, new float[][]{
{cos(co.y), 0, -sin(co.y)},
{0, 1, 0},
{sin(co.y), 0, cos(co.y)}
}),
matmul(new float[][]{
{cos(co.z), sin(co.z), 0},
{-sin(co.z), cos(co.z), 0},
{0, 0, 1}
}, new float[][]{
{p.x - c.x},
{p.y - c.y},
{p.z - c.z},
}));
PVector d = new PVector(dxyz[0][0], dxyz[1][0], dxyz[2][0]);
return d;
}
PVector applyPerspectiveTransform(PVector d) {
// e = displays surface pos relative to camera pinhole c
PVector e = new PVector(0, 0, 300);
return new PVector((e.z / d.z) * d.x + e.x, (e.z / d.z) * d.y + e.y);
}
void draw() {
background(255);
translate(width/2, height/2);
scale(1,-1);
noStroke();
fill(0, 100, 0, 50);
PVector[][] points_view = new PVector[vertices.length][];
for(int i=0; i < vertices.length; i++) {
points_view[i] = new PVector[vertices[i].length];
for(int j=0; j < vertices[i].length; j++)
points_view[i][j] = applyViewTransform(Rotate3d_y(vertices[i][j], angle));
}
// The following snippet I got from: https://stackoverflow.com/questions/74443149/3d-projection-axis-inversion-problem-java-processing?noredirect=1#comment131433616_74443149
float nearPlane = 1.0;
for (int c = 0; c < points_view.length; c++) {
beginShape();
for (int r = 0; r < points_view[c].length-1; r++) {
// Alle Punkte verbinden
//if (i == a) continue;
PVector p0 = points_view[c][r];
PVector p1 = points_view[c][r+1];
if(p0.z < nearPlane && p1.z < nearPlane){ continue; };
if(p0.z >= nearPlane && p1.z < nearPlane)
p1 = PVector.lerp(p0, p1, (p0.z - nearPlane) / (p0.z - p1.z));
if(p0.z < nearPlane && p1.z >= nearPlane)
p0 = PVector.lerp(p1, p0, (p1.z - nearPlane) / (p1.z - p0.z));
// project
p0 = applyPerspectiveTransform(p0);
p1 = applyPerspectiveTransform(p1);
vertex(p0.x, p0.y);
vertex(p1.x, p1.y);
}
endShape();
}
}
Ray tracing doesn't determine whether or not a polygon is visible. It determines what point (if any) on what polygon is visible in a given direction.
As a simplification: rasterisation works by taking a set of geometry and for each one determining what pixels it affects. Ray tracing works by taking a set of pixels and, for each one determining what geometry is visible along that direction.
With rasterisation, there are many ways of making sure that polygons don't draw in the wrong order. One approach is to sort them by distance to the camera, but that doesn't work with polygons that overlap. The usual approach is to use a z-buffer: when a polygon is rasterised, calculate the distance to the camera in each pixel, and only update the buffer if the new value is nearer to the camera than the old value.
With ray tracing, each ray returns the nearest hit location along a direction, along with what it hit. Since each pixel will only be visited once, you don't need to worry about triangles drawing on top of each other.
If you just want to project a piece of 3D geometry onto a plane, rasterisation will likely be much, much faster. At a very high level, do this:
create an RGBA buffer of size X*Y
create a z buffer of size X*Y and fill it with 'inf'
for each triangle:
project the triangle onto the projection plane
for each pixel the triangle might affect:
calculate distance from camera to the corresponding position on the triangle
if the distance is lower than the current value in the z buffer:
replace the value in the RGBA and z buffers with the new values
I'm trying to take this code I finally got working for drawing one star and am confused on how to get it working for drawing 25 different stars (such as different sides and spikinness but I am unsure how to go about it exactly. I assume I would make a new random variable int randomStars = (int)(Math.random()*25+1); // Variable for 25 Random Stars to randomly generate the stars but I'm kind of confused where to take it from there.
I'd appreciate the help.
My Code (Using DrawingPanel.java):
import java.awt.*;
public class StarSampler {
public static void main(String[] args)
{
DrawingPanel panel = new DrawingPanel(500, 500);
Graphics2D g = panel.getGraphics();
g.setColor(Color.BLUE);
panel.setBackground(new Color(250, 0, 0));
fillStar(g, 250, 250, 150, 5, .3); // How to rotate it to start at center?
}
public static void fillStar(Graphics2D g, int ctrX, int ctrY, int radius, int nPoints, double spikiness)
{
double xDouble[] = new double[2*nPoints];
double yDouble[] = new double[2*nPoints];
int xPoint[] = new int[2*nPoints];
int yPoint[] = new int[2*nPoints];
nPoints = (int) (nPoints * 2);
int randomStars = (int)(Math.random()*25+1); // Variable for 25 Random Stars
// Would Nestest loop go here? for (randomStars++; randomStars < 25; randomStars++)
for (int i = 0; i < nPoints; i++)
{
double iRadius = (i % 2 == 0) ? radius : (radius * spikiness);
double angle = (270) + (i * 360.0) / (nPoints);
xPoint[i] = (int) (ctrX + iRadius * Math.cos(Math.toRadians(angle)));
yPoint[i] = (int) (ctrY + iRadius * Math.sin(Math.toRadians(angle)));
}
g.fillPolygon(xPoint, yPoint, nPoints); // Creates polygon
}
}
My Output:
Build up slowly. First concentrate on positioning two stars, just two. Pick the coordinates for the centre of the stars and place them. Adjust to get it right (your first attempt will probably have errors). Test, fix, test again, fix again. Repeat.
Then play with the spikiness and other changes so the stars aren't identical. When, and only when, that is tested and working move on to three, four etc. stars. With more stars you will have to be more careful to avoid overlaps.
Update: this question is seeking guidance on how to get a set of neighbors for any given coordinate.
I created a 2d array that contains coordinates,
int[][] coordinates= { { -1, -1 }, { -1, 0 }, { -1, +1 },
{ 0, -1 }, { 0, +1 }, { +1, -1 }, { +1, 0 }, { +1, -1 } };
As you can tell, these are the neighbors for coordinates (0,0).
Now I am trying to implement a method that takes two parameters (int positionX, int positionY), and use the input parameters value coordiante(x,y) as the starting coordinate and find all the neighbors for this coordinate.
I am thinking about something like this:
int getNearCoordinates(int positionX, int positionY) {
for (int[] coordinate: coordinates) {
//I am not sure what to do after this
}
}
I am trying to use a loop to get the individual coordinate from the 2d array I created and I am stuck at here. How do I find a way to appropriately find positionX's and positionY's neighbor?
What are neighbours?
All orange points in diagram below are neighbours of Origin (0,0)
I'd recommend to
Use a dedicated class (Coordinate) instead of int[]. This makes your code easier to extend (3rd dimention, etc) or to change (using double instead of int, etc.). In the example you can see an imutable class - this hinders code to have side effects.
Use Collection instead of Array. This makes handling much easier (you can simply add and remove items)
Use java8-Streaming-API. It is lightning fast and makes your code better readable.
Additional ideas:
You could even make getNearCoordinates part of the Coordinate class. This would make new Coordinate(27,35).getNearCoordinates() available.
Instead of storing x and y in separate fields you could also use a Map<Axis, Integer>. This would make your code a little bit harder to understand - but would reduce duplicated code.
You could also generate the collection of directions by using two nested loops for (int x = -1; x <= 1; x++) for (int y = -1; y <= 1; y++) use(new Coordinate(x,y)). This would make your code cleaner, but might be harder to understand.
Example code:
import java.util.*;
import java.util.stream.Collectors;
public class Snippet {
// make a class to be more flexible
class Coordinate {
// final fields are making this an "imutable"
final int x;
final int y;
/** constructor to take coordinate values */
Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
/** moves this coordinate by another coordinate */
Coordinate move(Coordinate vector) {
return new Coordinate(x + vector.x, y + vector.y);
}
}
/** using Collection instead of Array makes your live easier. Consider renaming this to "directions". */
Collection<Coordinate> coordinates = Arrays.asList(
new Coordinate( -1, -1 ), // left top
new Coordinate( -1, 0 ), // left middle
new Coordinate( -1, +1 ), // left bottom
new Coordinate( 0, -1 ), // top
new Coordinate( 0, +1 ), // bottom
new Coordinate( +1, -1 ), // right top
new Coordinate( +1, 0 ), // right middle
new Coordinate( +1, +1 ) // right bottom
);
/** #return a collection of eight nearest coordinates near origin */
Collection<Coordinate> getNearCoordinates(Coordinate origin) {
return
// turn collection into stream
coordinates.stream()
// move the origin into every direction
.map(origin::move)
// turn stream to collection
.collect(Collectors.toList());
}
}
Same behaviour without Java8-streaming API would look like this:
/** #return a collection of eight nearest coordinates near origin */
Collection<Coordinate> getNearCoordinates(Coordinate origin) {
Collection<Coordinate> neighbours = new ArrayList<>();
for (Coordinate direction : coordinates)
neighbours.add(origin.move(direction));
return neighbours;
}
Two points A(x1,y1), B(x2,y2) are neighbours if this expression is true:
Math.abs(x1-x2) <= 1 && Math.abs(y1-y2) <= 1
Here if both differences are equal to zero then A equals B.
This is not the best way to implement it (using int[] for points), the purpose of this answer is to show the algorithms.
If you are talking about an unbounded plane then you will always have 8 points, so you could implement it the following way:
// first point index, 2nd: 0 = x, 1 = y
public int[][] getNeighbours(int x, int y) {
int[][] ret = new int[8][2];
int count = 0;
for (int i = -1; i <= 1; i++)
for (int j = -1; j <= 1; j++) {
if (i == 0 && j == 0)
continue;
ret[count][0] = x + i;
ret[count++][1] = y + j;
}
return ret;
}
It gets more interesting if the plane is bounded, using an ArrayList this time:
public List<int[]> getNeighbours(int x, int y, int minX, int maxX, int minY, int maxY) {
List<int[]> ret = new ArrayList<int[]>(8); // default initial capacity is 100
for (int i = Math.max(x - 1, minX); i <= Math.min(x + 1, maxX); i++)
for (int j = Math.max(y - 1, minY); j <= Math.min(y + 1, maxY); j++) {
if (i == x && j == y)
continue;
ret.add(new int[] {i, j});
}
return ret;
}
The latter will work for any point, also outside of the plane or just at the border.
That depends on how you define a neighbour. The code below will test the coordinates and return true for the diagonal as well as horizontal and vertical neighbours.
if (Math.abs(coordinate[0] - positionX) <= 1 && Math.abs(coordinate[1] - positionY) <= 1)
{
System.out.println(Arrays.toString(coordinate));
}
make sure to import java.lang.Math
Printing of the coordinates is just an example of course, but may be useful for debugging.
It may seem obvious, but you could duplicate coordinates, and add the given coordinate's x and y values to those of each coordinate, fit example using a for loop.
I am currently making a program to procedurally generate 2d terrain maps, with different technics such as perlin noise, simplex, voronoi, fractal noise, etc. on a size-defined image to be able to use it in my games requiring a 2d terrain.
I've come across the "Modelling fake planets" section of http://paulbourke.net/fractals/noise and I need to make it on a 2d texture, and not on a 3d world like it is explained.
Now I'm trying to
create a line from point 'X' to point 'Y'
That line will define a zone with a boolean value for left or right of the line to be "darker".
Doing that for a number of iteration to create a texture.
Using the RGB value of the final image to change stuffs such as forests, lakes, etc.
this would work this way:
overrides with this method below,
http://img35.imageshack.us/img35/24/islf.png
I used my high school maths powers to create a code sample but it's not really working...
Questions:
How should i change it so it works instead of just being failing?
Is there a simpler way than using what i am using?
Java file:
if i need an example on how i will proceed, here it is:
package Generator;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;
import VectorialStuffs.Vector2;
public class Linear
{
public static BufferedImage generateImage(Dimension dim, int iterations)
{
BufferedImage image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB);
//point X and point Y
Vector2 pointX;
Vector2 pointY;
//difference between those
Vector2 diff;
Vector2 side;
double slope;
//random
Random rand = new Random();
boolean direction; //the orientation of the dark zone. (left/right)
for (int i = 0; i < iterations; ++i)
{
pointX = new Vector2(0, 0);
pointY = new Vector2(0, 0);
direction = rand.nextBoolean();
System.out.println(direction);
side = new Vector2(0, 0); //there are 4 sides of the image.
while (side.x == side.y)
{
side.x = rand.nextInt(3); //0 - 1 - 2 - 3
side.y = rand.nextInt(3);
}
switch(side.x) //not the x coord, the X point! ;D
{
//x = random and y = 0
case 0:
pointX.x = rand.nextInt(dim.width);
pointX.y = 0;
break;
//x = max and y = random
case 2:
pointX.x = dim.width;
pointX.y = rand.nextInt(dim.height);
break;
//x = random and y = max
case 1:
pointX.x = rand.nextInt(dim.width);
pointX.y = dim.height;
break;
//x = 0 and y = random
case 3:
pointX.x = 0;
pointX.y = rand.nextInt(dim.height);
break;
}
switch(side.y) //not the y coord, the Y point! ;D
{
//x = random and y = 0
case 0:
pointY.x = rand.nextInt(dim.width);
pointY.y = 0;
break;
//x = max and y = random
case 2:
pointY.x = dim.width;
pointY.y = rand.nextInt(dim.height);
break;
//x = random and y = max
case 1:
pointY.x = rand.nextInt(dim.width);
pointY.y = dim.height;
break;
//x = 0 and y = random
case 3:
pointY.x = 0;
pointY.y = rand.nextInt(dim.height);
break;
}
diff = new Vector2((pointY.x - pointX.x), (pointY.y - pointX.y));
slope = diff.y / diff.x;
Graphics graph = image.getGraphics();
if (direction) //true = right | false = left
{
int start; //the start x coordinate, on the line then increases until reaching the end of the image
int end = dim.width;
graph.setColor(Color.red);
graph.fillRect(pointX.x - 8, pointX.y -8, 16, 16);
graph.setColor(Color.yellow);
graph.fillRect(pointY.x - 8, pointY.y -8, 16, 16);
for (int times = 0; times < dim.height; ++times) //horizontal drawer
{
System.out.println(times);
start = (int)((times-diff.y)/slope + diff.y); //this is where it goes wrong?
for (int value = start; value < end; ++value)
{
graph.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255), 100));
graph.fillRect(value, times, 1, 1);
}
}
graph.dispose();
}
else
{
int start; //the start x coordinate, on the line then increases until reaching the end of the image
int end = dim.width;
graph.setColor(Color.red);
graph.fillRect(pointX.x - 8, pointX.y -8, 16, 16);
graph.setColor(Color.yellow);
graph.fillRect(pointY.x - 8, pointY.y -8, 16, 16);
for (int times = 0; times < dim.height; ++times) //horizontal drawer
{
System.out.println(times);
start = (int)((times-diff.y)/slope);
for (int value = end; value < start; --value)
{
graph.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255), 100));
graph.fillRect(value, times, 1, 1);
}
}
graph.dispose();
}
}
return image;
}
}
Note:
In this case vector2 is just a class with X and Y, which can be accessed (this is probably going to be temporary).
Startup part to avoid you losing time:
terrainImage = Linear.generateImage(size, 1); //size being a Dimension. -> "new Dimension(256, 256)"
if (terrainImage != null)
{
Icon wIcon = new ImageIcon(terrainImage);
JOptionPane.showMessageDialog(null, "message", "title", JOptionPane.OK_OPTION, wIcon);
}
//edit
here is the code that needs improvement:
if (direction) //true = right | false = left
{
int start; //the start x coordinate, on the line then increases until reaching the end of the image
int end = dim.width;
graph.setColor(Color.red);
graph.fillRect(pointX.x - 8, pointX.y -8, 16, 16);
graph.setColor(Color.yellow);
graph.fillRect(pointY.x - 8, pointY.y -8, 16, 16);
for (int times = 0; times < dim.height; ++times) //horizontal drawer
{
System.out.println(times);
start = (int)((times-diff.y)/slope + diff.y); //this is where it goes wrong?
for (int value = start; value < end; ++value)
{
graph.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255), 100));
graph.fillRect(value, times, 1, 1);
}
}
graph.dispose();
}
else
{
int start; //the start x coordinate, on the line then increases until reaching the end of the image
int end = dim.width;
graph.setColor(Color.red);
graph.fillRect(pointX.x - 8, pointX.y -8, 16, 16);
graph.setColor(Color.yellow);
graph.fillRect(pointY.x - 8, pointY.y -8, 16, 16);
for (int times = 0; times < dim.height; ++times) //horizontal drawer
{
System.out.println(times);
start = (int)((times-diff.y)/slope);
for (int value = end; value < start; --value)
{
graph.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255), 100));
graph.fillRect(value, times, 1, 1);
}
}
graph.dispose();
}
i can't get it to work like i showed in the picture above, all it does is either nothing, or offset from the 2 points.
Also, sometimes it freezes for no reason, so idk what will happen if i make more iterations of this :/
The pattern generation element of your code should only take about 3 lines, including rotation, colour pattern modulation and all as a function of iterations of i.
I will try and be clear:
you don't need a bar/line to generate your maps, you need any pattern on one/2 axes that starts off half of the period of the map and that gets a smaller and smaller proportion of the map or a smaller and smaller period.
pattern:
A line is round(x); or round (x+y) or round(sin(x+y +translatebar)+barwidth)<--a real bar in middle not just on side //
you can do curvy and zigzag lines later and 2D lines using additions and multiplications of X and Y functions. That function is essentially just a single line where you can change it X value so that rotates.
Rotation:
instead of a functional X every time which make a vertical line, you need to use sinus and co sinus function to generate X and Y values.
4 example 30; rotation is : round( X * 0.866+ Y* 0.5)
Get the sine and cosine of a random values and it will give you random rotations of your pattern the handy thing is that you just make a random value of your loop iteration and send it to a sign cosine.
OK i ll write this in pseudocode it will be simpler:
var pattern = 0; // black canvas
for(var i=1; i=100; i++)
{
pattern += round((sin (X*sin(pseudorand(i)) + Y*cos(pseudorand(i)) + translation) + roundshift )*strength;
}
The above loop will generate thousands of map patterns by adding bars of different rotations.
Round = quantizes your sin(XY) function so it is just black and white / red grey.
Sin(XY) = a variable function to use as a pattern, quantized by round to 0/1 values... multiply and clamp that value in the same line so it doesnt exceed 1 or 0
roundshift = value inside round(sin) pattern that shifts the sin down or up inside the round value resulting in smaller or larger amouts of black/white ration of each iteration. its a multiple of i so it's a function of i, gets smaller every loop.
xsin(rnd i) ycos(rnd i) = rotates your pattern both rnd's are same number necessarily.
translate value = when you +/- a number to a Sin(x+translate). it moves bar backwards/forwards
in the end your pattern value will equals maxiumum 100, so devide by 100 so it's 0-1 or mult by 2.56 for 256, and use a color randomiser to make RGB random multiples of your pattern value.
The above loop obviously needs to run once for every pixel x y.
i dont know how to do the canvas array/texture addin pixels in JS, it should be easy.
The above code will give you great patterns and visual feedback of your errors so you should be able to refine it very nicely, only think i missed is clamp to 0-1 values of sin (-1 1)+ roundshift result.
so a bar is round(sin(xy)+translate), and you can use many many functions of xy added muptiplied sins to add together everything else instead bars, graph circles, squares, wiggles, ovals, rectangles etc.
there is a website all about patterns of this type, except for ordered angles and say 5-6 iterations, using dots bars triangles etc, he is Canadian and on deviant art as well, if there weren't so many TD pattern generated I could find his website!
Here is a website explaining the process of "pattern piling" it's overlaying many shapes in smaller and smaller iterations.
only difference is he uses ordered rotations to create symmetry, and you want random rotations to create chaos maps.
see all the pics of piled patterns in 2d, he has many examples on deviant art and his site, i learnt alot from this guy:
http://algorithmic-worlds.net/info/info.php?page=pilpat
here is more work of superimposed smaller and smaller patterns in symmetry rotations:
https://www.google.com/search?q=Samuel+Monnier&espv=210&es_sm=93&source=lnms&tbm=isch&sa=X&ei=It0AU9uTCOn20gXXv4G4Cw&ved=0CAkQ_AUoAQ&biw=1365&bih=911
same as this using random sin cos rotations.
In order to represent a List of Objects with different colors in a GWT-Widget, we need to get dynamically a List of colors with as much different colors as objects. Since the size of the List can vary, we need to be able to compute such a List of colors.
Another version of my solution with ranges:
List<int> getUniqueColors(int amount) {
final int lowerLimit = 0x10;
final int upperLimit = 0xE0;
final int colorStep = (upperLimit-lowerLimit)/Math.pow(amount,1f/3);
final List<int> colors = new ArrayList<int>(amount);
for (int R = lowerLimit;R < upperLimit; R+=colorStep)
for (int G = lowerLimit;G < upperLimit; G+=colorStep)
for (int B = lowerLimit;B < upperLimit; B+=colorStep) {
if (colors.size() >= amount) { //The calculated step is not very precise, so this safeguard is appropriate
return colors;
} else {
int color = (R<<16)+(G<<8)+(B);
colors.add(color);
}
}
return colors;
}
This one is more advance as it generates the colors that differ from each other as much as possible (something like #aiiobe did).
Generally we split the range to 3 subranges of red green and blue, calculate how many steps do we need to iterate each of them (by applying a pow(range,1f/3)) and iterate them.
Given the number 3 for example, it will generate 0x0000B1, 0x00B100, 0x00B1B1. For number 10 it will be: 0x000076, 0x0000EC, 0x007600, 0x007676, 0x0076EC, 0x00EC00, 0x00EC76, 0x00ECEC, 0x760000, 0x760076
Something like this would do I guess. No randomness, just calculates which color steps to take and splits all color range to that steps. If you limit the lower limit - you will remove too dark colors, and limiting the upper limit will remove too bright colors.
List<Integer> getUniqueColors(int amount) {
final int lowerLimit = 0x101010;
final int upperLimit = 0xE0E0E0;
final int colorStep = (upperLimit-lowerLimit)/amount;
final List<Integer> colors = new ArrayList<Integer>(amount);
for (int i=0;i<amount;i++) {
int color = lowerLimit+colorStep*i;
colors.add(color);
}
return colors;
}
If I understand your situation correct, you're after a number of colors that look sort of "as different as possible"? I would in that case suggest that you vary the hue value (two red colors with slightly different brightness won't look much different), so you get something like a "rainbow-palette":
This can be achieved with the following code:
Color[] cols = new Color[n];
for (int i = 0; i < n; i++)
cols[i] = Color.getHSBColor((float) i / n, 1, 1);
An example usage with a screen shots below:
import java.awt.*;
public class TestComponent extends JPanel {
int numCols = 6;
public void paint(Graphics g) {
float h = 0, dh = (float) getHeight() / numCols;
Color[] cols = getDifferentColors(numCols);
for (int i = 0; i < numCols; i++) {
g.setColor(cols[i]);
g.fillRect(0, (int) h, getWidth(), (int) (h += dh));
}
}
public static Color[] getDifferentColors(int n) {
Color[] cols = new Color[n];
for (int i = 0; i < n; i++)
cols[i] = Color.getHSBColor((float) i / n, 1, 1);
return cols;
}
public static void main(String s[]) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new TestComponent());
f.setSize(200, 200);
f.setVisible(true);
}
}
numCols = 6 and numCols = 40 yields the following two screenshots:
If you need like more than 30 colors, you could of course change the brightness and perhaps the saturation as well, and have, for instance, 10 dark colors, 10 midtone colors, and 10 bright colors.