pygeos

pygeos is a pure python replacement for shapely, based on GEOS library. see Shapely

Usage Sample

This sample use osm vectors (roof boundarys) and build walls and roofs according. Consider osm vectors as being roof boundarys.

Preparation: project vectors over terrain using wrap modifiers, then run the script.

Parameters

  • preset: a full path for a roof preset, using optimized one here
  • roof_overflow: how much roof does overflow walls
  • storey_height: storey height for height estimation
  • use_small_as_axis: rotate wall so top part align to small axis
Buid walls and roofs from osm closed lines
import bpy
from archipack.archipack_polylines import Io, ShapelyOps, CoordSys
from math import ceil

# create buildings from closed polygons like osm ones
# preparation : project your curves over terrain using wrap modifier

# preset file full path
preset = "C:\\Users\\stephen\\AppData\\Roaming\\Blender Foundation\\Blender\\2.79\\scripts\\presets\\archipack_roof\\optimized.py"

# over width and length
roof_overflow = 1.0
storey_height = 2.7
use_small_as_axis = False

context = C
scene = context.scene
curves = context.selected_objects
# Coordinate system does store common world matrix of inputs
coords = CoordSys(curves)

# input closed curves as pygeos.multipolygon collection
multipolygon = Io.curves_to_geomcollection(curves, resolution=12, coordsys=coords)

# osm curves are roofs boundarys, so use a buffer to get wall boundarys (use roof_overflow as distance from roof to wall)
walls_buffer = []
walls_z = []

for geom in multipolygon.geoms:
    # tM a matrix world, at center
    tM, w, h, poly, w_pts = ShapelyOps.min_bounding_rect(geom)
    # w always the highest value
    bpy.ops.archipack.roof(auto_manipulate=False, filepath=preset)
    o = context.active_object
    z = min([co.z for co in geom.exterior.coords])
    zmax = max([co.z for co in geom.exterior.coords])
    # compute height from w / h
    height = storey_height * ceil(max(w, h) / 5) + zmax - z
    if use_small_as_axis:
        h, w = w, h
        rM = Matrix([
            [0, -1, 0, 0],
            [1, 0, 0, 0],
            [0, 0, 1, 0],
            [0, 0, 0, 1],
        ])
    else:
        rM = Matrix()
    o.matrix_world = coords.world * tM * rM * Matrix([
        [1, 0, 0, - (roof_overflow + 0.5 * w)],
        [0, 1, 0, 0],
        [0, 0, 1, z],
        [0, 0, 0, 1]
        ])
    d = o.data.archipack_roof[0]
    d.auto_update = False
    d.z = height
    d.width_left = roof_overflow + (h / 2)
    d.width_right = roof_overflow + (h / 2)
    d.parts[0].length = w + 2 * roof_overflow
    d.auto_update = True
    # pygeos geometry.buffer
    buffer = geom.buffer(-roof_overflow,
                        resolution=12,
                        join_style=2,
                        cap_style=3,
                        mitre_limit=10 * roof_overflow,
                        single_sided=False
                        )
    # output geom as curve
    result = Io.to_curve(scene, coords, geom, 'buffer')
    bpy.ops.archipack.roof_cutter(parent=o.name, auto_manipulate=False)
    cutter = context.active_object
    cutter.data.archipack_roof_cutter[0].operation = 'INTERSECTION'
    cutter.data.archipack_roof_cutter[0].user_defined_path = result.name
    # setup bottom z of wall (polygon.exterior)
    for co in buffer.exterior.coords:
        co.z = z
    walls_buffer.append(buffer)

# merge polygons into a pygeos.multipolygon collection
# using geom._factory
walls_polys = multipolygon._factory.buildGeometry(walls_buffer)
# output single curve with multiple splines
walls_curve = Io.to_curve(scene, coords, walls_polys, 'walls')

# Build walls
scene.objects.active = walls_curve
bpy.ops.archipack.wall2_from_curve(auto_manipulate=False)

# Fit wall to roof
for o in scene.objects:
    if o.data and "archipack_wall2" in o.data:
        scene.objects.active = o
        bpy.ops.archipack.wall2_fit_roof()