The main library of COMPAS

Overview

The main library consists of a core package and several additional packages for integration of the core into CAD software. The core package defines all real functionality. The CAD packages simply provide a unified framework for processing, visualising and interacting with datastructures and geometrical objects, and for building user interfaces in different CAD software.

Core functionality

To deal with the different academic backgrounds, programming skills, computational experience, and best/accepted practices of its users and their respective fields, COMPAS is implemented primarily in Python and designed to be entirely independent of the functionality of CAD software. As a result, it can be used on different platforms and in combination with external software and libraries, and at the same time take advantage of the various scientific and non-scientific libraries available in the Python ecosystem itself. Furthermore, and perhaps more importantly, it ensures that research based on COMPAS is not tied to a specific CAD-based ecosystem.

Currently compas contains several sub-packages, which can be divided into four categories.

Helpers

  • compas.com: communication with external software

  • compas.files: handlers for file formats related to geometry definition, cad interoperability, manufacturing

  • compas.interop: interoperability with C/C++ code and libraries

  • compas.utilities: other useful things

Datastructures

  • compas.datastructures: mesh (half-edge), network (graph), volmesh (half-plane)

Algorithms

  • compas.geometry: geometry processing

  • compas.numerical: numerical methods, solvers, …

  • compas.topology: combinatorics, traversal, subdivision, …

Visualisation

  • compas.plotters: 2D visualisation, dynamic plots, basic interaction

  • compas.viewers: basic 3D visualisation

CAD integration

The core functionality of compas is implemented independent of the functionality provided by CAD software. This ensures that research based on COMPAS is not tied to a specific tool chain en can be used more flexibly in different environments and on different platforms.

However, in the context of this framework CAD tools are obviously indispensible tools to construct and manipulate geometry, apply constraints interactively, make user interfaces, or even just to use as viewer for running scripts. The CAD helper packages (compas_blender, compas_maya, compas_rhino) provide a unified and consistent interface to CAD tools and their ecosystems.

Tutorial

Working with datastructures

Network, Mesh, VolMesh

The main library of the COMPAS framework contains three fundamental data structures:

  • Network

  • Mesh

  • VolMesh

These can be used as-is, but they can also be easily extended or combined to form entirely different data structures. For this tutorial, we will use the mesh data structure to demonstrate the general principles and give an overview of the possibilities.

_images/datastructures.png

Construction

All datastructures come with factory constructors. These are implemented as class methods (using the @classmethod decoreator) and are named using the following pattern .from_xxx.

mesh = Mesh.from_data(...)
mesh = Mesh.from_json(...)
mesh = Mesh.from_obj(...)
mesh = Mesh.from_vertices_and_faces(...)
mesh = Mesh.from_polygons(...)
mesh = Mesh.from_polyhedron(...)
mesh = Mesh.from_points(...)

compas also provides sample data that can be used together with the constructors, for example for debugging or to generate example code.

from __future__ import print_function

import compas
from compas.datastructures import Mesh

mesh = Mesh.from_obj(compas.get('faces.obj'))

print(mesh)

# ================================================================================
# Mesh summary
# ================================================================================
#
# - name: Mesh
# - vertices: 36
# - edges: 60
# - faces: 25
# - vertex degree: 2/4
# - face degree: 2/4
#
# ================================================================================

Printing the mesh produces a summary of the mesh’s properties: the number of vertices, edges and faces and information about vertex and face degree.

Data

All data accessors are iterators; they are meant to be iterated over. Lists of data have to be constructed explicitly.

  • mesh.vertices()

  • mesh.faces()

  • mesh.halfedges()

  • mesh.edges()

from __future__ import print_function

import compas
from compas.datastructures import Mesh

mesh = Mesh.from_obj(compas.get('faces.obj'))

for key in mesh.vertices():
    print(key)

for key, attr in mesh.vertices(True):
    print(key, attr)

print(list(mesh.vertices()))
print(mesh.number_of_vertices())
from __future__ import print_function

