When I plot point1(p1) and point2(p2), the line between p1 and p2 is drawn. I wanna know a set of the points making the line.
For example, I wanna get x, y coordinates (as array type: x[], y[]). Is there any algorithms or code?
Here's what I have come up with:
It is fair to say that we need to use the slope formula, y = m*x + b to find the slope so we can plot our points along that line. We need the following:
(x1, y1)
(x2, y2)
to find the following:
m = (y2 - y1) / (x2 - x1)
b = y1 - (m * x1)
minX = min(x1, x2) used for limiting our lower bound
maxX = max(x1, x2) used for limiting our upper bound
Now that everything is set, we can plot our line pixel by pixel and obtain all (x,y) coordinates we need. The logic is simple:
let x loop from minX to maxX and plug it in y = m*x + b (we already have all the variables except y). Then, store the (x,y) pair.
I have used Java for coding this logically and visually. Also, I used LinkedList instead of arrays (because I we can't know the number of points we will obtain).
I have also drawn what Java would draw (in blue) and my approach (in red). They are almost perfectly the exact output and coordinates. The image below is zoomed 5x the original size.
Note! The above explanation is what you would use if the line is not vertical (because the slope would be undefined, division by zero). If it is, then you will plug y (instead of x) values and find the x (instead of y) value from the following formula x = (y - b) / m (instead of y = m*x + b). Though, the code takes care of vertical lines.
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.LinkedList;
import javax.swing.JFrame;
public class LineDrawing extends Canvas {
int x1 = 5;
int y1 = 10;
int x2 = 105;
int y2 = 100;
double m = ((double) (y2 - y1)) / ((double) (x2 - x1));//slope
double b = y1 - (m * ((double) x1));//vertical shift
//Takes care of the domain we will loop between.
//min and max will be assigned minX and maxX if the line is not vertical.
//minY and maxY are assigned to min and max otherwise.
int minX = Math.min(x1, x2);//minimum x value we should consider
int maxX = Math.max(x1, x2);//maximum x value we should consider
int minY = Math.min(y1, y2);//minimum y value we should consider
int maxY = Math.max(y1, y2);//maximum y value we should consider
int min = 0;
int max = 0;
boolean plugX = true;//if true, the line is not vertical.
LinkedList<Point> points = new LinkedList<>();//Store all points here
public LineDrawing() {
if (x1 == x2) {//plug the y value instead the x, this is a vertical line.
plugX = false;
min = minY;
max = maxY;
} else {//dont change and plug x values.
min = minX;
max = maxX;
}
}
#Override
public void paint(Graphics g) {
super.paint(g);
//Draw the line, using default java drawLine in blue.
g.setColor(Color.BLUE);
g.drawLine(x1, y1, x2, y2);
//change the color to red, it will draw our verison.
g.setColor(Color.RED);
//Draw the points, point by point on screen.
//Plug m, x, and b in the formula y = m*x + b
//to obtain the y value.
//OR
//Plug m, y, and b in the formula x = (y - b) / m
//to obtain the x value if vertical line.
//Then plot (x,y) coordinate on screen and add the point to our linkedList.
for (int i = min; i <= max; i++) {
int obtained = 0;
if (plugX) {//not a vertical line
obtained = (int) Math.round((m * i + b));
System.out.println("x = " + i + " , y = " + obtained);
points.add(new Point(i, obtained));
//Uncomment to see the full blue line.
g.drawLine(i, obtained, i, obtained);
} else {//vertical line
obtained = (int) Math.round((double) (i - b) / (double) m);
System.out.println("x = " + x1 + " , y = " + i);
g.drawLine(x1, i, x1, i);//Uncomment to see the full blue line.
points.add(new Point(x1, i));
}
}
//Print out the number of points as well as the coordinates themselves.
System.out.println("Total points: " + points.size());
for (int i = 0; i < points.size(); i++) {
System.out.println(i + " ( " + points.get(i).x
+ ", " + points.get(i).y + " )");
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(120, 150);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new LineDrawing());
frame.setVisible(true);
}
}
I try to slice a DenseVector based on a elementwise boolean condition on another DenseVector:
import breeze.linalg.DenseVector
val x = DenseVector(1.0,2.0,3.0)
val y = DenseVector(10.0,20,0,30.0)
// I want a new DenseVector containing all elements of y where x > 1.5
// i.e. I want DenseVector(20,0,30.0)
val newy = y(x:>1.5) // does not give a DenseVector but a SliceVector
With Python/Numpy, I would just write y[x>1.5]
Using Breeze you have to use for comprehensions for filtering DenseVectors
val y = DenseVector(10.0,20,0,30.0)
val newY = for {
v <- y
if v > 1.5
} yield v
// or to write it in one line
val newY = for (v <- y if v > 1.5) yield v
The SliceVector resulting from y(x:>1.5) is just a view on the original DenseVector. To create a new DenseVector, use
val newy = y(x:>1.5).toDenseVector
I've been trying to generate an image of the Mandelbrot set but something is obviously very wrong. Here's the image this code produces: http://puu.sh/csUDd/bdfa6c1d98.png
I'm using Scala and processing. The coloring technique is very simple but i don't think it's the main problem by looking at the image's shape. Thank you.
for (i <- 0 to width){ // 0 to 700
for (j <- 0 to height){ // 0 to 400
val x = i/200.toDouble - 2.5 // scaled to -2.5, 1
val y = j/200.toDouble - 1 // scaled to -1, 1
val c = new Complex(x, y)
val iterMax = 1000
var z = c
var iterations = 0
var inSet = false
while (z.abs < 2 && iterations < iterMax) {
z = z.squared.plus(c)
iterations += 1
if (iterations == iterMax) {
inSet = true
}
}
// stroke() defines the current rgb color.
// If the point is in the set, it is coloured black.
// If not, the point is coloured as such: (iterations^5 mod 255, iterations^7 mod 255, iterations^11 mod 255)
// I use 5, 7 and 11 for no specific reason. Using iterations alone results in a black picture.
if (inSet) stroke(0, 0, 0)
else stroke(pow(iterations, 5).toInt % 255, pow(iterations, 7).toInt % 255, pow(iterations, 11).toInt % 255)
// Finally, draw the point.
point(i, j)
}
}
Here's the class for complex numbers
class Complex(val real: Double, val imag: Double) {
def squared = new Complex(real*real - imag*imag, 2*real*imag)
def abs = sqrt(real*real + imag*imag)
def plus(another: Complex) = new Complex(real + real, imag + imag)
}
Your plus method doesn't add with another, I think it should be
def plus(another: Complex) = new Complex(real + another.real, imag + another.imag)
Ok, i figured it out. There was a mistake in the complex number class.
def plus(another: Complex) = new Complex(real + real, imag + imag)
--->
def plus(another: Complex) = new Complex(this.real + another.real, this.imag + another.imag)
Here's the result for anyone interested
http://puu.sh/csYbb/b2a0d882e1.png
I finally got a working tile-able version of Simplex noise working after much work, but I can't seem to get it to record and display correctly when using a BufferedImage. Whenever I try to create an image, it ends up with bands or rings of black and white, instead of a smooth change of shades, which is what I'm expecting. I'm guessing there's something simple I'm not doing, but for the life of me, I can't find it.
This is my code (quite a bit of which is from Stefan Gustavson's Simplex noise implementation):
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import java.io.File
import scala.util.Random
object ImageTest {
def main(args: Array[String]): Unit = {
val image = generate(1024, 1024, 1)
ImageIO.write(image, "png", new File("heightmap.png"))
}
def generate(width: Int, height: Int, octaves: Int) = {
val map = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY)
val pi2 = Math.PI * 2
for ( x <- 0 until width;
y <- 0 until height) {
var total = 0.0
for (oct <- 1 to octaves) {
val scale = (1 - 1/Math.pow(2, oct))
val s = x / width.toDouble
val t = y / height.toDouble
val dx = 1-scale
val dy = 1-scale
val nx = scale + Math.cos(s*pi2) * dx
val ny = scale + Math.cos(t*pi2) * dy
val nz = scale + Math.sin(s*pi2) * dx
val nw = scale + Math.sin(t*pi2) * dy
total += (((noise(nx,ny,nz,nw)+1)/2)) * Math.pow(0.5, oct)
}
map.setRGB(x,y, (total * 0xffffff).toInt)
}
map
}
// Simplex 4D noise generator
// returns -1.0 <-> 1.0
def noise(x: Double, y: Double, z: Double, w: Double) = {
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
val s = (x + y + z + w) * F4; // Factor for 4D skewing
val i = Math.floor(x+s).toInt
val j = Math.floor(y+s).toInt
val k = Math.floor(z+s).toInt
val l = Math.floor(w+s).toInt
val t = (i+j+k+l) * G4 // Factor for 4D unskewing
val xBase = x - (i-t) // Unskew the cell space and set the x, y, z, w
val yBase = y - (j-t) //distances from the cell origin
val zBase = z - (k-t)
val wBase = w - (l-t)
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
// To find out which of the 24 possible simplices we're in, we need to
// determine the magnitude ordering of x0, y0, z0 and w0.
// Six pair-wise comparisons are performed between each possible pair
// of the four coordinates, and the results are used to rank the numbers.
var rankx = 0
var ranky = 0
var rankz = 0
var rankw = 0
if(xBase > yBase) rankx+=1 else ranky+=1
if(xBase > zBase) rankx+=1 else rankz+=1
if(xBase > wBase) rankx+=1 else rankw+=1
if(yBase > zBase) ranky+=1 else rankz+=1
if(yBase > wBase) ranky+=1 else rankw+=1
if(zBase > wBase) rankz+=1 else rankw+=1
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
// impossible. Only the 24 indices which have non-zero entries make any sense.
// We use a thresholding to set the coordinates in turn from the largest magnitude.
// Rank 3 denotes the largest coordinate.
val i1 = if (rankx >= 3) 1 else 0
val j1 = if (ranky >= 3) 1 else 0
val k1 = if (rankz >= 3) 1 else 0
val l1 = if (rankw >= 3) 1 else 0
// Rank 2 denotes the second largest coordinate.
val i2 = if (rankx >= 2) 1 else 0
val j2 = if (ranky >= 2) 1 else 0
val k2 = if (rankz >= 2) 1 else 0
val l2 = if (rankw >= 2) 1 else 0
// Rank 1 denotes the second smallest coordinate.
val i3 = if (rankx >= 1) 1 else 0
val j3 = if (ranky >= 1) 1 else 0
val k3 = if (rankz >= 1) 1 else 0
val l3 = if (rankw >= 1) 1 else 0
// The fifth corner has all coordinate offsets = 1, so no need to compute that.
val xList = Array(xBase, xBase-i1+G4, xBase-i2+2*G4, xBase-i3+3*G4, xBase-1+4*G4)
val yList = Array(yBase, yBase-j1+G4, yBase-j2+2*G4, yBase-j3+3*G4, yBase-1+4*G4)
val zList = Array(zBase, zBase-k1+G4, zBase-k2+2*G4, zBase-k3+3*G4, zBase-1+4*G4)
val wList = Array(wBase, wBase-l1+G4, wBase-l2+2*G4, wBase-l3+3*G4, wBase-1+4*G4)
// Work out the hashed gradient indices of the five simplex corners
val ii = if (i < 0) 256 + (i % 255) else i % 255
val jj = if (j < 0) 256 + (j % 255) else j % 255
val kk = if (k < 0) 256 + (k % 255) else k % 255
val ll = if (l < 0) 256 + (l % 255) else l % 255
val gradIndices = Array(
perm(ii+perm(jj+perm(kk+perm(ll)))) % 32,
perm(ii+i1+perm(jj+j1+perm(kk+k1+perm(ll+l1)))) % 32,
perm(ii+i2+perm(jj+j2+perm(kk+k2+perm(ll+l2)))) % 32,
perm(ii+i3+perm(jj+j3+perm(kk+k3+perm(ll+l3)))) % 32,
perm(ii+1+perm(jj+1+perm(kk+1+perm(ll+1)))) % 32)
// Calculate the contribution from the five corners
var total = 0.0
for (dim <- 0 until 5) {
val (x,y,z,w) = (xList(dim), yList(dim), zList(dim), wList(dim))
var t = 0.5 - x*x - y*y - z*z - w*w
total += {
if (t < 0) 0.0
else {
t *= t
val g = grad4(gradIndices(dim))
t * t * ((g.x*x)+(g.y*y)+(g.z*z)+(g.w*w))
}
}
}
// Sum up and scale the result to cover the range [-1,1]
27.0 * total
}
case class Grad(x: Double, y: Double, z: Double, w: Double = 0.0)
private lazy val grad4 = Array(
Grad(0,1,1,1), Grad(0,1,1,-1), Grad(0,1,-1,1), Grad(0,1,-1,-1),
Grad(0,-1,1,1),Grad(0,-1,1,-1),Grad(0,-1,-1,1),Grad(0,-1,-1,-1),
Grad(1,0,1,1), Grad(1,0,1,-1), Grad(1,0,-1,1), Grad(1,0,-1,-1),
Grad(-1,0,1,1),Grad(-1,0,1,-1),Grad(-1,0,-1,1),Grad(-1,0,-1,-1),
Grad(1,1,0,1), Grad(1,1,0,-1), Grad(1,-1,0,1), Grad(1,-1,0,-1),
Grad(-1,1,0,1),Grad(-1,1,0,-1),Grad(-1,-1,0,1),Grad(-1,-1,0,-1),
Grad(1,1,1,0), Grad(1,1,-1,0), Grad(1,-1,1,0), Grad(1,-1,-1,0),
Grad(-1,1,1,0),Grad(-1,1,-1,0),Grad(-1,-1,1,0),Grad(-1,-1,-1,0))
private lazy val perm = new Array[Short](512)
for(i <- 0 until perm.length)
perm(i) = Random.nextInt(256).toShort
private lazy val F4 = (Math.sqrt(5.0) - 1.0) / 4.0
private lazy val G4 = (5.0 - Math.sqrt(5.0)) / 20.0
}
I've checked the output values of the noise function I'm using, which as of yet hasn't returned anything outside of (-1, 1) exclusive. And for a single octave, the value I'm supplying to the image (total) has not gone outside of (0,1) exclusive, either.
The only thing I can see that would be a problem is either the BufferedImage type is set incorrectly, or I'm multiplying total by the wrong hex value when setting the values in the image.
I've looked through the Javadocs on BufferedImage for information about the types and the values they accept, though nothing I've found seems to be out of place in my code (though, I am fairly new to using BufferedImage in general). And I've tried changing the hex value, but neither seems to change anything. The only thing I've found that has any affect is if I divide the (total * 0xffffff).toInt value by 256, which seems to darken the bands a bit and a slight gradient appears over the areas it should, but if I increase the division too much, the image just becomes black. So as of right now I'm stuck on what could be the issue.
(total * 0xffffff).toInt doesn't seem to make sense. You are creating an ARGB value from a grayscale float with a single multiplication?
I think you want something like this:
val i = (total * 0xFF).toInt
val rgb = 0xFF000000 | (i << 16) | (i << 8) | i
That gives me a smooth random texture, although with very low contrast—with 1 octave, your total seems to vary approx from 0.2 to 0.3, so you may need to adjust the scale a bit.
I'm not sure though how you can get 16-bit grayscale resolution. Perhaps you need to set the raster data directly instead of using setRGB (which forces you down to 8 bits). The comments below this question suggest that you use the raster directly.