Arranging misshapen circles on the exterior of a sphere

My current project involves utilizing Three.js to position circles of various sizes evenly across the surface of a sphere, inspired by the concept seen in the periodic table of elements example.

Despite extensive research efforts, I've come to the realization that achieving this seemingly simple task is much more complex than anticipated.

For reference, here are some demonstrations showcasing a similar idea:

Vimeo

Picture

circlePack Java applet

I am searching for an algorithm that can facilitate this task efficiently, without requiring an overly high packing ratio. Ideally, this algorithm would be easily implementable in JavaScript for rendering in Three.js, utilizing either the Cartesian or Coordinate system.

The sizes of the circles vary significantly. To illustrate, consider this demonstration using the periodic table code:

Answer №1

Consider this unique approach: implementing an iterative search technique utilizing a simulated repulsive force.

Strategy

To begin, set up the dataset by positioning the circles on the surface in an algorithmic manner for initialization purposes. This initial arrangement does not need to be precise, so a simple periodic table layout will suffice. Additionally, assign each circle a "mass" equivalent to its radius.

Next, start the iteration process in order to converge towards a solution. For each cycle through the primary loop, follow these steps:

  1. Calculate repulsive forces for each circle, resembling the formula for gravitational force but with adjustments: (a) objects are repelled rather than attracted, and (b) adjust the "force constant" according to the model's scale. It may be beneficial to pre-determine a suitable constant or experiment initially to find an optimal value.

  2. Upon determining the total forces on each circle (refer to the n-body problem if needed), displace each circle along the direction of the total force vector, with the vector's magnitude as the distance to traverse. It might be necessary to fine-tune the force constant to ensure movements are less than 5% of the circle's radius initially.

  3. Due to the movements in the previous step, circles may be pushed off the surface of the sphere. Return each circle to the surface, moving towards the sphere's center.

  4. Compute the distance between the circle's previous and new positions to determine the iteration's movement length.

Continue iterating through the main loop until movement length diminishes, indicating stabilized circle positions that meet the criteria. Terminate the loop when the movement length falls below a minute threshold.

Adjustments

To achieve convergence, adjustments to the force calculation may be necessary. The modifications depend on the desired outcome. Initially, modify the force constant. If ineffective, adjust the mass values or alter the exponent in the force calculation. For instance, instead of:

f = ( k * m[i] * m[j] ) / ( r * r );

Consider experimenting with:

f = ( k * m[i] * m[j] ) / pow( r, p );

Explore varying values for p during experimentation.

Additionally, test out alternate algorithms for the initial arrangement.

The level of trial and error will hinge on your specific design objectives.

Answer №2

Here is a starting point for you to work with. This code will help in distributing your spheres randomly along a larger sphere. The goal is to eventually achieve an even distribution by iterating over the initial setup.

// Random point on sphere of radius R
var sphereCenters = []
var numSpheres = 100;
for(var i = 0; i < numSpheres; i++) {
    var R = 1.0;
    var vec = new THREE.Vector3(Math.random(), Math.random(), Math.random()).normalize();
    var sphereCenter = new THREE.Vector3().copy(vec).multiplyScalar(R);
    sphereCenter.radius = Math.random() * 5; // Varying sphere sizes can be added here.
    sphereCenters.push(sphereCenter);

    // Create a Three.js sphere at sphereCenter
    ...
}

To efficiently pack the spheres, run the following code multiple times:

for(var i = 0; i < sphereCenters.length; i++) {
    for(var j = 0; j < sphereCenters.length; j++) {
        if(i === j)
            continue;

        // Calculate the distance between sphereCenters[i] and sphereCenters[j]
        var dist = new THREE.Vector3().copy(sphereCenters[i]).sub(sphereCenters[j]);
        if(dist.length() < sphereSize) {
             // Adjust the position of the sphere to prevent overlap.

             // Determine the required movement distance
             var mDist = sphereSize - dist.length();

             // Shift the sphere in the direction of dist by magnitude mDist
             var mVec = new THREE.Vector3().copy(dist).normalize();
             mVec.multiplyScalar(mDist);

             // Update the position of the sphere
             sphereCenters[i].add(mVec).normalize().multiplyScalar(R);
        }
    }
}

Repeatedly running the adjustment section will gradually move towards the desired distribution. It is essential to find the optimal balance between speed and accuracy by deciding the number of iterations to perform.

Answer №3

To achieve a similar effect to the periodic table of elements, you can utilize the code provided. Instead of rectangles that do not touch, you can create circles by adjusting the code appropriately.

Take a look at the code snippet below:

            var vector = new THREE.Vector3();

            for ( var i = 0, l = objects.length; i < l; i ++ ) {

                var phi = Math.acos( -1 + ( 2 * i ) / l );
                var theta = Math.sqrt( l * Math.PI ) * phi;

                var object = new THREE.Object3D();

                object.position.x = 800 * Math.cos( theta ) * Math.sin( phi );
                object.position.y = 800 * Math.sin( theta ) * Math.sin( phi );
                object.position.z = 800 * Math.cos( phi );

                vector.copy( object.position ).multiplyScalar( 2 );

                object.lookAt( vector );

                targets.sphere.push( object );

            }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can someone provide a description for a field within typedoc documentation?