import compas
from compas.datastructures import Mesh

mesh = Mesh.from_obj(compas.get('faces.obj'))

for fkey in mesh.faces():
    print(fkey)

for fkey, attr in mesh.faces(True):
    print(fkey, attr)

print(len(list(mesh.faces()))
print(mesh.number_of_faces())

Topology

  • mesh.is_valid()

  • mesh.is_regular()

  • mesh.is_connected()

  • mesh.is_manifold()

  • mesh.is_orientable()

  • mesh.is_trimesh()

  • mesh.is_quadmesh()

  • mesh.vertex_neighbours()

  • mesh.vertex_degree()

  • mesh.vertex_faces()

  • mesh.vertex_neighbourhood()

  • mesh.face_vertices()

  • mesh.face_halfedges()

  • mesh.face_neighbours()

  • mesh.face_neighbourhood()

  • mesh.face_vertex_ancestor()

  • mesh.face_vertex_descendant()

import compas
from compas.datastructures import Mesh
from compas.plotters import MeshPlotter

mesh = Mesh.from_obj(compas.get('faces.obj'))

plotter = MeshPlotter(mesh)

root = 17
nbrs = mesh.vertex_neighbours(root, ordered=True)

text = {nbr: str(i) for i, nbr in enumerate(nbrs)}
text[root] = root

facecolor = {nbr: '#cccccc' for nbr in nbrs}
facecolor[root] = '#ff0000'

plotter.draw_vertices(
    text=text,
    facecolor=facecolor,
    radius=0.15
)
plotter.draw_faces()
plotter.draw_edges()

plotter.show()
_images/datastructures-1.png

Geometry

  • mesh.vertex_coordinates()

  • mesh.vertex_area()

  • mesh.vertex_centroid()

  • mesh.vertex_normal()

  • mesh.face_coordinates()

  • mesh.face_area()

  • mesh.face_centroid()

  • mesh.face_center()

  • mesh.face_normal()

  • mesh.face_flatness()

  • mesh.edge_coordinates()

  • mesh.edge_vector()

  • mesh.edge_direction()

  • mesh.edge_length()

  • mesh.edge_midpoint()

import compas
from compas.datastructures import Mesh
from compas.plotters import MeshPlotter

mesh = Mesh.from_obj(compas.get('faces.obj'))

plotter = MeshPlotter(mesh)

plotter.draw_vertices()
plotter.draw_faces(text={fkey: '%.1f' % mesh.face_area(fkey) for fkey in mesh.faces()})
plotter.draw_edges()

plotter.show()
_images/datastructures-2.png

Geometry processing

Applying algorithms

Using C/C++ code

Summary

In this tutorial we will write a smoothing function in C++ and make it available directly in Python. We will also use a callback to live visualise the iterations of the algorithm and to dynamically change the boundary conditions. An implementation of the code in this tutorial is available in the geometry package:

  • compas.geometry.smooth_centroid_cpp()

Requirements

  • a C++ compiler (for example g++, which is part of the GNU compiler collection)

Setup

For this tutorial, we will use the following files and folders:

+ smoothing_cpp
    + src
        - main.cpp
    - smoothing.so
    - smoothing.py

The smoothing function

First, the C++ code. The smoothing function will implement a simple barycentric smoothing algorithm, in which at every iteration, each vertex is moved to the barycentre of its neighbours.

By the way, I have no real experience in writing C++ code, so i am sure that this can be done a lot more elegantly and efficiently. I would be happy to hear about any and all suggestions (vanmelet@ethz.ch).

#include <vector>

using namespace std;

extern "C"
{
    typedef void callback(int k);

    void smooth_centroid(int v, int *nbrs, int *fixed, double **vertices, int **neighbours, int kmax, callback func);
}

void smooth_centroid(int v, int *nbrs, int *fixed, double **vertices, int **neighbours, int kmax, callback func)
{
    int k;
    int i;
    int j, n;
    double cx, cy, cz;

    vector< vector<double> > xyz(v, vector<double>(3, 0.0));

    for (k = 0; k < kmax; k++) {

        // make a copy of the current vertex positions

        for (i = 0; i < v; i++) {
            xyz[i][0] = vertices[i][0];
            xyz[i][1] = vertices[i][1];
            xyz[i][2] = vertices[i][2];
        }

        // move each vertex to the barycentre of its neighbours

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

            // skip the vertex if it is fixed

            if (fixed[i]) {
                continue;
            }

            cx = 0.0;
            cy = 0.0;
            cz = 0.0;

            for (j = 0; j < nbrs[i]; j++) {
                n = neighbours[i][j];

                cx += xyz[n][0];
                cy += xyz[n][1];
                cz += xyz[n][2];
            }

            vertices[i][0] = cx / nbrs[i];
            vertices[i][1] = cy / nbrs[i];
            vertices[i][2] = cz / nbrs[i];
        }

        // call the callback

        func(k);
    }
}

