 # Nature of code - force, attracto

I want to implement the following:

# 2.6: Gravitational Attraction - The Nature of Code !
by rhino, grasshopper, python

also I Referenced:

but it didn’t work well…
it have to go round, but it comes out straight.

Here is my script

coding train, python.gh (5.7 KB) mover.3dm (23.5 KB)

Hi @bjbj7878,

Is this what you’re looking for? It works pretty much like Shiffman’s script does. It also recomputes the GHPython component itself, much like the `Update()` function works in Processing, instead of saving the frame-by-frame data in a list, which can be quite expensive.

``````import Rhino.Geometry as rg
import Grasshopper as gh
import scriptcontext as sc

WIDTH = 100 # Global volume width
DEPTH = 100 # Global volume depth
HEIGHT = 100 # Gobal volume height
G = 1 # Global gravitational constant

class Mover():
def __init__(self, position):
global WIDTH, DEPTH, HEIGHT
self.position = position
self.velocity = rg.Vector3d.Zero
self.acceleration = rg.Vector3d.Zero
self.mass = 1
self.max_speed = 10

def apply_force(self, force):
#Newton's 2nd Law with mass!
self.acceleration += force / self.mass

def update(self):
#update location, velocity
self.velocity += self.acceleration
self.position += self.velocity
print "Acceleration: ", self.acceleration
self.acceleration *= 0.0

def check_edges(self):
bounce = -1
if self.position.X >= WIDTH or self.position.X <= 0:
self.velocity.X = self.velocity.X * bounce
if self.position.Y >= DEPTH or self.position.Y <= 0:
self.velocity.Y = self.position.Y * bounce
if self.position.Z >= HEIGHT or self.position.Z <= 0:
self.velocity.Z = self.velocity.Z * bounce

def display(self):
return self.position

class Attractor():
def __init__(self):
global WIDTH, DEPTH, HEIGHT, G
self.position = rg.Vector3d(WIDTH / 2, DEPTH / 2, HEIGHT / 2)
self.mass = 20 # Mass, tied to size

def attract(self, mover):
# Calculate the direction of force vector
force = self.position - mover.position
# Constrain the magnitude of the force vector to eliminate
# "extreme" results for very close or very far objects
force = vector_constrain(force, 5.0, 25.0)
dist = force.Length # constrained distance/magnitude
# Normalize the force vector (distance doesn't matter here, we just
# want this vector for direction)
force.Unitize()
# Calculate the gravitational force magnitude
strength = (G * self.mass * mover.mass) / dist**2
# Get the force vector (magnitude x direction)
force *= strength
return force

def display(self):
return rg.Sphere(rg.Point3d(self.position), self.mass / 2)

################################################################################

def vector_constrain(vec, min_mag=0, max_mag=20):
"""Returns a vector whose magnitude is constrained
inbetween a minimum and maximum value."""
if (vec.Length <= max_mag and vec.Length >= min_mag):
return vec
if (vec.Length > max):
mag = max_mag
if (vec.Length < min):
mag = min_mag
vec.Unitize()
vec *= mag
return vec

def update_component():
"""Updates this GH component, similar to using a Grasshopper timer."""
def call_back(e):
"""Defines a callback action."""
ghenv.Component.ExpireSolution(False)
# Get the Grasshopper document
ghDoc = ghenv.Component.OnPingDocument()
# Schedule this component to expire
ghDoc.ScheduleSolution(1, gh.Kernel.GH_Document.GH_ScheduleDelegate(call_back))

################################################################################

if Reset or "count" not in globals():
sc.sticky.pop("Simulation", None)
count = 0
print count
ghenv.Component.Message = "Reset"

if Run:
# Setup the simulation
if not "Simulation" in sc.sticky.keys():
# Initialise a new mover and attractor
mv = Mover(Location)
a = Attractor()
else:
# Get the existing mover and attractor
mv = sc.sticky["Simulation"]["Mover"]
a = sc.sticky["Simulation"]["Attractor"]

# Update/draw the simulation
f = a.attract(mv)
mv.apply_force(f)
mv.update()
count += 1

# Store the current iteration/frame in the sticky dictionary
sc.sticky["Simulation"] = {"Mover": mv, "Attractor": a}

if count < MaxCount:
update_component()
ghenv.Component.Message = "Running..."
else:
ghenv.Component.Message = "Converged"

