[OpenSCAD] Improve helical extrusion, fix scaling
Modify the auxiliary spine to be a true helix, resulting in a much smoother linear extrusion when a twist angle is applied. This also corrects a bug in the scaling during linear extrusion, where non-uniform scaling was not handled correctly.
This commit is contained in:
@@ -390,7 +390,7 @@ class Twist:
|
||||
def __init__(self, obj,child=None,h=1.0,angle=0.0,scale=[1.0,1.0]):
|
||||
obj.addProperty("App::PropertyLink","Base","Base",
|
||||
"The base object that must be tranfsformed")
|
||||
obj.addProperty("App::PropertyAngle","Angle","Base","Twist Angle in degrees") #degree or rad
|
||||
obj.addProperty("App::PropertyAngle","Angle","Base","Twist angle") #degree or rad
|
||||
obj.addProperty("App::PropertyDistance","Height","Base","Height of the Extrusion")
|
||||
obj.addProperty("App::PropertyFloatList","Scale","Base","Scale to apply during the Extrusion")
|
||||
|
||||
@@ -404,45 +404,45 @@ class Twist:
|
||||
self.createGeometry(fp)
|
||||
|
||||
def onChanged(self, fp, prop):
|
||||
pass
|
||||
#if prop in ["Angle","Height"]:
|
||||
# self.createGeometry(fp)
|
||||
if prop in ["Angle","Height"]:
|
||||
self.createGeometry(fp)
|
||||
|
||||
def createGeometry(self,fp):
|
||||
import FreeCAD,Part,math,sys
|
||||
if fp.Base and fp.Height and \
|
||||
fp.Base.Shape.isValid():
|
||||
#wire=fp.Base.Shape.Wires[0].transformGeometry(fp.Base.Placement.toMatrix())
|
||||
solids=[]
|
||||
for faceb in fp.Base.Shape.Faces:
|
||||
#fp.Base.Shape.Faces[0].check()
|
||||
if fp.Base and fp.Height and fp.Base.Shape.isValid():
|
||||
solids = []
|
||||
for lower_face in fp.Base.Shape.Faces:
|
||||
upper_face = lower_face.copy()
|
||||
face_transform = FreeCAD.Matrix()
|
||||
face_transform.rotateZ(math.radians(fp.Angle.Value))
|
||||
face_transform.scale(fp.Scale[0], fp.Scale[1], 1.0)
|
||||
face_transform.move(FreeCAD.Vector(0,0,fp.Height.Value))
|
||||
upper_face.transformShape(face_transform, False, True) # True to check for non-uniform scaling
|
||||
|
||||
#faceb=fp.Base.Shape.Faces[0]
|
||||
#faceb=fp.Base.Shape.removeSplitter().Faces[0]
|
||||
faceu=faceb.copy()
|
||||
facetransform=FreeCAD.Matrix()
|
||||
facetransform.rotateZ(math.radians(fp.Angle.Value))
|
||||
facetransform.scale(fp.Scale[0],fp.Scale[1], 1.0)
|
||||
facetransform.move(FreeCAD.Vector(0,0,fp.Height.Value))
|
||||
faceu.transformShape(facetransform)
|
||||
step = 2 + abs(int(fp.Angle.Value // 90)) #resolution in z direction
|
||||
zinc = fp.Height.Value/(step-1.0)
|
||||
angleinc = math.radians(fp.Angle.Value)/(step-1.0)
|
||||
spine = Part.makePolygon([(0,0,i*zinc) \
|
||||
for i in range(step)])
|
||||
auxspine = Part.makePolygon([(math.cos(i*angleinc),\
|
||||
math.sin(i*angleinc),i*zinc) \
|
||||
for i in range(step)])
|
||||
faces=[faceb,faceu]
|
||||
for wire1,wire2 in zip(faceb.Wires,faceu.Wires):
|
||||
pipeshell=Part.BRepOffsetAPI.MakePipeShell(spine)
|
||||
pipeshell.setSpineSupport(spine)
|
||||
pipeshell.add(wire1)
|
||||
pipeshell.add(wire2)
|
||||
pipeshell.setAuxiliarySpine(auxspine,True,0)
|
||||
print(pipeshell.getStatus())
|
||||
assert(pipeshell.isReady())
|
||||
pipeshell.build()
|
||||
spine = Part.makePolygon([(0,0,0),(0,0,fp.Height.Value)])
|
||||
if fp.Angle.Value == 0.0:
|
||||
auxiliary_spine = Part.makePolygon([(1,1,0),(fp.Scale[0],fp.Scale[1],fp.Height.Value)])
|
||||
else:
|
||||
num_revolutions = abs(fp.Angle.Value)/360.0
|
||||
pitch = fp.Height.Value / num_revolutions
|
||||
height = fp.Height.Value
|
||||
radius = 1.0
|
||||
if fp.Angle.Value < 0.0:
|
||||
left_handed = True
|
||||
else:
|
||||
left_handed = False
|
||||
auxiliary_spine = Part.makeHelix(pitch, height, radius, 0.0, left_handed)
|
||||
|
||||
faces = [lower_face,upper_face]
|
||||
for wire1,wire2 in zip(lower_face.Wires,upper_face.Wires):
|
||||
pipe_shell = Part.BRepOffsetAPI.MakePipeShell(spine)
|
||||
pipe_shell.setSpineSupport(spine)
|
||||
pipe_shell.add(wire1)
|
||||
pipe_shell.add(wire2)
|
||||
pipe_shell.setAuxiliarySpine(auxiliary_spine,True,0)
|
||||
print(pipe_shell.getStatus())
|
||||
assert(pipe_shell.isReady())
|
||||
pipe_shell.build()
|
||||
faces.extend(pipeshell.shape().Faces)
|
||||
try:
|
||||
fullshell=Part.Shell(faces)
|
||||
|
||||
@@ -26,6 +26,7 @@ import OpenSCAD
|
||||
import importCSG
|
||||
import tempfile
|
||||
import os
|
||||
import math
|
||||
|
||||
from os.path import join
|
||||
|
||||
@@ -244,22 +245,26 @@ polyhedron(
|
||||
self.assertAlmostEqual (object.Shape.Volume, 4000.000, 3)
|
||||
FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
doc = self.utility_create_scad("linear_extrude(height = 20, scale = 0.2) square([20, 10], center = true);", "linear_extrude_scale")
|
||||
object = doc.ActiveObject
|
||||
self.assertTrue (object is not None)
|
||||
self.assertAlmostEqual (object.Shape.Volume, 1945.2745, 3)
|
||||
FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
doc = self.utility_create_scad("linear_extrude(height = 20, twist = 90) square([20, 10], center = true);", "linear_extrude_twist")
|
||||
object = doc.ActiveObject
|
||||
self.assertTrue (object is not None)
|
||||
self.assertAlmostEqual (object.Shape.Volume, 3999.9961, 3)
|
||||
self.assertAlmostEqual (object.Shape.Volume, 4000.000, 2)
|
||||
FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
doc = self.utility_create_scad("linear_extrude(height = 40, twist = 180, scale=0.25) square([20, 10], center = true);", "linear_extrude_twist")
|
||||
doc = self.utility_create_scad("linear_extrude(height = 20, scale = 0.2) square([20, 10], center = true);", "linear_extrude_scale")
|
||||
object = doc.ActiveObject
|
||||
self.assertTrue (object is not None)
|
||||
self.assertAlmostEqual (object.Shape.Volume, 4144.9071, 3)
|
||||
h = 20
|
||||
a1 = 20*10
|
||||
a2 = 20*0.2 * 10*0.2
|
||||
expected_volume = (h/3) * (a1+a2+math.sqrt(a1*a2))
|
||||
self.assertAlmostEqual (object.Shape.Volume, expected_volume, 3)
|
||||
FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
doc = self.utility_create_scad("linear_extrude(height = 20, twist = 180, scale=0.2) square([20, 10], center = true);", "linear_extrude_twist_scale")
|
||||
object = doc.ActiveObject
|
||||
self.assertTrue (object is not None)
|
||||
self.assertAlmostEqual (object.Shape.Volume, expected_volume, 2)
|
||||
FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
def test_import_rotate_extrude_file(self):
|
||||
@@ -369,7 +374,36 @@ resize(newsize = [0,0,10], auto = [0,0,0]) {
|
||||
os.chdir(cwd)
|
||||
|
||||
def test_import_projection(self):
|
||||
pass
|
||||
base_shape = "linear_extrude(height=5,center=true,twist=90,scale=0.5){square([1,1],center=true);}"
|
||||
hole = "cube([0.25,0.25,6],center=true);"
|
||||
cut_shape = f"difference() {{ {base_shape} {hole} }}"
|
||||
|
||||
doc = self.utility_create_scad(f"projection(cut=true) {base_shape}", "projection_slice_square")
|
||||
object = doc.getObject("projection_cut")
|
||||
self.assertTrue (object is not None)
|
||||
self.assertAlmostEqual (object.Shape.Area, 0.75*0.75, 3)
|
||||
FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
doc = self.utility_create_scad(f"projection(cut=true) {cut_shape}", "projection_slice_square_with_hole")
|
||||
object = doc.getObject("projection_cut")
|
||||
self.assertTrue (object is not None)
|
||||
self.assertAlmostEqual (object.Shape.Area, 0.75*0.75 - 0.25*0.25, 3)
|
||||
FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
# Unimplemented functionality:
|
||||
|
||||
# With cut=false, the twisted unit square projects to a circle of radius sqrt(0.5)
|
||||
#doc = self.utility_create_scad(f"projection(cut=false) {base_shape}", "projection_circle")
|
||||
#object = doc.getObject("projection")
|
||||
#self.assertTrue (object is not None)
|
||||
#self.assertAlmostEqual (object.Shape.Area, 2*math.pi*math.sqrt(2), 3)
|
||||
#FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
#doc = self.utility_create_scad(f"projection(cut=false) {cut_shape}", "projection_circle_with_hole")
|
||||
#object = doc.getObject("projection")
|
||||
#self.assertTrue (object is not None)
|
||||
#self.assertAlmostEqual (object.Shape.Area, 2*math.pi*math.sqrt(0.5) - 0.125, 3)
|
||||
#FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
def test_import_hull(self):
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user