The ctypes wrapper

Looking a the signature of the C++ function, the code is expecting the following input arguments:

  1. int v

  2. int *nbrs

  3. int *fixed

  4. double **vertices

  5. int **neighbours

  6. int kmax

  7. callback func

Or, in other words:

  1. the number of vertices, as an integer

  2. the number of neighbours per vertex, as a an array of integers

  3. a mask identifying the fixed vertices, as an array of integers (0/1)

  4. the vertex coordinates, as a two-dimensional array of doubles

  5. the vertex neighbours, as a two-dimensional array of integers

  6. the maximum number of iterations, as an integer

  7. the callback function, as a function of type callback

Note that the sizes of the arrays are unknown at compile time, since they depend on the number of vertices in the system. Therefore they are passed as pointers. My understanding of this is based on whatever google spat out and a few SO posts…

We want to be able to call the function from Python, which essentially boils down to something like this:

import ctypes
import compas
from compas.datastructures import Mesh
from compas.plotters import MeshPlotter
from compas.interop.core.cpp.xdarray import Array1D
from compas.interop.core.cpp.xdarray import Array2D

# get the C++ smoothing library

smoothing = ctypes.cdll.LoadLibrary('smoothing.so')

# make a mesh

mesh = Mesh.from_obj(compas.get('faces.obj'))

# extract the required data for smoothing

vertices   = mesh.get_vertices_attributes('xyz')
adjacency  = [mesh.vertex_neighbours(key) for key in mesh.vertices()]
fixed      = [int(mesh.vertex_degree(key) == 2) for key in mesh.vertices()]
v          = len(vertices)
nbrs       = [len(adjacency[i]) for i in range(v)]
neighbours = [adjacency[i] + [0] * (10 - nbrs[i]) for i in range(v)]
kmax       = 50

# ==============================================================================
# convert the python data to C-compatible types
# ==============================================================================

# ...

# ==============================================================================

# make a plotter for visualisation

plotter = MeshPlotter(mesh, figsize=(10, 7))

# plot the original line geometry as a reference

lines = []
for a, b in mesh.edges():
    lines.append({
        'start': mesh.vertex_coordinates(a, 'xy'),
        'end'  : mesh.vertex_coordinates(b, 'xy'),
        'color': '#cccccc',
        'width': 0.5
    })

plotter.draw_lines(lines)

# plot the starting point

plotter.draw_vertices()
plotter.draw_edges()

plotter.update(pause=0.5)

# ==============================================================================
# define the callback function
# ==============================================================================

def callback(k):
    print(k)

    # update the plot
    # and change the boundary conditions

    # ...

# ==============================================================================

# ==============================================================================
# set the argument types for the smoothing function
# and call it with C-compatible data
# ==============================================================================

smoothing.smooth_centroid.argtypes = [...]

smoothing.smooth_centroid(...)

# ==============================================================================
C-compatible types and data

Some of these conversion are quite trivial. For example, converting an integer is simply:

c_v = ctypes.c_int(v)

Also the 1D arrays are not too complicated. For example:

