I am working on a 2D platformer game. There are star objects in the background and these stars move around. I wanted to draw lines between them and I've managed to do this without much effort. What I am now trying to do is to add an alpha value(transparency) to the lines being drawn.
I have tried to write an equation where alpha value is inversely proportional to the value of distance between two objects but have not succeeded.
How do I mathematically express the following rule ?
The larger the distance is, the lesser value of alpha gets
For example, if the distance is 400 then the transparency value should be 0 (java.awt.Color uses 0 as 100% transparency and 255 as no transparency)
here is an example of what I am trying to achieve:
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var stars = [], // Array that contains the stars
FPS = 60, // Frames per second
x = 40, // Number of stars
mouse = {
x: 0,
y: 0
}; // mouse location
// Push stars to the array
for (var i = 0; i < x; i++) {
stars.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
radius: Math.random() * 1 + 1,
vx: Math.floor(Math.random() * 50) - 25,
vy: Math.floor(Math.random() * 50) - 25
});
}
// Draw the scene
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.globalCompositeOperation = "lighter";
for (var i = 0, x = stars.length; i < x; i++) {
var s = stars[i];
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.arc(s.x, s.y, s.radius, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = 'black';
ctx.stroke();
}
ctx.beginPath();
for (var i = 0, x = stars.length; i < x; i++) {
var starI = stars[i];
ctx.moveTo(starI.x,starI.y);
if(distance(mouse, starI) < 150) ctx.lineTo(mouse.x, mouse.y);
for (var j = 0, x = stars.length; j < x; j++) {
var starII = stars[j];
if(distance(starI, starII) < 150) {
//ctx.globalAlpha = (1 / 150 * distance(starI, starII).toFixed(1));
ctx.lineTo(starII.x,starII.y);
}
}
}
ctx.lineWidth = 0.05;
ctx.strokeStyle = 'white';
ctx.stroke();
}
function distance( point1, point2 ){
var xs = 0;
var ys = 0;
xs = point2.x - point1.x;
xs = xs * xs;
ys = point2.y - point1.y;
ys = ys * ys;
return Math.sqrt( xs + ys );
}
// Update star locations
function update() {
for (var i = 0, x = stars.length; i < x; i++) {
var s = stars[i];
s.x += s.vx / FPS;
s.y += s.vy / FPS;
if (s.x < 0 || s.x > canvas.width) s.vx = -s.vx;
if (s.y < 0 || s.y > canvas.height) s.vy = -s.vy;
}
}
canvas.addEventListener('mousemove', function(e){
mouse.x = e.clientX;
mouse.y = e.clientY;
});
// Update and draw
function tick() {
draw();
update();
requestAnimationFrame(tick);
}
tick();
canvas {
background: #232323;
}
<canvas id="canvas"></canvas>
You should use:
((MAX_DISTANCE - distance) / MAX_DISTANCE) * 255
Explanation:
(MAX_DISTANCE - distance) makes sure that the larger the distance, the smaller the result.
Then, diving by MAX_DISTANCE and multiplying by 255, scales it from 0-MAX_DISTANCE to 0-255.
Related
I'm currently trying to make the ends of a Cylinder completely transparent whilst keeping the sides a Material.
I'm unsure how to achieve this. This thread mentions it but all the links are broken
I think I need to use a clipping plane? Although I don't know where to start with that.
Here's what I'm currently using to just simply set a translucent material!
Cylinder line = new Cylinder(getRadius()/4, height);
lineMaterial = new PhongMaterial();
lineMaterial.setDiffuseColor(new Color(1,1,1,0.5));
lineMaterial.diffuseMapProperty();
line.setMaterial(lineMaterial);
One possible way to get transparency is by using a png as the diffuse image, with some transparent pixels.
While this works, if you apply over a built-in JavaFX Cylinder, you won't get the expected result, because the Cylinder applies the same image to both the vertical tube and the two cap faces. So this won't work for your case.
There could be an option to remove the caps from the cylinder mesh and get just a tube, but unfortunately it doesn't export its triangle mesh.
So far, the best option is to create directly the mesh of a tube, and for that we can reuse the mesh of the Cylinder by checking the open source code at the (new) OpenJDK/JFX GitHub repository.
The following method creates a TriangleMesh of a Tube of height h, radius r, with div divisions (producing 2 * div triangles). It is the exact same code as the one in Cylinder, but without the caps points, texture coordinate and faces arrays.
private static TriangleMesh createMesh(int div, float h, float r) {
final int nPoints = div * 2;
final int tcCount = (div + 1) * 2;
final int faceCount = div * 2;
float textureDelta = 1.f / 256;
float dA = 1.f / div;
h *= .5f;
float points[] = new float[nPoints * 3];
float tPoints[] = new float[tcCount * 2];
int faces[] = new int[faceCount * 6];
int smoothing[] = new int[faceCount];
int pPos = 0, tPos = 0;
for (int i = 0; i < div; ++i) {
double a = dA * i * 2 * Math.PI;
points[pPos + 0] = (float) (Math.sin(a) * r);
points[pPos + 1] = h;
points[pPos + 2] = (float) (Math.cos(a) * r);
tPoints[tPos + 0] = 1 - dA * i;
tPoints[tPos + 1] = 1 - textureDelta;
pPos += 3; tPos += 2;
}
// top edge
tPoints[tPos + 0] = 0;
tPoints[tPos + 1] = 1 - textureDelta;
tPos += 2;
for (int i = 0; i < div; ++i) {
double a = dA * i * 2 * Math.PI;
points[pPos + 0] = (float) (Math.sin(a) * r);
points[pPos + 1] = -h;
points[pPos + 2] = (float) (Math.cos(a) * r);
tPoints[tPos + 0] = 1 - dA * i;
tPoints[tPos + 1] = textureDelta;
pPos += 3; tPos += 2;
}
// bottom edge
tPoints[tPos + 0] = 0;
tPoints[tPos + 1] = textureDelta;
tPos += 2;
int fIndex = 0;
// build body faces
for (int p0 = 0; p0 < div; ++p0) {
int p1 = p0 + 1;
int p2 = p0 + div;
int p3 = p1 + div;
// add p0, p1, p2
faces[fIndex+0] = p0;
faces[fIndex+1] = p0;
faces[fIndex+2] = p2;
faces[fIndex+3] = p2 + 1;
faces[fIndex+4] = p1 == div ? 0 : p1;
faces[fIndex+5] = p1;
fIndex += 6;
// add p3, p2, p1
faces[fIndex+0] = p3 % div == 0 ? p3 - div : p3;
faces[fIndex+1] = p3 + 1;
faces[fIndex+2] = p1 == div ? 0 : p1;
faces[fIndex+3] = p1;
faces[fIndex+4] = p2;
faces[fIndex+5] = p2 + 1;
fIndex += 6;
}
for (int i = 0; i < div * 2; ++i) {
smoothing[i] = 1;
}
TriangleMesh m = new TriangleMesh();
m.getPoints().setAll(points);
m.getTexCoords().setAll(tPoints);
m.getFaces().setAll(faces);
m.getFaceSmoothingGroups().setAll(smoothing);
return m;
}
Now you will need to create a MeshView so you can add it to your scene:
private static MeshView createTube(int div, float h, float r) {
MeshView meshView = new MeshView(createMesh(div, h, r));
// meshView.setDrawMode(DrawMode.LINE);
meshView.setCullFace(CullFace.NONE);
PhongMaterial material = new PhongMaterial(Color.RED);
meshView.setMaterial(material);
return meshView;
}
Create and add one to your scene:
MeshView tube = createTube(64, 5f, 1.6f);
Scene scene = new Scene(new Group(tube), 600, 600, true, SceneAntialiasing.BALANCED);
And you will get your tube:
You can also apply a diffuse image as texture:
material.setDiffuseMap(new Image(getClass.getResourceAsStream("440px-JavaFX_Logo.png")));
The equation for generating a Mandelbrot fractal is Zn+1 = Zn^2+C. The issue is that, in a computer program, C is used for zoom/resolution and location on screen. My question is, how can I make it so that I can get a fractal like this:
Wolfram
(equation f(z) = sin(z/c), z0 = c )
My code (from Rosetta Code):
public class MandelbrotSet extends JFrame {
private static final long serialVersionUID = 5513426498262284949L;
private final int MAX_ITER = 570;
private final double ZOOM = 150;
private BufferedImage image;
private double zx, zy, cX, cY, tmp;
public MandelbrotSet() {
super("Mandelbrot Set");
setBounds(100, 100, 800, 600);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
zx = zy = 0;
cX = (x - 400) / ZOOM;
cY = (y - 300) / ZOOM;
int iter = MAX_ITER;
while (zx * zx + zy * zy < 4 && iter > 0) {
tmp = zx * zx - zy * zy + cX;
zy = 2.0 * zx * zy + cY;
zx = tmp;
iter--;
}
image.setRGB(x, y, iter | (iter << 8));
}
}
}
#Override
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
}
public static void main(String[] args) {
new MandelbrotSet().setVisible(true);;
}
}
By trigonometric theorems
sin(A+i*B)=sin(A)*cos(i*B)+ cos(A)*sin(i*B)
=sin(A)*cosh(B )+i*cos(A)*sinh(B )
and for the quotient using z=x+i*y and c=a+i*b
(x+i*y)/(a+i*b)=(x+i*y)*(a-i*b)/(a*a+b*b)
so that for the sine expression above
A = (a*x+b*y)/(a*a+b*b)
B = (a*y-b*x)/(a*a+b*b)
In javascript a small script to generate this fractal can look like this:
function cosh(x) { return 0.5*(Math.exp(x)+Math.exp(-x)); }
function sinh(x) { return 0.5*(Math.exp(x)-Math.exp(-x)); }
function rgb(r,g,b) { return "rgb("+r+","+g+","+b+")"; }
var colors = new Array(24);
for(var k=0; k<8; k++) {
colors[ k] = rgb(k*64,(7-k)*64,(7-k)*64);
colors[ 8+k] = rgb((7-k)*64,k*64,(7-k)*64);
colors[16+k] = rgb((7-k)*64,(7-k)*64,k*64);
}
var cvs = document.getElementById('sine-fractal');
var ctx = cvs.getContext('2d');
var cx = 0.0, cy = 0.0;
var dx = 1.0;
var tiles = 100;
var scale = Math.min(cvs.width, cvs.height) / tiles;
ctx.scale(scale, scale);
function localx(i) { return cx-dx + 2*i*dx/tiles; }
function localy(j) { return cy-dx + 2*j*dx/tiles; }
for (var i = 0; i < tiles; i++) {
var a = localx(i);
for (var j = 0; j < tiles; j++) {
var b = localy(j);
var r2 = a*a + b*b;
var x = a, y = b;
var rounds = 0;
var max = 500;
while (x * x + y * y < 4 && rounds < max) {
var u = (a*x + b*y) / r2, v = (a*y - b*x) / r2;
x = Math.sin(u) * cosh(v);
y = Math.cos(u) * sinh(v);
rounds++;
}
ctx.fillStyle = colors[rounds % 24];
ctx.fillRect(i, j, 1, 1);
}
}
<canvas id='sine-fractal' width=200 height=200></canvas>
How can I rotate an image about the Z axis in JOGL without using glRotatef function? I don't understand how to get z' since there is no e.getZ() function. How might one calculate a z_prime? Right now when I try to rotate about the z axis, it like rotates it about the z axis in the beggining and then begins rotating it about the y axis and then back to the z (which makes sense because i have z_rot += y_prime. How can I just rotate it about the z and calculate a z'?
public void transform(float[] vertices_in, float[] vertices_out){
// perform your transformation
int length = vertices_in.length;
float[] transformMatrix =
{
r11, r12, r13, tx,
r21, r22, r23, ty,
r31, r32, r33, tz,
0, 0, 0, 1
};
// Fill in the empty verticesout vector
for(int i = 0; i < vertices_in.length; i++)
{
vertices_out[i] = vertices_in[i];
}
// loop through and set the new matrix (after translation)
// because
// 1 0 0 0
// 0 1 0 0
// 0 0 1 0
// X Rotation
for(int i = 0; i < vertices_out.length; i += 3)
{
vertices_out[i+1] = vertices_out[i+1] * (float)Math.cos(xRot) - vertices_out[i+2] * (float)Math.sin(xRot); //New Y
vertices_out[i+2] = vertices_out[i+1] * (float)Math.sin(xRot) + vertices_out[i+2] * (float)Math.cos(xRot); //New Z
}
// Y Rotation
for(int i = 0; i < vertices_out.length; i += 3)
{
vertices_out[i] = vertices_out[i] * (float)Math.cos(yRot) + vertices_out[i+2] * (float)Math.sin(yRot);
vertices_out[i+2] = vertices_out[i]*-(float)Math.sin(yRot) + vertices_out[i+2]*(float)Math.cos(yRot);
}
// Z Rotation
for(int i = 0; i < vertices_out.length; i += 3)
{
vertices_out[i] = vertices_out[i] * (float)Math.cos(zRot) - vertices_out[i+1] * (float)Math.sin(zRot);
vertices_out[i+1] = vertices_out[i] * (float)Math.sin(zRot) + vertices_out[i+1] * (float)Math.cos(zRot);
}
// Translation & Scaling
for(int i = 0; i < vertices_in.length; i+=3)
{
vertices_out[i] = vertices_out[i] * transformMatrix[0] + transformMatrix[3]; //x'
vertices_out[i+1] = vertices_out[i+1] * transformMatrix[5] + transformMatrix[7]; //y'
vertices_out[i+2] = vertices_out[i+2] * transformMatrix[10] + transformMatrix[11]; //z'
}
}
#Override
public void mouseDragged(MouseEvent e)
{
if(rotating)
{
float XX = (e.getX()-windowWidth*0.5f)*orthoX/windowWidth;
float YY = -(e.getY()-windowHeight*0.5f)*orthoX/windowWidth;
float x_prime = (StartXX - XX);
float y_prime = (StartYY - YY);
if(rightMouseClick)
{
zRot += y_prime/50;
}
else
{
xRot += y_prime/50;
yRot += x_prime/50;
StartYY = YY;
StartXX = XX;
}
}
}
All these rotations are using an inconsistent intermediate state. E.g (I just substituted for shorter variable names).
y = y * (float)Math.cos(xRot) - z * (float)Math.sin(xRot); //New Y
z = y * (float)Math.sin(xRot) + z * (float)Math.cos(xRot); //New Z
The first line is correct. However, the second line already uses the new y when it should use the old one. Hence, you have to save the old variable somewhere to make it work. The same applies to all other rotations.
The rest of the code looks ok.
I am trying to determine all of the points where two 3D but coplanar triangles intersect. I have found methods that detect if the triangles intersect but what I truly need is the actual points where the intersection occurs. Below I have shown a few cases of this situation.
Also, I will be coding this in Java but I am sure I could convert a different language as long as I understand the math!
I know all the vertices but that is it!
Edited for clarification.
Thanks,
Michael
https://www.dropbox.com/s/yoszrlfqbx3usrf/cases.png?dl=0
if you don't have the equation for the plane you'll need to calculate it.
ignore the z axis for now (unless that makes all your points fall on the same line in which case ignore another axis :)
find the intersections for every edge of the first triangle and every edge of the other triangle
plug the intersections back into the equation of your plane to recover z
for step 3: from line line intersection wikipedia article
discard any intersections that don't fall on both edges
here's a js snippet that does step 3
var a;
var b;
var bs = document.body.style;
var ds = document.documentElement.style;
bs.height = bs.width = ds.height = ds.width = "100%";
bs.border = bs.margin = bs.padding = 0;
var c = document.createElement("canvas");
c.style.display = "block";
c.addEventListener("mousedown", randomize, false);
c.addEventListener("mousemove", follow, false);
document.body.appendChild(c);
var ctx = c.getContext("2d");
window.addEventListener("resize", redraw);
randomize();
function randomPoint() {
return {x:Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight};
}
function randomize(e) {
a = [];
b = [];
for (var i = 0; i < 3; i++) {
a[i] = randomPoint();
b[i] = randomPoint();
}
redraw();
}
function follow(e) {
var average = {x:0, y:0};
for (var i = 0; i < 3; i++) {
average.x += a[i].x / 3;
average.y += a[i].y / 3;
}
for (var i = 0; i < 3; i++) {
a[i].x += e.clientX - average.x;
a[i].y += e.clientY - average.y;
}
redraw();
}
function drawPoint(p, color) {
ctx.strokeStyle = color;
ctx.beginPath();
ctx.arc(p.x, p.y, 10, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.stroke();
}
function isPointOnLine(p, v1, v2) {
if (v1.x === v2.x)
return (Math.max(v1.y, v2.y) >= p.y && Math.min(v1.y, v2.y) <= p.y)
else
return (Math.max(v1.x, v2.x) >= p.x && Math.min(v1.x, v2.x) <= p.x)
}
function calculateIntersection(a1, a2, b1, b2) {
var d = (a1.x - a2.x)*(b1.y - b2.y) -
(a1.y - a2.y)*(b1.x - b2.x);
if (!d) return null;
return {
x:((a1.x*a2.y - a1.y*a2.x)*(b1.x - b2.x) -
(a1.x - a2.x)*(b1.x*b2.y - b1.y*b2.x)) / d,
y:((a1.x*a2.y - a1.y*a2.x)*(b1.y - b2.y) -
(a1.y - a2.y)*(b1.x*b2.y - b1.y*b2.x)) / d
};
}
function drawIntersections(a, b) {
a.forEach(function (a1, i) {
var a2 = a[(i + 1) % a.length];
b.forEach(function (b1, j) {
var b2 = b[(j + 1) % b.length];
var p = calculateIntersection(a1, a2, b1, b2);
if(!p) return;
if (isPointOnLine(p, a1, a2) && isPointOnLine(p, b1, b2))
drawPoint(p, "red");
else
drawPoint(p, "yellow");
});
});
}
function drawShape(shape) {
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(shape[0].x, shape[0].y);
for (var i = 1; i <= shape.length; i++) {
ctx.lineTo(shape[i % shape.length].x, shape[i % shape.length].y);
}
ctx.closePath();
ctx.stroke();
}
function redraw() {
c.width = window.innerWidth;
c.height = window.innerHeight;
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillStyle = "rgb(200, 200, 200)";
ctx.font = "40px serif";
ctx.fillText("click to randomize", 20, 40);
drawShape(a);
drawShape(b);
drawIntersections(a, b);
}
I am rendering terrain using a quad-tree system. I need to split a height-map into four sections using the method splitHeightmap(float[] originalMap, int quadrant) with the quadrant being a number from 0-3. The map needs to be split into quarters so if 0 is passed as the quadrant, the bottom left quarter of the array is returned as a new float array. I have a little base code, but I'm not sure how to actually sample the map depending on the desired quadrant:
protected float[] splitHeightMap(float[] heightMap, TerrainQuadrant quadrant) {
float[] newHeightMap = new float[size >> 1];
int newSize = size >> 1;
for (int i = 0; i < newSize; i++)
for (int j = 0; j < newSize; j++)
newHeightMap[i * newSize + j] = sampleHeightAt(heightMap, i, j);
return newHeightMap;
}
protected float sampleHeightAt(float[] heightMap, int x, int z) {
return heightMap[z + x * size];
}
Edit: I have written what I think should work, but am getting a ArrayIndexOutOfBoundsException for index 65792 (with a original heightmap of 512x512):
protected float[] splitHeightMap(float[] heightMap, TerrainQuadrant quadrant) {
float[] newHeightMap = new float[(size >> 1) * (size >> 1)];
int newSize = size >> 1;
int xOffset = 0, zOffset = 0;
int xCount = 0, yCount = 0;
switch (quadrant) {
case BottomRight:
xOffset = newSize;
break;
case TopLeft:
zOffset = newSize;
break;
case TopRight:
xOffset = newSize;
zOffset = newSize;
break;
default:
break;
}
for (int x = xOffset; x < xOffset + newSize; x++)
for (int z = zOffset; z < zOffset + newSize; z++) {
newHeightMap[xCount + yCount * newSize] = heightMap[z + x * size]; // should this be 'z + x * size' or 'x + z * size'?
xCount++;
yCount++;
}
return newHeightMap;
}
If I understand you correctly, then you have to use two-dimensional array. With such type of array it's easy to fetch any area of the array.
Your heightMap now gona be float[][] type, so you must correct your code, where you fill this array.
I have implemented one examlpe for you and used 4x4 array:
| 2 2 3 3 |
| 2 2 3 3 |
| 0 0 1 1 |
| 0 0 1 1 |
As I understand you want to fetch areas like all '0', all '1', all '2' and all '3'.
public static void main ( String[] args )
{
//setting up initial array 'heightMap' (you can name it like you want)
float[][] f = { { 2, 2, 3, 3 }, { 2, 2, 3, 3 }, { 0, 0, 1, 1 }, { 0, 0, 1, 1 } };
float[][] f2 = splitHeightMap ( f, TerrainQuadrant.BotttomRight );
for ( float[] floats : f2 )
{
System.out.println ( Arrays.toString ( floats ) );
}
}
protected static float[][] splitHeightMap ( float[][] heightMap, TerrainQuadrant quadrant )
{
//this gives you half of the 'heightMap' length
int newSize = heightMap.length >> 1;
float[][] newHeightMap = new float[ newSize ][ newSize ];
//its your offsets, indicating from what place to start iteration
int xOffset = 0;
int yOffset = newSize;
//its max values to reach while iterating
int xRestriction = newSize;
int yRestriction = heightMap.length;
//setting up params according to 'quadrant'
switch ( quadrant )
{
case BottomRight:
xOffset = newSize;
yOffset = newSize;
xRestriction = heightMap.length;
break;
case TopLeft:
yOffset = 0;
yRestriction = newSize;
break;
case TopRight:
yOffset = 0;
xOffset = newSize;
xRestriction = heightMap.length;
yRestriction = newSize;
break;
default:
break;
}
//counters not to reach new array bounds
int xCount = 0, yCount = 0;
for ( int y = yOffset; y < yRestriction; y++ )
{
//taking row at 'y' position
float[] row = heightMap[ y ];
for ( int x = xOffset; x < xRestriction; x++ )
{
//taking value from 'y' row at 'x' position.
float value = row[ x ];
//set fetched value to new map.
newHeightMap[ yCount ][ xCount ] = value;
//increase x position, but do not touch row
xCount++;
}
//new row - new 'x' position
xCount = 0;
yCount++;
}
return newHeightMap;
}
This implementation shows you :
| 1 1 |
| 1 1 |
To change it - change main method.