Controlling for cylinder twist/rotation in Netgen.occ?

Hello, this issue is a bit related to the sweep geometry post, as it is my current workaround for this.

I have a table which defines a bunch of cylinders (or circular profiles depending on how you look at it):

As you can see, there are somehow three groups of cylinders which are rotated around their main axis relative to the other groups. I think this results in this undesirable behaviour when I loft the cross-sections with ThruSections:

Do you have an idea what causes this rotation of some of the cylinders or how it could be controlled? I generated hundreds of cylinder tables like this with their overall curve following different parabolas but could not find a relationship between how the cylinders are generated and where the twists occur, this seems random to me.

This is my code (files attached below):

from netgen.occ import *
from netgen.webgui import Draw as DrawGeo
import pandas as pd

def make_smooth_solid(segments):
    radii = segments['raw_radius'].tolist()
    # Create lists of points and normals for each circular profile 
    pnts = list()
    normals = list()
    for index, row in segments.iterrows():
            if index == segments.index.min():
                    pnts.append(Pnt(row['start_x'], row['start_y'], row['start_z']))
                    normals.append(Vec(row['end_x'] - row['start_x'], row['end_y'] - row['start_y'], row['end_z'] - row['start_z']))
            pnts.append(Pnt(row['end_x'], row['end_y'], row['end_z']))
            normals.append(Vec(row['end_x'] - row['start_x'], row['end_y'] - row['start_y'], row['end_z'] - row['start_z']))
    # Create list of profiles
    profiles = list()
    for pnt, normal, r in zip(pnts, normals, radii):
        circle = Circle(pnt, normal, r)
        profiles.append(Wire([circle]))
    # Create lofted shape connecting the profiles
    solid = ThruSections(profiles, solid=True)
    return(solid)

def make_cylinders(segments):
    # Initial Cylinder to add to
    cylinders = Cylinder(Pnt(segments['start_x'].iloc[0], segments['start_y'].iloc[0], segments['start_z'].iloc[0]),
                         Vec(segments['end_x'].iloc[0] - segments['start_x'].iloc[0], segments['end_y'].iloc[0] - segments['start_y'].iloc[0], segments['end_z'].iloc[0] - segments['start_z'].iloc[0]),
                         segments['raw_radius'].iloc[0],
                         segments['length'].iloc[0])
    # Add every cylinder one by one
    for index, row in segments.iterrows():
        if index > segments.index.min():
            cylinder = Cylinder(Pnt(row['start_x'], row['start_y'], row['start_z']),
                                Vec(row['end_x'] - row['start_x'], row['end_y'] - row['start_y'], row['end_z'] - row['start_z']),
                                row['raw_radius'],
                                row['length'])
            cylinders = cylinders + cylinder
    
    return(cylinders)

cly_table = pd.read_csv('data/clys_example.csv')
DrawGeo(make_cylinders(cly_table))

DrawGeo(make_smooth_solid(cly_table))

cylinder_twisting_example.ipynb (4.5 KB)
clys_example.csv (10.6 KB)

If you want to control the cut-edge of the cylinder, use Cylinder with a local coordinate system defined by Axes:

cyl = Cylinder(Axes( (0,0,0), Z, X+0.2*Y), 1, 3)

Joachim

@joachim Thanks a lot, this works!