Mapping points to surface

Alright, so I’m trying to map boids on a surface to make a flocking behavior on a surface for my new plugin
(based on long Nguyen’s workshop)

My approach was to convert positions to UV parameters by remapping x,y values of points in 2d to surface domain in U and V.

It doesn’t give the desired effect can anyone help me with this ?

thanks.
here is the code

using System;
using System.Collections.Generic;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;

namespace SurfaceTrails2.FlockingMapToSurface
{
public class GhcFlockingSimulation : GH_Component
{
private FlockSystem flockSystem;

    public GhcFlockingSimulation()
        : base(
              "Flocking Map To Surface",
              "FlockingMapToSurface",
              "Flocking Map To Surface",
              "YFAtools",
              "AgentBased")
    {
    }


    protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
    {
        pManager.AddBooleanParameter("Reset", "Reset", "Reset", GH_ParamAccess.item, false);
        pManager.AddBooleanParameter("Play", "Play", "Play", GH_ParamAccess.item, false);
        pManager.AddBooleanParameter("3D", "3D", "3D", GH_ParamAccess.item, true);
        pManager.AddSurfaceParameter("srf", "srf", "srf", GH_ParamAccess.item);
        pManager.AddIntegerParameter("Count", "Count", "Number of Agents", GH_ParamAccess.item, 50);
        pManager.AddNumberParameter("Timestep", "Timestep", "Timestep", GH_ParamAccess.item, 0.02);
        pManager.AddNumberParameter("Neighbourhood Radius", "Neighbourhood Radius", "Neighbourhood Radius", GH_ParamAccess.item, 3.5);
        pManager.AddNumberParameter("Alignment", "Alignment", "Alignment", GH_ParamAccess.item, 0.5);
        pManager.AddNumberParameter("Cohesion", "Cohesion", "Cohesion", GH_ParamAccess.item, 0.5);
        pManager.AddNumberParameter("Separation", "Separation", "Separation", GH_ParamAccess.item, 0.5);
        pManager.AddNumberParameter("Separation Distance", "Separation Distance", "Separation Distance", GH_ParamAccess.item, 1.5);
        pManager.AddCircleParameter("Repellers", "Repellers", "Repellers", GH_ParamAccess.list);
        pManager[10].Optional = true;
        pManager.AddBooleanParameter("Use Parallel", "Use Parallel", "Use Parallel", GH_ParamAccess.item, false);
        pManager.AddBooleanParameter("Use R-Tree", "Use R-Tree", "Use R-Tree", GH_ParamAccess.item, false);
    }


    protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
    {
        pManager.AddTextParameter("Info", "Info", "Information", GH_ParamAccess.item);
        pManager.AddPointParameter("Positions", "Positions", "The agent positions", GH_ParamAccess.list);
        pManager.AddVectorParameter("Velocities", "Velocities", "The agent veloctiies", GH_ParamAccess.list);
    }


