AntFarm - data management for Rhino and GH - Pre-Release out now


AntFarm (InCore Design - AntFarm) - 0.0.20-pre-release

AntFarm is now available for Rhino 7 (Windows only for now - Mac version soon) in the PackageManager (make sure to check ‘include pre-releases’). We are working towards a stable, feature rich, first release. During this initial pre-release phase the software and plugins are free to use. If this is useful to you please provide feedback, comments and wishes for further features.

*Please note that AntFarm is currently English only, but as we are a multilingual team we are working hard to support additional languages in the future.

AntFarm is a plugin for McNeel’s Rhino 3D allowing for the creation of data rich models by linking Rhino Objects with a set of data types through a simple user interface and a full suite of command line operations all backed by a SQL Database. Objects and Data can be grouped into “DataSets” allowing for tracking, grouping and sorting any combination of meaningful relationships. AntFarm is specifically designed to be discipline agnostic but has uses in AEC, Urban Design, Manufacturing and many more. It seeks to be a solution to the following problem.

https://mcneel.myjetbrains.com/youtrack/issue/RH-35934

AntFarm provides a core component plugin to Rhino and an API allowing for additional features/plugins and domain specific uses to be integrated. As of now it is bundled with full Grasshopper integration (AntFarmGH) as well as an import plugin (AntFarmIO) allowing for the import of Urban and Geographic data (for example .shp, .cityGML, .osm formats). Please note that the core focus is not Urban and GIS only. These formats were chosen for ease of implication, as well as a case study to showcase the geometric and data potential of AntFarm.

We are working towards supporting hierarchical data with the intent of being able to provide access to formats such as IFC, and a more feature rich CityGML implementation with part to whole relationships. Please note that development in this initial pre-release phase will primarily focus on the core data features and roadmap as well as flushing out the UI/UX workflows. We are working towards supporting additional data formats, but only as they align with the development goals in the core component - such as supporting hierarchical data. Data export for the various formats is on the planned roadmap but is not intended to be prioritized in this current development round.

Core System with a full suite of commands that allow for the management and manipulation of data and Rhino Objects.

  • 47 individual Rhino Commands.
  • Data is serialized to the .3dm files.
  • A full SQL database is provided through SQLite (could be any backend).
  • Copy/Paste and merge data between different Rhino Files.
  • Track object data such as Area, Length, Volume and/or Location.
  • Filter and sort data in the UI

A simplified User Interface that provides a familiar spreadsheet view of the data that is linked to the selected object(s).

  • Import/Export Data to csv.
  • Import/Export DataSet schemas for standardization and reuse.
  • Export to SQLite .db3 files for further use and inspection.

An API allowing for domain specific integration.

  • Grasshopper Integrated (sample file).
  • Urban Data Importer based on GDAL and own developed parser

Currently supports importing of

  • .shp (ShapeFile)
  • .gml (cityGML)
  • .tif (geoTIFF)
  • .osm

Example file found here.

Similar capabilities to the existing Rhino Plugins:

  • Elefront
  • The Proving Ground - Semantic
  • Rhino Terrain / Rhino City
  • MEERKAT GIS
  • @IT

As working practitioners and educators we sincerely hope that you find AntFarm useful and that it aids you in your creations.

The Incore Team
Christian + Joel

12 Likes

Could you please make youtube tutorial. I am kind of lost.

2 Likes

Sure. We will. Starting with the very basics.

Kindest
Christian

1 Like

Here we go.

More to come.

Kindest
Joel+Christian

I’m not sure how this works in practise or if it’s for me, would be nice to have a sales pitch for this or a demo video of some kind? :slight_smile:

Hey Julian,

we are working on a series of videos to get people an idea what this tool is about. As of this post I guess you are interested in geo data. The CAD Mapper website you are linking uses OpenStreetMap data to generate the dxf files. With AntFarm/AntFarmIO you can import OpenStreetMap files with the associated data into Rhino. Once you have both installed just use the Rhino import and for *.osm files an import window should popup that guides you through the import (video is coming soon :wink: ).

Kindest
Christian

2 Likes

The second video we just finished and published covering the very basics of AntFarm.

Hopefully this gets you started.

Kindest
Christian+Joel

2 Likes

Good evening,

just finished the next tutorial covering Categories, Elements, Properties and PropertyTypes.

Any feedback is more than welcome.

Kindest
Christian+Joel

2 Likes

And an other one showing the very basics of the Grasshopper intergration.

To get a bit off an idea of what you can do, this sample grasshopper file might be helpful.

Tower.gh (45.1 KB)

