Using bmesh.
Recommend using bmesh for this. Where instead of splines, use edges. The vertices in the same location as the spline points.
In the test script below, for part 1 I've used the ball of string curves to make one bmesh, from the transformed points of all bits of string. Would change up the creation script in question to create a bmesh in first place, for answer sake I've scripted to use the curves created via the script in question.
All edges which have a vertex in the positive quadrant are selected. The selection is then bisected by one of the 3 axis planes. The resulting geometry bisected again by other two axes. This will add a vertex at each cutting points.
Next all edges that have both verts in the positive quadrant are removed and the subsequent mesh converted to a curve. I've used the operator here for example sake. Your question already displays how this is done via API.
The resulting curve object has multiple splines rather than being multiple objects, see note below.
import bpy
import bmesh
context = bpy.context
#PART 1 load all single splne curves to one bmesh
scene = context.scene
bm = bmesh.new()
curves = [o for o in scene.objects if o.type == 'CURVE']
for c in curves:
verts = [bm.verts.new(p.co.xyz) for p in c.data.splines[0].points[1:-1]]
for i in range(len(verts) - 1):
bm.edges.new((verts[i], verts[i + 1]))
bmesh.ops.transform(bm, verts=verts, matrix=c.matrix_world)
# select all edges with a vert in pos quadrant
def is_inquadrant(v):
return all(getattr(v.co, axis) >= -1e-4 for axis in "xyz")
geom = [e for e in bm.edges if any(is_inquadrant(v) for v in e.verts)]
# bisect the selection by three axis planes
ret = bmesh.ops.bisect_plane(bm,
geom=geom,
plane_no=(0, 0, 1),
plane_co=(0, 0, 0))
ret = bmesh.ops.bisect_plane(bm,
geom=ret['geom'],
plane_no=(0, 1, 0),
plane_co=(0, 0, 0))
ret = bmesh.ops.bisect_plane(bm,
geom=ret['geom'],
plane_no=(1, 0, 0),
plane_co=(0, 0, 0))
# Now have new verts on cuts, select edges with both verts in quad
for v in bm.verts:
v.select = False
for e in bm.edges:
e.select = all(is_inquadrant(v) for v in e.verts)
# remove them
geom = [e for e in bm.edges if e.select]
bmesh.ops.delete(bm, geom=geom, context=2)
me = bpy.data.meshes.new("BallEdgeMesh")
bm.to_mesh(me)
bm.free()
# add as mesh to scene.
ob = bpy.data.objects.new("BallMesh", me)
scene.objects.link(ob)
scene.objects.active = ob
ob.select = True
# convet to curve
bpy.ops.object.convert(target='CURVE')
curveData = context.object.data
curveData.dimensions = '3D'
curveData.resolution_u = 2
curveData.fill_mode = 'FULL'
curveData.bevel_depth = 0.02
for s in context.object.data.splines:
s.use_smooth = True
s.type = 'NURBS'
Note:
If you must have one object for each string:
It would be more efficient to make only one "string" curve object (bpy.data.curves
) and link to each object instance. Using methods similar to above would only need to add a new curve if it is bisected.
Finally welcome to bse, and kudos on writing a great first question.