Beginner question:
I create some random points in java script. How can I later "call" each single one of them? I believe it was called an Object (the list of all points), where I can manipulate their position in the list or "pull" the ones I need. How do I do that in js?
what is the line:
var dots = []; for?
The comment after that was added from another person and I don't get it.
How can I make a line between two points (let's say the first and second from the list - the 0 and the 1) in Three.js?
More complicated question: I create here X (in this case 20) random points in java script. I am trying to figure out a way to randomly create faces using groups of 3 points and form a mesh.
Well, not so randomly, because I need a result, where all faces bind together in a continuous mesh. The result should look like a terrain poly-surface made out of the many vertex points.
What rules should be applied?
var numpoints = 20;
var dots = []; //If you want to use for other task
for (var i = 0 ; i < numpoints ; i++) {
var x = Math.random() * (80 - 1) + 1 //Math.random() * (max - min) + min
var y = Math.random() * (80 - 1) + 1
var z = Math.random() * (10 - 1) + 1
var dotGeometry = new THREE.Geometry();
dots.push(dotGeometry);
dotGeometry.vertices.push(new THREE.Vector3(x, y, z));
var dotMaterial = new THREE.PointCloudMaterial( { size: 3, sizeAttenuation: false, color: 0xFF0000 });
var dot = new THREE.PointCloud( dotGeometry, dotMaterial);
scene.add(dot);
}
To get a random element from array:
function randChoice(array){
return array[Math.floor(Math.random()*array.length)];
};
There are 2 method to generate a mesh from random points that I know of:
convex hull and alpha shape. Creating a mesh by repeating picking 3 random points would almost surely result in spaghetti.
If you just want to generate terrain like in the picture, there is terrain generation from heightmap.
Related
I'm working on a simulation of the famous Prague Astronomical Clock: https://shetline.com/orloj/
I've already produced a lat/lon grid in a crude way, by 2-d drawing lines onto the image which is used for the surface of the globe, but this method doesn't work very well:
(Yes, the globe is supposed to be upside down, as it is on the original clock.)
The grid lines are very jagged this way, and they fade out near the poles because of the scaling near the poles of the original rectangular map image.
I'm trying to find a tutorial on how to do a simple bit of decoration like this by using Three.js/WebGL constructs, but no luck so far. I can get a bit of a start on what I want with the following code:
this.camera = new PerspectiveCamera(FIELD_OF_VIEW, 1);
this.scene = new Scene();
const globe = new SphereGeometry(GLOBE_RADIUS, 50, 50);
globe.rotateY(-PI / 2);
this.globeMesh = new Mesh(globe, new MeshBasicMaterial({ map: new CanvasTexture(Globe.mapCanvas) }));
this.renderer = new WebGLRenderer({ alpha: true });
this.renderer.setSize(GLOBE_PIXEL_SIZE, GLOBE_PIXEL_SIZE);
this.rendererHost.appendChild(this.renderer.domElement);
this.scene.add(this.globeMesh);
const circle = new Line(new CircleGeometry(GLOBE_RADIUS + 0.1, 50),
new LineBasicMaterial({ color: GRID_COLOR, linewidth: 20 }));
this.globeMesh.add(circle);
...by adding a circle, and making the edges of the circle poke out just a bit beyond the surface of the globe, but the result is only a very faint line that doesn't respond to my attempts to make the line thicker. I've also tried something similar with a torus floating just above the surface of the globe, but that doesn't work very well either.
What I want is some sort of additional layer for the lines, or a composite material that combines my pixel image with geometrically-defined lines (rather than painted lines), but I'm not finding the Three.js documentation very clear for figuring out how to do this.
I'm not even sure what kind of geometric model to use for these lines. They're all circles of a sort, but to have a real visual extent it seems the geometry would need to be some sort of globe-hugging, narrow, thin ribbons resting on the surface of the globe.
I ended up using (very, very short) cylinders as my lines of longitude and latitude, floating ever so slightly above the surface of the globe. It seems like anything composed of true line objects can't be guaranteed to honor linewidth, and I didn't want to settle for always-one-pixel lines.
The lines of longitude are simple hollow cylinders, as is the equator. All of the other lines of latitude are flared cylinders, wider at one end to conform to the shape of the globe.
const LINE_THICKNESS = 0.03;
const HAG = 0.01; // Sleight distance above globe that longitude/latitude lines are drawn.
// ...
// Lines of longitude
for (let n = 0; n < 24; ++n) {
const line = new CylinderGeometry(GLOBE_RADIUS + HAG, GLOBE_RADIUS + HAG, LINE_THICKNESS, 50, 1, true);
line.translate(0, -LINE_THICKNESS / 2, 0);
line.rotateX(PI / 2);
line.rotateY(n * PI / 12);
const mesh = new Mesh(line, new MeshBasicMaterial({ color: GRID_COLOR }));
this.globeMesh.add(mesh);
}
// Lines of latitude
for (let n = 1; n < 12; ++n) {
const lat = (n - 6) * PI / 12;
const r = GLOBE_RADIUS * cos(lat);
const y = GLOBE_RADIUS * sin(lat);
const r1 = r - LINE_THICKNESS * sin(lat) / 2;
const r2 = r + LINE_THICKNESS * sin(lat) / 2;
const line = new CylinderGeometry(r1 + HAG, r2 + HAG, cos(lat) * LINE_THICKNESS, 50, 8, true);
line.translate(0, -cos(lat) * LINE_THICKNESS / 2 + y, 0);
const mesh = new Mesh(line, new MeshBasicMaterial({ color: GRID_COLOR }));
this.globeMesh.add(mesh);
}
Perhaps there is a better way where the lines are part of some sort of texture applied to the globe, but this is at least giving satisfactory results for now.
I'm trying to create a 2d circle procedurally with uniform faces like so.
Normally, I would create it with a triangle fan structure, but I need faces to be roughly identical. I looked for examples, but I could only find "cube to sphere" examples. A compromise could be something similar to this :
Could you help me finding a way to draw this structure? I'd like to do it in C# but js or even pseudo code would do!
Thanks a lot
You got me interested with your question, and I think I've got the solution you were looking for. Here is how we can create a topology that you desired:
1) We start with a hexagon. Why hexagon and not other shape? Because hexagon is the only magic shape with its radius equal too the length of its side. We will call this radius R. We will now try to create a shape that resembles circle and is made of triangles with side length approximately R.
2) Now imagine some concentric circles, with radius R, 2R, 3R and so on - the more, the higher is the resolution.
3) Circle number 1 has radius R. We will now replace that circle with a hexagon with radius R.
4) We will now add more nodes on second circle to expand our hexagon. What is the circumference of circle number N? It is 2PiRN. Now we want to divide it into X edges of length approximately R. Hence X=2PiN, which is approximately 6N. So we will divide first circle into 6 edges (hexagon), second one into 12, then 18, 24 and so on.
5) Now we have lots of circles divided into edges. We now need to connect edges into triangles. How do we build triangles between circle N (outer) and N-1 (inner)? Outer circle has 6 more edges than the inner one. If they had identical number of vertices, we could connect them with quads. But they don't. So, we will still try to build quads, but for each N quads we build, we will need to add 1 triangle. Each quad uses 2 vertices from inner and 2 vertices from outer circle. Each triangle uses 2 vertices from the outer circle and only 1 from inner, thus compensating the excess of vertices.
6) And now at last, there is some tested sample code that does what you need. It will generate a circle with uniform topology, with center point at origin and radius of 1, divided into *resolution sub circles. It could use some minor performance optimization (that's out of scope for now), but all in all it should do the job.
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class UniformCirclePlane : MonoBehaviour {
public int resolution = 4;
// Use this for initialization
void Start() {
GetComponent<MeshFilter>().mesh = GenerateCircle(resolution);
}
// Update is called once per frame
void Update() {
}
// Get the index of point number 'x' in circle number 'c'
static int GetPointIndex(int c, int x) {
if (c < 0) return 0; // In case of center point
x = x % ((c + 1) * 6); // Make the point index circular
// Explanation: index = number of points in previous circles + central point + x
// hence: (0+1+2+...+c)*6+x+1 = ((c/2)*(c+1))*6+x+1 = 3*c*(c+1)+x+1
return (3 * c * (c + 1) + x + 1);
}
public static Mesh GenerateCircle(int res) {
float d = 1f / res;
var vtc = new List<Vector3>();
vtc.Add(Vector3.zero); // Start with only center point
var tris = new List<int>();
// First pass => build vertices
for (int circ = 0; circ < res; ++circ) {
float angleStep = (Mathf.PI * 2f) / ((circ + 1) * 6);
for (int point = 0; point < (circ + 1) * 6; ++point) {
vtc.Add(new Vector2(
Mathf.Cos(angleStep * point),
Mathf.Sin(angleStep * point)) * d * (circ + 1));
}
}
// Second pass => connect vertices into triangles
for (int circ = 0; circ < res; ++circ) {
for (int point = 0, other = 0; point < (circ + 1) * 6; ++point) {
if (point % (circ + 1) != 0) {
// Create 2 triangles
tris.Add(GetPointIndex(circ - 1, other + 1));
tris.Add(GetPointIndex(circ - 1, other));
tris.Add(GetPointIndex(circ, point));
tris.Add(GetPointIndex(circ, point));
tris.Add(GetPointIndex(circ, point + 1));
tris.Add(GetPointIndex(circ - 1, other + 1));
++other;
} else {
// Create 1 inverse triange
tris.Add(GetPointIndex(circ, point));
tris.Add(GetPointIndex(circ, point + 1));
tris.Add(GetPointIndex(circ - 1, other));
// Do not move to the next point in the smaller circle
}
}
}
// Create the mesh
var m = new Mesh();
m.SetVertices(vtc);
m.SetTriangles(tris, 0);
m.RecalculateNormals();
m.UploadMeshData(true);
return m;
}
}
Final Result:
I've been researching and playing with examples of particle clouds in Three.js. Most use shape geometries to define a field of particles, or parameters for distributing them randomly throughout the field of view. What I would like to do is create a particle cloud in which each particle has a relative proximity to an invisible vector path. For example, if I defined a lightly curved vector path, all the particles might float within a consistent radius along that invisible and then maybe taper toward the ends to form a hotdog-shaped cloud of particles. So, I know how to create particles and I know how to create vector paths, how do I link these two things together? Thanks!
You can define the path using two points. Let those points be p and q and let v = p - q. Any point M lying on the path must satisfy the vector equation
M = (1 - lambda) * p + lambda * q
for some 0 <= lambda <= 1. Thus, you can generate a random point on the path by generating a random lambda and using its value in the equation above:
// p and q are instances of THREE.Vector3
function pointOnPath(p, q) {
var lambda = Math.random();
var scaledp = (new THREE.Vector3()).copy(p).multiplyScalar(1 - lambda);
var scaleq = (new THREE.Vector3()).copy(q).multiplyScalar(lambda);
var result = (new THREE.Vector3()).addVectors(scaledp, scaledq);
return result;
}
Next, you want to modify the computed coordinates with some small radius so that they circle around the path. You do that by adding a small vector offset. How do we compute that vector then?
The vector we are after lies in a plane that's perpendicular to the line from p to q. There are an infinite number of vectors satisfying the above condition, two of them being e1 = (v.y, -v.x, 0) and e2 = (v.z, 0, -v.x). Any vector of the form lambda * e1 + mu * e2 will also be perpendicular to the v. Thus, we need only generate lambda and mu and everything's ready.
NOTE: lambda and mu must be random numbers in the interval [-1; 1], not [0; 1]. Since we are normalizing the offset vector, the interval [-0.5; 0.5] will suffice because normalization will map it to [-1; 1]
function getVectorOffset(p, q, radius) {
var v = (new THREE.Vector3()).subVectors(q, p);
v.normalize();
var e1 = new THREE.Vector3(v.y, -v.x, 0),
e2 = new THREE.Vector3(v.z, 0, -v.x);
e1.normalize();
e2.normalize();
var lambda = Math.random() - 0.5,
mu = Math.random() - 0.5;
var offset = e1.multiplyScalar(lambda).add(e2.multiplyScalar(mu));
offset.normalize();
offset.multiplyScalar(radius) // multiply the compute offset by the radius you'd like it to circle around
return offset;
}
Finally, to generate your desired point:
function pointOnHotDog(p, q, radius) {
return pointOnPath(p, q).add(getVectorOffset(p, q, radius));
}
Here's a working jsfiddle
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Did you ever played the "Tank wars" game?
I'm programming this game with JavaScript + Canvas (for a personal challenge), and what I need is an algorithm for generating that random green land every time I start the game, but I'm not too good at maths, so I can't do it myself.
I don't want someone to give me the code, I only want the idea for the algorithm.
Thanks!
(9 segments)
Fiddle demo
(7 segments)
The main generation function look like this:
var numOfSegments = 9; // split horizontal space
var segment = canvas.width / numOfSegments; // calc width of each segment
var points = [], calcedPoints;
var variations = 0.22; // adjust this: lower = less variations
var i;
//produce some random heights across the canvas
for(i=0; i < numOfSegments + 1; i++) {
points.push(segment * i);
points.push(canvas.height / 2.8 + canvas.height * variations * Math.random());
}
//render the landscape
ctx.beginPath();
ctx.moveTo(canvas.width, canvas.height);
ctx.lineTo(0, canvas.height);
calcedPoints = ctx.curve(points); // see below
ctx.closePath();
ctx.fillStyle = 'green';
ctx.fill();
The curve() function is a separate function which generate a cardinal spline. In here you can modify it to also store tension values to make more spikes. You can also used the generated points as a basis for where and at what angle the tanks will move at.
The function for cardinal spline:
CanvasRenderingContext2D.prototype.curve = function(pts, tension, numOfSegments) {
tension = (tension != 'undefined') ? tension : 0.5;
numOfSegments = numOfSegments ? numOfSegments : 16;
var _pts = [], res = [], t, i, l, r = 0,
x, y, t1x, t2x, t1y, t2y,
c1, c2, c3, c4, st, st2, st3, st23, st32;
_pts = pts.concat();
_pts.unshift(pts[1]);
_pts.unshift(pts[0]);
_pts.push(pts[pts.length - 2]);
_pts.push(pts[pts.length - 1]);
l = (_pts.length - 4);
for (i = 2; i < l; i+=2) {
//overrides and modifies tension for each segment.
tension = 1 * Math.random() - 0.3;
for (t = 0; t <= numOfSegments; t++) {
t1x = (_pts[i+2] - _pts[i-2]) * tension;
t2x = (_pts[i+4] - _pts[i]) * tension;
t1y = (_pts[i+3] - _pts[i-1]) * tension;
t2y = (_pts[i+5] - _pts[i+1]) * tension;
st = t / numOfSegments;
st2 = st * st;
st3 = st2 * st;
st23 = st3 * 2;
st32 = st2 * 3;
c1 = st23 - st32 + 1;
c2 = -(st23) + st32;
c3 = st3 - 2 * st2 + st;
c4 = st3 - st2;
x = c1 * _pts[i] + c2 * _pts[i+2] + c3 * t1x + c4 * t2x;
y = c1 * _pts[i+1] + c2 * _pts[i+3] + c3 * t1y + c4 * t2y;
res[r++] = x;
res[r++] = y;
} //for t
} //for i
l = res.length;
for(i=0;i<l;i+=2) this.lineTo(res[i], res[i+1]);
return res; //return calculated points
}
Look into perlin noise generation, this in combination with a good smoothing algorithm can produce some pretty good terrain, and is fairly quick. There is a reference version of the code kicking around the net somewhere, which should provide you with a fairly hefty headstart
First you need a point that is random y (between 55,65); got x=0
So this is the origin point for the green, lets keep it as x1,y1 (x1 always 0).
Then you need a random integer between 30 to 40. This is x2. And a random y which is in the range y1 + 8 to y1 + 20.
Then x3 and y3 on same principle (lets call it formula type 1)
Now you need to first get a random either -1 or 1, this will be directions of y4. So y4 can go higher than y3 or lower ... this will be formula type 2.
You need to keep a max and min y for a new y, if it crosses that then go the other way -> this will be a correction type formula 3.
Xn keeps increasing till its >= width of board.
Join the lines in a eclipses ... and looks like web searches is the way to go !
I am sure there are a lot of coded libraries that you could use to make this easy. But if you are trying to code this by yourself, here is my idea.
You need to define terrain from everything else. So every part of your environment is a cluster for example. You need to define how are separated these clusters, by nodes(points) for example.
You can create a polygon from a sequence of points, and this polygon can become whatever you want, in this case terrain.
See that on the image you passed, there are peaks, those are the nodes (points). Remember to define also nodes on the borders of your environment.
There are surely a novel, written algorithms, either fractal as #DesertIvy pointed out or others, maybe there are libraries as well, but if you want toi generate what is in the image, it can be pretty straightforward, since it is just (slightly curved) lines between points. If you do it in phases, not trying to be correct at once, it is easy:
Split x region of your game screen into sections (with some minimal and maximal width) using random (you may be slightly off in last section, but it does not matter as much, I think). Remember the x-es where sections meet (including the ones at game screen border)
Prepare some data structure to include y-s as well, on previously remembered x-s. Start with leftmost.y = 0, slope = Math.random()-0.5;.
Generate each next undefined y beginning with 1: right.y = left.y + slope * (right.x-left.x); as well as update slope after each y: slope += Math.random()-0.5;. Do not bother, for the moment, if it all fits into game screen.
If you want arcs, you can generate "curviness" parameter for each section randomly which represent how much the middle of the line is bumped compared to straight lines.
Fit the ys into the game screen: first find maximal and minimal generated y (mingeny, maxgeny) (you can track this while generating in point 4). Choose where the max and min y in game screen (minscry, maxscry) (say at the top fourth and at the bottom fourth). Then transform generated ys so that it spans between minscry and maxscry: for every point, do apoint.y = minscry + (maxscry-minscry)/(maxgeny-mingeny)*(apoint.y-mingeny).
Now use lines between [x,y] points as a terrain, if you want to use "curviness", than add curvemodifier to y for any particular x in a section between leftx and rightx. The arc need not to be a circle: I would suggest a parabola or cosine which are easy to produce: var middle = (left.x+right.x)/2; var excess = (x-left)/(middle-left); and then either var curvemodifier = curviness * (1-excess*excess); or var curvemodifier = curviness * Math.cos(Math.PI/2*excess).
Wow...At one point I was totally addicted to tank wars.
Since you are on a learning adventure...
You might also learn about the context.globalCompositeOperation.
This canvas operation will let you grab an image of actual grass and composite it into your game.
You can randomize the grass appearance by changing the x/y of your drawImage();
Yes, the actual grass would probably be too distracting to include in your finished game, but learning about compositing would be valuable knowledge to have.
...and +1 for the question: Good for you in challenging yourself !
I'm sorry to say that Math really isn't my strong suit. Normally I can get by, but this has got me totally stumped.
I'm trying to code up a quiz results screen in HTML/CSS/Javascript.
On my interface, I have a semicircle (the right hemisphere of a target).
I have a range of 'scores' (integers out of 100 - so 50, 80, 90 etc.).
I need to plot these points on the semicircle to be n% away from the centre, where n is the value of each score - the higher the score, the closer to the centre of the target the point will appear.
I know how wide my semicircle is, and have already handled the conversion of the % values so that the higher ones appear closer to the centre while the lower ones appear further out.
What I can't wrap my head around is plotting these points on a line that travels out from the centre point (x = 0, y = target height/2) of the target at a random angle (so the points don't overlap).
Any suggestions are gratefully received!
Do you have an example of what you want this to look like? It sounds like you want to divide up the circle into N slices where N is the number of points you need to display, then plot the points along each of those radii. So you might have something like:
Edit: code was rotating about the origin, not the circle specified
var scores = [];
//...
//assume scores is an array of distances from the center of the circle
var points = [];
var interval = 2 * Math.PI / N;
var angle;
for (var i = 0; i < N; i++) {
angle = interval * i;
//assume (cx, cy) are the coordinates of the center of your circle
points.push({
x: scores[i] * Math.cos(angle) + cx,
y: scores[i] * Math.sin(angle) + cy
});
}
Then you can plot points however you see fit.
After much headscratching, I managed to arrive at this solution (with the help of a colleague who's much, much better at this kind of thing than me):
(arr_result is an array containing IDs and scores - scores are percentages of 100)
for (var i = 0; i < arr_result.length; i++){
var angle = angleArray[i]; // this is an array of angles (randomised) - points around the edge of the semicircle
var radius = 150; // width of the semicircle
var deadZone = 25 // to make matters complicated, the circle has a 'dead zone' in the centre which we want to discount
var maxScore = 100
var score = parseInt(arr_result[i]['score'], 10)
var alpha = angle * Math.PI
var distance = (maxScore-score)/maxScore*(radius-deadZone) + deadZone
var x = distance * Math.sin(alpha)
var y = radius + distance * Math.cos(alpha)
$('#marker_' + arr_result[i]['id'], templateCode).css({ // target a specific marker and move it using jQuery
'left' : pointX,
'top': pointY
});
}
I've omitted the code for generating the array of angles and randomising that array - that's only needed for presentational purposes so the markers don't overlap.
I also do some weird things with the co-ordinates before I move the markers (again, this has been omitted) as I want the point to be at the bottom-centre of the marker rather than the top-left.