c_fixed_type = ctypes.c_int * v
c_fixed_data = c_fixed_type(*fixed)

The 2D arrays are already a bit trickier. For example:

c_vertex_type = ctypes.c_double * 3
c_vertices_type = ctypes.POINTER(ctypes.c_double) * v
c_vertices_data = c_vertices_type(*[c_vertex_type(x, y, z) for x, y, z in vertices])

Converting the callback is also quite straightforward:

c_callback_type = ctypes.CFUNCTYPE(None, c_int)
c_callback = c_callback_type(callback)

To simplify the construction of C-compatible types, and C-compatible data, there are a few helper classes in compas.interop:

  • compas.interop.core.cpp.xdarray.Array1D

  • compas.interop.core.cpp.xdarray.Array2D

  • compas.interop.core.cpp.xdarray.Array3D

With these helpers, the code for the conversion becomes:

# ==============================================================================
# convert the python data to C-compatible types
# ==============================================================================

c_nbrs       = Array1D(nbrs, 'int')
c_fixed      = Array1D(fixed, 'int')
c_vertices   = Array2D(vertices, 'double')
c_neighbours = Array2D(neighbours, 'int')
c_callback   = ctypes.CFUNCTYPE(None, ctypes.c_int)

# ==============================================================================

Then we let the smoothing function what it can expect in terms of types by setting the argument types of the callable:

# ==============================================================================
# set the argument types for the smoothing function
# and call it with C-compatible data
# ==============================================================================

smoothing.smooth_centroid.argtypes = [
    c_int,
    c_nbrs.ctype,
    c_fixed.ctype,
    c_vertices.ctype,
    c_neighbours.ctype,
    c_int,
    c_callback
]

smoothing.smooth_centroid(
    c_int(v),
    c_nbrs.cdata,
    c_fixed.cdata,
    c_vertices.cdata,
    c_neighbours.cdata,
    c_int(kmax),
    c_callback(wrapper)
)

# ==============================================================================

The last step is to define the functionality of the callback. The goal is to visualise the changing geometry and to change the location of the fixed points during the smoothing process; in C++, but from Python.

# ==============================================================================
# define the callback function
# ==============================================================================

def callback(k):
    print(k)

    xyz = c_vertices.cdata

    # change the boundary conditions

    if k < kmax - 1:
        xyz[18][0] = 0.1 * (k + 1)

    # update the plot

    plotter.update_vertices()
    plotter.update_edges()
    plotter.update(pause=0.001)

    for key, attr in mesh.vertices(True):
        attr['x'] = xyz[key][0]
        attr['y'] = xyz[key][1]
        attr['z'] = xyz[key][2]

# ==============================================================================

The result

Putting it all together, we get the following script. Simply copy-paste it and run…

import ctypes
from ctypes import *
import compas
from compas.datastructures import Mesh
from compas.plotters import MeshPlotter
from compas.interop.core.cpp.xdarray import Array1D
from compas.interop.core.cpp.xdarray import Array2D


# get the C++ smoothing library

smoothing = ctypes.cdll.LoadLibrary('smoothing.so')


# make a mesh

mesh = Mesh.from_obj(compas.get('faces.obj'))


# extract the required data for smoothing

vertices   = mesh.get_vertices_attributes('xyz')
adjacency  = [mesh.vertex_neighbours(key) for key in mesh.vertices()]
fixed      = [int(mesh.vertex_degree(key) == 2) for key in mesh.vertices()]
v          = len(vertices)
nbrs       = [len(adjacency[i]) for i in range(v)]
neighbours = [adjacency[i] + [0] * (10 - nbrs[i]) for i in range(v)]
kmax       = 50


# convert the python data to C-compatible types

c_nbrs       = Array1D(nbrs, 'int')
c_fixed      = Array1D(fixed, 'int')
c_vertices   = Array2D(vertices, 'double')
c_neighbours = Array2D(neighbours, 'int')
c_callback   = CFUNCTYPE(None, c_int)


# make a plotter for visualisation