    protected override void SolveInstance(IGH_DataAccess DA)
    {
        // ===============================================================================================
        // Read input parameters
        // ===============================================================================================

        bool iReset = true;
        bool iPlay = false;
        bool i3D = false;
        Surface baseSurface = null;
        int iCount = 0;
        double iTimestep = 0.0;
        double iNeighbourhoodRadius = 0.0;
        double iAlignment = 0.0;
        double iCohesion = 0.0;
        double iSeparation = 0.0;
        double iSeparationDistance = 0.0;
        List<Circle> iRepellers = new List<Circle>();
        bool iUseParallel = false;
        bool iUseRTree = false;

        DA.GetData("Reset", ref iReset);
        DA.GetData("Play", ref iPlay);
        DA.GetData("3D", ref i3D);
        DA.GetData("srf", ref baseSurface);
        DA.GetData("Count", ref iCount);
        DA.GetData("Timestep", ref iTimestep);
        DA.GetData("Neighbourhood Radius", ref iNeighbourhoodRadius);
        DA.GetData("Alignment", ref iAlignment);
        DA.GetData("Cohesion", ref iCohesion);
        DA.GetData("Separation", ref iSeparation);
        DA.GetData("Separation Distance", ref iSeparationDistance);
        DA.GetDataList("Repellers", iRepellers);
        DA.GetData("Use Parallel", ref iUseParallel);
        DA.GetData("Use R-Tree", ref iUseRTree);


        // ===============================================================================================
        // Read input parameters
        // ===============================================================================================

        if (iReset || flockSystem == null)
        {
            flockSystem = new FlockSystem(iCount, i3D);
        }
        else
        {
            // ===============================================================================================
            // Assign the input parameters to the corresponding variables in the  "flockSystem" object
            // ===============================================================================================

            flockSystem.Timestep = iTimestep;
            flockSystem.NeighbourhoodRadius = iNeighbourhoodRadius;
            flockSystem.AlignmentStrength = iAlignment;
            flockSystem.CohesionStrength = iCohesion;
            flockSystem.SeparationStrength = iSeparation;
            flockSystem.SeparationDistance = iSeparationDistance;
            flockSystem.Repellers = iRepellers;
            flockSystem.UseParallel = iUseParallel;


            // ===============================================================================
            // Update the flock
            // ===============================================================================

            if (iUseRTree)
                flockSystem.UpdateUsingRTree();
            else
                flockSystem.Update();

            if (iPlay) ExpireSolution(true);
        }

        // ===============================================================================
        // Output the agent positions and velocities so we can see them on display
        // ===============================================================================

        List<GH_Point> positions = new List<GH_Point>();
        List<GH_Vector> velocities = new List<GH_Vector>();
        List<GH_Point> surfacePositions = new List<GH_Point>();

        var surface = baseSurface.ToNurbsSurface();
        foreach (FlockAgent agent in flockSystem.Agents)
        {
            positions.Add(new GH_Point(agent.Position));
            velocities.Add(new GH_Vector(agent.Velocity));


            // ===============================================================================
            // Position on surface 
            // ===============================================================================
            Interval u = new Interval(0, 1);
            Interval v = new Interval(0, 1);

            surface.SetDomain(0, u);
            surface.SetDomain(1, v);



            var remappedU = NumberOperations.remap(0,1,surface.Domain(0).T0,surface.Domain(0).T1,agent.Position.X);
            var remappedV = NumberOperations.remap(0,1,surface.Domain(1).T0,surface.Domain(1).T1,agent.Position.Y);

            surfacePositions.Add(new GH_Point(surface.PointAt(remappedU, remappedV)/*surface.PointAt(agent.Position.X, agent.Position.Y*/)); 

        }

       

        DA.SetDataList("Positions", surfacePositions);
        DA.SetDataList("Velocities", velocities);
    }


    protected override System.Drawing.Bitmap Icon { get { return Properties.Resources._28_8_18_FlockSimulation; } }


    public override Guid ComponentGuid { get { return new Guid("7066d2ac-3c0a-47db-b22f-4d8c25bf8baa"); } }
}

}

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Rhino.Geometry;

namespace SurfaceTrails2.FlockingMapToSurface
{
public class FlockSystem
{
public List Agents;

    public double Timestep;
    public double NeighbourhoodRadius;
    public double AlignmentStrength;
    public double CohesionStrength;
    public double SeparationStrength;
    public double SeparationDistance;
    public List<Circle> Repellers;
    public bool UseParallel;


    public FlockSystem(int agentCount, bool is3D)
    {
        Agents = new List<FlockAgent>();

        if (is3D)
            for (int i = 0; i < agentCount; i++)
            {
                FlockAgent agent = new FlockAgent(
                    Util.GetRandomPoint(0.0, 30.0, 0.0, 30.0, 0.0, 30.0),
                    Util.GetRandomUnitVector() * 4.0);

                agent.FlockSystem = this;

                Agents.Add(agent);
            }
        else
            for (int i = 0; i < agentCount; i++)
            {
                FlockAgent agent = new FlockAgent(
                    Util.GetRandomPoint(0.0, 30.0, 0.0, 30.0, 0.0, 0.0),
                    Util.GetRandomUnitVectorXY() * 4.0);

                agent.FlockSystem = this;

                Agents.Add(agent);
            }
    }