Well, to use the file you will need at least to install from the package manager AntFarm and AntFarmGH. Enjoy.

Kindest
Joel + Christian

1 Like

Alright - so as we covered the very basics in the first 4 videos, the next video is about getting GIS data into Rhino.

Hopefully that explains how the import is working.

Kindest
Christian+Joel

I cannot see the xyz to mesh converter in the grasshopper plugin. Have you guys removed that component?

Ishan,

my bad. Just pushed updated packages (0.0.24-beta) to Rhino packages server.

You find the component in the AntFarmML package. Not as robust as I would like the component to work to always generate a reasonable mesh, but a first start. Make sure your XYZ data has an overall rectangular footprint. Something like this (2x3 1km x 1km patches).

Kindest
Joel+Christian

1 Like

Attached the code we use to load the XYZ files.

using System;
using Grasshopper.Kernel;
using System.Collections.Generic;
using AntFarmML.Properties;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using System.Linq;
using System.IO;
using System.Globalization;
using Grasshopper.Kernel.Geometry.Delaunay;
using Grasshopper.Kernel.Geometry;

namespace AntFarmML.GH.Components.Analysis
{
    public class AF_GH_XYZToMesh : GH_Component
    {
        /// <summary>
        /// Initializes a new instance of the AF_GH_DataSetNew class.
        /// </summary>
        public AF_GH_XYZToMesh()
          : base("XYZ To Mesh", "XYZ2Mesh",
              "Generates a quad mesh based on XYZ point data.",
              GrasshopperCategories.AntFarm.ToString(), GrasshopperSubCategories.Analysis.ToString())
        {
        }

        /// <summary>
        /// Registers all the input parameters for this component.
        /// </summary>
        protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
            pManager.AddTextParameter("Path", "P", "Path to the XYZ files", GH_ParamAccess.list);
            pManager.AddBooleanParameter("Combine", "C", "Combine to one mesh", GH_ParamAccess.item);