plotter = MeshPlotter(mesh, figsize=(10, 7))


# plot the original line geometry as a reference

lines = []
for a, b in mesh.edges():
    lines.append({
        'start': mesh.vertex_coordinates(a, 'xy'),
        'end'  : mesh.vertex_coordinates(b, 'xy'),
        'color': '#cccccc',
        'width': 0.5
    })

plotter.draw_lines(lines)


# plot the starting point

plotter.draw_vertices(facecolor={key: '#000000' for key in mesh.vertices() if mesh.vertex_degree(key) == 2})
plotter.draw_edges()

plotter.update(pause=0.5)


# define the callback function

def callback(k):
    print(k)

    xyz = c_vertices.cdata

    # change the boundary conditions

    if k < kmax - 1:
        xyz[18][0] = 0.1 * (k + 1)

    # update the plot

    plotter.update_vertices()
    plotter.update_edges()
    plotter.update(pause=0.001)

    for key, attr in mesh.vertices(True):
        attr['x'] = xyz[key][0]
        attr['y'] = xyz[key][1]
        attr['z'] = xyz[key][2]


# set the argument types for the smoothing function
# and call it with C-compatible data

smoothing.smooth_centroid.argtypes = [
    c_int,
    c_nbrs.ctype,
    c_fixed.ctype,
    c_vertices.ctype,
    c_neighbours.ctype,
    c_int,
    c_callback
]

smoothing.smooth_centroid(
    c_int(v),
    c_nbrs.cdata,
    c_fixed.cdata,
    c_vertices.cdata,
    c_neighbours.cdata,
    c_int(kmax),
    c_callback(callback)
)


# keep the plotting window alive

plotter.show()

CAD environments

This setup can also be used in CAD environments. Assuming that “if it works in RhinoPython, it works everywhere”, here is a script for Rhino that does the same as the one above, but uses compas.geometry.smooth_centroid_cpp() to make things a bit simpler.

from __future__ import print_function
from __future__ import absolute_import
from __future__ import division

import compas
import compas_rhino

from compas.datastructures import Mesh
from compas.geometry import smooth_centroid_cpp

from compas_rhino.helpers import MeshArtist

kmax = 50

# make a mesh
# and set the default vertex and edge attributes

mesh = Mesh.from_obj(compas.get('faces.obj'))

edges = list(mesh.edges())

# extract numerical data from the datastructure

vertices  = mesh.get_vertices_attributes(('x', 'y', 'z'))
adjacency = [mesh.vertex_neighbours(key) for key in mesh.vertices()]
fixed     = [int(mesh.vertex_degree(key) == 2) for key in mesh.vertices()]

# make an artist for dynamic visualization
# and define a callback function
# for drawing the intermediate configurations
# and for changing the boundary conditions during the iterations

slider = 30  # this is the top left corner

artist = MeshArtist(mesh, layer='SmoothMesh')

artist.clear_layer()


def callback(k, xyz):
    compas_rhino.wait()

    print(k)

    if k < kmax - 1:
        xyz[slider][0] = 0.1 * (k + 1)

    artist.clear_edges()
    artist.draw_edges()
    artist.redraw()

    for key, attr in mesh.vertices(True):
        attr['x'] = xyz[key][0]
        attr['y'] = xyz[key][1]
        attr['z'] = xyz[key][2]


xyz = smooth_centroid_cpp(vertices, adjacency, fixed, kmax=kmax, callback=callback)

for key, attr in mesh.vertices(True):
    attr['x'] = xyz[key][0]
    attr['y'] = xyz[key][1]
    attr['z'] = xyz[key][2]

artist.clear_edges()
artist.draw_vertices()
artist.draw_edges()
artist.redraw()

Using callbacks

compas implements a callback mechanism that provides a consistent way to customise algorithms, apply constraints, visualise progress of iterative algorithms, …

Note

