how do you draw a line in a pixel array - java

I like to have maximum control over the screen, so I have to control every pixel, and that has some pros and cons. one con is that I don't really have the help from any built-in functions. so I have no idea how to draw a line.
I've tried to make a function to handle line drawing but I just can't get it to work!
here's the code I used to draw the line
int startX;
int startY;
int deltaX = x1/x2;
int deltaY = y1/y2;
float deltaPixl = deltaX/deltaY;
for(int i=0;i<deltaY;i=i+1){
if(x1>x2){ startX = x2;}else{ startX=x1;}
if(y1>y2){ startY = y2;}else{ startY=y1;}
pixl(startX+i,round(startY+(deltaPixl*i)),0);
}
it uses a function called pixl so that it easily draw a pixel to the pixel array,
just to clarify why there's a function called pixl in the code.
and when I try to use this code, it doesn't crash, like processing usually does when it has an error!
it just doesn't work, instead just doing nothing!
I'd like some help on this subject, please.

You could get away with simply using PGraphics.
The idea is once you have a PGraphics instance you use dot notation to access the drawing functions used to (as long as they're called between .beginDraw() and .endDraw()).
Using noSmooth() you can get it looking pixel perfect.
Here's a basic sketch to illustrate the idea:
// disable anti-aliasing
noSmooth();
// create a PGraphics layer
PGraphics layer = createGraphics(25, 25);
// render a line
layer.beginDraw();
layer.line(0, 24, 24, 0);
layer.endDraw();
// render the line at 100%
image(layer, 0, 0);
// render the line scaled up
image(layer, 0, 0, width, height);
This should do for most cases. (It's only trickier cases with very small values and transparency that might give you headaches)
If for some reason you need a lot more control, you can you always implement your own method of rasterising. Once place you can probably start with is Bresenham's line algorithm
Regarding your code there are a few things that could go wrong:
float deltaPixl = deltaX/deltaY;: if deltaY is zero you'll run into an exception
you're doing integer division for deltaX and deltaY (potentially making it likely to get 0 for either of the values)
you should try a println() statement before the for loop with the start/end values to get a feel if that loop will actually execute or not. Additionally, within the for loop you can println(i) to see if you get the value you expect.
Overall I recommend checking Kevin Workman's How to Debug guide.
Additionally you could use lerp() to calculate linearly interpolated position between the line's start and end points. Pass each coordinate and a normalized (between 0.0, 1.0) value, where 0.0 = at the start point, 1.0 = at the end point and anything in between is on the line (e.g. 0.5 = 50% along the line).
Here's a basic example:
void drawLinePoints(int x1, int y1, int x2, int y2, int numberOfPoints){
// for each point
for(int i = 0; i < numberOfPoints; i++){
// map the counter to a normalized (0.0 to 1.0) value for lerp
// 0.0 = 0 % along the line, 0.5 = 50% along the line, 1.0 = 100% along the line
float t = map(i, 0, numberOfPoints, 0.0, 1.0);
// linearly interpolate between the start / end points (and snap to whole pixels (casting to integer type))
int x = (int)lerp(x1, x2, t);
int y = (int)lerp(y1, y2, t);
// render the point
point(x, y);
}
}
void setup(){
// render points are large squares
strokeWeight(6);
strokeCap(PROJECT);
}
void draw(){
// clear frame
background(255);
// calculate distance
float distance = dist(10, 10, mouseX, mouseY);
// map distance the number of points to illustrate interpolation (more points = continuous line)
int numPoints = (int)distance / 8;
// render points along the line
drawLinePoints(10, 10, mouseX, mouseY, numPoints);
}
For the sake of completeness here's the above snippet using the pixels[] instead:
void drawLinePoints(int x1, int y1, int x2, int y2, int numberOfPoints){
// for each point
for(int i = 0; i < numberOfPoints; i++){
// map the counter to a normalized (0.0 to 1.0) value for lerp
// 0.0 = 0 % along the line, 0.5 = 50% along the line, 1.0 = 100% along the line
float t = map(i, 0, numberOfPoints, 0.0, 1.0);
// linearly interpolate between the start / end points (and snap to whole pixels (casting to integer type))
int x = (int)lerp(x1, x2, t);
int y = (int)lerp(y1, y2, t);
// convert the x, y coordinate to pixels array index and render the point in black
pixels[x + (y * width)] = color(0);
}
}
void setup(){
noSmooth();
}
void draw(){
// clear frame
loadPixels();
java.util.Arrays.fill(pixels, color(255));
// calculate distance
float distance = dist(10, 10, mouseX, mouseY);
// map distance the number of points to illustrate interpolation (more points = continuous line)
int numPoints = (int)distance;
// render points along the line
drawLinePoints(10, 10, mouseX, mouseY, numPoints);
// update pixels
updatePixels();
}

I'm a bit late but I found a very simple method for line drawing to a pixel array on this website.
Here is a simple implementation I made in Monogame (btw sorry its not using processing - I have never used it):
public void drawLine(int x1, int y1, int x2, int y2)
{
//this will store the colour data of the canvas pixels
Color[] canvasData = new Color[canvas.Width * canvas.Height];
//store the pixel data of the canvas in canvasData
canvas.GetData<Color>(canvasData);
//drawing line starts here
int dx = x2 - x1;
int dy = y2 - y1;
for (int x = x1; x < x2; x++)
{
int y = y1 + dy * (x - x1) / dx;
//[y*canvas.Width+x] converts the 2d array index to a 1d array index
canvasData[y * canvas.Width + x] = Color.Black;
}
//line drawing ended
//setting the canvas' pixels to the modified pixels with the line
canvas.SetData<Color>(canvasData);
}

Related

Incomplete Light Circle

I've made a lighting engine which allows for shadows. It works on a grid system where each pixel has a light value stored as an integer in an array. Here is a demonstration of what it looks like:
The shadow and the actual pixel coloring works fine. The only problem is the unlit pixels further out in the circle, which for some reason makes a very interesting pattern(you may need to zoom into the image to see it). Here is the code which draws the light.
public void implementLighting(){
lightLevels = new int[Game.WIDTH*Game.HEIGHT];
//Resets the light level map to replace it with the new lighting
for(LightSource lightSource : lights) {
//Iterates through all light sources in the world
double circumference = (Math.PI * lightSource.getRadius() * 2),
segmentToDegrees = 360 / circumference, distanceToLighting = lightSource.getLightLevel() / lightSource.getRadius();
//Degrades in brightness further out
for (double i = 0; i < circumference; i++) {
//Draws a ray to every outer pixel of the light source's reach
double radians = Math.toRadians(i*segmentToDegrees),
sine = Math.sin(radians),
cosine = Math.cos(radians),
x = lightSource.getVector().getScrX() + cosine,
y = lightSource.getVector().getScrY() + sine,
nextLit = 0;
for (double j = 0; j < lightSource.getRadius(); j++) {
int lighting = (int)(distanceToLighting * (lightSource.getRadius() - j));
double pixelHeight = super.getPixelHeight((int) x, (int)y);
if((int)j==(int)nextLit) addLighting((int)x, (int)y, lighting);
//If light is projected to have hit the pixel
if(pixelHeight > 0) {
double slope = (lightSource.getEmittingHeight() - pixelHeight) / (0 - j);
nextLit = (-lightSource.getRadius()) / slope;
/*If something is blocking it
* Using heightmap and emitting height, project where next lit pixel will be
*/
}
else nextLit++;
//Advances the light by one pixel if nothing is blocking it
x += cosine;
y += sine;
}
}
}
lights = new ArrayList<>();
}
The algorithm i'm using should account for every pixel within the radius of the light source not blocked by an object, so i'm not sure why some of the outer pixels are missing.
Thanks.
EDIT: What I found is, the unlit pixels within the radius of the light source are actually just dimmer than the other ones. This is a consequence of the addLighting method not simply changing the lighting of a pixel, but adding it to the value that's already there. This means that the "unlit" are the ones only being added to once.
To test this hypothesis, I made a program that draws a circle in the same way it is done to generate lighting. Here is the code that draws the circle:
BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, WIDTH, HEIGHT);
double radius = 100,
x = (WIDTH-radius)/2,
y = (HEIGHT-radius)/2,
circumference = Math.PI*2*radius,
segmentToRadians = (360*Math.PI)/(circumference*180);
for(double i = 0; i < circumference; i++){
double radians = segmentToRadians*i,
cosine = Math.cos(radians),
sine = Math.sin(radians),
xPos = x + cosine,
yPos = y + sine;
for (int j = 0; j < radius; j++) {
if(xPos >= 0 && xPos < WIDTH && yPos >= 0 && yPos < HEIGHT) {
int rgb = image.getRGB((int) Math.round(xPos), (int) Math.round(yPos));
if (rgb == Color.white.getRGB()) image.setRGB((int) Math.round(xPos), (int) Math.round(yPos), 0);
else image.setRGB((int) Math.round(xPos), (int) Math.round(yPos), Color.red.getRGB());
}
xPos += cosine;
yPos += sine;
}
}
Here is the result:
The white pixels are pixels not colored
The black pixels are pixels colored once
The red pixels are pixels colored 2 or more times
So its actually even worse than I originally proposed. It's a combination of unlit pixels, and pixels lit multiple times.
You should iterate over real image pixels, not polar grid points.
So correct pixel-walking code might look as
for(int x = 0; x < WIDTH; ++x) {
for(int y = 0; y < HEIGHT; ++y) {
double distance = Math.hypot(x - xCenter, y - yCenter);
if(distance <= radius) {
image.setRGB(x, y, YOUR_CODE_HERE);
}
}
}
Of course this snippet can be optimized choosing good filling polygon instead of rectangle.
This can be solved by anti-aliasing.
Because you push float-coordinate information and compress it , some lossy sampling occur.
double x,y ------(snap)---> lightLevels[int ?][int ?]
To totally solve that problem, you have to draw transparent pixel (i.e. those that less lit) around that line with a correct light intensity. It is quite hard to calculate though. (see https://en.wikipedia.org/wiki/Spatial_anti-aliasing)
Workaround
An easier (but dirty) approach is to draw another transparent thicker line over the line you draw, and tune the intensity as needed.
Or just make your line thicker i.e. using bigger blurry point but less lit to compensate.
It should make the glitch less obvious.
(see algorithm at how do I create a line of arbitrary thickness using Bresenham?)
An even better approach is to change your drawing approach.
Drawing each line manually is very expensive.
You may draw a circle using 2D sprite.
However, it is not applicable if you really want the ray-cast like in this image : http://www.iforce2d.net/image/explosions-raycast1.png
Split graphic - gameplay
For best performance and appearance, you may prefer GPU to render instead, but use more rough algorithm to do ray-cast for the gameplay.
Nonetheless, it is a very complex topic. (e.g. http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/ )
Reference
Here are more information:
http://what-when-how.com/opengl-programming-guide/antialiasing-blending-antialiasing-fog-and-polygon-offset-opengl-programming/ (opengl-antialias with image)
DirectX11 Non-Solid wireframe (a related question about directx11 with image)

What it's wrong in translation function?

I tried to create and translate a polygon in openGL, I create a function for translation but this create a white line from x0 to v_size and I don't understand why ?
This is my function for polygon translation
public void translate1(GL2 gl, double x0, double x1, double y0, double y1){
double step = 0.2;
for(double i = 0; i < v_size; i += step){
gl.glBegin(GL2.GL_POLYGON);
gl.glVertex2d(x0 + i, y0);
gl.glVertex2d(x0 + i, y1);
gl.glVertex2d(x1 + i, y1);
gl.glVertex2d(x1 + i, y0);
gl.glEnd();
}
}
Initial x0 = 0, x1 = 10, y0 = 20, y1 = 30.
Thanks !
Have a nice day!
The reason for this is, that you draw squares every step units away from each other. Since nothing gets cleared in the meantime, the overlapping quads form a line.
It is rather unclear what you are trying to achieve. A translation would usually not draw multiple quads. If you are trying to do an animation, then you'll have to split the movement over multiple frames and draw exactly one square in every frame.

drawCircle of Android's canvas draws not a circle

I'm trying to draw a circle on my canvas. Pseudocode of my algorithm looks like that
double R = 1.0;
// Draw 11 points, lying on the circle with the radius of 1
// and angle from 0 to 90 degrees
for(int i=0; i<=10; i++)
{
drawPoint( R*cos(PI*i/20), R*sin(PI*i/20) );
}
// Draw a circle with center at the (0; 0) and with the radius of 1
drawCircle(0, 0, R);
That's what I've got:
Looks fine, but there is one problem. When I increase radius only points with angles 0, 45 and 90 lie on a circle.
That's how it looks 72 degrees:
There is no any info about accuracy of the method drawCircle on developer.android.com.
I guess that it draws, based on the values at points with angles 0, 45, 90, ..., and calculate line in other positions very approximately.
I know, that I can draw circle as accurate as I want to, if I'll draw it like a polyline with tiny step, but it will work very slow.
So I want to find out - is there any methods to draw circle accurate in Android?
UPD 1:
How do I draw a points:
int x, y;
x = getPixelX(point.getX());
y = getPixelY(point.getY());
canvas.drawCircle(x, y, point.radius, paint);
Where getPixelX and getPixelY takes a coorditate of the point on plane and returns the same coordinate on the screen, basing on scale and offset.
I thought that I could make some mistake in those methods, but they work perfectly with the lines. I can zoom in lines and there is no error, all the points lies just on the line.
UPD 2:
There are comments, that probably I make a mistake in my calculations. I do not argue, perhaps you're right. So here is my "calculations".
How do I zoom in:
public void mouseWheelMoved(MouseWheelEvent e) {
// zoomQ is 0.9 or 1.1
double zoomQ = (e.getWheelRotation() + 10) / 10.0;
scaleX *= zoomQ;
scaleY *= zoomQ;
}
How do I move the plane:
public void mouseDragged(MouseEvent e) {
centerX -= (e.getX() - lastMouseX)/scaleX;
centerY -= (e.getY() - lastMouseY)/scaleY;
lastMouseX = e.getX();
lastMouseY = e.getY();
}
How do getPixelX/Y works:
public int getPixelX(double planeX) {
return (int)Math.round( scaleX*(planeX - centerX) + ScreenWidth/2 );
}
public int getPixelY(double planeY) {
return (int)Math.round( scaleY*(planeY - centerY) + ScreenHeight/2 );
}

why move position that i use affine transform in java 1.6se

i want to scale shape.
so i use setToScale method in affine transform.
then, not only shape's length is trans, but also shape's starting points are moved
why that?
public void initResize(int x, int y) {
oldX = x;
oldY = y;
}
public void resize(int x, int y) {
double xratio = (double)(x - shape.getBounds().x) / (shape.getBounds().width);
double yratio = (double)(y - shape.getBounds().y) / (shape.getBounds().height);
af.setToScale(xratio, 1);
shape = af.createTransformedShape(shape);
anchor.resize(shape.getBounds());
oldX = x;
oldY = y;
}
method call's order is MousePress : initResize, MouseDrgged : resize
x,y is mouse's coord
You have to apply operations on the AffineTransform object. In your case, you want to scale:
af.scale(xratio, 1);
The idea is that you multiply the current matrix with the matrix that represents the scale. That is how combining transformations works: by multiplying the matrixes (Be aware! the order of the multiplications are important and also A*B != B*A when multiplying matrixes).
This, instead of clearing the whole matrix and inserting scaling values following the default linear scale template for a matrix.

Java 1.5 Issue with drawing shape using lines and angles

Hey, I'm trying to write a method that takes a starting Cartesian coordinate(x,y) an angle (in degrees), a length and a number of sides and draws a shape to an applet. So far this is what I have but, I cant figure out what I'm doing wrong. I plan on using line transformations for the actual angle change and that's not written in yet but the logic for drawing a line at an angle should work but isn't as far as I can tell. Could I get a couple of new eyes to look at this and tell me if I'm missing something.
public void paint(Graphics g)
{
g.setColor(Color.BLACK);
Point startPt = new Point(0,0);
//Function in question
drawRegularPolygon(g, startPt, 5,60,50);
}
public static void drawRegularPolygon(Graphics g, Point2D startPoint, int numOfSides, int angle, int length)
{
Point2D current = startPoint;
for(int i=0; i<numOfSides; i++)
{
drawAngularLine(g, current, angle, length);
current = getEndPoint(current ,length,angle);
}
}
public static void drawAngularLine(Graphics g, Point2D startPoint, int angle, int length)
{
g.setColor(Color.BLACK);
Point2D endPoint = getEndPoint(startPoint, length, angle);
((Graphics2D) g).draw(new Line2D.Double(startPoint, endPoint));
}
private static Point2D getEndPoint(Point2D p, int length, int angle)
{
//Starting point you know (x1, x2),
//end point is (x1 + l * cos(ang), y1 + l * sin(ang))
//where l is the length and ang is the angle.
Point2D retVal = p;
double x = Math.cos(Math.toRadians(angle)*length+p.getX());
double y = Math.sin(Math.toRadians(angle)*length+p.getY());
retVal.setLocation(x,y);
return retVal;
}
A couple things. The first is to be careful about what you're taking sin/cosine of. It's not cos(angle*length) but rather length*cos(angle).
The second point is to think about coordinate systems. It might help to do the math assuming the initial point is (0,0), and then translate to the screen coordinates. This helps avoid the confusion of the y-axis seeming to be upside-down (values increase from top to bottom).
So assuming we just want a point that's length,angle away from the origin in a standard right-handed system, we'd get:
x1 = length * cos(angle)
y1 = length * sin(angle)
But since negative-y is up, we actually want
x2 = length * cos(angle)
y2 = -length * sin(angle)
To mentally check this, picture that you're doing this math at the origin (0,0) which is in the upper left, and have an angle of 45°. If y2 were positive, we'd end up seeing an angle that looks to us like -45°.
Now translate the origin to our starting point (x_i, y_i), to get our final values:
x_f = x_i + length * cos(angle)
y_f = y_i + (-length * cos(angle)) = y_i - length * cos(angle)
Alternatively, if it makes more sense to work in a standard right-handed coordinate system, you probably could get away with doing all the math as if (0,0) were in the center, and then applying a translation and a y-axis mirror transformation, but this screen coordinate system isn't too difficult to work within once you get used to flipping the y values around.
You are drawing a line with the same start point and end point - so nothing is drawn.
Java objects are passed by reference, so:
private static Point2D getEndPoint(Point2D p, int length, int angle){
Point2D retVal = p;
retVal.setLocation(x,y);
return retVal;
}
is also changing the starting point p. So it draws a line of length 1 (does it show a dot on the screen?).
Try using:
Point2D retVal = p.clone();

Categories

Resources