Hi Rolf

… Talking about performance, I tried your algorithm (nice idea BTW ) with some changes that … might make it faster … not sure though.

Here is the idea: splitting the point cloud into smaller ‘volumes’ and then apply your ‘pipes subtraction’ independently to them.

In order to make it faster, for any volume we only use a single pipe axis direction,

and for easier calculation, we orient the volume so that the pipes axes are vertical.

we will also use ‘square pipes’ … but I think cylindrical pipes could be used too … not sure about the difference in performance.

TAI.3dm (1.0 MB)

Here is, more or less, how it should work:

- move the points to have the centroid in the origin
- splt the point into 24 sets ( see the ‘walls’ that divide the volumes )
- for each point set:

3A) orient it so that its ‘central axis’ lie on the Z axis

3B) purge the points by the 'square pipes’

3C) orient the remaining point back to their original orientation

Unfortunately my Grasshopper is very far from fluent … so I’ll stick to plain Python scripts …

This is the test script I used to … build the idea

```
import Rhino
import rhinoscriptsyntax as rs
import scriptcontext
def xforms( xside, yside, zside, subindex, xfor, xbak ):
'''
xside, yside, zside : 1.0 OR -1.0
subindex in ( 0, 1, 2 )
calc transform, transform-back
update xfor, xbak lists
'''
subside = [ xside, yside, zside ]
vec0 = ( Rhino.Geometry.Vector3d.XAxis * xside +
Rhino.Geometry.Vector3d.YAxis * yside +
Rhino.Geometry.Vector3d.ZAxis * zside ) / 3.0
vec0.Unitize()
vec1 = [ Rhino.Geometry.Vector3d.XAxis * xside,
Rhino.Geometry.Vector3d.YAxis * yside,
Rhino.Geometry.Vector3d.ZAxis * zside ][ subindex ]
vec2 = vec0 + vec1
pla = Rhino.Geometry.Plane.WorldXY
plb = Rhino.Geometry.Plane( Rhino.Geometry.Point3d.Origin, vec2 )
xfa = Rhino.Geometry.Transform.PlaneToPlane( plb, pla )
xfb = Rhino.Geometry.Transform.PlaneToPlane( pla, plb )
if not xfa.IsValid:
print 'XFA is not valid'
if not xfb.IsValid:
print 'XFB is not valid'
xfor.append( xfa )
xbak.append( xfb )
def purge( pset, xfor, xbak, side, deltaz ):
# ( unable to make this work )
# pset = list( xfor.TransformList( pset ) )
for ix in range( len( pset ) ):
pset[ ix ].Transform( xfor )
# rs.ObjectColor( rs.AddPoints( pset ), [ 255, 0, 0 ] )
pset.sort( key = lambda x: x.Z, reverse = True )
# for ix in range( len( pset ) ):
# print str( pset[ ix ] )
hsid = side * 0.5
for ix in range( len( pset ) ):
pt = pset[ ix ]
if not pt:
continue
xmi = pt.X - hsid
xma = pt.X + hsid
ymi = pt.Y - hsid
yma = pt.Y + hsid
zma = pt.Z - deltaz
for iy in range( len( pset ) ):
pp = pset[ iy ]
if not pp:
continue
if ( ( pp.X > xmi ) and ( pp.X < xma ) and
( pp.Y > ymi ) and ( pp.Y < yma ) and
( pp.Z < zma ) ):
pset[ iy ] = None
res = []
for pt in pset:
if pt:
res.append( pt )
for ix in range( len( res ) ):
res[ ix ].Transform( xbak )
return res
def main():
# get points
gids = rs.GetObjects( 'Points ?', filter = 1, preselect = True )
pts = [ rs.PointCoordinates( gid ) for gid in gids ]
# get centroid
cen = Rhino.Geometry.Point3d.Origin
for pt in pts:
cen += pt
cen /= len( pts )
# move to origin
for ix in range( len( pts ) ):
pts[ ix ] -= cen
# split into 24 point sets
# points
pset = []
for ix in range( 24 ):
pset.append( [] )
# transforms
xfor = []
# back transforms
xbak = []
# build transforms
# 0 1 2
xforms( 1.0, 1.0, 1.0, 0, xfor, xbak )
xforms( 1.0, 1.0, 1.0, 1, xfor, xbak )
xforms( 1.0, 1.0, 1.0, 2, xfor, xbak )
# 3 4 5
xforms( -1.0, 1.0, 1.0, 0, xfor, xbak )
xforms( -1.0, 1.0, 1.0, 1, xfor, xbak )
xforms( -1.0, 1.0, 1.0, 2, xfor, xbak )
# 6 7 8
xforms( -1.0, -1.0, 1.0, 0, xfor, xbak )
xforms( -1.0, -1.0, 1.0, 1, xfor, xbak )
xforms( -1.0, -1.0, 1.0, 2, xfor, xbak )
# 9 10 11
xforms( 1.0, -1.0, 1.0, 0, xfor, xbak )
xforms( 1.0, -1.0, 1.0, 1, xfor, xbak )
xforms( 1.0, -1.0, 1.0, 2, xfor, xbak )
# 12 13 14
xforms( 1.0, 1.0, -1.0, 0, xfor, xbak )
xforms( 1.0, 1.0, -1.0, 1, xfor, xbak )
xforms( 1.0, 1.0, -1.0, 2, xfor, xbak )
# 15 16 17
xforms( -1.0, 1.0, -1.0, 0, xfor, xbak )
xforms( -1.0, 1.0, -1.0, 1, xfor, xbak )
xforms( -1.0, 1.0, -1.0, 2, xfor, xbak )
# 18 19 20
xforms( -1.0, -1.0, -1.0, 0, xfor, xbak )
xforms( -1.0, -1.0, -1.0, 1, xfor, xbak )
xforms( -1.0, -1.0, -1.0, 2, xfor, xbak )
# 21 22 23
xforms( 1.0, -1.0, -1.0, 0, xfor, xbak )
xforms( 1.0, -1.0, -1.0, 1, xfor, xbak )
xforms( 1.0, -1.0, -1.0, 2, xfor, xbak )
# build point sets
for pt in pts:
ax = abs( pt.X )
ay = abs( pt.Y )
az = abs( pt.Z )
# 0 1 2
if ( pt.X > 0.0 ) and ( pt.Y > 0.0 ) and ( pt.Z > 0.0 ):
if ( ax > ay ) and ( ax > az ):
pset[ 0 + 0 ].append( pt )
elif ( ay > ax ) and ( ay > az ):
pset[ 0 + 1 ].append( pt )
else:
pset[ 0 + 2 ].append( pt )
# 3 4 5
elif ( pt.X <= 0.0 ) and ( pt.Y > 0.0 ) and ( pt.Z > 0.0 ):
if ( ax > ay ) and ( ax > az ):
pset[ 3 + 0 ].append( pt )
elif ( ay > ax ) and ( ay > az ):
pset[ 3 + 1 ].append( pt )
else:
pset[ 3 + 2 ].append( pt )
# 6 7 8
elif ( pt.X <= 0.0 ) and ( pt.Y <= 0.0 ) and ( pt.Z > 0.0 ):
if ( ax > ay ) and ( ax > az ):
pset[ 6 + 0 ].append( pt )
elif ( ay > ax ) and ( ay > az ):
pset[ 6 + 1 ].append( pt )
else:
pset[ 6 + 2 ].append( pt )
# 9 10 11
elif ( pt.X > 0.0 ) and ( pt.Y <= 0.0 ) and ( pt.Z > 0.0 ):
if ( ax > ay ) and ( ax > az ):
pset[ 9 + 0 ].append( pt )
elif ( ay > ax ) and ( ay > az ):
pset[ 9 + 1 ].append( pt )
else:
pset[ 9 + 2 ].append( pt )
# 12 13 14
elif ( pt.X > 0.0 ) and ( pt.Y > 0.0 ) and ( pt.Z <= 0.0 ):
if ( ax > ay ) and ( ax > az ):
pset[ 12 + 0 ].append( pt )
elif ( ay > ax ) and ( ay > az ):
pset[ 12 + 1 ].append( pt )
else:
pset[ 12 + 2 ].append( pt )
# 15 16 17
elif ( pt.X <= 0.0 ) and ( pt.Y > 0.0 ) and ( pt.Z <= 0.0 ):
if ( ax > ay ) and ( ax > az ):
pset[ 15 + 0 ].append( pt )
elif ( ay > ax ) and ( ay > az ):
pset[ 15 + 1 ].append( pt )
else:
pset[ 15 + 2 ].append( pt )
# 18 19 20
elif ( pt.X <= 0.0 ) and ( pt.Y <= 0.0 ) and ( pt.Z <= 0.0 ):
if ( ax > ay ) and ( ax > az ):
pset[ 18 + 0 ].append( pt )
elif ( ay > ax ) and ( ay > az ):
pset[ 18 + 1 ].append( pt )
else:
pset[ 18 + 2 ].append( pt )
# 21 22 23
elif ( pt.X > 0.0 ) and ( pt.Y <= 0.0 ) and ( pt.Z <= 0.0 ):
if ( ax > ay ) and ( ax > az ):
pset[ 21 + 0 ].append( pt )
elif ( ay > ax ) and ( ay > az ):
pset[ 21 + 1 ].append( pt )
else:
pset[ 21 + 2 ].append( pt )
# debug
# for ix in range( 24 ):
# print len( pset[ ix ] )
# rs.ObjectColor( rs.AddPoints( pset[ 0 ] ), [ 255, 0, 0 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 1 ] ), [ 0, 255, 0 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 2 ] ), [ 0, 0, 255 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 3 ] ), [ 255, 255, 0 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 4 ] ), [ 255, 0, 255 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 5 ] ), [ 0, 255, 255 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 6 ] ), [ 255, 0, 127 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 7 ] ), [ 255, 127, 0 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 8 ] ), [ 255, 127, 127 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 9 ] ), [ 127, 0, 0 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 10 ] ), [ 127, 127, 0 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 11 ] ), [ 127, 0, 127 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 12 ] ), [ 127, 255, 0 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 13 ] ), [ 0, 255, 127 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 14 ] ), [ 127, 255, 127 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 15 ] ), [ 0, 127, 0 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 16 ] ), [ 127, 127, 0 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 17 ] ), [ 0, 127, 127 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 18 ] ), [ 127, 0, 255 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 19 ] ), [ 0, 127, 255 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 20 ] ), [ 127, 127, 255 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 21 ] ), [ 0, 0, 127 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 22 ] ), [ 127, 0, 127 ] )
# rs.ObjectColor( rs.AddPoints( pset[ 23 ] ), [ 0, 127, 127 ] )
ppts = []
for ix in range( len( pset ) ):
ppts.extend( purge( pset[ ix ], xfor[ ix ], xbak[ ix ], 15.0, 5.0 ) )
print( '%d / 24' % ix )
# move points back
for ix in range( len( ppts ) ):
ppts[ ix ] += cen
# delete original points
# rs.DeleteObjects( gids )
# draw purged points
rs.AddPoints( ppts )
main()
```

… for what it’s worth …

Regards