A callback is a function that is passed to another function as a parameter such that the latter function can call the former at any time during its own execution. Perhaps the name callback is based on the fact that through the callback the second function can “call back” into the scope where the first function was defined. Or perhaps not :), but it is a convenient way to think about it because at time of execution, the callback has access to the variables of the scope in which it was defined.

In principle, the mechanism can be summarised with the following snippets.

# algorithm.py

def algo(..., callback=None):

    if callback:
        if not callable(callback):
            raise Exception('The callback function is not callable.')

    # stuff

    for k in range(kmax):
        # stuffs

        if callback:
            callback(k)

    # more stuffs
# somescript.py

from algorithm import algo

text = 'iteration number:'

def callback(k):
    print(text, k)

algo(..., callback=callback)

In this case, the result would be a bit boring, because the callback would simply print the number of the current iteration of the algorithm. Note, however, that the callback has access to the variable text, even though that ariable was defined in a different context that the one in which the callback is called.

Dynamic visualisation

Throughout the main library, callbacks are often used in combination with the plotters to visualise intermediate steps of an algorithm, or to visualise the progress of an iterative algorithm. Both can be very useful mechanisms for debugging.

For example, from compas.geometry, an code snippet visualising the progress of an iterative smoothing algorithm (compas.geometry.mesh_smooth_centroid()).

import compas

from compas.datastructures import Mesh
from compas.plotters import MeshPlotter
from compas.geometry import mesh_smooth_centroid

mesh = Mesh.from_obj(compas.get('faces.obj'))

fixed = [key for key in mesh.vertices() if mesh.vertex_degree(key) == 2]

plotter = MeshPlotter(mesh, figsize=(10, 7))

lines = []
for u, v in mesh.edges():
    lines.append({
        'start' : mesh.vertex_coordinates(u, 'xy'),
        'end'   : mesh.vertex_coordinates(v, 'xy'),
        'color' : '#cccccc',
        'width' : 0.5
    })
plotter.draw_lines(lines)

plotter.draw_vertices(facecolor={key: '#ff0000' for key in fixed})
plotter.draw_faces()
plotter.draw_edges()

plotter.update(pause=1.0)

def callback(mesh, k, args):
    print(k)
    plotter.update_vertices()
    plotter.update_faces()
    plotter.update_edges()
    plotter.update(pause=0.001)

mesh_smooth_centroid(mesh, kmax=50, fixed=fixed, callback=callback)

plotter.show()

We use a mesh plotter as visualisation tool.

plotter = MeshPlotter(mesh, figsize=(10, 7))

First, as a reference, we plot a set of lines corresponding to the original configuration of the mesh.

lines = []
for u, v in mesh.edges():
    lines.append({
        'start' : mesh.vertex_coordinates(u, 'xy'),
        'end'   : mesh.vertex_coordinates(v, 'xy'),
        'color' : '#cccccc',
        'width' : 0.5
    })
plotter.draw_lines(lines)

Then we initialise the vertices, edges and faces that will be updated at every iteration to visualise the process. We also tell the plotter to pause for a second, to be able to digest the orginal configuration before the smoothing starts.

plotter.draw_vertices(facecolor={key: '#ff0000' for key in fixed})
plotter.draw_faces()
plotter.draw_edges()

plotter.update(pause=1.0)

The next step is to define the callback function that will update the plotter. The plotter has dedicated functions for this. They update the geometry of the collections of vertices, edges and faces while keeping the style attributes as they were set by the original calls to the draw functions. With a call to the general update function we update the drawing.

The callback is handed off to the smoothing algorithm, which will call it at every iteration. By default, the callback receives the mesh object and the number of the current iteration as firs and second parameter, and then any additional parameters that were passed to the algorithm.

def callback(mesh, k, args):
    print(k)
    plotter.update_vertices()
    plotter.update_faces()
    plotter.update_edges()
    plotter.update(pause=0.001)

mesh_smooth_centroid(mesh, kmax=50, fixed=fixed, callback=callback)

Finally, we make sure that the plotting window remains active and visible.

plotter.show()

The result shpould be something like this.

