I'm learning how to draw basic shapes using points in my graphics course, and I can't figure out how to draw a triangle. I thought it would be similar to drawing a rectangle, but a lot of variables (such as slope and different kinds of triangles) need to be taken into account.
Below is my working function of drawing a rectangle
drawRectangle(point 1, point 2){
xStart = min(point 1.x, point 2.x)
yStar = min(point 1.y, point 2.y)
xEnd = max(point 1.x, point 2.x)
yEnd = max(point 1.y, point 2.y)
for(int i = yStart, i<= yEnd, i++){
for(int j = xStart, j<= yEnd, j++){
drawPoint(i, j);
}
}
}
drawRectangle is pretty straight forward, since I just have to loop over the starting point to the ending points of the vertices. However, a triangle has three points, what should I do? I thought about maybe dividing a triangle into two halves, so each half would have a flat "base", but I am not sure if that's a viable approach or not.
Edit: Maybe I was unclear, when I say draw a triangle, I meant a color-filled triangle
You should use the Graphics interface for this. You just need to connect your three points with lines, like this:
void drawTriangle(Point one, Point two, Point three, Graphics g){
g.drawLine(one.x, one.y, two.x, two.y);
g.drawLine(one.x, one.y, three.x, three.y);
g.drawLine(two.x, two.y, three.x, three.y);
}
This will draw a triangle, given three points and an instance of the Graphics object. This is a lot easier than using for loops.
EDIT:
Here is how to do this "from scratch", pixel by pixel using only the methods in your class (I am assuming that drawPoint draws 1 pixel), using the same "connect the dots" idea but with for loops:
drawTriangle(point 1, point 2, point 3) {
for(int x = 1.x, x <= 2.x, x++){
for(int y = 1.y, y <= 2.y, y++){
drawPoint(x, y);
}
}
for(int x = 1.x, x <= 3.x, x++){
for(int y = 1.y, y <= 3.y, y++){
drawPoint(x, y);
}
}
for(int x = 2.x, x <= 3.x, x++){
for(int y = 2.y, y <= 3.y, y++){
drawPoint(x, y);
}
}
}
This connects all 3 points to each other.
Related
(please don't mark this question as not clear, I spent a lot of time posting it ;) )
Okay, I am trying to make a simple 2d java game engine as a learning project, and part of it is rendering a filled polygon as a feature.
I am creating this algorithm my self, and I really can't figure out what I am doing wrong.
My though process is something like so:
Loop through every line, get the number of points in that line, then get the X location of every point in that line,
Then loop through the line again this time checking if the x in the loop is inside one of the lines in the points array, if so, draw it.
Disclaimer: the Polygon class is another type of mesh, and its draw method returns an int array with lines drawn through each vertex.
Disclaimer 2: I've tried other people's solutions but none really helped me and none really explained it properly (which is not the point in a learning project).
The draw methods are called one per frame.
FilledPolygon:
#Override
public int[] draw() {
int[] pixels = new Polygon(verts).draw();
int[] filled = new int[width * height];
for (int y = 0; y < height; y++) {
int count = 0;
for (int x = 0; x < width; x++) {
if (pixels[x + y * width] == 0xffffffff) {
count++;
}
}
int[] points = new int[count];
int current = 0;
for (int x = 0; x < width; x++) {
if (pixels[x + y * width] == 0xffffffff) {
points[current] = x;
current++;
}
}
if (count >= 2) {
int num = count;
if (count % 2 != 0)
num--;
for (int i = 0; i < num; i += 2) {
for (int x = points[i]; x < points[i+1]; x++) {
filled[x + y * width] = 0xffffffff;
}
}
}
}
return filled;
}
The Polygon class simply uses Bresenham's line algorithm and has nothing to do with the problem.
The game class:
#Override
public void load() {
obj = new EngineObject();
obj.addComponent(new MeshRenderer(new FilledPolygon(new int[][] {
{0,0},
{60, 0},
{0, 60},
{80, 50}
})));
((MeshRenderer)(obj.getComponent(MeshRenderer.class))).color = CYAN;
obj.transform.position.Y = 100;
}
The expected result is to get this shape filled up.(it was created using the polygon mesh):
The actual result of using the FilledPolygon mesh:
You code seems to have several problems and I will not focus on that.
Your approach based on drawing the outline then filling the "inside" runs cannot work in the general case because the outlines join at the vertices and intersections, and the alternation outside-edge-inside-edge-outside is broken, in an unrecoverable way (you can't know which segment to fill by just looking at a row).
You'd better use a standard polygon filling algorithm. You will find many descriptions on the Web.
For a simple but somewhat inefficient solution, work as follows:
process all lines between the minimum and maximum ordinates; let Y be the current ordinate;
loop on the edges;
assign every vertex a positive or negative sign if y ≥ Y or y < Y (mind the asymmetry !);
whenever the endpoints of an edge have a different sign, compute the intersection between the edge and the line;
you will get an even number of intersections; sort them horizontally;
draw between every other point.
You can get a more efficient solution by keeping a trace of which edges cross the current line, in a so-called "active list". Check the algorithms known as "scanline fill".
Note that you imply that pixels[] has the same width*height size as filled[]. Based on the mangled output, I would say that they are just not the same.
Otherwise if you just want to fill a scanline (assuming everything is convex), that code is overcomplicated, simply look for the endpoints and loop between them:
public int[] draw() {
int[] pixels = new Polygon(verts).draw();
int[] filled = new int[width * height];
for (int y = 0; y < height; y++) {
int left = -1;
for (int x = 0; x < width; x++) {
if (pixels[x + y * width] == 0xffffffff) {
left = x;
break;
}
}
if (left >= 0) {
int right = left;
for (int x = width - 1; x > left; x--) {
if (pixels[x + y * width] == 0xffffffff) {
right = x;
break;
}
}
for (int x = left; x <= right; x++) {
filled[x + y * width] = 0xffffffff;
}
}
}
return filled;
}
However this kind of approach relies on having the entire polygon in the view, which may not always be the case in real life.
I am trying to render pixels from an array.
I have an array of data that looks like this (except much larger). From this array I would like to somehow render it so that each number in the array corresponds to a pixel with a shade of gray based on the number value. (0.0 would be white, and 1.0 would be black)
I don't know where to start.
For the array you have given; If you know the width and height of the image you want rendered you can do this:
int indx = 0;
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
glColor3d(data[indx],data[indx],data[indx]);
//drawing of it goes here; assuming glVertex2d(x,y);
indx++;
}
}
For this to work it should be known that width*height < data.length. Increment index for each pixel drawn to go to the next number in the array and draw it accordingly.
Modify the x and y so it draws where you want. Say if locX = locY = 10 then depending on the viewport you should have already set up, then the image will start rendering 10px away from (probably) either the top left or bottom left corner. This part is simple maths if you have already started to learn how to draw in OpenGl and/or LWJGL.
int locX, locY;
int indx = 0;
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
glColor3d(data[indx],data[indx],data[indx]);
glVertex2d(locX + x, locY + y);
indx++;
}
}
Hope this helps.
I'm using the method of dividing the x and y coordinates by the z value, then rendering it just like you would a 2D game/thing to create 3D looking thing. The problem is when the z value is less then 0 it flips the quadrants for the coordinate and looks very weird. At the moment there is only movement if someone could help me fix the negative z value thing and show me how to rotate. I'm not using and matrices only vectors that take in x,y and z for the maths if that helps. I'm making this using Java with no extra libraries.
Thanks for the help.
I used the perspective matrix and multiplied it by my vector but it didn't work here is my code there might be something wrong with it. I just turned the vector into a 1 by 3 matrix and then did this.
public Matrix multiply(Matrix matrix)
{
Matrix result = new Matrix(getWidth(),getHeight());
for(int y = 0; y < getHeight()-1; y++)
{
for(int x = 0; x < getWidth()-1; x++)
{
float sum = 0.0f;
for(int e = 0; e < this.getWidth()-1; e++)
{
sum += this.matrix[e + y * getWidth()] * matrix.matrix[x + e * matrix.getWidth()];
}
result.matrix[x + y * getWidth()] = sum;
}
}
return result;
}
Just guessing here, but it sounds like you are trying to do a projection transform: You are modeling 3D objects (things with X, Y, and Z coordinates) and you want to project them onto a 2D window (i.e., the screen).
The meaning of Z in the naive projection transform is the distance between the point and a plane parallel to the screen, that passes through your eyeball. If you have points with -Z, those represent points that are behind your head.
Sounds like you need to translate the Z coordinates so that Z=0 is the plane of the screen, or a plane parallel to and behind the screen. (In other words, Add a constant to all of your Zs, so that none of them is negative.)
http://en.wikipedia.org/wiki/3D_projection
The applet used is like the first quadrant of a Cartisian Plane with the domain and range (0, 200). My assignment is to draw a house and a sun in this applet.
I am trying to draw the circle for the sun. I really have no idea where to start. We are learning about for loops and nested loops so it probably pertains to that. We haven't got to arrays and general functions like draw.circle do not exist for this applet. If it helps, here is how I drew my roof for the house (two right triangles): Notice it is drawn pixel by pixel. I suspect my teacher wants the same kind of thing for the circle.
//roof
//left side
double starty = 100;
for(double x = 16; x <= 63; x++){
for(int y = 100; y <= starty; y++){
img.set(x, y, JRaster.purple);
}
starty += 1;
}
//right side
double startx = 110;
for(int y = 100; y <= 147; y++){
for(double x = 63; x <= startx; x++){
img.set(x , y, JRaster.purple);
}
startx -= 1;
}
Here's how I would draw the north-east quarter of a circle, pixel by pixel. You can just repeat this with slight variations for the other three quarters. No trigonometry required!
Start by drawing the eastern most point of the circle. Then you'll draw more pixels, moving northwards and westwards, until you get to the northern most point of the circle.
Calculate the distance of the point you've just drawn from the centre. If it's more than the radius, then your next pixel will be one to the left, otherwise, your next pixel will be the one above.
Repeat the previous step till you get to the northern most point.
Post a comment if you get stuck, with converting this to Java, or with adjusting it for the other three quarters of the circle.
I won't give you code, but you should remember how a circle is made. Going from theta=0 to theta=2*pi, the circle is traced by x=cos x, y=sin x.
So, using a for loop that increments a double(here called theta) by something like 0.01 until 2*pi(2*Math.PI or roughly 6.28) plot off Math.cos(theta), Math.sin(theta).
I've got a problem: I have two coordinates (start and end point positions) and a polygon. I want to check whether this route is (1) from outside to inside of the polygon, (2) from inside to outside, (3) only inside, or (4) only outside.
I would really appreciate any help.
You are lucky! Java contains a Polygon class which has a method contains(double x, double y). Use it!
See this page:
http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
This function is taken from there.
It is based on the observation that a test point is within a polygon if when projected on the y-axis it's x value is below odd number of polygon edges. Works for both convex and concave polygons.
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}