    private List<FlockAgent> FindNeighbours(FlockAgent agent)
    {
        List<FlockAgent> neighbours = new List<FlockAgent>();

        foreach (FlockAgent neighbour in Agents)
            if (neighbour != agent && neighbour.Position.DistanceTo(agent.Position) < NeighbourhoodRadius)
                neighbours.Add(neighbour);

        return neighbours;
    }

    private void ComputeAgentDesiredVelocity(FlockAgent agent)
    {
        List<FlockAgent> neighbours = FindNeighbours(agent);
        agent.ComputeDesiredVelocity(neighbours);
    }


    public void Update()
    {
        if (UseParallel)
            Parallel.ForEach(Agents, ComputeAgentDesiredVelocity);
        else
            foreach (FlockAgent agent in Agents)
                ComputeAgentDesiredVelocity(agent);

        // Once the desired velocity for each agent has been computed, we update each position and velocity
        foreach (FlockAgent agent in Agents)
            agent.UpdateVelocityAndPosition();
    }


    public void UpdateUsingRTree()
    {
        /* First, build the R-Tree */

        RTree rTree = new RTree();

        for (int i = 0; i < Agents.Count; i++)
            rTree.Insert(Agents[i].Position, i);

        /* Then, we use the R-Tree to find the neighbours
            and compute the desired velocity */

        foreach (FlockAgent agent in Agents)
        {
            List<FlockAgent> neighbours = new List<FlockAgent>();

            EventHandler<RTreeEventArgs> rTreeCallback =
            (object sender, RTreeEventArgs args) =>
            {
                if (Agents[args.Id] != agent)
                    neighbours.Add(Agents[args.Id]);
            };

            rTree.Search(new Sphere(agent.Position, NeighbourhoodRadius), rTreeCallback);

            agent.ComputeDesiredVelocity(neighbours);
        }



        // Once the desired velocity for each agent has been computed, we can update each position and velocity               
        foreach (FlockAgent agent in Agents) agent.UpdateVelocityAndPosition();
    }
}

}

using System.Collections.Generic;
using Rhino.Geometry;

namespace SurfaceTrails2.FlockingMapToSurface
{
public class FlockAgent
{
public Point3d Position;
public Vector3d Velocity;

    public FlockSystem FlockSystem;

    private Vector3d desiredVelocity;
  private  double boundingBoxSize = 1;

    public double BoundingBox
    {
        get { return boundingBoxSize;} set { boundingBoxSize=value; } }

    public FlockAgent(Point3d position, Vector3d velocity)
    {
        Position = position;
        Velocity = velocity;
    }
     
    public void UpdateVelocityAndPosition()
    {
        Velocity = 0.97 * Velocity + 0.03 * desiredVelocity;

        if (Velocity.Length > 8.0) Velocity *= 8.0 / Velocity.Length;
        else if (Velocity.Length < 4.0) Velocity *= 4.0 / Velocity.Length;

        Position += Velocity * FlockSystem.Timestep;
    }