            pManager[1].Optional = true;
        }

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_OutputParamManager pManager)
        {
            pManager.AddMeshParameter("Mesh", "M", "Quad Mesh", GH_ParamAccess.tree);
        }

        /// <summary>
        /// This is the method that actually does the work.
        /// </summary>
        /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            List<string> paths = new List<string>();
            GH_Boolean combine = new GH_Boolean(false);

            if (!DA.GetDataList(0, paths)) return;
            DA.GetData(1, ref combine);

            List<GH_Mesh> meshes = new List<GH_Mesh>();
            GetGeometry(paths, combine, ref meshes);
            DA.SetDataList(0, meshes);
        }

        /// <summary>
        /// Provides an Icon for the component.
        /// </summary>
        protected override System.Drawing.Bitmap Icon
        {
            get
            {
                return Resources.XYZ;
            }
        }

        /// <summary>
        /// Gets the unique ID for this component. Do not change this ID after release.
        /// </summary>
        public override Guid ComponentGuid
        {
            get { return new Guid("5C5B14F6-4780-4F21-8CB7-26B8748509E9"); }
        }

        public override GH_Exposure Exposure
        {
            get
            {
                return GH_Exposure.primary;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="paths"></param>
        /// <param name="combine"></param>
        /// <param name="meshes"></param>
        private void GetGeometry(List<string> paths, GH_Boolean combine, ref List<GH_Mesh> meshes)
        {
            if (combine.Value)
            {
                List<Point3d> points = new List<Point3d>();
                foreach (string path in paths)
                {
                    if (File.Exists(path))
                    {
                        points.AddRange(GetPointsFromFile(path));
                    }
                }
                meshes.Add(new GH_Mesh(GetMesh(points.OrderBy(p => p.Y).ThenBy(p => p.X).ToList())));
            }
            else
            {
                foreach (string path in paths)
                {
                    if (File.Exists(path))
                    {
                        List<Point3d> points = GetPointsFromFile(path);
                        meshes.Add(new GH_Mesh(GetMesh(points)));
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private List<Point3d> GetPointsFromFile(string path)
        {
            CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
            List<Point3d> points = new List<Point3d>();

            foreach (string line in File.ReadLines(@path))
            {
                string[] ss = line.Trim().Split(' ');

                if (ss.Length == 3)
                {
                    double x, y, z;
                    if (!double.TryParse(ss[0], NumberStyles.Number, culture, out x))
                    {
                        break;
                    }
                    if (!double.TryParse(ss[1], NumberStyles.Number, culture, out y))
                    {
                        break;
                    }
                    if (!double.TryParse(ss[2], NumberStyles.Number, culture, out z))
                    {
                        break;
                    }
                    points.Add(new Point3d(x, y, z));
                }
                else
                {
                    AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Something went wrong while parsing XYZ point data (line data count mismatch).");
                }
            }
            return points;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        private Mesh GetMesh(List<Point3d> points)
        {
            double lastX = points[0].X;

            List<List<Point3d>> ppoints = new List<List<Point3d>>();
            List<Point3d> pp = new List<Point3d>();

            foreach (Point3d p in points)
            {
                if (p.X < lastX)
                {
                    ppoints.Add(pp);
                    pp = new List<Point3d>();
                }
                pp.Add(p);
                lastX = p.X;
            }
            ppoints.Add(pp);

            bool hasConstantUV = true;
            int v_count = ppoints[0].Count;

            foreach (List<Point3d> _pp in ppoints)
            {
                if (_pp.Count != v_count)
                {
                    hasConstantUV = false;
                }
            }

            if (hasConstantUV)
            {
                Mesh mesh = new Mesh();

                foreach (List<Point3d> _pp in ppoints)
                {
                    mesh.Vertices.AddVertices(_pp);
                }

                for (int i = 0; i < ppoints.Count - 1; i++)
                {
                    for (int j = 0; j < ppoints[i].Count - 1; j++)
                    {
                        int i0 = i * ppoints[i].Count + j;
                        int i1 = i * ppoints[i].Count + j + 1;
                        int i2 = (i + 1) * ppoints[i].Count + j + 1;
                        int i3 = (i + 1) * ppoints[i].Count + j;

                        mesh.Faces.AddFace(new MeshFace(i0, i1, i2, i3));
                    }
                }

                mesh.FaceNormals.ComputeFaceNormals();
                mesh.Normals.ComputeNormals();
                mesh.Compact();

                return mesh;
            }
            else
            {
                Mesh mesh = new Mesh();
                Mesh delMesh = new Mesh();

                Node2List node2List = new Node2List();
                List<Point3d> node3List = new List<Point3d>();

                foreach (List<Point3d> _pp in ppoints)
                {
                    foreach(Point3d p in _pp)
                    {
                        node2List.Append(new Node2(p.X, p.Y));
                        node3List.Add(p);
                    }
                }

                List<Face> faces = Solver.Solve_Faces(node2List, 1);

                //output
                delMesh = Solver.Solve_Mesh(node2List, 1, ref faces);

                mesh.Vertices.AddVertices(node3List);
                mesh.Faces.AddFaces(delMesh.Faces);

                mesh.FaceNormals.ComputeFaceNormals();
                mesh.Normals.ComputeNormals();
                mesh.Compact();

                return mesh;
            }
        }
    }
}

Any suggestions and/or improvements are more than welcome.

Kindest
Christian+Joel

Good evening,

new version is out.

We improved a lot of things. Some bugs are still waiting to be fixed. Some will need to wait till code base is fixed/improved by others (@curtisw - waiting for this one here RH-69687 :wink: - might make it to Rhino 8 - 7.22 would be much better).

Main new features are:

  • filter textbox at the top for each attribute (toggle the button left to the textbox for different filter options ‘%’ - like, ‘=’ - equals).
  • Summary dropdown at the bottom of double, integer and float attributes to either display the sum, average, mean, min, max value of that attribute column.

Kindest
Christian

1 Like

Hi,

just fixed a bug regarding the file opening and importing.

Please find the newest build in the PackageMananger.

Kindest
Joel+Christian

Good evening,

just made a new build against Rhino 7.23. Please find the latest version using the package manager:

Kindest
Joel+Christian

1 Like

@Christian_Hartz Hi Christian, thanks so much for making this! It is exactly what I have been looking for.
I am running into one issue. It won’t let me create a dataset. When I run DataSetNew and type in a name, nothing happens. I tried getting around it by using the Grasshopper definitions. I could make a dataset in Grasshopper and it’s values would show up in Rhino, but it would never “stick” in Rhino, for lack of better words.

I’ve attached a test file and definition showing what I mean. Am I doing something completely wrong?

Antfarm GH test.gh (13.9 KB)
Antfarm test.3dm (253.4 KB)

Hi Cstone,

I tested here on a clean windows machine with the current Rhino 7.24 build and everything worked fine. We might need to setup a Zoom call or something.

When are you available?

Kindest
Christian

PS: I will make new packages this evening - version 0.0.32 beta.

Hi everyone,

just made a new build for Rhino 7.24.

Enjoy
Joel & Christian

I’m not sure how it fixed whatever I was doing wrong, but I installed 0.0.32-beta and it’s working just fine now. I will play around with it and let you know if I have any questions. Thanks!

1 Like