sghr / iGeo

iGeo: Computational Design and 3D Modeling Library for Processing
http://igeo.jp
GNU Lesser General Public License v3.0
146 stars 34 forks source link

Applying forces of a compound field to a non-IGeo object #24

Closed co-ord closed 4 years ago

co-ord commented 4 years ago

Dear Sugihara San,

I would like to apply forces of 2 ICompoundField() (curl field + attractor field) to a Boid() object that I've coded myself. This boid has an applyForce() function where it reads the force value corresponding to its position in the compound field and then add it to its acceleration vector.

def applyForce(self):
        curlForce = curl.get(IVec(self.pos.x, self.pos.y, self.pos.z))
        curlForce = PVector(curlForce.x(), curlForce.y(), curlForce.z())

        attrForce = attr.get(IVec(self.pos.x, self.pos.y, self.pos.z))
        attrForce = PVector(attrForce.x(), attrForce.y(), attrForce.z())

        self.acc.add(curlForce + attrForce)  # simple addition of the 2 forces

Unfortunately, doing so gives me very different results from your original sketch entitled "Swarm Formation by Force Fields".

Capture d’écran (115)

I would really appreciate if you could explain how did you mix the curl force and attraction force together to get such a smooth motion.

Respectfully,

solub

Edit: Full script if needed

add_library('peasycam')
add_library('hemesh')
add_library('igeo')

W, H, D, s = 10, 15, 10, 100 
N = W*H*D

def setup():
    #size(1000, 600, P3D)
    fullScreen(P3D)
    randomSeed(1008)
    strokeWeight(.7)
    stroke(60)
    noFill()

    global curl, attr, boids

    cam = PeasyCam(this, 1600)
    boids = [Boid() for i in xrange(25)]

    curl = ICompoundField()
    attr = ICompoundField()

    for i in xrange(10):
        pt = IRand.pt(W*s*.25, 100, D*s*.25, W*s*.75, H*s, D*s*.75)
        curl.add(IPointCurlField(pt, IVec(0, -1, 0)).intensity(-20))
        attr.add(IAttractor(pt).intensity(20))

    IGravity(0,0,-2)

def draw():
    background('#FFFFFF')
    translate((-W*s)>>1, (-H*s)>>1, (-D*s)>>1)

    # Displaying the random points in the field
    pushStyle()
    stroke(255, 30, 30)
    strokeWeight(8)
    for p in curl.pointFields:
        point(p.pos().x(), p.pos().y(), p.pos().z())
    popStyle()

    # Updating boids
    for b in boids:
        b.render()
        b.update()
        b.flock()
        b.applyForce() #Trying to apply the forces of the compound fields to the boids

class Boid(object):
    def __init__(self):
        self.pos = PVector(random(W*s), 0, random(D*s))
        self.vel = PVector().setMag(3)
        self.acc = PVector()
        self.radius = 50
        self.maxSpeed = 1.5
        self.maxForce = .1
        self.trail = []

    def update(self):    
        self.pos.add(self.vel)
        self.vel.add(self.acc)
        self.vel.limit(self.maxSpeed)
        self.acc.mult(0)

    def applyForce(self):
        curlForce = curl.get(IVec(self.pos.x, self.pos.y, self.pos.z))
        curlForce = PVector(curlForce.x(), curlForce.y(), curlForce.z())

        attrForce = attr.get(IVec(self.pos.x, self.pos.y, self.pos.z))
        attrForce = PVector(attrForce.x(), attrForce.y(), attrForce.z())

        self.acc.add(curlForce + attrForce)

    def flock(self):
        count = 0
        aVel = PVector() #average velocity
        aPos = PVector() #average position
        aDif = PVector() #average difference

        for b in boids:
            d = self.pos.dist(b.pos)
            if b != self and d < self.radius:
                count += 1

                aVel += b.vel
                aPos += b.pos

                diff = self.pos.copy().sub(b.pos)
                aDif += diff.div(d) #inversional to distance (the farther, the lower the magnitude)

        if count > 0:      

            #Alignment 
            aVel.div(count).setMag(self.maxSpeed) #maxSpeed prevent from steering too hard
            alignSteering = aVel.sub(self.vel).limit(self.maxForce)

            #Cohesion
            aPos.div(count)
            coheSteering = aPos.sub(self.pos).setMag(self.maxSpeed).sub(self.vel).limit(self.maxForce)

            #Separation
            aDif.div(count)
            sepaSteering = aDif.setMag(self.maxSpeed).sub(self.vel).limit(self.maxForce)

            self.acc.add(coheSteering + sepaSteering) #+ alignSteering # Alignment set to 0 by Sugihara san in his original sketch

    def render(self):
        pushStyle()
        strokeWeight(8)
        point(self.pos.x, self.pos.y, self.pos.z) 
        popStyle()

        # Trail
        if frameCount%10 == 0:
            self.trail.append(PVector(self.pos.x, self.pos.y, self.pos.z))

        if len(self.trail) > 0:
            for i in xrange(1, len(self.trail)):
                line(self.trail[i-1].x, self.trail[i-1].y, self.trail[i-1].z, self.trail[i].x, self.trail[i].y, self.trail[i].z)