Getting some distortion on the helix genrated
Opened this issue · 2 comments
`import sys
import numpy as np
from OCC.Display.backend import load_backend
load_backend("pyqt5") # Ensure Qt backend is loaded
from OCC.Core.gp import gp_Pnt
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeCylinder
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeWire
from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_ThruSections, BRepOffsetAPI_MakeThickSolid
from OCC.Core.GeomAPI import GeomAPI_PointsToBSpline
from OCC.Core.TColgp import TColgp_Array1OfPnt
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut
from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB
from OCC.Display.qtDisplay import qtViewer3d
from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs
from OCC.Core.IFSelect import IFSelect_RetDone
from OCC.Core.TopoDS import TopoDS_Compound
from OCC.Core.BRep import BRep_Builder
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QDialog, QFormLayout, QLineEdit, QDialogButtonBox
class InputDialog(QDialog):
def init(self):
super().init()
self.setWindowTitle("Enter Parameters")
self.layout = QFormLayout()
self.radius_input = QLineEdit("5.0")
self.height_input = QLineEdit("20.0")
self.turns_input = QLineEdit("5")
self.width_input = QLineEdit("1.0")
self.thickness_input = QLineEdit("1.0")
self.layout.addRow("Cylinder Radius:", self.radius_input)
self.layout.addRow("Cylinder Height:", self.height_input)
self.layout.addRow("Helix Turns:", self.turns_input)
self.layout.addRow("Ribbon Width:", self.width_input)
self.layout.addRow("Ribbon Thickness:", self.thickness_input)
self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttons.accepted.connect(self.accept)
self.buttons.rejected.connect(self.reject)
self.layout.addWidget(self.buttons)
self.setLayout(self.layout)
def get_values(self):
return {
"cylinder_radius": float(self.radius_input.text()),
"cylinder_height": float(self.height_input.text()),
"helix_turns": int(self.turns_input.text()),
"ribbon_width": float(self.width_input.text()),
"ribbon_thickness": float(self.thickness_input.text()),
}
class HelixRibbonApp(QMainWindow):
def init(self):
super().init()
self.setWindowTitle("Helix Ribbon on Cylinder")
self.setGeometry(100, 100, 800, 600)
self.display_widget = qtViewer3d(self)
self.display_widget.setMinimumSize(800, 600)
self.display = self.display_widget._display
self.display.EraseAll()
self.params = {
"cylinder_radius": 75.0,
"cylinder_height": 300.0,
"helix_turns": 5,
"ribbon_width": 1.0,
"ribbon_thickness": 1.0,
}
self.shapes = [] # Store references to displayed shapes
self.initUI()
def initUI(self):
central_widget = QWidget()
layout = QVBoxLayout()
layout.addWidget(self.display_widget)
input_button = QPushButton("Enter Inputs")
input_button.clicked.connect(self.open_input_dialog)
layout.addWidget(input_button)
generate_button = QPushButton("Generate Model")
generate_button.clicked.connect(self.generate_model)
layout.addWidget(generate_button)
export_button = QPushButton("Export as STEP") # NEW BUTTON
export_button.clicked.connect(self.export_as_step)
layout.addWidget(export_button)
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
def open_input_dialog(self):
# Set initial values in dialog based on current params
dialog = InputDialog()
# Set initial values to dialog fields
dialog.radius_input.setText(str(self.params["cylinder_radius"]))
dialog.height_input.setText(str(self.params["cylinder_height"]))
dialog.turns_input.setText(str(self.params["helix_turns"]))
dialog.width_input.setText(str(self.params["ribbon_width"]))
dialog.thickness_input.setText(str(self.params["ribbon_thickness"]))
if dialog.exec_() == QDialog.Accepted:
# After dialog is accepted, get the new values and update the params
self.params = dialog.get_values()
print(self.params) # You can print or log to check the updated values
def clear_previous_shapes(self):
"""Remove all previously displayed shapes from the viewer."""
context = self.display.Context # Get the interactive context
for shape in self.shapes:
ais_object = context.SelectedInteractive() # Try getting the AIS object
if ais_object:
context.Remove(ais_object, True) # Remove AIS object from context
self.display.EraseAll() # Ensure all objects are erased from the viewer
self.shapes.clear() # Clear the stored shapes list
def export_as_step(self):
"""Export the generated model as a STEP file."""
if not self.shapes:
print("No shapes to export!")
return
# Create a compound to store all shapes
compound = TopoDS_Compound()
builder = BRep_Builder()
builder.MakeCompound(compound)
for shape in self.shapes:
builder.Add(compound, shape)
# Initialize the STEP writer
step_writer = STEPControl_Writer()
step_writer.Transfer(compound, STEPControl_AsIs)
# Write to file
status = step_writer.Write("exported_model.step")
if status == IFSelect_RetDone:
print("STEP file exported successfully: exported_model.step")
else:
print("Failed to export STEP file.")
def generate_model(self):
self.clear_previous_shapes() # Clear existing shapes before generating new ones
# Remaining code for generating the cylinder and ribbon remains unchanged
cylinder_radius = self.params["cylinder_radius"]
cylinder_height = self.params["cylinder_height"]
helix_turns = self.params["helix_turns"]
ribbon_width = self.params["ribbon_width"]
ribbon_thickness = self.params["ribbon_thickness"]
points_per_turn = 30000
cylinder_wall_thickness = 0.5
total_points = helix_turns * points_per_turn
#dz = cylinder_height / total_points
dz=0.0005
inner_points = []
outer_points = []
for i in range(total_points):
angle = 2 * np.pi * i / points_per_turn
z = dz * i
x_inner = cylinder_radius * np.cos(angle)
y_inner = cylinder_radius * np.sin(angle)
inner_points.append(gp_Pnt(x_inner, y_inner, z))
x_outer = (cylinder_radius + ribbon_width) * np.cos(angle)
y_outer = (cylinder_radius + ribbon_width) * np.sin(angle)
outer_points.append(gp_Pnt(x_outer, y_outer, z))
def build_bspline(points_list):
pts_array = TColgp_Array1OfPnt(1, len(points_list))
for idx, pt in enumerate(points_list):
pts_array.SetValue(idx + 1, pt)
return GeomAPI_PointsToBSpline(pts_array).Curve()
inner_bspline = build_bspline(inner_points)
outer_bspline = build_bspline(outer_points)
inner_edge = BRepBuilderAPI_MakeEdge(inner_bspline).Edge()
outer_edge = BRepBuilderAPI_MakeEdge(outer_bspline).Edge()
inner_wire = BRepBuilderAPI_MakeWire(inner_edge).Wire()
outer_wire = BRepBuilderAPI_MakeWire(outer_edge).Wire()
sections = BRepOffsetAPI_ThruSections(True, True, 1e-6)
sections.AddWire(inner_wire)
sections.AddWire(outer_wire)
ribbon = sections.Shape()
thick_builder = BRepOffsetAPI_MakeThickSolid()
thick_builder.MakeThickSolidBySimple(ribbon, ribbon_thickness)
thick_ribbon = thick_builder.Shape()
outer_cylinder = BRepPrimAPI_MakeCylinder(cylinder_radius, cylinder_height).Shape()
inner_cylinder = BRepPrimAPI_MakeCylinder(cylinder_radius - cylinder_wall_thickness, cylinder_height).Shape()
hollow_cylinder = BRepAlgoAPI_Cut(outer_cylinder, inner_cylinder).Shape()
self.display.DisplayShape(hollow_cylinder, update=True, transparency=0.5)
self.shapes.append(hollow_cylinder) # Keep reference to hollow cylinder
red_color = Quantity_Color(1.0, 0.0, 0.0, Quantity_TOC_RGB)
self.display.DisplayShape(thick_ribbon, update=True, color=red_color, transparency=0)
self.shapes.append(thick_ribbon) # Keep reference to thick ribbon
self.display.FitAll()
if name == "main":
app = QApplication(sys.argv)
mainWin = HelixRibbonApp()
mainWin.show()
sys.exit(app.exec_())
`
Make many shorter (<180 degree) parts instead of one.
Isn't there an option called Frenet? I guess this twisting is due to some issues with the curvature and the alignment of the tangent.
My guess is that the points on the spline can cause the distortion it appears the last points are fixed and the solid is stressed forming the distortion as it wraps around the cylinder
