Help Positioning Objects around another Object

Hello all,


Please let me start with a plea to be patient with me since I am new to all this 3D stuff. I am trying to create a scene where I have a sphere that will be connected to from 0 to 1000 other spheres (or cubes) that will be connected via a line (or a cylinder but that is for another question I guess). What I want is to be able to auto place the objects around the sphere and connect them with a line and not have the spheres overlap. I prefer the connecting lines to not intersect the spheres but at the large number of objects, it may not be possible. At the high end of the number of objects, the interior sphere would be hidden by multiple levels of connecting spheres. Anyone have any ideas for how to code a behavior like this? BTW, I will be doing this is Swift for the iPad if that matters. Feel free to ask more questions if you need.


John


PS: The parenthetical comment has to do with my current two sphere connected solution uses a OpenGL line to connect the spheres. I cannot figure the 3D rotation to make a cylinder start at the center of one sphere and go through the center of the second sphere. I have a solution just using the line but if anyone can help me with positioning a cylinder to connect to the center of two 3D objects that also would be great. I am struggling with calculations for positioning objects in general in the 3D world.😕

I'm having trouble understanding what you're trying to do...


Could this be described as similar to:


Sequentially filling a room with floating balloons, randomly placed around a central balloon?


Each balloon, once placed, is connected by a straw to the middle balloon, and remains stationary?

Deeds,


Yes. That is a way of thinking about the problem. The straws are rigid and the ballons will sit in space (infinite space) so they can be further and further from the the central balloon.


John

the cheatiest way to do this would be to use physics.


Make spheres with an associated physics body of exactly the same size as themselves, and attempt to place them. If they intersect, take a look at where they're intersecting and make an effort to determine what size fits in the available space.


But I don't really understand the "auto place the objects around the sphere" line, as this seems to be the criteria process for where to place them, but not explicit enough for me to figure out what you're trying to do in terms of placement.


I do a LOT of 3D work in 3D apps (design apps) so I have a lot of ways of thinking about how to fit things into 3D space, and describe how to do that, much of which is far hackier and dodgier than the riguour programmers use 😉


However... it mostly works, most often.


Scene Kit is basically a very primitive 3D app, kind of like using 3D Studio 2, from the early 1990's.


But programmable. Which is where I get lost 😀


Just let me know how/what you're trying to do, and I'll suggest a few ways to do it. Any metaphors will work. I think visually!

Deeeds,


Ok. Think of a molecule if that helps. You have a core (sphere) and I am wanting to position a bunch of related items connected to the sphere. One way to look at it is like the Sun with static planets around it. I want to be able to programmatically keep adding planets with a connector to the sun. These planets themselves will later have other suns connected to them. This is a visual way of defining relationships. If it helps, this is mapping servers and applications when I get it to work. So there could be 1000 applications running on one server. Those applications then will be attached to mulitple servers. So I want to visualize this complex world of connections visually in the 3D space. I expect to have spheres as servers and cubes perhaps as applications. I need to place them without knowing ahead of time how many connections I have and the data can change to add and delete connections. Does this help?


John

A few notes that can help you get going:


1) A node added as a child node will stay in the parent node's space as the parent node moves

2) You can joint two nodes together with differing anchor points, they will move, but be "stuck" like molecules

3) As deeeds suggested if you create physics nodes for thes SCNodes, they will naturally avoid being inside of each other


The main things to consider are which nodes need to be joined to other nodes and start experimenting as such.


You also might want to start with a general loop that look something like this (psuedo code):


for ( i=0; i<numNodes; i++) {

SCNVector3 newPos = SCNVector3Make( 1+ random(3.0), 1+ random(3.0), 1+ random(3.0) );

SNNode *newBall = [SCNNode nodeWithGeometry: [SCNSphere sphereWithRadius: 1.0] ];

newBall.position = newPos;

newBall.physicsBody = [SCNPhysicsBody kineticBody]; // or dynamic if you want gravity

[someNode addChildNode:newBall];

}



-Orff

Oh... this could be SEXY!


But it will be visually enormous.


And a little tricky to find the space. You might need to pre-think the patterns of dominance, and then plan for edge cases that defy that, and then do something at odds with programming... design how it might ideally look.


Once that's done you have a rough idea of the constraints of building out.


As of now, the only pattern I know of is what you're telling me, that there can be 1000 applications on a server, and that each app can connect to multiple servers.