_images/tutorial_callbacks_smoothing.gif

Applying constraints

Live interaction

CAD integration

  • general concepts

  • helper = artist + selector + modifier

  • working in Rhino

  • working in Blender

  • working in Maya

CPython in Rhino

In Rhino, Python scripts can be used to

(from the docs what is python)

  • Automate a repetitive task in Rhino much faster than you could do manually.

  • Perform tasks in Rhino or Grasshopper that you don’t have access to in the standard set of Rhino commands or Grasshopper components.

  • Generate geometry using algorithms.

  • Many, many other things. It is a programming language after all.

Or, create

(also from the docs where can you use python in rhino)

  • Interactive scripts.

  • New custom commands.

  • Create new plug-ins.

  • Read and Write customized file formats.

  • Interact with cloud applications.

  • Create realtime links to other applications

  • Create customer Grasshopper components

  • Store and display project specific information beyond what basic Rhino can store.

Rhino runs Python scripts using IronPython 2.7. This is great because it provides access to the .NET framework. However, this also means that many (C)Python libraries are not available. For example Numpy, Scipy, Matplotlib, SimPy, Numba, NetworkX, CVXPY, Shapely, PyOpenGL, PySide, …

With compas.utilities.XFunc this limitation can be partly removed. Below is an example from the API reference.

import compas
import compas_rhino

from compas.datastructures import Mesh
from compas.utilities import XFunc
from compas_rhino.helpers import MeshArtist

mesh = Mesh.from_obj(compas.get('faces.obj'))

vertices = mesh.get_vertices_attributes('xyz')
edges    = list(mesh.edges())
fixed    = list(mesh.vertices_where({'vertex_degree': 2}))
q        = mesh.get_edges_attribute('q', 1.0)
loads    = mesh.get_vertices_attributes(('px', 'py', 'pz'), (0.0, 0.0, 0.0))

xyz, q, f, l, r = XFunc('compas.numerical.fd_numpy')(vertices, edges, fixed, q, loads)

for key, attr in mesh.vertices(True):
    attr['x'] = xyz[key][0]
    attr['y'] = xyz[key][1]
    attr['z'] = xyz[key][2]

artist = MeshArtist(mesh)

artist.clear()

artist.draw_vertices()
artist.draw_edges()

artist.redraw()

Making tools

In this tutorial, we will make a form finding tool for Rhino providing basic user interaction, management of settings, data persistence, and different options for solving the equilibrium problem.

The goal is to link framework functionality to a Rhino toolbar through a controller and to simplify the process of generating the required Rhino user interface file or rui.

Table of contents

Examples

API

Definition

Reference

compas

compas.com

compas.com provides functionality for communicating with external software.

Matlab

MatlabClient

Communicate with Matlab through Windows’ COM interface.

MatlabEngine

Communicate with Matlab through the MATLAB engine.

MatlabProcess

Communicate with Matlab through a subprocess.

MatlabSession

Communicate with Matlab through a shared session.

Rhino

RhinoClient

Communicate with Rhino through Window’s COM interface.

ssh

SSH

Initialse an SSH object.

compas.interop

This package includes utility functions for seamless integration of C and C++ code, and wrappers for external libraries.

Warning

The functionality of this package is experimental and subject to frequent change. For now, don’t use it for anything important :)

ShapeOp

shapeop.ShapeOpSolver

Core

core.cpp.xdarray.Array1D

core.cpp.xdarray.Array2D

core.cpp.xdarray.Array3D

core.cpp.xdarray.shape

core.cpp.xdarray.zeros

core.cpp.xdarray.ones

core.cpp.xdarray.eye

Citing

If you use the main library of COMPAS in a project, please refer to the GitHub repository.

@misc{compas-dev,
    title  = {{compas}: A framework for computational research in architecture and structures.},
    author = {Tom Van Mele and Andrew Liew and Tomas Mendéz and Matthias Rippmann and others},
    note   = {http://compas-dev.github.io/compas/},
    year   = {2017},
}

Versions

Stable