    public void ComputeDesiredVelocity(List<FlockAgent> neighbours)
    {
        // First, reset the desired velocity to 0
        desiredVelocity = new Vector3d(0.0, 0.0, 0.0);

        // ===============================================================================
        // Pull the agent back if it gets out of the bounding box 
        // ===============================================================================

        

        if (Position.X < 0.0)
            desiredVelocity += new Vector3d(-Position.X, 0.0, 0.0);
        else if (Position.X > boundingBoxSize)
            desiredVelocity += new Vector3d(boundingBoxSize - Position.X, 0.0, 0.0);

        if (Position.Y < 0.0)
            desiredVelocity += new Vector3d(0.0, -Position.Y, 0.0);
        else if (Position.Y > boundingBoxSize)
            desiredVelocity += new Vector3d(0.0, boundingBoxSize - Position.Y, 0.0);

        if (Position.Z < 0.0)
            desiredVelocity += new Vector3d(0.0, 0.0, -Position.Z);
        else if (Position.Z > boundingBoxSize)
            desiredVelocity += new Vector3d(0.0, 0.0, boundingBoxSize - Position.Z);


        // ===============================================================================
        // If there are no neighbours nearby, the agent will maintain its veloctiy,
        // else it will perform the "alignment", "cohension" and "separation" behaviours
        // ===============================================================================

        if (neighbours.Count == 0)
            desiredVelocity += Velocity; // maintain the current velocity
        else
        {
            // -------------------------------------------------------------------------------
            // "Alignment" behavior 
            // -------------------------------------------------------------------------------

            Vector3d alignment = Vector3d.Zero;

            foreach (FlockAgent neighbour in neighbours)
                alignment += neighbour.Velocity;

            // We divide by the number of neighbours to actually get their average velocity
            alignment /= neighbours.Count;

            desiredVelocity += FlockSystem.AlignmentStrength * alignment;


            // -------------------------------------------------------------------------------
            // "Cohesion" behavior 
            // -------------------------------------------------------------------------------

            Point3d centre = Point3d.Origin;

            foreach (FlockAgent neighbour in neighbours)
                centre += neighbour.Position;

            // We divide by the number of neighbours to actually get their centre of mass
            centre /= neighbours.Count;

            Vector3d cohesion = centre - Position;

            desiredVelocity += FlockSystem.CohesionStrength * cohesion;


            // -------------------------------------------------------------------------------
            // "Separation" behavior 
            // -------------------------------------------------------------------------------

            Vector3d separation = Vector3d.Zero;

            foreach (FlockAgent neighbour in neighbours)
            {
                double distanceToNeighbour = Position.DistanceTo(neighbour.Position);

                if (distanceToNeighbour < FlockSystem.SeparationDistance)
                {
                    Vector3d getAway = Position - neighbour.Position;

                    /* We scale the getAway vector by inverse of distanceToNeighbour to make 
                       the getAway vector bigger as the agent gets closer to its neighbour */
                    separation += getAway / (getAway.Length * distanceToNeighbour);
                }


            }

            desiredVelocity += FlockSystem.SeparationStrength * separation;
        }


        // ===============================================================================
        // Avoiding the obstacles (repellers)
        // ===============================================================================

        foreach (Circle repeller in FlockSystem.Repellers)
        {
            double distanceToRepeller = Position.DistanceTo(repeller.Center);

            Vector3d repulsion = Position - repeller.Center;

            // Repulstion gets stronger as the agent gets closer to the repeller
            repulsion /= (repulsion.Length * distanceToRepeller);

            // Repulsion strength is also proportional to the radius of the repeller circle/sphere
            // This allows the user to tweak the repulsion strength by tweaking the radius
            repulsion *= 30.0 * repeller.Radius;

            desiredVelocity += repulsion;

        }
    }
}

}

I assume your system operates in 2d and you want to map it to a surface, instead of operating directly on the surface (which works better).
You need to normalize the surface and the position of the boids, so both systems are using the same space. You’ve normalized the surface, but I don’t know if you’ve normalized the position. For this, if the region is from x0 to x1 and y0 to y1, and the position is x,y, the normalized position will be:
nx = (x-x0)/(x1-x0);
ny = (y-y0)/y1-y0);
then with nx,ny you can place it on the normalized surface.

Two considerations, 1. if the surface is trimmed surface, it is possible that the position will come out of the visible surface. 2. Before I mentioned that it works best to operate directly on the surface, because if you treat this rule (pull to the surface) as a restriction rather than a force, you can for example, make the velocity always tangential to the surface, or project the position to the surface after updating it. It depends on how you have implemented the force system (I haven’t stopped to look at the whole code), but it’s not a big deal either, maybe it’s just my preference.

1 Like

to be honest I tried a similar approach by using u,v coordinates instead of points but I failed horrible. so I thought I 'll just map them to the surface in my v1.0 and then build up the complexity.

I will try your current solution ahead and later I’ll be posting my V2 here.

@Dani_Abalde your solution worked great.
now I’m making v2 to make it operate directly on the surface.
it doesn’t seem to work at all. can you tell me what I’m missing here ?

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;

namespace SurfaceTrails2.FlockingOnSrf
{
public class FlockSystem
{
public List Agents;

