Here is my problem:
I have a bunch of circles that I need to display inside a canvas.
There are an arbitrary number of circles, each with a predefined radius.
The summed area of circles is always smaller than the area of the canvas.
I want to position the circles so that they take up the maximal space available inside the canvas, without touching each other. My goal is to achieve a visually pleasing effect where the circles appear well distributed inside the canvas. I don't know if this is really "space filling", as my goal is not to minimize the distance between elements, but rather to maximize it.
Here is an example of what I am trying to achieve:
My first "brute force" idea was the following:
For each circle: calculate the shortest distance between its border and each other circle's border; sum all of these distances, call that X.
Calculate the sum of all X's.
Randomly change the distances between the circles.
Redo 1-3 for a preset number of iterations and take the maximal value obtained at step (2).
However, this does not seem elegant; I'm sure there is a better way to do it. Is there any existing algorithm to achieve such a layout? Is there any existing library that I could use (JavaScript or Ruby) to achieve this?
Edit
Here is a Javascript version of the accepted answer, which uses Raphael to draw the circles.
I would try to insert sphere after sphere (largest first). Each one is added in the largest available space, with some random jitter.
One relatively easy way to find (more or less) the largest available space, is to imagine a grid of points on your view and store for each grid point (in a 2D array) the closest distance to any item: edge or sphere, whichever is closest. This array is updated as each new sphere is added.
To add a new sphere, just take the grid point with highest distance and apply some random jitter (you actually know how much you can jitter, because you know the distance to the closest item). (I would randomize not more than (d-r)/2 where d is the distance in the array and r is the radius of the sphere to add.
Updating this array after adding another circle is no rocket science: you calculate for each grid point the distance to newly added sphere and replace the stored value if that was larger.
It is possible that your grid is too coarse, and you can't add any more circle (when the 2D array contains no distances larger than the radius of the circle to add). Then you have to increase (e.g. double) the grid resolution before continuing.
Here are some result of this implementation (it took me about 100 lines of code)
100 Circles of varying size
500 Circles of varying size
100 Circles of same size
And here is some rough C++ code (just the algorithm, don't expect this to compile)
// INITIALIZATION
// Dimension of canvas
float width = 768;
float height = 1004;
// The algorithm creates a grid on the canvas
float gridSize=10;
int gridColumns, gridRows;
float *dist;
void initDistances()
{
// Determine grid dimensions and allocate array
gridColumns = width/gridSize;
gridRows = height/gridSize;
// We store a 2D array as a 1D array:
dist = new float[ gridColumns * gridRows ];
// Init dist array with shortest distances to the edges
float y = gridSize/2.0;
for (int row=0; row<gridRows; row++)
{
float distanceFromTop = y;
float distanceFromBottom = height-y;
for (int col=0; col<gridColumns; col++)
{
int i = row*gridColumns+col;
dist[i]=(distanceFromTop<distanceFromBottom?distanceFromTop:distanceFromBottom);
}
y+=gridSize;
}
float x = gridSize/2.0;
for (int col=0; col<gridColumns; col++)
{
float distanceFromLeft = x;
float distanceFromRight = width-x;
for (int row=0; row<gridRows; row++)
{
int i = row*gridColumns+col;
if (dist[i]>distanceFromLeft) dist[i] = distanceFromLeft;
if (dist[i]>distanceFromRight) dist[i] = distanceFromRight;
}
x+=gridSize;
}
}
void drawCircles()
{
for (int circle = 0; circle<getNrOfCircles(); circle++)
{
// We assume circles are sorted large to small!
float radius = getRadiusOfCircle( circle );
// Find gridpoint with largest distance from anything
int i=0;
int maxR = 0;
int maxC = 0;
float maxDist = dist[0];
for (int r=0; r<gridRows; r++)
for (int c=0; c<gridColumns; c++)
{
if (maxDist<dist[i]) {
maxR= r; maxC= c; maxDist = dist[i];
}
i++;
}
// Calculate position of grid point
float x = gridSize/2.0 + maxC*gridSize;
float y = gridSize/2.0 + maxR*gridSize;
// Apply some random Jitter
float offset = (maxDist-radius)/2.0;
x += (rand()/(float)RAND_MAX - 0.5) * 2 * offset;
y += (rand()/(float)RAND_MAX - 0.5) * 2 * offset;
drawCircle(x,y,radius);
// Update Distance array with new circle;
i=0;
float yy = gridSize/2.0;
for (int r=0; r<gridRows; r++)
{
float xx = gridSize/2.0;
for (int c=0; c<gridColumns; c++)
{
float d2 = (xx-x)*(xx-x)+(yy-y)*(yy-y);
// Naive implementation
// float d = sqrt(d2) - radius;
// if (dist[i]>d) dist[i] = d;
// Optimized implementation (no unnecessary sqrt)
float prev2 = dist[i]+radius;
prev2 *= prev2;
if (prev2 > d2)
{
float d = sqrt(d2) - radius;
if (dist[i]>d) dist[i] = d;
}
xx += gridSize;
i++;
}
yy += gridSize;
}
}
}
Perhaps some application of force-directed layout would be useful.
Since your goal is just to "achieve a pleasing effect", not solve a math problem, you should try the simplest algorithm which could work first and see if it looks good. There should be no need to use very complex math.
I understand that you want the spheres to "fill" the available space, not leaving large empty areas while other areas are crowded. You also want the layout to appear random -- not lined up on a grid or anything like that.
The obvious, dead-simple way to achieve that is simply to place the spheres one by one, in random locations. If one lands on top of an already-placed sphere, generate another random location until you find one where it fits.
It appears there are about 40 spheres in the image shown. The chances of 40 spheres all landing in the same area of the image, leaving the rest of the image empty, is very, very small. As the number of spheres increases, the chances of getting a very unbalanced layout will become close to zero.
Try that first and see if it meets your needs. If it is not "even" enough, you should be able to use some very simple math to bias the randomly chosen locations in favor of choosing empty areas. There should be no need to use complex algorithms.
Related
I'm trying to figure out how I can - or at least where I can read more about it - create a circle / square gradient and store the data in a 2d array.
My goal is to use the gradient with simplex-noise to create a procedural island by subtracting the gradient from the generated noise.
Here are picture references of the gradients that I'm talking about and trying to achieve:
The use of a gradient with noise is explained e.g. in a blog post I found here: https://medium.com/#travall/procedural-2d-island-generation-noise-functions-13976bddeaf9
But it only mentions creating the gradient without any details on how it's supposed to be done. I have tried looking around but I have been unsuccessful in finding what I'm looking for.
I would appreciate any tips or pointers in the right direction.
I've been trying to do pretty much the same thing.
For the square one, you can find the answer here: 2D Array Gradient Generation in Unity
And for the circle one: You can generate it based on the distance of each "pixel" from the centre of the array.
this code is in C#, because that's what I'm using, but I think it's basic enough the be easily converted.
int arrayWidth, arrayHeight;
float[,] gradientArray = new var[arrayWidth, arrayHeight]
int centerX = arrayWidth / 2 -1; //find center of the array
int centerY = arrayHeight / 2 -1; //find center of the array
for (int x = 0; x < arrayWidth; x++)
{
for (int y = 0; y < arrayHeight; y++)
{
float distanceX = (centerX - x) * (centerX - x);
float distanceY = (centerY - y) * (centerY - y);
//find distance from center
float distanceToCenter = Mathf.Sqrt(distanceX + distanceY);
//divide distance by one of the sides (width or height).
//by messing with this line you can easily alter the gradient
distanceToCenter = distanceToCenter / arrayHeight;
gradientArray[x, y] = distanceToCenter; //set value
}
}
Given a simple (non intersecting) polygon such as a floor plan (with doors between rooms missing so as to give 1 simple uninterrupted boundary). How can I find all areas within the polygon reachable from a (x, y) point (within or on the boundary of the polygon)? I’d ideally like from this to return a polygon which could be then overlaid to show all reachable areas.
I’ve considered A* search type methods where I would search the shortest path iterating over all points located on the polygon perimeter (as destination) then plotting new points at the set distance limit along the shortest path polyline to give a new polygon hull.
I’ve also thought about wave propagation as a method.
I’m wondering if I’m missing something obvious here library/method wise and if anyone has any other ideas of how I might achieve this.
Given a polygon like this:
I am creating a polygon showing internal space (excluding internal doors) like this:
This is the part my question refers to. I would like to find all reachable points within the polygon (shown in red as a new polygon) from a given point on the polygon boundary at a set maximum travel distance from that point (donated below with a red square) like this:
Triangulate the polygon.
If your chosen origin vertex is not a polygon vertex (i.e. it's a point within the polygon), include this point as a steiner point in the triangulation.
Build an undirected weighted graph from the triangulation's vertices and constrained edges (where graph edge weights are triangulation edge lengths).
Constrained edges are edges that do not lie outside the polygon.
Compute shortest paths from your origin vertex to all other vertices in the graph (using Dijkstra or Bellman-Ford algorithm). The path distance from the origin to a vertex is that vertex's Z value.
Update/create another triangulation mesh, using the same vertices with the Z values calculated from before.
Calculate distance values for every pixel by interpolating within/between triangles (interpolating based on each triangle's vertices' Z values). This is readily done by using barycentric coordinates. The interpolation output for a coordinate gives you the distance from the origin position to that coordinate.
For the illustrations below I used the NaturalNeighborInterpolator from the TinFour Java library. It eases the interpolation step by operating on a triangulation — I simply call the interpolator at each pixel coordinate and finally mask the output with the original polygon (since it effectively computes over the convex hull of the polygon).
Illustrative Code
The graph and Dijkstra implementation use the JGraphT library.
IncrementalTin tin = new IncrementalTin();
tin.add(listOfPolygonVertices); // triangulates upon insertion
Graph<Vertex, IQuadEdge> graph = new DefaultUndirectedWeightedGraph<>(IQuadEdge.class);
tin.edges().forEach(e -> {
if (e.isConstrainedRegionInterior() || e.isConstrainedRegionBorder()) {
graph.addVertex(e.getA());
graph.addVertex(e.getB());
graph.addEdge(e.getA(), e.getB(), e);
graph.setEdgeWeight(e.getA(), e.getB(), e.getLength());
}
});
DijkstraShortestPath<Vertex, IQuadEdge> shortestPaths = new DijkstraShortestPath<>(graph);
Vertex originVertex = tin.getNavigator().getNearestVertex(originX, originY);
var paths = shortestPaths.getPaths(originVertex);
IncrementalTin distanceMesh = new IncrementalTin();
for (Vertex v : graph.vertexSet()) {
var d = paths.getWeight(v);
distanceMesh.add(new Vertex(v.x, v.y, d)); // add vertices with Z to new tin
}
IInterpolatorOverTin interpolator = new NaturalNeighborInterpolator(distanceMesh);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
double z = interpolator.interpolate(x, y, null);
if (!Double.isNaN(z)) {
pixels[y * width + x] = someColour;
}
}
}
Update: Distance Boundary Vertices
If you want merely the distance boundary line, you can forgo step 5. Instead, compute (if applicable) the isoline for each triangle, based on the desired distance. If an isoline crosses a triangle (as in the illustration below), it will intersect with two of the triangle's edges — plotting a line segment between each pair of intersecting points for every such triangle give you a distance boundary.
Call a method (such as below) for each edge for every constrained triangle in the triangulation. If the distance isoline crosses the triangle, you'll get two points of intersection for that triangle; otherwise none.
/**
* Compute isoline vertex (if applicable) for a triangle side given by two vertices
*/
Vertex isoVertex(Vertex a, Vertex b, double d) {
Vertex min, max;
if (a.getZ() > b.getZ()) {
max = a;
min = b;
} else {
max = b;
min = a;
}
if (d > min.getZ() && d < max.getZ()) {
double diff = max.getZ() - min.getZ();
double numerator = d - min.getZ();
double fract = numerator / diff;
double xDiff = max.getX() - min.getX();
double yDiff = max.getY() - min.getY();
return new Vertex(min.getX() + fract * xDiff, min.getY() + fract * yDiff);
}
return null;
}
I have a plane with a detail of 400 by 400.
When defining the y positions of all of the vertices, I do this.
var position = floorGeometry.attributes.position;
for ( var i = 0; i <= complexity + 1; i ++ ) {
for (var i2 = 0; i2 <= complexity; i2 ++) {
vertex.fromBufferAttribute( position, i * complexity + i2);
var x = vertex.x;
var y = vertex.z;
vertex.y = noise(x,y)
position.setXYZ(i * complexity + i2, vertex.x, vertex.y, vertex.z );
}
}
Complexity represents the detail of the plane.
As you can see... I use geometry.attributes.position to access the vertices, but it is important to note that this stores all of the "sqaure" coordinates
But when it comes to the color attribute... it actually uses the points (and expects an array) of each and every vertex of the tris that make up the plane in a specific order...
What I am doing is making an array of colors (3 elements per vertex representing rgb) and then trying to add it as an attribute to the geometry, and I am trying to make vertices of different heights different colors. For example
count = floorGeometry.attributes.position.count;
var colors = [];
for ( var i = 0; i < count; i ++ ) {
vertex.fromBufferAttribute( position, Math.floor(i)); //<---- NOTE
if (vertex.y > 500) {
colors.push(1,0,0);
}
else colors.push(0,1,0);
}
At the point in the code with the comment "NOTE" I dont know what i am doing here in terms of turing an index from that square system to the color attributes tri based vertex system.
Any ideas? Should I try to access the vertices of the tri based system instead? Is there a mathematical way to do this correctly?
The simple solution is to not use:
vertex.fromBufferAttribute( position, index );
because that uses the square system I discussed in my question, instead use:
geometry.attributes.position.getY(i);
or .getX(i) or .getZ(i) because these use the vertices of the tris!
I'm currently working on a JavaScript project which involves 3D point rotation. Using simple trigonometry, I have sketched my own 3D point rotation algorithm, but I have to deal with a huge amount of data (+300 000 points) and my function slows down the runtime substantially (the FPS rate drops from 60 to 12).
I'm looking for another 3D point rotation ALGORITHM which...
rotates points around origin by X, Y and Z axes' angles (PITCH, YAW and ROLL)
has a quite good efficiency (don't worry about this too much, it will always be faster than mine)
is written in JavaScript, C-like code or pseudo-code
Any help will be greatly appreciated :)
Context: 3D point cloud renderer (I want every point to be rotated)
A rotated vector can be described as a product of a rotation matrix with that vector. The German Wikipedia page on pitch, roll and yaw describes the rotation matrix for given Euler rotation angles.
With that information, the rotation of all points with the same angles can be written as JavaScript function, where the points array is global:
function rotate(pitch, roll, yaw) {
var cosa = Math.cos(yaw);
var sina = Math.sin(yaw);
var cosb = Math.cos(pitch);
var sinb = Math.sin(pitch);
var cosc = Math.cos(roll);
var sinc = Math.sin(roll);
var Axx = cosa*cosb;
var Axy = cosa*sinb*sinc - sina*cosc;
var Axz = cosa*sinb*cosc + sina*sinc;
var Ayx = sina*cosb;
var Ayy = sina*sinb*sinc + cosa*cosc;
var Ayz = sina*sinb*cosc - cosa*sinc;
var Azx = -sinb;
var Azy = cosb*sinc;
var Azz = cosb*cosc;
for (var i = 0; i < points.length; i++) {
var px = points[i].x;
var py = points[i].y;
var pz = points[i].z;
points[i].x = Axx*px + Axy*py + Axz*pz;
points[i].y = Ayx*px + Ayy*py + Ayz*pz;
points[i].z = Azx*px + Azy*py + Azz*pz;
}
}
Most of that is setting up the rotation matrix as described in the article. The last three lines inside the loop are the matrix multiplication. You have made a point of not wanting to get into matrices, but that's hardly intimidating, is it? Sooner or later you will encounter more matrices and you should be prepared to deal with them. The stuff you need – multiplication, mainly – is simple. The more complicated stuff like inverting matrices is not needed for your requirements.
Anyway, that performs reasonably fast for 300,000 points. I was able to rotate a point cloud of that size and render it on a 1000px × 1000px canvas in about 10ms.
From wikipedia:
If you multiply your points by each of these matrices they will be rotated by the amount you want.
For example, if I want to rotate point [1, 0, 0] by 90° around the z axis (in the xy plane), sin(90) = 1 and cos(90) = 0 so you get this:
| 0 -1 0 | |1| |0|
| 1 0 0 | * |0| = |1|
| 0 0 1 | |0| |0|
EDIT: I updated the program with the answer and it works great!
I am making a program (feel free to try it out) that lets users draw polygons which it then triangulates. They can click to add vertices and hit enter to triangulate. Anyways, the algorithm works fine as long as I tell it if the points were drawn in a clockwise or counterclockwise fashion (right now I have it set only to work with clockwise polygons). I have been trying to figure this out for days, but have no idea how to determine whether the points are clockwise or counterclockwise. Try drawing shapes with the program mentioned earlier to get a better idea, you can experience what I am talking about better than I can try to explain it.
Here is how the points are defined:
function Point(x, y) {
this.x = x;
this.y = y;
}
var vertices = [];
// Called on click
function addPoint(mouseX, mouseY) {
vertices.push(new Point(mouseX, mouseY));
}
Here is an image of a clockwise polygon:
Here is an image of a counterclockwise polygon:
If you could help me figure out how to determine the "clockwise-ness" of the points, I would be very grateful!
Compute the polygon area using the shoelace formula, but without the absolute value sign. If the result is positive, the points are ordered counterclockwise, and if negative - clockwise.
function polygonArea() {
var area = 0;
for (var i = 0; i < vertices.length; i++) {
j = (i + 1) % vertices.length;
area += vertices[i].x * vertices[j].y;
area -= vertices[j].x * vertices[i].y;
}
return area / 2;
}
var clockwise = polygonArea() > 0;
In case someone is using three.js the ShapeUtils comes with an inbuilt isClockWise method which internally uses the area method to determine the sign of the calculated area.
isClockWise: function ( pts ) {
return ShapeUtils.area( pts ) < 0;
}
The ShapeUtils.isClockWise Method can be found here.
area: function ( contour ) {
var n = contour.length;
var a = 0.0;
for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
}
return a * 0.5;
},
The ShapeUtils.area Method can be found here.
A general idea would be to take a look at the convex hull of your polygone and guess the orientation from there. However, I think that you do not need to build the whole hull to find the orientation, but just one segment belonging to it.
So:
Find two points of your polygones so that all the other points are on one side of this line.
If all the points are on the left (just check one of the points), it's counterclockwise. If they are on the right, it's clockwise.
Example:
On the top figure: 4-5 let the figure on the right, 5-11 let the figure on the right, ...
On the bottom figure: 6-7 let the figure on the left, 7-14 let the figure on the left, ...
Warning: While "walking" on your polygon, do not restart the numeration, otherwise it will be wrong. On the top figure, 4-(n-1) let the figure on the left!
Your intuitive definition of clockwisedness is not well defined. For example, If I draw a horseshoe:
/---a-b--\
/ _d_c_ \
/ / \ \
| | | |
| | | |
\ \ / /
\ \ / /
-- --
If 0 = a < b < b < d and I look at a and b I would conclude from your description that the shape has been drawn clockwise, but if 0 = c < d < a < b I would conclude that the shape has been drawn anticlockwise. Since both of these scenarios involve the same direction in which the points were drawn, just from different starting points, I can only conclude that your definition is lacking.
The horseshoe I drew isn't the best; the idea is that it is almost a circle with just a small hole at the bottom, to allow the other side to be drawn in the opposite direction.
If you are interested in defining things more strictly, then I suggest something along the following lines:
Considering any finite simple polygon as separating the plane into two distinct areas (one finite and one infinite), we can always consider the finite area to be the interior of the polygon. In such a scenario we define a vertex ordering to be clockwise iff the order of the points runs with the exterior along its right-hand side. This is called curve orientation.
Once you have this more solid definition, implementation can be as simple as counting the winding number. Take the midpoint of any ordered pair, say 0 and 1, take a line segment to the right of the ordered pair (at any angle, say perpendicular), and count how many intersections it has with other line segments: The curve is clockwise iff the number is odd.
This is simple to implement, linear in time O(n), and adds constant space O(1).
This a function function that specialized for OpenLayers. As You Can See The Condition Of Clockwise Polygon Is area<0 This Reference Confirm It.
function IsClockwise(feature)
{
if(feature.geometry==null)return -1;
var vertices=feature.geometry.getVertices();
var area=0;
for (var i = 0; i < (vertices.length); i++)
{
j = (i + 1) % vertices.length;
area += vertices[i].x * vertices[j].y;
area -= vertices[j].x * vertices[i].y;
// console.log(area);
}
return (area < 0);
}