[Python] How can i avoid openscad helix in my project from distorting and taking long to...

Discussão em 'Python' iniciado por Stack, Fevereiro 12, 2025.

  1. Stack

    Stack Membro Participativo

    i am trying to work on a complete coverage model of a cylinder using a ribbon but in the code i have when i increase the number of turns and the thicknes of the ribbon the ribbon distorts and warps i have noticed that the start and end of the ribbon appear to be constained on the same x,y cordinate and when i increase the number of turns and thickness it kind of bends as shown in the picture.

    output for thickness 10 and number of turns 15

    not i am using pythonocc-core library with anoconda

    i tried the code below and i am want to have a completely covered cylinder without any distortion

    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 = 3000
    cylinder_wall_thickness = 0.5
    total_points = helix_turns * points_per_turn
    dz = cylinder_height / total_points

    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(False, 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_())

    Continue reading...

Compartilhe esta Página