    public double Timestep;
    public double NeighbourhoodRadius;
    public double AlignmentStrength;
    public double CohesionStrength;
    public double SeparationStrength;
    public double SeparationDistance;
    public List<Circle> Repellers;
    public bool UseParallel;
     NurbsSurface _surface;
    private double _boundingBoxSize = 1;
    private Random _randomX = new Random();
    private Random _randomY = new Random();


    public double BoundingBox { get { return _boundingBoxSize; } set { _boundingBoxSize = value; } }
    public NurbsSurface Surface { get { return _surface; } set { _surface = value; } }


    public FlockSystem(int agentCount/*, bool is3D*/,NurbsSurface surface)
    {
        Surface = surface;
        Agents = new List<FlockAgent>();
        Random random = new Random();
        Interval domainU = _surface.Domain(0);
        Interval domainV = _surface.Domain(1);

        _surface.Domain(0);
        _surface.Domain(1);


        _surface.SetDomain(0, domainU);
        _surface.SetDomain(0, domainV);

        double randomCoordinateX = _randomX.NextDouble();
        double randomCoordinateY = _randomX.NextDouble();

        _surface.Domain(0);
        _surface.Domain(1);

        var tu = domainU.ParameterAt(random.NextDouble());
        var tv = domainV.ParameterAt(random.NextDouble());
        var vectorX = _surface.PointAt(Util.GetRandomUnitVectorX(), Util.GetRandomUnitVectorY()).X;
        var vectory = _surface.PointAt(Util.GetRandomUnitVectorX(), Util.GetRandomUnitVectorY()).Y;


        //if (is3D)
        //    for (int i = 0; i < agentCount; i++)
        //    {
        //        FlockAgent agent = new FlockAgent(
        //            Util.GetRandomPoint(0.0, 30.0, 0.0, 30.0, 0.0, 30.0),
        //            Util.GetRandomUnitVector() * 4.0);

        //        agent.FlockSystem = this;

        //        Agents.Add(agent);
        //    }
        //else
        for (int i = 0; i < agentCount; i++)
            {
                FlockAgent agent = new FlockAgent(
                    _surface.PointAt(_randomX.NextDouble(), random.NextDouble()),
                   Util.GetRandomUnitVectorXY() * 0.2667 *BoundingBox);

                agent.FlockSystem = this;

                Agents.Add(agent);
            }
    }


    private List<FlockAgent> FindNeighbours(FlockAgent agent)
    {
        var neighbours = new List<FlockAgent>();

        foreach (FlockAgent neighbour in Agents)
            if (neighbour != agent && neighbour.Position.DistanceTo(agent.Position) < NeighbourhoodRadius)
                neighbours.Add(neighbour);

        return neighbours;
    }

    private void ComputeAgentDesiredVelocity(FlockAgent agent)
    {
        List<FlockAgent> neighbours = FindNeighbours(agent);
        agent.ComputeDesiredVelocity(neighbours);
    }


    public void Update()
    {
        if (UseParallel)
            Parallel.ForEach(Agents, ComputeAgentDesiredVelocity);
        else
            foreach (var agent in Agents)
                ComputeAgentDesiredVelocity(agent);

        // Once the desired velocity for each agent has been computed, we update each position and velocity
        foreach (var agent in Agents)
            agent.UpdateVelocityAndPosition();
    }


    public void UpdateUsingRTree()
    {
        /* First, build the R-Tree */

        var rTree = new RTree();

        for (var i = 0; i < Agents.Count; i++)
            rTree.Insert(Agents[i].Position, i);

        /* Then, we use the R-Tree to find the neighbours
            and compute the desired velocity */

        foreach (var agent in Agents)
        {
            List<FlockAgent> neighbours = new List<FlockAgent>();

            EventHandler<RTreeEventArgs> rTreeCallback =
            (object sender, RTreeEventArgs args) =>
            {
                if (Agents[args.Id] != agent)
                    neighbours.Add(Agents[args.Id]);
            };

            rTree.Search(new Sphere(agent.Position, NeighbourhoodRadius), rTreeCallback);

            agent.ComputeDesiredVelocity(neighbours);
        }



        // Once the desired velocity for each agent has been computed, we can update each position and velocity               
        foreach (FlockAgent agent in Agents) agent.UpdateVelocityAndPosition();
    }
}

}

