Hi,
I’ve ported the Population
method from the Vector GHa lib, mentioned by @DavidRutten in this thread to GHPython. Now I’m looking for advice, on how to optimise the code in order to make it run faster, since I need it to produce about 10 000 to 20 000 points (tuples with 3 coordinates) for me.
Here are the current speeds of the Python component:
Population | Time | Percentage |
---|---|---|
10 | 6 ms | 28 % |
100 | 853 ms | 85 % |
1000 | 1.3 min | 100 % |
10 000 | 60 min | >100 % |
This is what the code looks like! The most interesting section to optimise might be the populate()
method of the Population
class.
import Rhino.Geometry as rg
import random
import math
import sys
class Population:
"""A population of random points within a region.
To use:
>>> ppl = Population(10, (25,25))
>>> ppl.populate()
>>> ppl.get_coordinates()
[(x0, y0, z0), (x1, y1, y2), ..., (x10, y10, z10)]
>>> ppl.get_points()
[Rhino.Geometry.Point3d, Rhino.Geometry.Point3d, ..., Rhino.Geometry.Point3d]
"""
def __init__(self, count, region):
self.count = int(count)
self.region = region
self.population = []
def distance_to(self, ptA, ptB):
"""Calculate the distance between two 3-dimensional points.
Args:
ptA: 3-dimensional point A (Rhino.Geometry.Point3d, tuple or list)
ptB: 3-dimensional point B (Rhino.Geometry.Point3d, tuple or list)
Returns:
The distance between point A and point B.
"""
dist = math.sqrt((ptA[0] - ptB[0])**2 + (ptA[1] - ptB[1])**2 + (ptA[2] - ptB[2])**2)
return dist
def next_point(self, domain):
"""Constructs a random 2-dimensional point coordinate.
Args:
domain: Tuple of 2 maximum values in x and y.
Returns:
Returns a tuple of random 2D point coordinates.
"""
maxX = domain[0]
minX = -maxX
maxY = domain[1]
minY = -maxY
return (random.uniform(minX, maxX), random.uniform(minY, maxY), 0)
def populate(self):
"""Populates a 2-dimensional region with random 2-dimensional points."""
distance_threshold = sys.maxint
for i in range(self.count):
if (len(self.population) == 0): # first point
pt = self.next_point(self.region)
self.population.append(pt)
else: # other points
attempts = int(max(50, math.sqrt(i)))
max_dist = -sys.maxint
max_pt = 0
for k in range(attempts):
if (k > 100) and (max_dist > distance_threshold * 0.9):
break
elif (k > 50) and (max_dist > distance_threshold * 0.92):
break
elif (k > 25) and (max_dist > distance_threshold * 0.95):
break
pt = self.next_point(self.region)
dist = sorted(self.distance_to(pt, prev_pt) for prev_pt in self.population)[0]
if dist == sys.maxint:
break
if dist > max_dist:
max_dist = dist
max_pt = pt
if (max_dist > distance_threshold * 1.25):
break
distance_threshold = max_dist
self.population.append(max_pt)
def get_coordinates(self):
"""Returns a list of random 2-dimensional point coordinates witihn a region."""
return self.population
def get_points(self):
"""Returns a list of Rhino.Geometry.Point3d witin a redion."""
pts = [rg.Point3d(c[0], c[1], c[2]) for c in self.population]
return pts
# Variables
count = 10 # number of random points to produce
domain = (5.0, 5.0) # max. extends of the region in x and y
# Initialise class
ppl = Population(count, domain)
ppl.populate()
# Output
a = ppl.get_points()
(To test the script, just copy and paste the python code into a GHPython component and run it.)
Any advice is welcome and appreciated!