| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import unittest |
| | import math |
| |
|
| | import FreeCAD |
| | from FreeCAD import Base |
| | from FreeCAD import Units |
| | import Part |
| | import Sketcher |
| | import TestSketcherApp |
| |
|
| |
|
| | class TestLoft(unittest.TestCase): |
| | """Loft Tests""" |
| |
|
| | def setUp(self): |
| | self.Doc = FreeCAD.newDocument("PartDesignTestLoft") |
| |
|
| | def testSimpleAdditiveLoftCase(self): |
| | self.Body = self.Doc.addObject("PartDesign::Body", "Body") |
| | self.ProfileSketch = self.Doc.addObject("Sketcher::SketchObject", "ProfileSketch") |
| | self.Body.addObject(self.ProfileSketch) |
| | TestSketcherApp.CreateRectangleSketch(self.ProfileSketch, (0, 0), (1, 1)) |
| | self.Doc.recompute() |
| | self.LoftSketch = self.Doc.addObject("Sketcher::SketchObject", "LoftSketch") |
| | self.Body.addObject(self.LoftSketch) |
| | self.LoftSketch.MapMode = "FlatFace" |
| | self.LoftSketch.AttachmentSupport = (self.Doc.XZ_Plane, [""]) |
| | self.Doc.recompute() |
| | TestSketcherApp.CreateRectangleSketch(self.LoftSketch, (0, 1), (1, 1)) |
| | self.Doc.recompute() |
| | self.AdditiveLoft = self.Doc.addObject("PartDesign::AdditiveLoft", "AdditiveLoft") |
| | self.Body.addObject(self.AdditiveLoft) |
| | self.AdditiveLoft.Profile = self.ProfileSketch |
| | self.AdditiveLoft.Sections = [self.LoftSketch] |
| | self.Doc.recompute() |
| | self.assertAlmostEqual(self.AdditiveLoft.Shape.Volume, 1) |
| |
|
| | def testSimpleSubtractiveLoftCase(self): |
| | self.Body = self.Doc.addObject("PartDesign::Body", "Body") |
| | self.PadSketch = self.Doc.addObject("Sketcher::SketchObject", "SketchPad") |
| | self.Body.addObject(self.PadSketch) |
| | TestSketcherApp.CreateRectangleSketch(self.PadSketch, (0, 0), (1, 1)) |
| | self.Doc.recompute() |
| | self.Pad = self.Doc.addObject("PartDesign::Pad", "Pad") |
| | self.Body.addObject(self.Pad) |
| | self.Pad.Profile = self.PadSketch |
| | self.Pad.Length = 2 |
| | self.Doc.recompute() |
| | self.ProfileSketch = self.Doc.addObject("Sketcher::SketchObject", "ProfileSketch") |
| | self.Body.addObject(self.ProfileSketch) |
| | TestSketcherApp.CreateRectangleSketch(self.ProfileSketch, (0, 0), (1, 1)) |
| | self.Doc.recompute() |
| | self.LoftSketch = self.Doc.addObject("Sketcher::SketchObject", "LoftSketch") |
| | self.Body.addObject(self.LoftSketch) |
| | self.LoftSketch.MapMode = "FlatFace" |
| | self.LoftSketch.AttachmentSupport = (self.Doc.XZ_Plane, [""]) |
| | self.Doc.recompute() |
| | TestSketcherApp.CreateRectangleSketch(self.LoftSketch, (0, 1), (1, 1)) |
| | self.Doc.recompute() |
| | self.SubtractiveLoft = self.Doc.addObject("PartDesign::SubtractiveLoft", "SubtractiveLoft") |
| | self.Body.addObject(self.SubtractiveLoft) |
| | self.SubtractiveLoft.Profile = self.ProfileSketch |
| | self.SubtractiveLoft.Sections = [self.LoftSketch] |
| | self.Doc.recompute() |
| | self.assertAlmostEqual(self.SubtractiveLoft.Shape.Volume, 1) |
| |
|
| | def testClosedAdditiveLoftCase(self): |
| | """Test issue #6156: Loft tool "Closed" option not working""" |
| | body = self.Doc.addObject("PartDesign::Body", "Body") |
| |
|
| | sketch1 = body.newObject("Sketcher::SketchObject", "Sketch") |
| | sketch1.AttachmentSupport = (self.Doc.XZ_Plane, [""]) |
| | sketch1.MapMode = "FlatFace" |
| | sketch1.addGeometry( |
| | Part.Circle(Base.Vector(-40.0, 0.0, 0.0), Base.Vector(0, 0, 1), 10.0), False |
| | ) |
| | sketch1.addConstraint(Sketcher.Constraint("PointOnObject", 0, 3, -1)) |
| | sketch1.addConstraint(Sketcher.Constraint("Diameter", 0, 20.0)) |
| | sketch1.setDatum(1, Units.Quantity("20.000000 mm")) |
| | sketch1.addConstraint(Sketcher.Constraint("Distance", -1, 1, 0, 3, 40.0)) |
| | sketch1.setDatum(2, Units.Quantity("40.000000 mm")) |
| |
|
| | sketch2 = body.newObject("Sketcher::SketchObject", "Sketch001") |
| | sketch2.AttachmentSupport = (self.Doc.YZ_Plane, "") |
| | sketch2.MapMode = "FlatFace" |
| | sketch2.addGeometry( |
| | Part.Circle(Base.Vector(-10.0, 0.0, 0.0), Base.Vector(0, 0, 1), 10.0), False |
| | ) |
| | sketch2.addConstraint(Sketcher.Constraint("PointOnObject", 0, 3, -1)) |
| | sketch2.addConstraint(Sketcher.Constraint("Diameter", 0, 20.0)) |
| | sketch2.setDatum(1, Units.Quantity("20.000000 mm")) |
| | sketch2.addConstraint(Sketcher.Constraint("Distance", -1, 1, 0, 3, 40.0)) |
| | sketch2.setDatum(2, Units.Quantity("40.000000 mm")) |
| |
|
| | sketch3 = body.newObject("Sketcher::SketchObject", "Sketch002") |
| | sketch3.AttachmentSupport = (self.Doc.getObject("YZ_Plane"), "") |
| | sketch3.MapMode = "FlatFace" |
| | sketch3.addGeometry( |
| | Part.Circle(Base.Vector(40.0, 0.0, 0.0), Base.Vector(0, 0, 1), 10.0), False |
| | ) |
| | sketch3.addConstraint(Sketcher.Constraint("PointOnObject", 0, 3, -1)) |
| | sketch3.addConstraint(Sketcher.Constraint("Distance", -1, 1, 0, 3, 40.0)) |
| | sketch3.setDatum(1, Units.Quantity("40.000000 mm")) |
| | sketch3.addConstraint(Sketcher.Constraint("Diameter", 0, 20.0)) |
| | sketch3.setDatum(2, Units.Quantity("20.000000 mm")) |
| |
|
| | sketch4 = body.newObject("Sketcher::SketchObject", "Sketch003") |
| | sketch4.AttachmentSupport = (self.Doc.XZ_Plane, "") |
| | sketch4.MapMode = "FlatFace" |
| | sketch4.addGeometry( |
| | Part.Circle(Base.Vector(40.0, 0.0, 0.0), Base.Vector(0, 0, 1), 10.0), False |
| | ) |
| | sketch4.addConstraint(Sketcher.Constraint("PointOnObject", 0, 3, -1)) |
| | sketch4.addConstraint(Sketcher.Constraint("Distance", -1, 1, 0, 3, 40.0)) |
| | sketch4.setDatum(1, Units.Quantity("40.000000 mm")) |
| | sketch4.addConstraint(Sketcher.Constraint("Diameter", 0, 20.0)) |
| | sketch4.setDatum(2, Units.Quantity("20.000000 mm")) |
| |
|
| | self.Doc.recompute() |
| |
|
| | loft = body.newObject("PartDesign::AdditiveLoft", "AdditiveLoft") |
| | loft.Profile = sketch1 |
| | loft.Sections = [sketch2, sketch4, sketch3] |
| | loft.Closed = True |
| |
|
| | sketch1.Visibility = False |
| | sketch2.Visibility = False |
| | sketch3.Visibility = False |
| | sketch4.Visibility = False |
| |
|
| | self.Doc.recompute() |
| |
|
| | self.assertGreater(loft.Shape.Volume, 80000.0) |
| |
|
| | def testPadOnLoftBetweenCones(self): |
| | """Test issue #15138 adapted from a script by chennes""" |
| | body = self.Doc.addObject("PartDesign::Body", "Body") |
| | body.Label = "Body" |
| | coneBottomSketch = body.newObject("Sketcher::SketchObject", "ConeBottomSketch") |
| | coneBottomSketch.AttachmentSupport = (self.Doc.getObject("XY_Plane"), [""]) |
| | coneBottomSketch.MapMode = "FlatFace" |
| |
|
| | geoList = [] |
| | geoList.append( |
| | Part.Circle( |
| | Base.Vector(0.000000, 0.000000, 0.000000), |
| | Base.Vector(0.000000, 0.000000, 1.000000), |
| | 25.000000, |
| | ) |
| | ) |
| | coneBottomSketch.addGeometry(geoList, False) |
| | del geoList |
| |
|
| | constraintList = [] |
| | coneBottomSketch.addConstraint(Sketcher.Constraint("Diameter", 0, 25.000000)) |
| | coneBottomSketch.addConstraint(Sketcher.Constraint("Coincident", 0, 3, -1, 1)) |
| |
|
| | geoList = [] |
| | geoList.append( |
| | Part.Circle( |
| | Base.Vector(0.000000, 0.000000, 0.000000), |
| | Base.Vector(0.000000, 0.000000, 1.000000), |
| | 40.000000, |
| | ) |
| | ) |
| | coneBottomSketch.addGeometry(geoList, False) |
| | del geoList |
| |
|
| | constraintList = [] |
| | coneBottomSketch.addConstraint(Sketcher.Constraint("Diameter", 1, 40.000000)) |
| | coneBottomSketch.addConstraint(Sketcher.Constraint("Coincident", 1, 3, 0, 3)) |
| |
|
| | coneTopSketch = body.newObject("Sketcher::SketchObject", "ConeTopSketch") |
| | coneTopSketch.AttachmentSupport = (self.Doc.getObject("XY_Plane"), [""]) |
| | coneTopSketch.MapMode = "FlatFace" |
| |
|
| | geoList = [] |
| | geoList.append( |
| | Part.Circle( |
| | Base.Vector(0.000000, 0.000000, 0.000000), |
| | Base.Vector(0.000000, 0.000000, 1.000000), |
| | 8.000000, |
| | ) |
| | ) |
| | coneTopSketch.addGeometry(geoList, False) |
| | del geoList |
| |
|
| | constraintList = [] |
| | coneTopSketch.addConstraint(Sketcher.Constraint("Diameter", 0, 8.000000)) |
| | coneTopSketch.addConstraint(Sketcher.Constraint("Coincident", 0, 3, -1, 1)) |
| |
|
| | geoList = [] |
| | geoList.append( |
| | Part.Circle( |
| | Base.Vector(0.000000, 0.000000, 0.000000), |
| | Base.Vector(0.000000, 0.000000, 1.000000), |
| | 15.000000, |
| | ) |
| | ) |
| | coneTopSketch.addGeometry(geoList, False) |
| | del geoList |
| |
|
| | constraintList = [] |
| | coneTopSketch.addConstraint(Sketcher.Constraint("Diameter", 1, 15.000000)) |
| | coneTopSketch.addConstraint(Sketcher.Constraint("Coincident", 1, 3, 0, 3)) |
| | coneTopSketch.AttachmentOffset = Base.Placement( |
| | Base.Vector(0, 0, 20), Base.Rotation(Base.Vector(0, 0, 1), 0) |
| | ) |
| | self.Doc.recompute() |
| |
|
| | cone = body.newObject("PartDesign::AdditiveLoft", "Cone") |
| | cone.Profile = coneBottomSketch |
| | cone.Sections = [(coneTopSketch, [""])] |
| | coneBottomSketch.Visibility = False |
| | coneTopSketch.Visibility = False |
| | self.Doc.recompute() |
| |
|
| | pad = body.newObject("PartDesign::Pad", "Pad") |
| | pad.Profile = ( |
| | cone, |
| | [ |
| | "Face3", |
| | ], |
| | ) |
| | pad.Length = 10.000000 |
| | pad.TaperAngle = 0.000000 |
| | pad.UseCustomVector = 0 |
| | pad.Direction = (0, 0, 1) |
| | pad.ReferenceAxis = None |
| | pad.AlongSketchNormal = 1 |
| | pad.Type = 0 |
| | pad.UpToFace = None |
| | pad.Reversed = False |
| | pad.SideType = "One side" |
| | pad.Offset = 0 |
| | cone.Visibility = True |
| | self.Doc.recompute() |
| |
|
| | outerConeBottomRadius = 40 / 2 |
| | outerConeTopRadius = 15 / 2 |
| | innerConeBottomRadius = 25 / 2 |
| | innerConeTopRadius = 8 / 2 |
| | coneHeight = 20.0 |
| | padHeight = 10.0 |
| | |
| | outerConeVolume = ( |
| | 1.0 |
| | / 3.0 |
| | * math.pi |
| | * coneHeight |
| | * ( |
| | outerConeBottomRadius**2 |
| | + outerConeTopRadius**2 |
| | + outerConeBottomRadius * outerConeTopRadius |
| | ) |
| | ) |
| | innerConeVolume = ( |
| | 1.0 |
| | / 3.0 |
| | * math.pi |
| | * coneHeight |
| | * ( |
| | innerConeBottomRadius**2 |
| | + innerConeTopRadius**2 |
| | + innerConeBottomRadius * innerConeTopRadius |
| | ) |
| | ) |
| | frustumVolume = outerConeVolume - innerConeVolume |
| | topArea = math.pi * (outerConeTopRadius**2) - math.pi * (innerConeTopRadius**2) |
| | bottomArea = math.pi * (outerConeBottomRadius**2) - math.pi * (innerConeBottomRadius**2) |
| | padVolume = topArea * padHeight |
| | self.assertEqual(len(cone.Shape.Faces), 4) |
| | self.assertAlmostEqual(cone.Shape.Faces[2].Area, topArea) |
| | self.assertAlmostEqual(cone.Shape.Faces[0].Area, bottomArea) |
| | self.assertAlmostEqual(cone.Shape.Volume, frustumVolume) |
| | self.assertAlmostEqual( |
| | pad.Shape.Volume, frustumVolume + padVolume |
| | ) |
| | self.assertAlmostEqual( |
| | body.Shape.Volume, frustumVolume + padVolume |
| | ) |
| |
|
| | def testTwoFacesAdditiveLoftCase(self): |
| | """Test issue #19183: Loft tool "Loft between faces no longer works""" |
| | body = self.Doc.addObject("PartDesign::Body", "Body") |
| |
|
| | sketch1 = body.newObject("Sketcher::SketchObject", "Sketch") |
| |
|
| | sketch1.addGeometry( |
| | Part.LineSegment( |
| | Base.Vector(-2.060394, -1.332045, 0), |
| | Base.Vector(-19.922129, -27.589359, 0), |
| | ), |
| | False, |
| | ) |
| | sketch1.addGeometry( |
| | Part.LineSegment( |
| | Base.Vector(1.940183, -1.501086, 0), |
| | Base.Vector(16.928263, -28.265512, 0), |
| | ), |
| | False, |
| | ) |
| | sketch1.addGeometry( |
| | Part.ArcOfCircle( |
| | Part.Circle(Base.Vector(0.418837, -1.275699, 0), Base.Vector(0, 0, 1), 32.879378), |
| | -1.751723, |
| | -1.454683, |
| | ), |
| | False, |
| | ) |
| | sketch1.addGeometry( |
| | Part.ArcOfCircle( |
| | Part.Circle( |
| | Base.Vector(-9.385396, -30.124937, 0), |
| | Base.Vector(0, 0, 1), |
| | 8.359959, |
| | ), |
| | 2.847554, |
| | 4.831818, |
| | ), |
| | False, |
| | ) |
| | sketch1.addGeometry( |
| | Part.ArcOfCircle( |
| | Part.Circle( |
| | Base.Vector(3.236143, -29.279745, 0), |
| | Base.Vector(0, 0, 1), |
| | 11.449505, |
| | ), |
| | -1.076432, |
| | 0.044306, |
| | ), |
| | False, |
| | ) |
| | sketch1.addConstraint(Sketcher.Constraint("Coincident", 0, 1, 1, 1)) |
| | sketch1.addConstraint(Sketcher.Constraint("Coincident", 0, 1, 2, 3)) |
| | sketch1.addConstraint(Sketcher.Constraint("Coincident", 0, 1, -1, 1)) |
| | sketch1.addConstraint(Sketcher.Constraint("Tangent", 0, 2, 3, 1)) |
| | sketch1.addConstraint(Sketcher.Constraint("Tangent", 1, 2, 4, 2)) |
| | sketch1.addConstraint(Sketcher.Constraint("Tangent", 2, 1, 3, 2)) |
| | sketch1.addConstraint(Sketcher.Constraint("Tangent", 2, 2, 4, 1)) |
| | sketch1.addConstraint(Sketcher.Constraint("Symmetric", 0, 2, 1, 2, -2)) |
| | sketch1.addConstraint(Sketcher.Constraint("Symmetric", 2, 1, 2, 2, -2)) |
| | sketch1.delConstraint(8) |
| | sketch1.addConstraint(Sketcher.Constraint("Radius", 2, 39.936694)) |
| | sketch1.setDatum(8, Units.Quantity("40.000000 mm")) |
| | sketch1.addConstraint(Sketcher.Constraint("DistanceX", 2, 1, 2, 2, 16.771341)) |
| | sketch1.setDatum(9, Units.Quantity("10.000000 mm")) |
| | sketch1.addConstraint(Sketcher.Constraint("Distance", 0, 30.965710)) |
| | sketch1.setDatum(10, Units.Quantity("30.000000 mm")) |
| |
|
| | sketch2 = body.newObject("Sketcher::SketchObject", "Sketch001") |
| | sketch2.addGeometry( |
| | Part.LineSegment( |
| | Base.Vector(-2.060394, -1.332045, 0), |
| | Base.Vector(-19.922129, -27.589359, 0), |
| | ), |
| | False, |
| | ) |
| | sketch2.addGeometry( |
| | Part.LineSegment( |
| | Base.Vector(1.940183, -1.501086, 0), |
| | Base.Vector(16.928263, -28.265512, 0), |
| | ), |
| | False, |
| | ) |
| | sketch2.addGeometry( |
| | Part.ArcOfCircle( |
| | Part.Circle(Base.Vector(0.418837, -1.275699, 0), Base.Vector(0, 0, 1), 32.879378), |
| | -1.751723, |
| | -1.454683, |
| | ), |
| | False, |
| | ) |
| | sketch2.addGeometry( |
| | Part.ArcOfCircle( |
| | Part.Circle( |
| | Base.Vector(-9.385396, -30.124937, 0), |
| | Base.Vector(0, 0, 1), |
| | 8.359959, |
| | ), |
| | 2.847554, |
| | 4.831818, |
| | ), |
| | False, |
| | ) |
| | sketch2.addGeometry( |
| | Part.ArcOfCircle( |
| | Part.Circle( |
| | Base.Vector(3.236143, -29.279745, 0), |
| | Base.Vector(0, 0, 1), |
| | 11.449505, |
| | ), |
| | -1.076432, |
| | 0.044306, |
| | ), |
| | False, |
| | ) |
| | sketch2.addConstraint(Sketcher.Constraint("Coincident", 0, 1, 1, 1)) |
| | sketch2.addConstraint(Sketcher.Constraint("Coincident", 0, 1, 2, 3)) |
| | sketch2.addConstraint(Sketcher.Constraint("Coincident", 0, 1, -1, 1)) |
| | sketch2.addConstraint(Sketcher.Constraint("Tangent", 0, 2, 3, 1)) |
| | sketch2.addConstraint(Sketcher.Constraint("Tangent", 1, 2, 4, 2)) |
| | sketch2.addConstraint(Sketcher.Constraint("Tangent", 2, 1, 3, 2)) |
| | sketch2.addConstraint(Sketcher.Constraint("Tangent", 2, 2, 4, 1)) |
| | sketch2.addConstraint(Sketcher.Constraint("Symmetric", 0, 2, 1, 2, -2)) |
| | sketch2.addConstraint(Sketcher.Constraint("Symmetric", 2, 1, 2, 2, -2)) |
| | sketch2.delConstraint(8) |
| | sketch2.addConstraint(Sketcher.Constraint("Radius", 2, 39.936694)) |
| | sketch2.setDatum(8, Units.Quantity("30.000000 mm")) |
| | sketch2.addConstraint(Sketcher.Constraint("Radius", 3, 1.020288)) |
| | sketch2.setDatum(9, Units.Quantity("3.600000 mm")) |
| | sketch2.addConstraint(Sketcher.Constraint("DistanceX", 2, 1, 2, 2, 10.926759)) |
| | sketch2.setDatum(10, Units.Quantity("10.000000 mm")) |
| | self.Doc.recompute() |
| |
|
| | pad1 = body.newObject("PartDesign::Pad", "Pad") |
| |
|
| | pad1.Profile = sketch1 |
| | pad1.Length = 10.000000 |
| | pad1.TaperAngle = 0.000000 |
| | pad1.Reversed = 1 |
| | self.Doc.recompute() |
| | sketch1.Visibility = False |
| |
|
| | pad2 = body.newObject("PartDesign::Pad", "Pad001") |
| |
|
| | pad2.Profile = sketch2 |
| | pad2.Length = 10.000000 |
| | pad2.TaperAngle = 0.000000 |
| | self.Doc.recompute() |
| | sketch2.Visibility = False |
| | pad1.Visibility = False |
| | pad2.Visibility = True |
| | body.Tip = pad2 |
| | self.assertGreater(pad2.Shape.Volume, 8715.0) |
| |
|
| | loft = body.newObject("PartDesign::AdditiveLoft", "AdditiveLoft") |
| | loft.Profile = ( |
| | self.Doc.getObject("Pad001"), |
| | [ |
| | "Face7", |
| | ], |
| | ) |
| | loft.Sections = [ |
| | ( |
| | self.Doc.getObject("Pad001"), |
| | [ |
| | "Face10", |
| | ], |
| | ) |
| | ] |
| | loft.Closed = False |
| |
|
| | self.Doc.recompute() |
| | body.Tip = loft |
| | self.assertGreater(body.Shape.Volume, 9220.0) |
| | self.Doc.recompute() |
| |
|
| | def tearDown(self): |
| | |
| | FreeCAD.closeDocument("PartDesignTestLoft") |
| | |
| |
|