using System.Collections.Generic;
using Rhino.Geometry;

namespace SurfaceTrails2.FlockingOnSrf
{
public class FlockAgent
{
public Point3d Position;
public Vector3d Velocity;
public FlockSystem FlockSystem;
private Vector3d desiredVelocity;

    public FlockAgent(Point3d position, Vector3d velocity)
    {
        Position = position;
        Velocity = velocity;
    }
     
    public void UpdateVelocityAndPosition()
    {
        double maxVelocity = 0.2667 * FlockSystem.BoundingBox;
        double minVelocity = 0.1333 * FlockSystem.BoundingBox;


        Velocity = 0.97 * Velocity + 0.03 * desiredVelocity;

        if (Velocity.Length > maxVelocity) Velocity *= maxVelocity / Velocity.Length;
        else if (Velocity.Length < minVelocity) Velocity *= minVelocity / Velocity.Length;

        Position += Velocity * FlockSystem.Timestep;
    }



    public void ComputeDesiredVelocity(List<FlockAgent> neighbours/*, Surface surface*/)
    {
        // First, reset the desired velocity to 0
        desiredVelocity = new Vector3d(0.0, 0.0, 0.0);
        var bounceMultiplier = 10;
        // ===============================================================================
        // Pull the agent back if it gets out of the bounding box 
        // ===============================================================================



        if (Position.X < 0.0)
            desiredVelocity += new Vector3d((FlockSystem.BoundingBox - Position.X) * bounceMultiplier, 0.0, 0.0);

        else if (Position.X > FlockSystem.BoundingBox)
            desiredVelocity += new Vector3d(-Position.X * bounceMultiplier, 0.0, 0.0);


        if (Position.Y < 0.0)
            desiredVelocity += new Vector3d(0.0, (FlockSystem.BoundingBox - Position.Y) * bounceMultiplier, 0.0);

        else if (Position.Y > FlockSystem.BoundingBox)
            desiredVelocity += new Vector3d(0.0, (-Position.Y) * bounceMultiplier, 0.0);

        //if (Position.Z < 0.0)
        //    desiredVelocity += new Vector3d(0.0, 0.0, -Position.Z);
        //else if (Position.Z > boundingBoxSize)
        //    desiredVelocity += new Vector3d(0.0, 0.0, boundingBoxSize - Position.Z);


        // ===============================================================================
        // If there are no neighbours nearby, the agent will maintain its veloctiy,
        // else it will perform the "alignment", "cohension" and "separation" behaviours
        // ===============================================================================

        if (neighbours.Count == 0)
            desiredVelocity += Velocity; // maintain the current velocity
        else
        {
            // -------------------------------------------------------------------------------
            // "Alignment" behavior 
            // -------------------------------------------------------------------------------

            Vector3d alignment = Vector3d.Zero;

            foreach (FlockAgent neighbour in neighbours)
                alignment += neighbour.Velocity;

            // We divide by the number of neighbours to actually get their average velocity
            alignment /= neighbours.Count;

            desiredVelocity += FlockSystem.AlignmentStrength * alignment;


            // -------------------------------------------------------------------------------
            // "Cohesion" behavior 
            // -------------------------------------------------------------------------------

            Point3d centre = Point3d.Origin;

            foreach (FlockAgent neighbour in neighbours)
                centre += neighbour.Position;

            // We divide by the number of neighbours to actually get their centre of mass
            centre /= neighbours.Count;

            Vector3d cohesion = centre - Position;

            desiredVelocity += FlockSystem.CohesionStrength * cohesion;


            // -------------------------------------------------------------------------------
            // "Separation" behavior 
            // -------------------------------------------------------------------------------

            Vector3d separation = Vector3d.Zero;

            foreach (FlockAgent neighbour in neighbours)
            {
                double distanceToNeighbour = Position.DistanceTo(neighbour.Position);

                if (distanceToNeighbour < FlockSystem.SeparationDistance)
                {
                    Vector3d getAway = Position - neighbour.Position;

                    /* We scale the getAway vector by inverse of distanceToNeighbour to make 
                       the getAway vector bigger as the agent gets closer to its neighbour */
                    separation += getAway / (getAway.Length * distanceToNeighbour);
                }


            }

            desiredVelocity += FlockSystem.SeparationStrength * separation;
        }


        // ===============================================================================
        // Avoiding the obstacles (repellers)
        // ===============================================================================

        foreach (Circle repeller in FlockSystem.Repellers)
        {
            double distanceToRepeller = Position.DistanceTo(repeller.Center);

            Vector3d repulsion = Position - repeller.Center;

            // Repulstion gets stronger as the agent gets closer to the repeller
            repulsion /= (repulsion.Length * distanceToRepeller);

            // Repulsion strength is also proportional to the radius of the repeller circle/sphere
            // This allows the user to tweak the repulsion strength by tweaking the radius
            repulsion *= 30.0 * repeller.Radius;

            desiredVelocity += repulsion;

        }
    }
}

}

