Python Help! Stuck with one rectangle...trying to create offsets

Hi everyone,

Not getting any errors but I’ve tried both + and * and still not getting the nesting square effect.

Essentially I am trying to recreate this artwork. https://thomafoundation.org/artwork/transformations/

Can someone tell me what’s missing? Is it just my line 31?

Much appreciated!

If rect is a list, you need to “append” any rectangle to it at line 31.

Hi @tamsamantha06,

I attempted practically exactly the same thing about a year ago in Python. :slight_smile:

My goal was then to animate Vera Molnár’s artwork.

Here’s my script, if you’re interested:

import Rhino.Geometry as rg
from ghpythonlib import treehelpers
import random
import math


def construct_square_grid(plane, size, extendx, extendy, padding=0):
    """Creates a two-dimensional grid of square cells.
    
    Args:
      plane (Rhino.Geometry.Plane): Base plane for the grid
      size (int|float): Size of the grid cells
      extendx (int): Number of grid cells in the base plane x-direction
      extendy (int): Number of grid cells in the base plane y-direction
      padding (int|float): Optional padding between the grid cells, 
        by default 0, which means no padding
    
    Returns:
      A list of grid cells as polylines.
    """
    xform = rg.Transform.PlaneToPlane(rg.Plane.WorldXY, plane)
    grid = []
    for y in range(extendy):
        for x in range(extendx):
            pt1 = rg.Point3d(
                    x * size + padding * x, 
                    y * size + padding * y,
                    0
                )
            pt2 = rg.Point3d(
                    (x + 1) * size + padding * x, 
                    (y + 1) * size + padding * y,
                    0
                )
            rectangle = rg.Rectangle3d(rg.Plane.WorldXY, pt1, pt2)
            rectangle.Transform(xform)
            grid.append(rectangle.ToPolyline())
    return grid


def random_vector2d(plane=rg.Plane.WorldXY, scale=1.0):
    """Returns a random, two-dimensional vector that can optionally
        be scaled to a desired magnitude. If no scale is provided
        a unit vector of magnitude 1.0 is returned."""
    theta = random.uniform(0, 2 * math.pi)
    x = math.cos(theta)
    y = math.sin(theta)
    vec = rg.Vector3d(x, y, 0)
    vec.Unitize()
    if scale != 1.0:
        vec *= scale
    xform = rg.Transform.PlaneToPlane(rg.Plane.WorldXY, plane)
    vec.Transform(xform)
    return vec


def distort(polyline_crv, dmin=0, dmax=1):
    """Randomly distorts a polyline curve.
    
    Args:
      plane:
      polyline_crv (Rhino.Geometry.PolylineCurve): Polyline Curve to distort
      dmin (int|float): Optional minimum distortion radius, by default 0.0
      dmax (int|float): Optional maximum distortion radius, by default 1.0
    
    Returns:
      The distorted polyline curve on success, otherwise the initial curve.
    """
    if not isinstance(polyline_crv, rg.PolylineCurve):
        return polyline_crv
    
    rc, plane = polyline_crv.TryGetPlane()
    if not rc:
        return polyline_crv
    
    pline = polyline_crv.ToPolyline()
    if pline is None:
        return polyline_crv
    
    for i in range(pline.Count-1):
        rscale = random.uniform(dmin, dmax)
        pline[i] += random_vector2d(plane, rscale)
        
    if polyline_crv.IsClosed:
        pline[pline.Count-1] = pline[0]

    return pline.ToPolylineCurve()

# ------------------------------------------------------------------------------   
    
if __name__ == "__main__":
    if Seed is not None:
        random.seed(Seed)
    
    grid = construct_square_grid(Plane, Size, ExtendX, ExtendY, Padding)

    transformations = []

    for cell in grid:
        prev_crv = cell.ToPolylineCurve()
        sub_cells = [prev_crv]
        
        for i in range(Count-1):
            offset_crv = prev_crv.Offset(Plane, -Padding, 0.01, 0)
            if offset_crv is not None:
                offset_crv = offset_crv[0]
                if not isinstance(offset_crv, rg.PolylineCurve):
                    break
                if not offset_crv.IsClosed or offset_crv.PointCount != 5:
                    break
            
                prev_crv = offset_crv
            
                if i == Index:
                    offset_crv = distort(offset_crv, Padding, Padding * 2)
            
                sub_cells.append(offset_crv)
                
        transformations.append(sub_cells)

    
    # Outputs
    Curves = treehelpers.list_to_tree(transformations)

transformations_2019.gh (11.2 KB)

3 Likes

Thanks for your response! While I have figured it out it’s always nice to see the different approaches! Really appreciate you sharing this! Cheers :smiley:

Noted! Thank you Tom