Simple arch supported membrane

../_images/example_arch.png

Rhino input

You can generate an input file (COMPAS data file in JSON format) from the following model and script.

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

import os

import compas_rhino
import compas_rhino.objects

import compas
from compas.itertools import pairwise
from compas.tolerance import TOL
from compas_bender.datastructures import BendNetwork

HERE = os.path.dirname(__file__)
FILE = os.path.join(HERE, "example_arch.json")

# ==============================================================================
# Input
# ==============================================================================

guids = compas_rhino.objects.get_lines(layer="lines")
lines = compas_rhino.objects.get_line_coordinates(guids)

guids = compas_rhino.objects.get_points(layer="points")
points = compas_rhino.objects.get_point_coordinates(guids)

guids = compas_rhino.objects.get_polylines(layer="splines")
polylines = compas_rhino.objects.get_polyline_coordinates(guids)

# ==============================================================================
# Network from lines
# ==============================================================================

network = BendNetwork.from_lines(lines)

gkey_key = {TOL.geometric_key(network.node_attributes(node, "xyz")): node for node in network.nodes()}

# ==============================================================================
# Identify anchors
# ==============================================================================

for point in points:
    gkey = TOL.geometric_key(point)
    if gkey in gkey_key:
        key = gkey_key[gkey]
        network.node_attribute(key, "is_anchor", True)

# ==============================================================================
# Identify splines
# ==============================================================================

splines = []

for polyline in polylines:
    start = None
    edges = []
    for a, b in pairwise(polyline):
        a_gkey = TOL.geometric_key(a)
        b_gkey = TOL.geometric_key(b)
        if a_gkey in gkey_key and b_gkey in gkey_key:
            u = gkey_key[a_gkey]
            v = gkey_key[b_gkey]
            if start is None:
                start = u
            if network.has_edge((u, v)):
                edges.append((u, v))
            else:
                edges.append((v, u))
    splines.append({"start": start, "edges": edges})

# ==============================================================================
# Export
# ==============================================================================

data = {"network": network, "splines": splines, "cables": []}

compas.json_dump(data, FILE)

Equilibrium

The script below requires an input file (COMPAS data file in JSON format). You can generate an input file with the information above, or use a precompiled input file directly.

import os
from math import fabs
from typing import List

from compas_view2.app import App
from compas_view2.objects import Collection
from compas_view2.shapes import Arrow

import compas
from compas.colors import Color
from compas.geometry import Cylinder
from compas.geometry import Polygon
from compas.geometry import Vector
from compas.geometry import sum_vectors
from compas.utilities import remap_values
from compas_bender.bend import bend_splines
from compas_bender.datastructures import BendNetwork

HERE = os.path.dirname(__file__)
FILE = os.path.join(HERE, "example_arch.json")

# ==============================================================================
# Network from file
# ==============================================================================

data = compas.json_load(FILE)

network: BendNetwork = data["network"]

splines = data["splines"]
cables = data["cables"]

for spline in splines:
    spline["edges"] = [(u, v) for u, v in spline["edges"]]

# ==============================================================================
# Spline parameters
# ==============================================================================

splines[0]["E"] = 30
splines[0]["radius"] = 10
splines[0]["thickness"] = 10

for key, attr in network.edges(True):
    attr["linit"] = 0

# ==============================================================================
# Bend
# ==============================================================================

bend_splines(
    network,
    cables,
    splines,
    config={"kmax": 5000, "tol1": 1e-2, "tol2": 1e-1, "tol3": 1e-4},
)

# ==============================================================================
# Viz
# ==============================================================================

edge_index = {edge: index for index, edge in enumerate(network.edges())}
edge_index.update({(v, u): index for (u, v), index in edge_index.items()})

radii = [fabs(f) for f in network.edges_attribute("f")]
radii = remap_values(radii, 0.01, 0.2)

viewer = App()

anchors = []
anchor_properties = []
for node in network.nodes_where(is_anchor=True):
    anchors.append(network.node_point(node))
    anchor_properties.append({"pointcolor": Color.black(), "pointsize": 50})
viewer.add(Collection(anchors, anchor_properties), pointsize=20)

for node in network.nodes_where(is_anchor=True):
    point = network.node_point(node)
    nbrs = network.neighbors(node)
    edges = [(node, nbr) if network.has_edge((node, nbr)) else (nbr, node) for nbr in nbrs]
    forces = network.edges_attribute("f", keys=edges)
    edgevectors: List[Vector] = [network.node_point(nbr) - point for nbr in nbrs]
    edgevector = Vector(*sum_vectors(edgevectors))
    forcevectors = [vector.scaled(force) for force, vector in zip(forces, edgevectors)]
    forcevector = Vector(*sum_vectors(forcevectors))
    color = Color.green().darkened(50)
    vector = network.node_reaction(node)
    vector.scale(0.2)
    if vector.length > 0.1:
        if edgevector.dot(forcevector) > 0:
            position = point
        else:
            position = point - vector
        arrow = Arrow(
            position,
            vector,
            head_portion=0.2,
            head_width=0.07,
            body_width=0.02,
        )
        viewer.add(arrow, u=16, facecolor=color)

for spline in splines:
    pipes = []
    pipe_properties = []
    for u, v in spline["edges"]:
        edge = (u, v) if network.has_edge((u, v)) else (v, u)
        index = edge_index[edge]
        # bending moment
        ma = Vector(*network.node_attributes(u, ["mx", "my", "mz"]))
        mb = Vector(*network.node_attributes(v, ["mx", "my", "mz"]))
        a = network.node_point(u)
        b = network.node_point(v)
        aa = a + ma * 0.03
        bb = b + mb * 0.03
        viewer.add(Polygon([a, b, bb, aa]), facecolor=(1, 1, 0))
        # axial force
        force = network.edge_attribute(edge, "f")
        line = network.edge_line((u, v))
        radius = radii[index]
        color = Color.red() if force > 0 else Color.blue()
        pipe = Cylinder.from_line_and_radius(line, radius)
        pipes.append(pipe)
        pipe_properties.append({"facecolor": color})
    viewer.add(Collection(pipes, pipe_properties))

for cable in cables:
    pipes = []
    pipe_properties = []
    for edge in cable["edges"]:
        index = edge_index[edge]
        force = network.edge_attribute(edge, "f")
        line = network.edge_line((u, v))
        radius = radii[index]
        color = Color.red() if force > 0 else Color.blue()
        pipe = Cylinder.from_line_and_radius(line, radius)
        pipes.append(pipe)
        pipe_properties.append({"facecolor": color})
    viewer.add(Collection(pipes, pipe_properties))

viewer.add(network, show_points=False, linewidth=2)
viewer.show()