Hi,
I am trying to code a bouquet of flowers. I took this up as an exercise because I am a beginner in python and wanted a project to have a steep learning curve.
The code is supposed to work like this:
- Creating a pot:
Using a couple of simple operations I want to model a pot. This has been working out fine. - Creating the halms/straws of the flowers:
this was a little more challenging, but still manageble. Starting with a set of points as the seeds I generated positions for the blossoms and smooth curves connecting them. - Creating the blossoms:
I wanted to create a simple geometric representation of a blossom. Now, for me, this is challenging. I have decided to divide this process into 3 seperate steps:
3.1: Pascals theorem:
I am trying to model blossoms and as the fundamental geometry I want to use ellipses. Pascals theorem states, that from any ellipse I can get six tangents whos intersections have connecting lines that intersect in one point. This allows me to create concentric blossoms, that are not completely symmetric.
3.2: creating petal rings:
Each blossom is build up of rings of petals that are stacked into each other. I am trying to create these petal rings from the information that pascals theorem gave me.
3.3: Creating a blossom:
This again should be relativly easy: by stacking the petal rings they should create a blossom. - Bringing everything together:
Finally, the Blossoms should be attached to the straws.
I have been working on this code for the last few days. Unfortunately it seems like I am meeting no end with my debugging efforts. This is why I assume that fundamentally my coding style (for example using both rhinoscript and rhinocommon methods) is flawed.
I would like to ask you all for some advice, just when you observe this code. What could I fundamentally change for the better (to make my coding more fruitful)?
Thanks,
Merlin
The Code:
"""Grasshopper Script Instance"""
import sys as sys
import Rhino
import Grasshopper
import rhinoscriptsyntax as rs
import Rhino.Geometry as rg
import random as r
# I would like to make some flowers.
#############################################
# 0. Making a pot
#############################################
plane1 = rs.WorldXYPlane()
circle1 = rs.AddCircle(plane1, 0.2)
plane2 = rs.MovePlane(rs.WorldXYPlane(), [0,0,0.6])
circle2 = rs.AddCircle(plane2, 0.25)
plane3 = plane2
circle3 = rs.AddCircle(plane3, 0.2)
plane4 = rs.MovePlane(rs.WorldXYPlane(), [0,0,0.5])
circle4 = rs.AddCircle(plane4, 0.19)
loft_this = [circle1, circle2, circle3, circle4]
pot = rs.AddLoftSrf(loft_this, start=[0,0,0],
end=[0,0,0.5], loft_type=2)
earth = rs.AddPlanarSrf(circle4)
#############################################
# 1. Creating the seeds
#############################################
# We have to plant seeds inside the pot.
# Unfortunately not all seeds will flourish,
# if they are planted at too close a distance.
number_of_flowers = 10
seeds = []
for i in range(number_of_flowers):
# Generate random (u, v) parameters
u = r.random()
v = r.random()
# Evaluate the surface at (u, v) to get the 3D point
point = rs.EvaluateSurface(earth, u, v)
seeds.append(point)
#############################################
# 2. Create the positions for the flowerheads
#############################################
# Moving them in z-direction...
# ...and pushing the flowerhead positions outwards.
flower_height = 0.5
flowerheads = seeds
centroid = (rs.SurfaceAreaCentroid(earth)[0][0],
rs.SurfaceAreaCentroid(earth)[0][1],
flower_height)
flowerheads = rs.MoveObjects(flowerheads, [0,0,flower_height])
flowerheads = rs.ScaleObjects(flowerheads, centroid, [3,3,0], False)
#############################################
# 3. Creating the growth_paths
#############################################
# I do this by simply using interpolating curves,....
# ... which are perfect for this purpose:
# Define a starting point and vector and an end point and vector....
# .... and you will get a smooth cuve between the points....
# .... with the vectors as tangents.
# First creating the vetors...
flowerhead_vectors = []
for i,seed in enumerate(seeds):
vector = rs.VectorCreate(seed, flowerheads[i])
flowerhead_vectors.append(vector)
# ...then the growth_paths.
growth_paths = []
for i,seed in enumerate(seeds):
growth_path = rs.AddInterpCurve(
[seed,flowerheads[i]], degree=3, knotstyle=0,
start_tangent=(0,0,1), end_tangent = flowerhead_vectors[i])
growth_paths.append(growth_path)
# Now, for creating the flower straws.
#############################################
# 4. Creating the flower_operation
#############################################
# 4.1 Pascals Operation
# Pascals Theorem states the following for every ellipse:
# If you draw 6 different tangents on the elipse ....
# ....and take their intersection points...
# .... you can create connecting lines between these points.....
# .... that will always intersect each other in one point.
# Using this theorem allows me to create flowerheads...
# ....with petals that all converge to one point.
def Pascal_Operation(x,y):
ellipse_id = rs.AddEllipse(rs.WorldXYPlane(), x, y)
# Calculate tangent lines .... only as point pairs because thats all we need for now
tangent_lines = []
for i in range(6): # Divide curve into 6 equal parts to get tangent points
param = i * rs.CurveLength(ellipse_id) / 6
point = rs.EvaluateCurve(ellipse_id, param)
tangent = rs.CurveTangent(ellipse_id, param)
points_of_tangent_line = [
point + x * tangent,
point - x * tangent,
]
tangent_lines.append(points_of_tangent_line)
# Calculate intersections of neighboring tangents
intersections_of_tangents = []
for i in range(6):
tangent_1 = tangent_lines[i]
tangent_2 = tangent_lines[(i + 3) % 6]
intersection = rs.LineLineIntersection(
(tangent_1[0], tangent_1[1]), (tangent_2[0], tangent_2[1])
)
if intersection:
intersections_of_tangents.append(intersection[0])
# Calculate intersecting lines (again only as point pairs because thats all we need for now) and...
# ...determine the intersection of just two lines (the third is automatically concurrent with the others).
line_0 = intersections_of_tangents[0], intersections_of_tangents[3]
line_1 = intersections_of_tangents[1], intersections_of_tangents[4]
central_intersection = rs.LineLineIntersection(
(line_0[0], line_0[1]), (line_1[0], line_1[1]))
return tangent_lines, intersections_of_tangents, central_intersection
# now using this definition of pascals sentence...
# ... we make some nice flowers.
# Its gonna be fun!
# 4.2 Creating a function for what i like to call "petal_rings"
def petal_ring(x,y, petal_division):
ellipse_id = rs.AddEllipse(rs.WorldXYPlane(), x, y)
rs.CurveSeam(ellipse_id, petal_division)
intersections_of_tangents = Pascal_Operation(x,y)[1]
central_intersection = Pascal_Operation(x,y)[2]
convex_curves = rs.SplitCurve(ellipse_id, [i * 1/6 for i in range(6)])
concave_curves = []
# Creating the upper end of each petal...
# ...by mirroring the ellipse-segments along the ellipse-tangents
for i in range(6):
start_point = intersections_of_tangents[i]
end_point = intersections_of_tangents[(i+1) % 6 ]
concave = rs.MirrorObject(convex_curves[i % 5], start_point, end_point, True)
concave = rs.MoveObject(concave, [0,0, x * 1.5])
concave_curves.append(concave)
petals = []
for i in range(6):
petal = rs.AddLoftSrf([concave_curves[i % 5], convex_curves[i % 5]],
loft_type=1)
petals.append(petal)
return ellipse_id, petals, central_intersection
# 4.3 Creating the flowerheads
def blossom(x,y):
step_size = 0.03
# generate the outer ring of petals
outer_ring = petal_ring(x,y, 0)[1]
# generate the inner, offset ring of petals
outer_ring_center = petal_ring(x,y, 0)[2]
x2 = x * 0.75
y2 = y * 0.75
inner_ring = petal_ring(x2,y2, 1/6)[1]
inner_ring_center = petal_ring(x2,y2, 1/6)[2]
# Aligning the inner and the outer ring
direction = rs.VectorAdd(outer_ring_center - inner_ring_center, [0, 0, step_size])
inner_ring = rs.MoveObjects(inner_ring, direction)
# putting both rings together and creating the entire blossom
double_ring = inner_ring | outer_ring
blossom = []
for i in range(6):
ring = rs.ScaleObjects(double_ring, outer_ring_center, 0.5, copy=True)
ring = rs.MoveObjects(ring, [0, 0, step_size])
for i in ring:
blossom.append(ring)
return blossom, outer_ring_center
#############################################
# 5. Bringing it all together
#############################################
blossom_positioned = []
for i in range(number_of_flowers):
#Create radii for the ellipses, within a certain boundary...
# ...to create differently shaped flowerheads.
u = r.uniform(0.05, 0.09)
v = r.uniform(0.05, 0.09)
flowerhead = blossom(u,v)
# reorienting the flowerhead
reference = [flowerhead[1], [0,0,1], [0,1,0]]
for i in flowerhead:
target = [flowerheads[i], flowerhead_vectors[i] + flowerheads[i],
rs.VectorCrossProduct(flowerheads[i], flowerhead_vectors[i] + flowerheads[i])]
flowerhead = rs.OrientObject(flowerhead[0][i], reference, target, flags = 0)
blossom_positioned.append(flowerhead)
#############################################
# 6. Visualizing it
#############################################
blossoms = blossom_positioned
pot = pot
earth = earth
1 post - 1 participant