Hi @jiri.manak ,
I was able to modify a spline so that the set of splines form a profile.
Once you offset the curve, run this function with parameters sketcn_name and fix_gap set to True:
def close_loops(sketch_name: str, fix_gap: boolean):
# created 2022-09-16 - Fusion360 ver 2.0.14106
def change_point(loop: dict, point1_str: str, point2_str: str):
if loop['start'] == point1_str:
loop['start'] = point2_str
else:
loop['end'] = point2_str
if loop['start'] == loop['end']:
loop['closed'] = True
def add_control_point(scps: adsk.fusion.SketchControlPointSpline, x: float, y:float, z:float):
solution = 'B'
(returnValue, controlPoints, degree, knots, isRational, weights, isPeriodic) = scps.geometry.getData()
app.log(f'Control points before (#{len(controlPoints)}): {degree=}')
for cp in controlPoints:
app.log(f' {cp.asArray()}')
if solution == 'A':
# to add a control point to the spline
oc = scps.extend(adsk.core.Point3D.create(x, y, z), False)
app.log(f'Solution A) ----> curve fixed! extended to: ({x}, {y}, {z}) {oc.count=}')
(returnValue, controlPoints, degree, knots, isRational, weights, isPeriodic) = oc.item(0).geometry.getData()
elif solution == 'B':
# to replace the spline with a new created one using the same control points except for the last one
parent_scps = scps.parentSketch.sketchCurves.sketchControlPointSplines
new_controlPoints = [adsk.core.Point3D.create(p.x,p.y,p.z) for p in controlPoints]
new_controlPoints[-1] = adsk.core.Point3D.create(x, y, z) # replace the last point by this new one
returnValue = scps.deleteMe() # needed to be deleted first
n_scps = parent_scps.add(new_controlPoints, degree) # adding this spline it adds also construction lines for polygon
app.log(f'Solution B)----> curve fixed! extended to: ({x}, {y}, {z}) --> curve deleted: {returnValue}')
(returnValue, controlPoints, degree, knots, isRational, weights, isPeriodic) = n_scps.geometry.getData()
app.log(f'Control points after (#{len(controlPoints)}): {degree=}')
for cp in controlPoints:
app.log(f' {cp.asArray()}')
skt = root.sketches.itemByName(sketch_name)
if skt is None:
app.log(f'sketch "{sketch_name}" not found.')
return False
app.log(f'{skt.sketchCurves.count=} {skt.sketchCurves.sketchControlPointSplines.count=}')
loops = {}
curve: adsk.fusion.SketchCurve
curve_number=1
for curve in skt.sketchCurves:
if not isinstance(curve, adsk.fusion.SketchCircle) and not isinstance(curve, adsk.fusion.SketchEllipse) and not curve.isConstruction:
start_point_str = curve.startSketchPoint.geometry.asArray()
end_point_str = curve.endSketchPoint.geometry.asArray()
app.log(f'{curve_number} {curve.objectType} {start_point_str} {end_point_str}')
start_curve = {key:val for key,val in loops.items()
if val['closed'] == False and (val['start'] == start_point_str or val['end'] == start_point_str)}
end_curve = {key:val for key,val in loops.items()
if val['closed'] == False and (val['start'] == end_point_str or val['end'] == end_point_str)}
n_curves = len(start_curve) + len(end_curve)
if n_curves == 0:
new_loop = {'closed': False, 'start': start_point_str, 'end': end_point_str, 'count':1}
loops[len(loops)] = new_loop
elif n_curves == 1:
if len(start_curve):
(_, loop) = start_curve.popitem()
change_point(loop, start_point_str, end_point_str)
else:
(_, loop) = end_curve.popitem()
change_point(loop, end_point_str, start_point_str)
loop['count'] += 1
else:
(k1, loop1) = start_curve.popitem()
(k2, loop2) = end_curve.popitem()
if k1 == k2:
change_point(loop, start_point_str, end_point_str)
loop['count'] += 1
else:
change_point(loop1,
start_point_str,
loop2['start'] if loop2['end'] == end_point_str else loop2['end'])
loop1['count'] += loop2['count'] + 1
del loops[k2]
pass
curve_number += 1
loop_number = 1
for loop in loops.values():
if loop['closed']:
app.log(f"loop#{loop_number} closed with {loop['count']} segments")
else:
(segm1X, segm1Y, segm1Z) = (float(x) for x in loop['start'])
(segm2X, segm2Y, segm2Z) = (float(x) for x in loop['end'])
app.log(f"loop#{loop_number} not closed. DeltaX={segm1X-segm2X} DeltaY={segm1Y-segm2Y} DeltaZ={segm1Z-segm2Z} with {loop['count']} segments")
app.log(f" start={loop['start']}")
app.log(f" end ={loop['end']}")
if fix_gap and abs(segm1X-segm2X) < 0.0001:
for curve in skt.sketchCurves:
if isinstance(curve, adsk.fusion.SketchControlPointSpline):
# correct loop ONLY if curve is Sketch Control Point Spline
scps = adsk.fusion.SketchControlPointSpline.cast(curve)
end_point_str = scps.endSketchPoint.geometry.asArray()
if end_point_str == loop['start']:
add_control_point(scps, segm2X, segm2Y, segm2Z)
break
elif end_point_str == loop['end']:
add_control_point(scps, segm1X, segm1Y, segm1Z)
break
loop_number += 1
app.log(f'{skt.sketchCurves.count=} {skt.sketchCurves.sketchControlPointSplines.count=}')
It will iterate over the curves, to identify which set is open (not a profile) or not.
If you run it a second time, you will get the two curves as closed.
Lesson learned here:
The control points can be obtained from SketchControlPointSpline.geometry.getData(); the second returned value is the control points of the curve.
You will see I add solutions A and B on add_control_point function.
With solution A a new control point is added. Even the curves will form a profile, running the function for the second time won't report inner curve as closed because the endpoint is being calculated to some other point (here further investigation to understand better splines).
With solution B the last curve of the set is replaced to close the gap; in that new curve the end point is replaced by the start point of the first curve of the set of splines, using the same control points of the former spline curve.
Hope this function could help to correct the curves while a solution could be obtained from Fusion360 team.
Regards,
Jorge