using System;
using System.Collections.Generic;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;

namespace SurfaceTrails2.FlockingOnSrf
{
public class GhcFlockingOnSrf : GH_Component
{
private FlockSystem flockSystem;

    public GhcFlockingOnSrf()
        : base(
              "Flocking on surface",
              "FlockOnSrf",
              "Creates a swarm on a surface",
              "YFAtools",
              "AgentBased")
    {
    }


    protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
    {
        pManager.AddBooleanParameter("Reset", "Reset", "Reset", GH_ParamAccess.item, false);
        pManager.AddBooleanParameter("Play", "Play", "Play", GH_ParamAccess.item, false);
        //pManager.AddBooleanParameter("3D", "3D", "3D", GH_ParamAccess.item, true);
        pManager.AddSurfaceParameter("srf", "srf", "srf", GH_ParamAccess.item);
        pManager.AddIntegerParameter("Count", "Count", "Number of Agents", GH_ParamAccess.item, 50);
        pManager.AddNumberParameter("Timestep", "Timestep", "Timestep", GH_ParamAccess.item, 0.02);
        pManager.AddNumberParameter("Neighbourhood Radius", "Neighbourhood Radius", "Neighbourhood Radius", GH_ParamAccess.item, 3.5);
        pManager.AddNumberParameter("Alignment", "Alignment", "Alignment", GH_ParamAccess.item, 0.5);
        pManager.AddNumberParameter("Cohesion", "Cohesion", "Cohesion", GH_ParamAccess.item, 0.5);
        pManager.AddNumberParameter("Separation", "Separation", "Separation", GH_ParamAccess.item, 0.5);
        pManager.AddNumberParameter("Separation Distance", "Separation Distance", "Separation Distance", GH_ParamAccess.item, 1.5);
        pManager.AddCircleParameter("Repellers", "Repellers", "Repellers", GH_ParamAccess.list);
        pManager[10].Optional = true;
        pManager.AddBooleanParameter("Use Parallel", "Use Parallel", "Use Parallel", GH_ParamAccess.item, false);
        pManager.AddBooleanParameter("Use R-Tree", "Use R-Tree", "Use R-Tree", GH_ParamAccess.item, false);
    }


    protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
    {
        pManager.AddTextParameter("Info", "Info", "Information", GH_ParamAccess.item);
        pManager.AddPointParameter("Positions", "Positions", "The agent positions", GH_ParamAccess.list);
        pManager.AddVectorParameter("Velocities", "Velocities", "The agent veloctiies", GH_ParamAccess.list);
    }