print count

# Display/outputs
MPos = mv.display()
AGeo = a.display()

else:
# Pause the simulation
if "Simulation" in sc.sticky.keys():
# Get the existing mover and attractor, and pause
mv = sc.sticky["Simulation"]["Mover"]
a = sc.sticky["Simulation"]["Attractor"]

ghenv.Component.Message = "Paused"

print count

# Display/outputs
MPos = mv.display()
AGeo = a.display()
``````

I’ve changed the script to RhinoCommon, instead of rhinoscriptsyntax, which is better and closer to Processing, which should make translations easier.

coding train python 2.gh (7.8 KB)

2 Likes

wow thank you so much!

1. when it is rg.vector, Using ‘+’ makes calculations really easy This part is really amazing

class Mover():
def init(self, position):
global WIDTH, DEPTH, HEIGHT
self.position = position
self.velocity = rg.Vector3d.Zero
self.acceleration = rg.Vector3d.Zero
self.mass = 1
self.max_speed = 10

``````def apply_force(self, force):
#Newton's 2nd Law with mass!
self.acceleration += force / self.mass

def update(self):
#update location, velocity
self.velocity += self.acceleration
self.position += self.velocity
print "Acceleration: ", self.acceleration
self.acceleration *= 0.0
``````

you so amazing…

self.velocity = rg.Vector3d.Zero
self.acceleration = rg.Vector3d.Zero
" self.velocity += self.acceleration"

how could it possible??
i want to use this but i didn’t use it before so…
i dont understand very well but
It seems to be very helpful in solving the problem.

1. Because of my lack of skill, I haven’t fully understood the many skills you’ve done.lol
by any youtube or something, I want to know where to learn.

def update_component():
“”“Updates this GH component, similar to using a Grasshopper timer.”""
def call_back(e):
“”“Defines a callback action.”""
ghenv.Component.ExpireSolution(False)
# Get the Grasshopper document
ghDoc = ghenv.Component.OnPingDocument()
# Schedule this component to expire
ghDoc.ScheduleSolution(1, gh.Kernel.GH_Document.GH_ScheduleDelegate(call_back))

####################################
if Reset or “count” not in globals():
sc.sticky.pop(“Simulation”, None)
count = 0
print count
ghenv.Component.Message = “Reset”

i think i dont know about ‘ghenv’, ‘sc.sticky’ … and so on
I really want to learn how to use it, but I am not sure how to do it.

Thank you again and I will study more by referring. Thank you very much.

`rg.Vector3d.Zero` simply creates a zero vector. It’s analogues to creating it “manually” with `rg.Vector3d(0.0, 0.0, 0.0)`.

`self.velocity += self.acceleration` is simply a short way of doing `self.velocity = self.velocity + self.acceleration`!

I’ve never seen this being mentioned in any YouTube tutorial/video. I’ve learned here from @AndersDeleuran.
The `update_component()` function is responsible for the recomputation of the GHPython component. It’s basically what enables the animation! Each time the component expires/recomputes corresponds to an animation frame, much like the `Update()` function in Processing.

`globals()` is a function from Python that returns the global symbol table. I use it here to check whether `count` is defined as a global variable.
The sticky dictionary is a dictionary provided by Rhino that exists beyond your current session/document. You can save data in it that would otherwise expire and be lost, for instance when you’re GHPython component refreshes/recomputes. It works just like standard Python dictionaries.

`sc.sticky.pop(“Simulation”, None)` means that we delete the values/data, corresponding to the key “Simulation” from the sticky dictionary, since we want to reset everything. `None` simply means that nothing should be returned by `pop()`.

`sc.sticky["Simulation"] = {"Mover": mv, "Attractor": a}` means that we store the dictionary `{"Mover": mv, "Attractor": a}` as a value under the key “Simulation” inside the sticky dictionary.

`mv = sc.sticky["Simulation"]["Mover"]` means that we get the value (i.e. Mover object) that is stored under the key “Mover” from the dictionary that is stored under the key “Simulation” in the sticky dictionary.

`ghenv.Component.Message` simply displays a string/text under the component.

2 Likes

Thanks a lot once again. Thank you for explaining one by one. I will continue to study ‘nature of code’ in the future, and I will use the content that I have answered and implement it further. It was my first time asking a question like this, thank you very much for your kind answer.

1 Like