I have attached a simple script that wraps a simple sketch (only straight lines) around a cylinder, and then lofts it to a specified thickness.
However, this technique seems to fail when the wrapping is larger than pi (180 degrees). Is this an inherent limitation to Part.makeFilledFace?
Is there a better way to wrap a sketch around a cylinder (preferably one that allows wraps greater than 180 degrees, and also allows curves to be mapped)?
Thanks!
Code: Select all
from __future__ import division # allows floating point division from integers
import FreeCAD, FreeCADGui
from FreeCAD import Base
import Part
import math
def GetObjectByLabel(objectName):
for oo in FreeCAD.ActiveDocument.Objects:
if oo.Label == objectName:
return oo
return None
def getPointOnCylinder(ax, ay, Radius):
angle = ax/Radius
result = (math.cos(angle)*Radius, math.sin(angle)*Radius, ay)
return result
def WrapSketch(Sketch, Radius, XScale):
Edges = []
for gg in Sketch.Geometry:
if str(type(gg)) == "<type 'Part.GeomLineSegment'>":
if (gg.Construction):
continue
sp = gg.StartPoint
ep = gg.EndPoint
if (sp.x > ep.x):
tt = sp
sp = ep
ep = tt
sp.x = sp.x*XScale
ep.x = ep.x*XScale
if (sp.x == ep.x):
Edges.append(Part.makeLine(getPointOnCylinder(sp.x, sp.y, Radius), getPointOnCylinder(ep.x, ep.y, Radius)))
elif (sp.y == ep.y):
Angle0 = sp.x/Radius*180/math.pi
Angle1 = ep.x/Radius*180/math.pi
cc = Part.makeCircle(Radius, Base.Vector(0, 0, gg.StartPoint.y), Base.Vector(0, 0, 1), Angle0, Angle1)
Edges.append(cc)
else:
hh = Part.makeHelix(2*abs(ep.y - sp.y)*math.pi/(abs(ep.x - sp.x)/Radius), abs(ep.y - sp.y), Radius, 0, (sp.y > ep.y))
if (sp.y > ep.y):
hh.rotate(Base.Vector(0,0,0), Base.Vector(0,0,1), ep.x/Radius*180/math.pi);
else:
hh.rotate(Base.Vector(0,0,0), Base.Vector(0,0,1), sp.x/Radius*180/math.pi);
hh.Placement.Base = Base.Vector(0, 0, min(sp.y, ep.y))
Edges.append(hh)
return Edges
class WrappedSketch:
def __init__(self, obj, SketchLabel = "", RuledRadius = 20, Thickness = 2):
PropertyGroup = "WrappedSketch";
obj.addProperty("App::PropertyString", "SketchLabel", PropertyGroup, "").SketchLabel = SketchLabel
obj.addProperty("App::PropertyFloat", "RuledRadius", PropertyGroup, "Radius for outer surface").RuledRadius = RuledRadius
obj.addProperty("App::PropertyFloat", "Thickness", PropertyGroup, "Thickness of extrusion").Thickness = Thickness
obj.Proxy = self
def execute(self, obj):
Sketch = GetObjectByLabel(obj.SketchLabel)
if (Sketch == None):
return
InnerRadius = obj.RuledRadius - obj.Thickness
Edges = WrapSketch(Sketch, obj.RuledRadius, 1.0)
Shape = Part.makeCompound(Edges)
Face1 = Part.makeFilledFace(Part.__sortEdges__(Shape.Edges))
Edges = WrapSketch(Sketch, InnerRadius, InnerRadius/obj.RuledRadius)
Shape = Part.makeCompound(Edges)
Face2 = Part.makeFilledFace(Part.__sortEdges__(Shape.Edges))
loft = Part.makeLoft([Face1.OuterWire, Face2.OuterWire], True, True);
obj.Shape = loft.removeSplitter()
def makeWrappedSketch():
if FreeCAD.ActiveDocument is None:
return
aSketchLabel = ""
selections=FreeCAD.Gui.Selection.getSelectionEx()
if len(selections) > 0:
selObject = selections[0]
if str(type(selObject.Object)) == "<type 'Sketcher.SketchObject'>":
aSketchLabel = selObject.Object.Label
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","WrappedSketch")
WrappedSketch(a, SketchLabel=aSketchLabel)
a.ViewObject.Proxy=0 # just set it to something different from None (this assignment is needed to run an internal notification)
FreeCAD.ActiveDocument.recompute()
FreeCADGui.ActiveDocument.ActiveView.fitAll()
return a