Here is the code snippet: /** * Description of the class */ export class SomeClass { /** * Description of the field */ message: string; } I have tested it on the TSDoc playground and noticed that there is a summary for the class, but not for it ...

Clicking on the input triggers the appearance of a border using the OnClick function

I am currently developing my own website with a login feature that requires input tags of text-type. I would like to implement a functionality where clicking on these input tags will display a border, even when the mouse is not directly hovering over them. ...

What is the process for uploading a single file and an array of files with varying names using multer?

I am having trouble figuring out how to upload a main image and side images from 2 different file inputs using multer. It seems that multer only accepts one upload per route. How can I work around this issue? I keep getting an unexpected field error when a ...

Exploring the possibilities of utilizing classes in testing scenarios with Vue, Cypress, and Cucumber

I am currently working on setting up e2e tests using Cypress and Cucumber for my project. The application is built with Vue CLI 4.1.1, and I have added the package cypress-cucumber-preprocessor (V1.19.0) via NPM. Update: After extensive research and tes ...

Is there a way to apply a smooth fade-in effect to an object using Three.js animations?

I'm exploring Three.js for the first time and I'd love to implement a fade-in animation when adding a new mesh geometry to my scene. Specifically, I want the material's opacity to smoothly transition from 0 to 1 upon addition. Can anyone gui ...

Change the behavior of a submit button to trigger a custom JavaScript function instead

I am faced with a challenge where I need to override the default functionality of a button in code that cannot be altered. Instead, I must ensure that when the button is clicked, a custom JavaScript method is called rather than submitting the form as it no ...

Ways to transfer information from an axios API to a state in React

I am currently working with an API that consists of an array of objects. My goal is to pass this data into a state in order to display it within a component on a website. First Approach: // Fetches the API data const newData = axios.get(url).then( ...

Trigger event upon variable modification

Currently, I am working on a school project that involves creating a website where users can listen to music together. I have implemented a button that allows the user to listen to the same song another user is currently playing at the right position. Howe ...

Difficulty altering link hover background color

I'm having trouble changing the hover effect background-color on the tweets of this page: Despite my efforts, all the links except for the latest tweets are working perfectly. What am I missing? Here's what I've been trying... <script& ...

Enhance D3 Version 6 Stacked Bar Chart with Spacing and Interactive Features

My bar chart lacks the spacing between bars that I intended to achieve, and the interactive tooltip doesn't show up when hovering over the bars. I could use some assistance with this. The purpose is to display the value of a specific color when hoveri ...

Modifying an image or audio using document.getElementByID("...").src=x+".png" is not effective

I'm having trouble figuring out why it's not working. I've searched online and here, but all I could find were tutorials that didn't include the variable or questions that were too specific. Can someone help me with this? <html> ...

Obtaining status codes from URLs can be achieved by leveraging various methods

Hey there, I'm just starting out with javascript and AngularJS. Here's a function I wrote to retrieve JSON data from the server: function getProducts() { return $http.get(urlProducts).then( //Success function(resp ...

UPDATE: Choosing several classes and then toggling the classes independently for each one

I have managed to make this work, but I am considering if there is a more efficient solution. My objective is to modify the divs using classes exclusively. I aim to toggle 4 classes with just one click. First, I obtain the class for the button and then a ...

What is the best way to transfer information from a child Angular app to a parent non-Angular app

If I have a plain JavaScript parent application with a child Angular application, how can I notify the parent application of any data changes in the child Angular application? The child application is loaded in a div, not in an iframe. ...

Adding a class to the body for a specific route in web development

I'm facing a situation where there is a class named: product-page-bottom-padding The requirement is to apply this class only to the /product/{slug} route for the body element. It should not be present in any other routes. Can you suggest how to mana ...

Tips for causing the JavaScript confirm alert to appear just a single time

My latest project involves creating a confirm alert that notifies users when their password is about to expire, prompting them to change it. The functionality for this alert is located in the header section of the website. Upon successful login, users are ...

Arranging the index of an array according to a separate array of objects in JavaScript using Vue

I have two arrays of objects that need to be sorted based on the value of a key in the other array. array1: [ { group: 'GROUP1', sort_order: 1, }, { group ...

Dynamically access nested objects by utilizing an array of strings as a pathway

Struggling to find a solution for accessing nested object properties dynamically? The property path needs to be represented as an array of strings. For example, to retrieve the label, use ['type', 'label'] I'm at a roadblock wit ...

What measures can be taken to keep the rightmost element from moving when hovered over?

While I am generally happy with the layout, there seems to be a slight jump to the left when hovering over the right-most image (you need to click "show images" to view them). Strangely, this issue does not occur with the last row of images. Any suggestion ...

Effective ways to replace an image using JavaScript

I am currently working on implementing a show/hide function, but I am encountering an issue with swapping the image (bootstrap class). The show-hide function is functioning properly; however, I am struggling to determine which class is currently displayed. ...