But I don't know how many other servers an app can connect to, nor how many apps run on multiple servers, nor how many servers might be available.


And then there's the need to know how much this all changes over time, as I assume it does.


Once all of that's known, a basic visualisation - how to best present this information - can be designed.


That design can then inform how you code, what you code, and how everything adapts to changing/increasing data.

Thanks for this help. I have checked and this code seems to only produce positive values (and my current "base" node is at 0, 0, 0. Here is my code for anyone to play with/comment on:

import UIKit
import QuartzCore
import SceneKit
class GameViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    
        /
        let scene = SCNScene()
    
        /
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)
    
        /
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 60)
    
        /
        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light!.type = SCNLightTypeOmni
        lightNode.position = SCNVector3(x: 0, y: 100, z: 100)
        scene.rootNode.addChildNode(lightNode)
    
        /
        let ambientLightNode = SCNNode()
        ambientLightNode.light = SCNLight()
        ambientLightNode.light!.type = SCNLightTypeAmbient
        ambientLightNode.light!.color = UIColor.darkGrayColor()
        scene.rootNode.addChildNode(ambientLightNode)
    
        /
        let sphereNode = createSphere(1.0, color: UIColor.blueColor(), x: 0, y: 0, z: 0, name: "First Sphere")
        scene.rootNode.addChildNode(sphereNode)
    
        /
        let secondSphereNode = createSphere(1.0, color: UIColor.greenColor(), x:  Float(1+arc4random_uniform(3)), y:  Float(1+arc4random_uniform(3)), z:  Float(1+arc4random_uniform(3)), name: "Second Sphere")
        scene.rootNode.addChildNode(secondSphereNode)
    
        /
        let lineNode = lineBetweenNodes(sphereNode, nodeB: secondSphereNode)
        scene.rootNode.addChildNode(lineNode)
    
        /
        let scnView = self.view as! SCNView
    
        /
        scnView.scene = scene

      
        /
        scnView.backgroundColor = UIColor.blackColor()

     /* Removed gesture recoginizer stuff here
}

func cylinderBetweenNodes(nodeA: SCNNode, nodeB: SCNNode, radius: CGFloat = 0.3, lineColor: UIColor = UIColor.yellowColor()) -> SCNNode
{
    let height = sqrtf(pow(nodeA.position.x - nodeB.position.x,2) + pow(nodeA.position.y - nodeB.position.y,2) + pow(nodeA.position.z - nodeB.position.z,2))
    let cylinder = SCNCylinder(radius: radius, height: CGFloat(height))
    cylinder.firstMaterial?.diffuse.contents = lineColor
    let cylinderNode = SCNNode(geometry: cylinder)

    cylinderNode.position = SCNVector3Make((nodeB.position.x - nodeA.position.x)/2 + nodeA.position.x, (nodeB.position.y - nodeA.position.y)/2 + nodeA.position.y, (nodeB.position.z - nodeA.position.z)/2 + nodeA.position.z)
    cylinderNode.rotation = SCNVector4Make(1.0, 1.0, 0.0, Float(M_PI)/2.0)
    return cylinderNode
}
func lineBetweenNodes(nodeA: SCNNode, nodeB: SCNNode, lineWidth: Float = 1, lineColor: UIColor = UIColor.yellowColor()) -> SCNNode {
    glLineWidth(lineWidth)
    let positions: [Float32] = [nodeA.position.x, nodeA.position.y, nodeA.position.z, nodeB.position.x, nodeB.position.y, nodeB.position.z]
    let positionData = NSData(bytes: positions, length: sizeof(Float32)*positions.count)
    let indices: [Int32] = [0, 1]
    let indexData = NSData(bytes: indices, length: sizeof(Int32) * indices.count)

    let source = SCNGeometrySource(data: positionData, semantic: SCNGeometrySourceSemanticVertex, vectorCount: indices.count, floatComponents: true, componentsPerVector: 3, bytesPerComponent: sizeof(Float32), dataOffset: 0, dataStride: sizeof(Float32) * 3)
    let element = SCNGeometryElement(data: indexData, primitiveType: SCNGeometryPrimitiveType.Line, primitiveCount: indices.count, bytesPerIndex: sizeof(Int32))

    let line = SCNGeometry(sources: [source], elements: [element])
    line.firstMaterial?.diffuse.contents = lineColor
    return SCNNode(geometry: line)
}
func createSphere(radius: CGFloat, color: UIColor, x: Float, y: Float, z: Float, name: String) -> SCNNode
{
    let sphere = SCNSphere(radius: radius)
    sphere.firstMaterial?.diffuse.contents = color
    let sphereNode = SCNNode(geometry: sphere)
    sphereNode.position = SCNVector3Make(x, y, z)
    sphereNode.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Kinematic, shape: SCNPhysicsShape(geometry: sphereNode.geometry!, options: nil))
    sphereNode.name = name
    let text = SCNText(string: sphereNode.name, extrusionDepth: radius / 4.0)
    text.firstMaterial?.diffuse.contents = color
    let textNode = SCNNode(geometry: text)
    textNode.position = SCNVector3Make(-1.6 * Float(radius), -1.6 * Float(radius), 1)
    text.font = UIFont(name: "Palatino", size: radius / 2.0)
    sphereNode.addChildNode(textNode)

    return sphereNode;
}


This is from the scenekit default project. The cylinderBetweenNodes doesn't work at all. It is my attempt to not draw a line but a sphere. I want to be able to calculate the new position and then call createSphere repeatedly until I get all the data visualized. Eventually, I will create a createCube to do the application piece. This is just in proof of concept phase. I will add several hundred spheres in a loop to prove the positioning works when I get a solution.


Thanks!

John

I am trying to find a good (re-usable) solution to the base problem. The issue of later iterations are a variation on the same problem. Once I can place multiple spheres around one sphere, I then need to make sure I can connect the next sphere without hitting previously placed spheres. I think orff may have pointed me to how that will happen with the physics properties and just using random numbers. However, I will need to know when I need to expand the random number space because I have used all the current space available. This is a solvable problem I am sure but not easy.


John

BTW, I changed my code to handle the negative value as follows:

        /
        let secondSphereNode = createSphere(1.0, color: UIColor.greenColor(), x: 3.0-Float(arc4random_uniform(6)), y: 3.0-Float(arc4random_uniform(6)), z: 3.0-Float(arc4random_uniform(6)), name: "Second Sphere")


However, my code is allow overlapping spheres. Can someone explain why and how to fix that?


Thanks,

John

Ok. For anyone trying to help. I created a loop and this spheres still overlap:

        /
        for(var index = 1; index<26;index++)
        {
            let secondSphereNode = createSphere(1.0, color: UIColor.greenColor(), x: 3.0-Float(arc4random_uniform(6)), y: 3.0-Float(arc4random_uniform(6)), z: 3.0-Float(arc4random_uniform(6)), name: "\(index+1) Sphere")
            scene.rootNode.addChildNode(secondSphereNode)
       
            /
            let lineNode = lineBetweenNodes(sphereNode, nodeB: secondSphereNode)
            scene.rootNode.addChildNode(lineNode)
        }


That is in place of the second sphere call.


John

This is exactly what I was trying to help you avoid by pointing out the nature of programmers - a desire for a world without design consideration - and how that leads to attempting to hit everything with a hammer and makes everything a nail.


Without a focused pre-conception you're going to wind up spending weeks attempting to hone algorithms to make something navigatable and useful, which will never have any aesthetic consideration beyond what the algorithms do and your sense of aesthetics within them.


Or... you could preconceive how it would best serve its purpose before hand (this is design) and then build to that.


Need some inspiration of 3D space?


Drop the following words into a youtube search, the first hit should be the result I'm trying to show you:


"quite & orange" cdak

I agree, you need to define the solution before properly attacking it, but sometimes people need to know whats possible by prototyping.

Deeeds,


I know how I want it to look. The code above gives the general view (but with overlapping spheres). I will have to deal with the issues of saving the output so it looks the same when the same data runs on different devices. However, at bottom, a set of code to position things like I want it is needed. That is what programmers do. We allow the data to build the visual view.


John

Programmers prefer data to humans, and attempt to reduce humanity to data.


They're not the only group of people to think this way, but they're the only ones able to implement mechanisms that actually do it.


Unfortunately they're not particularly discerning about whom they do this for, nor to what ends and means.


Hence many of our current predicaments.

-----------


There are so many ways this could evolve with 1000's of objects and their relationships that it's better to think of how to design that appearance for human consumption than to attempt to divine a way to articulate something discernible via a post presentation process of trial and error bending and blending of algorithms.


But don't take my word for it. Just wait and see.


Designers consider. That's why we do what we do 😉

Help Positioning Objects around another Object
 
 
Q