    protected override void SolveInstance(IGH_DataAccess DA)
    {
        // ===============================================================================================
        // Read input parameters
        // ===============================================================================================

         bool iReset = true;
        bool iPlay = false;
        //bool i3D = false;
        Surface surface = null;
        int iCount = 0;
        double iTimestep = 0.0;
        double iNeighbourhoodRadius = 0.0;
        double iAlignment = 0.0;
        double iCohesion = 0.0;
        double iSeparation = 0.0;
        double iSeparationDistance = 0.0;
        List<Circle> iRepellers = new List<Circle>();
        bool iUseParallel = false;
        bool iUseRTree = false;

        DA.GetData("Reset", ref iReset);
        DA.GetData("Play", ref iPlay);
        //DA.GetData("3D", ref i3D);
        if (!DA.GetData(2, ref surface)) { return; }
        DA.GetData("Count", ref iCount);
        DA.GetData("Timestep", ref iTimestep);
        DA.GetData("Neighbourhood Radius", ref iNeighbourhoodRadius);
        DA.GetData("Alignment", ref iAlignment);
        DA.GetData("Cohesion", ref iCohesion);
        DA.GetData("Separation", ref iSeparation);
        DA.GetData("Separation Distance", ref iSeparationDistance);
        DA.GetDataList("Repellers", iRepellers);
        DA.GetData("Use Parallel", ref iUseParallel);
        DA.GetData("Use R-Tree", ref iUseRTree);


        // ===============================================================================================
        // Read input parameters
        // ===============================================================================================


        if (iReset || flockSystem == null)
        {
            flockSystem = new FlockSystem(iCount/*, i3D*/, surface.ToNurbsSurface());
        }
        else
        {
            // ===============================================================================================
            // Assign the input parameters to the corresponding variables in the  "flockSystem" object
            // ===============================================================================================

            flockSystem.Timestep = iTimestep;
            flockSystem.NeighbourhoodRadius = iNeighbourhoodRadius;
            flockSystem.AlignmentStrength = iAlignment;
            flockSystem.CohesionStrength = iCohesion;
            flockSystem.SeparationStrength = iSeparation;
            flockSystem.SeparationDistance = iSeparationDistance;
            flockSystem.Repellers = iRepellers;
            flockSystem.UseParallel = iUseParallel;
            //flockSystem.Surface = surface.ToNurbsSurface();


            // ===============================================================================
            // Update the flock
            // ===============================================================================

            if (iUseRTree)
                flockSystem.UpdateUsingRTree();
            else
                flockSystem.Update();

            if (iPlay) ExpireSolution(true);
        }

        // ===============================================================================
        // Output the agent positions and velocities so we can see them on display
        // ===============================================================================

        // Reparameterize Surface (You're not reparameterizing the surface here)
      

        List<GH_Point> positions = new List<GH_Point>();
        List<GH_Vector> velocities = new List<GH_Vector>();

        foreach (FlockAgent agent in flockSystem.Agents)
        {
            var pointToTranslate = agent.Position;
            positions.Add(new GH_Point(surface.PointAt(pointToTranslate.X, pointToTranslate.Y)));
            velocities.Add(new GH_Vector(agent.Velocity));
        }

        DA.SetDataList("Positions", positions);
        DA.SetDataList("Velocities", velocities);
    }


    protected override System.Drawing.Bitmap Icon { get { return Properties.Resources._28_8_18_FlockSimulation; } }


    public override Guid ComponentGuid { get { return new Guid("fbaaaf6f-e579-4f63-92e5-93642831c343"); } }
}

}

How do you expect me to find a mistake I don’t know about there?

1 Like

Okay so the mistake is here I created a point on the UV coordinates of surface I can’t make an xy vector into a UV vector

for (int i = 0; i < agentCount; i++)
{
FlockAgent agent = new FlockAgent(
_surface.PointAt(tu, tv),new Vector3d(Point3d.Subtract(_surface.PointAt(tu, tv), _surface.PointAt(vectorX, vectory))) );

            agent.FlockSystem = this;

                Agents.Add(agent);
            }
    }

An easy way, extract the perp frame at tu,tv and proyect your 3d vector in that plane. But of course you’ll have to project the new position to the surface after updating.

@youssef_aboualghar I am trying to solve the same problem through C# and being a novice, I haven’t reached very far.
Would it be possible for you to share your Rhino and Grasshopper / VS files for this solution?

@youssef_aboualghar Hi Youssef, your code is awesome.
the only error I am getting is with regards to the function of Util.cs.
Could you please share your Util.cs file, I believe it has more logic added to it than the original Util file from Long Nguyen’s workshop.
Thank you and best regards.