Merge remote-tracking branch 'upstream/main' into bim-cleanup-imports

This commit is contained in:
marcuspollio
2025-03-30 12:44:55 +02:00
807 changed files with 24639 additions and 19953 deletions

View File

@@ -26,7 +26,6 @@ SET(Arch_SRCS
ArchStairs.py
ArchSpace.py
ArchRebar.py
TestArch.py
ArchFrame.py
ArchPanel.py
ArchEquipment.py
@@ -46,6 +45,8 @@ SET(Arch_SRCS
ArchSketchObject.py
BimSelect.py
BimStatus.py
TestArch.py
TestArchGui.py
)
SET(importers_SRCS
@@ -196,6 +197,13 @@ SET(nativeifc_SRCS
nativeifc/ifc_classification.py
)
SET(bimtests_SRCS
bimtests/TestArchBase.py
bimtests/TestArchRoof.py
bimtests/TestArchSpace.py
bimtests/TestArchWall.py
)
SOURCE_GROUP("" FILES ${Arch_SRCS})
SET(BIMGuiIcon_SVG
@@ -214,6 +222,7 @@ ADD_CUSTOM_TARGET(BIM ALL
${ArchGuiIcon_SVG}
${importers_SRCS}
${bimcommands_SRCS}
${bimtests_SRCS}
${nativeifc_SRCS}
${BIMGuiIcon_SVG}
)
@@ -226,6 +235,7 @@ fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${Arch_SRCS})
fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${Dice3DS_SRCS})
fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${importers_SRCS})
fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${bimcommands_SRCS})
fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${bimtests_SRCS})
fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/Mod/BIM" ${nativeifc_SRCS})
fc_copy_sources(BIM "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/BIM" ${BIMGuiIcon_SVG})
@@ -273,6 +283,12 @@ INSTALL(
DESTINATION Mod/BIM/bimcommands
)
INSTALL(
FILES
${bimtests_SRCS}
DESTINATION Mod/BIM/bimtests
)
INSTALL(
FILES
${nativeifc_SRCS}

View File

@@ -35,3 +35,5 @@ FreeCAD.addExportType("Collada (*.dae)","importers.importDAE")
FreeCAD.addImportType("3D Studio mesh (*.3ds *.3DS)","importers.import3DS")
FreeCAD.addImportType("SweetHome3D (*.sh3d)","importers.importSH3D")
FreeCAD.addImportType("Shapefile (*.shp *.SHP)","importers.importSHP")
FreeCAD.__unit_test__ += ["TestArch"]

View File

@@ -686,6 +686,6 @@ FreeCADGui.addPreferencePage(":/ui/preferences-dae.ui", t)
FreeCADGui.addPreferencePage(":/ui/preferences-sh3d-import.ui", t)
# Add unit tests
FreeCAD.__unit_test__ += ["TestArch"]
FreeCAD.__unit_test__ += ["TestArchGui"]
# The NativeIFC tests require internet connection and file download
# FreeCAD.__unit_test__ += ["nativeifc.ifc_selftest"]

View File

@@ -34,352 +34,12 @@ import Sketcher
import TechDraw
import WorkingPlane
from FreeCAD import Units
from bimtests.TestArchRoof import TestArchRoof
from bimtests.TestArchSpace import TestArchSpace
from bimtests.TestArchWall import TestArchWall
from draftutils.messages import _msg
if App.GuiUp:
import FreeCADGui
brepArchiCAD = """
DBRep_DrawableShape
CASCADE Topology V1, (c) Matra-Datavision
Locations 3
1
1 0 0 0
0 1 0 0
0 0 1 0
1
0 1 0 0
-1 0 0 0
0 0 1 0
2 2 -1 0
Curve2ds 0
Curves 12
1 0 0 0 1 0 0
1 3000 0 0 0 0 1
1 3000 0 3000 -1 0 0
1 0 0 3000 0 0 -1
1 0 0 0 0 1 0
1 0 5000 0 1 0 0
1 3000 5000 0 0 -1 0
1 3000 5000 0 0 0 1
1 3000 5000 3000 0 -1 0
1 3000 5000 3000 -1 0 0
1 0 5000 3000 0 -1 0
1 0 5000 3000 0 0 -1
Polygon3D 0
PolygonOnTriangulations 24
2 1 2
p 18.3333333333333 1 0 3000
2 1 4
p 18.3333333333333 1 0 3000
2 2 3
p 18.3333333333333 1 0 3000
2 2 4
p 18.3333333333333 1 0 3000
2 3 4
p 18.3333333333333 1 0 3000
2 1 2
p 18.3333333333333 1 0 3000
2 4 1
p 18.3333333333333 1 0 3000
2 3 1
p 18.3333333333333 1 0 3000
2 1 2
p 18.3333333333333 1 0 5000
2 1 2
p 18.3333333333333 1 0 5000
2 2 3
p 18.3333333333333 1 0 3000
2 1 2
p 18.3333333333333 1 0 3000
2 3 4
p 18.3333333333333 1 0 5000
2 1 2
p 18.3333333333333 1 0 5000
2 1 3
p 18.3333333333333 1 0 3000
2 2 4
p 18.3333333333333 1 0 3000
2 3 4
p 18.3333333333333 1 0 5000
2 3 1
p 18.3333333333333 1 0 5000
2 3 4
p 18.3333333333333 1 0 3000
2 4 3
p 18.3333333333333 1 0 3000
2 4 2
p 18.3333333333333 1 0 5000
2 4 3
p 18.3333333333333 1 0 5000
2 4 2
p 18.3333333333333 1 0 3000
2 3 1
p 18.3333333333333 1 0 3000
Surfaces 6
1 1500 0 1500 -0 -1 -0 0 0 -1 1 0 0
1 1500 2500 0 -0 -0 -1 -1 0 0 0 1 0
1 3000 2500 1500 1 0 0 0 0 1 0 -1 0
1 1500 2500 3000 0 0 1 1 0 0 0 1 0
1 0 2500 1500 -1 -0 -0 0 0 -1 0 -1 0
1 1500 5000 1500 0 1 0 0 0 1 1 0 0
Triangulations 6
4 2 1 18.3333333333333
0 0 0 3000 0 0 3000 0 3000 0 0 3000 1500 -1500 1500 1500 -1500 1500 -1500 -1500 3 4 1 2 3 1
4 2 1 18.3333333333333
0 0 0 0 5000 0 3000 5000 0 3000 0 0 1500 -2500 1500 2500 -1500 2500 -1500 -2500 2 3 4 2 4 1
4 2 1 18.3333333333333
3000 5000 0 3000 0 0 3000 5000 3000 3000 0 3000 -1500 -2500 -1500 2500 1500 -2500 1500 2500 4 2 1 4 1 3
4 2 1 18.3333333333333
3000 0 3000 0 0 3000 3000 5000 3000 0 5000 3000 1500 -2500 -1500 -2500 1500 2500 -1500 2500 3 2 1 3 4 2
4 2 1 18.3333333333333
0 0 0 0 5000 0 0 0 3000 0 5000 3000 1500 2500 1500 -2500 -1500 2500 -1500 -2500 1 3 4 1 4 2
4 2 1 18.3333333333333
0 5000 0 3000 5000 0 0 5000 3000 3000 5000 3000 -1500 -1500 -1500 1500 1500 -1500 1500 1500 3 2 1 4 2 3
TShapes 35
Ve
0.1
0 0 0
0 0
0101101
*
Ve
0.1
0 -3000 0
0 0
0101101
*
Ed
0.0001 1 1 0
1 1 0 0 3000
6 1 1 0
6 2 2 0
0
0101000
+35 3 -34 3 *
Ve
0.1
0 -3000 3000
0 0
0101101
*
Ed
0.0001 1 1 0
1 2 0 0 3000
6 3 1 0
6 4 3 0
0
0101000
+34 3 -32 3 *
Ve
0.1
0 0 3000
0 0
0101101
*
Ed
0.0001 1 1 0
1 3 0 0 3000
6 5 1 0
6 6 4 0
0
0101000
+32 3 -30 3 *
Ed
0.0001 1 1 0
1 4 0 0 3000
6 7 1 0
6 8 5 0
0
0101000
+30 3 -35 3 *
Wi
0101100
+33 0 +31 0 +29 0 +28 0 *
Fa
0 0.1 1 0
2 1
0111000
+27 0 *
Ve
0.1
5000 0 0
0 0
0101101
*
Ed
0.0001 1 1 0
1 5 0 0 5000
6 9 2 0
6 10 5 0
0
0101000
+35 3 -25 3 *
Ve
0.1
5000 -3000 0
0 0
0101101
*
Ed
0.0001 1 1 0
1 6 0 0 3000
6 11 2 0
6 12 6 0
0
0101000
+25 3 -23 3 *
Ed
0.0001 1 1 0
1 7 0 0 5000
6 13 2 0
6 14 3 0
0
0101000
+23 3 -34 3 *
Wi
0101100
+24 0 +22 0 +21 0 -33 0 *
Fa
0 0.1 2 0
2 2
0111000
+20 0 *
Ve
0.1
5000 -3000 3000
0 0
0101101
*
Ed
0.0001 1 1 0
1 8 0 0 3000
6 15 3 0
6 16 6 0
0
0101000
+23 3 -18 3 *
Ed
0.0001 1 1 0
1 9 0 0 5000
6 17 3 0
6 18 4 0
0
0101000
+18 3 -32 3 *
Wi
0101100
-21 0 +17 0 +16 0 -31 0 *
Fa
0 0.1 3 0
2 3
0111000
+15 0 *
Ve
0.1
5000 0 3000
0 0
0101101
*
Ed
0.0001 1 1 0
1 10 0 0 3000
6 19 4 0
6 20 6 0
0
0101000
+18 3 -13 3 *
Ed
0.0001 1 1 0
1 11 0 0 5000
6 21 4 0
6 22 5 0
0
0101000
+13 3 -30 3 *
Wi
0101100
-29 0 -16 0 +12 0 +11 0 *
Fa
0 0.1 4 0
2 4
0111000
+10 0 *
Ed
0.0001 1 1 0
1 12 0 0 3000
6 23 5 0
6 24 6 0
0
0101000
+13 3 -25 3 *
Wi
0101100
-24 0 -28 0 -11 0 +8 0 *
Fa
0 0.1 5 0
2 5
0111000
+7 0 *
Wi
0101100
-22 0 -8 0 -12 0 -17 0 *
Fa
0 0.1 6 0
2 6
0111000
+5 0 *
Sh
0101100
+26 0 +19 0 +14 0 +9 0 +6 0 +4 0 *
So
0100000
+3 0 *
Co
1100000
+2 2 *
+1 1
"""
def like(a, b):
return abs(a-b) < 0.001
def checkBB(a, b):
return like(a.XMin, b.XMin) and like(a.YMin, b.YMin) and like(a.ZMin, b.ZMin) and like(a.XMax, b.XMax) and like(a.YMax, b.YMax) and like(a.ZMax, b.ZMax)
class ArchTest(unittest.TestCase):
@@ -392,72 +52,11 @@ class ArchTest(unittest.TestCase):
App.newDocument("ArchTest")
App.setActiveDocument("ArchTest")
def testWall(self):
App.Console.PrintLog ('Checking Arch Wall...\n')
l=Draft.makeLine(App.Vector(0,0,0),App.Vector(-2,0,0))
w = Arch.makeWall(l)
self.assertTrue(w,"Arch Wall failed")
def testWallMultiMatAlign(self):
App.Console.PrintLog ('Checking Arch Wall with MultiMaterial and 3 alignments...\n')
matA = Arch.makeMaterial()
matB = Arch.makeMaterial()
matMulti = Arch.makeMultiMaterial()
matMulti.Materials = [matA, matB]
matMulti.Thicknesses = [100, 200] # total width different from default 200
pts = [App.Vector( 0, 0, 0),
App.Vector(1000, 0, 0),
App.Vector(1000, 1000, 0),
App.Vector(2000, 1000, 0)]
# wall based on wire:
wire = Draft.makeWire(pts)
wallWire = Arch.makeWall(wire)
wallWire.Material = matMulti
# wall based on sketch:
sketch = App.activeDocument().addObject('Sketcher::SketchObject','Sketch')
sketch.addGeometry([Part.LineSegment(pts[0], pts[1]),
Part.LineSegment(pts[1], pts[2]),
Part.LineSegment(pts[2], pts[3])])
wallSketch = Arch.makeWall(sketch)
wallSketch.Material = matMulti
alignLst = ["Left", "Center", "Right"]
checkLst = [[App.Vector(0, -300, 0), App.Vector(2000, 1000, 0)],
[App.Vector(0, -150, 0), App.Vector(2000, 1150, 0)],
[App.Vector(0, 0, 0), App.Vector(2000, 1300, 0)]]
for i in range(3):
wallWire.Align = alignLst[i]
wallSketch.Align = alignLst[i]
App.ActiveDocument.recompute()
for box in [wallWire.Shape.BoundBox, wallSketch.Shape.BoundBox]:
ptMin = App.Vector(box.XMin, box.YMin, 0)
self.assertTrue(ptMin.isEqual(checkLst[i][0], 1e-8),
"Arch Wall with MultiMaterial and 3 alignments failed")
ptMax = App.Vector(box.XMax, box.YMax, 0)
self.assertTrue(ptMax.isEqual(checkLst[i][1], 1e-8),
"Arch Wall with MultiMaterial and 3 alignments failed")
def testStructure(self):
App.Console.PrintLog ('Checking BIM Structure...\n')
s = Arch.makeStructure(length=2,width=3,height=5)
self.assertTrue(s,"BIM Structure failed")
def testRebar(self):
App.Console.PrintLog ('Checking Arch Rebar...\n')
s = Arch.makeStructure(length=2,width=3,height=5)
sk = App.ActiveDocument.addObject('Sketcher::SketchObject','Sketch')
sk.AttachmentSupport = (s,["Face6"])
sk.addGeometry(Part.LineSegment(App.Vector(-0.85,1.25,0),App.Vector(0.75,1.25,0)))
sk.addGeometry(Part.LineSegment(App.Vector(0.75,1.25,0),App.Vector(0.75,-1.20,0)))
sk.addGeometry(Part.LineSegment(App.Vector(0.75,-1.20,0),App.Vector(-0.85,-1.20,0)))
sk.addGeometry(Part.LineSegment(App.Vector(-0.85,-1.20,0),App.Vector(-0.85,1.25,0)))
sk.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1))
sk.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1))
sk.addConstraint(Sketcher.Constraint('Coincident',2,2,3,1))
sk.addConstraint(Sketcher.Constraint('Coincident',3,2,0,1))
r = Arch.makeRebar(s,sk,diameter=.1,amount=2)
self.assertTrue(r,"Arch Rebar failed")
def testFloor(self):
App.Console.PrintLog ('Checking Arch Floor...\n')
s = Arch.makeStructure(length=2,width=3,height=5)
@@ -500,113 +99,6 @@ class ArchTest(unittest.TestCase):
App.ActiveDocument.recompute()
self.assertTrue(win, "'{}' failed".format(operation))
def testRoof(self):
App.Console.PrintLog ('Checking Arch Roof...\n')
r = Draft.makeRectangle(length=2,height=-1)
r.recompute() # required before calling Arch.makeRoof
ro = Arch.makeRoof(r)
self.assertTrue(ro,"Arch Roof failed")
def testRoof81Permutations(self):
"""Create 81 roofs using a range of arguments.
"""
operation = "Arch Roof testRoof81Permutations"
_msg(" Test '{}'".format(operation))
pts = [App.Vector( 0, 0, 0),
App.Vector(2000, 0, 0),
App.Vector(4000, 0, 0),
App.Vector(4000, 4000, 0),
App.Vector( 0, 4000, 0)]
ptsMod = [App.Vector(2000, 0, 0),
App.Vector(2000, -1000, 0),
App.Vector(2000, 1000, 0)]
angsMod = [[60, 60], [30, 60], [60, 30]]
runsMod = [[500, 500], [400, 500], [500, 400]]
overhangsMod = [[100, 100], [100, 200], [200, 100]]
delta = 6000
pla = App.Placement()
for iY in range(9):
for iX in range(9):
pts[1] = ptsMod[iY % 3] # to get different edge angles
angsLst = angsMod[iY // 3] + [90, 90, 90]
runsLst = runsMod[iX % 3] + [0, 0, 0]
overhangsLst = overhangsMod[iX // 3] + [0, 0, 0]
pla.Base = App.Vector(iX * delta, iY * delta, 0)
wire = Draft.makeWire(pts, closed = True)
wire.MakeFace = False
wire.Placement = pla
wire.recompute() # required before calling Arch.makeRoof
roof = Arch.makeRoof(wire,
angles = angsLst,
run = runsLst,
overhang = overhangsLst)
roof.recompute()
self.assertFalse(roof.Shape.isNull(),
"'{}' failed".format(operation))
self.assertTrue(roof.Shape.isValid(),
"'{}' failed".format(operation))
def testRoofAllAngles90(self):
"""Create a roof with the angles of all segments set at 90 degrees.
This corner case results in a flat roof.
"""
operation = "Arch Roof testRoofAllAngles90"
_msg(" Test '{}'".format(operation))
pts = [App.Vector( 0, 0, 0),
App.Vector(2000, 0, 0),
App.Vector(2000, 2000, 0),
App.Vector( 0, 2000, 0)]
wire = Draft.makeWire(pts, closed = True)
wire.MakeFace = False
wire.recompute() # required before calling Arch.makeRoof
roof = Arch.makeRoof(wire,
angles = [90, 90, 90, 90])
roof.recompute()
self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation))
self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation))
def testRoofApex(self):
"""Create a hipped roof that relies on apex calculation. The roof has
2 triangular segments with a single apex point.
"""
operation = "Arch Roof testRoofApex"
_msg(" Test '{}'".format(operation))
rec = Draft.makeRectangle(length = 4000,
height = 3000,
face = False)
rec.recompute() # required before calling Arch.makeRoof
roof = Arch.makeRoof(rec,
angles = [30, 40, 50, 60],
run = [2000, 0, 2000, 0],
idrel = [-1, 0, -1, 0],
thickness = [50.0],
overhang = [100.0])
roof.recompute()
self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation))
self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation))
def testRoofSingleEavePoint(self):
"""Create a roof with a single triangular segment that has a single
eave point.
"""
operation = "Arch Roof testRoofSingleEavePoint"
_msg(" Test '{}'".format(operation))
pts = [App.Vector( 0, 0, 0),
App.Vector( 2000, 0, 0),
App.Vector( 4000, 2000, 0),
App.Vector(-2000, 2000, 0)]
wire = Draft.makeWire(pts, closed = True)
wire.MakeFace = False
wire.recompute() # required before calling Arch.makeRoof
roof = Arch.makeRoof(wire,
angles = [45, 90, 90, 90],
run = [1000, 0, 0, 0],
overhang = [1000, 0, 0, 0])
roof.recompute()
self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation))
self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation))
def testAxis(self):
App.Console.PrintLog ('Checking Arch Axis...\n')
a = Arch.makeAxis()
@@ -617,27 +109,6 @@ class ArchTest(unittest.TestCase):
s = Arch.makeSectionPlane([])
self.assertTrue(s,"Arch Section failed")
def testSpace(self):
App.Console.PrintLog ('Checking Arch Space...\n')
sb = Part.makeBox(1,1,1)
b = App.ActiveDocument.addObject('Part::Feature','Box')
b.Shape = sb
s = Arch.makeSpace([b])
self.assertTrue(s,"Arch Space failed")
def testSpaceBBox(self):
shape = Part.Shape()
shape.importBrepFromString(brepArchiCAD)
bborig = shape.BoundBox
App.Console.PrintLog ("Original BB: "+str(bborig))
baseobj = App.ActiveDocument.addObject("Part::Feature","brepArchiCAD_body")
baseobj.Shape = shape
space = Arch.makeSpace(baseobj)
space.recompute()
bbnew = space.Shape.BoundBox
App.Console.PrintLog ("New BB: "+str(bbnew))
self.assertTrue(checkBB(bborig,bbnew),"Arch Space has wrong Placement")
def testStairs(self):
App.Console.PrintLog ('Checking Arch Stairs...\n')
s = Arch.makeStairs()
@@ -690,89 +161,6 @@ class ArchTest(unittest.TestCase):
r = (w.Shape.Volume < 0.75)
self.assertTrue(r,"Arch Remove failed")
def testBuildingPart(self):
"""Create a BuildingPart from a wall with a window and check its shape.
"""
# Also regression test for:
# https://github.com/FreeCAD/FreeCAD/issues/6178
operation = "Arch BuildingPart"
_msg(" Test '{}'".format(operation))
# Most of the code below taken from testWindow function.
line = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(3000, 0, 0))
wall = Arch.makeWall(line)
sk = App.ActiveDocument.addObject("Sketcher::SketchObject", "Sketch001")
sk.Placement.Rotation = App.Rotation(App.Vector(1, 0, 0), 90)
sk.addGeometry(Part.LineSegment(App.Vector( 500, 800, 0), App.Vector(1500, 800, 0)))
sk.addGeometry(Part.LineSegment(App.Vector(1500, 800, 0), App.Vector(1500, 2000, 0)))
sk.addGeometry(Part.LineSegment(App.Vector(1500, 2000, 0), App.Vector( 500, 2000, 0)))
sk.addGeometry(Part.LineSegment(App.Vector( 500, 2000, 0), App.Vector( 500, 800, 0)))
sk.addConstraint(Sketcher.Constraint('Coincident', 0, 2, 1, 1))
sk.addConstraint(Sketcher.Constraint('Coincident', 1, 2, 2, 1))
sk.addConstraint(Sketcher.Constraint('Coincident', 2, 2, 3, 1))
sk.addConstraint(Sketcher.Constraint('Coincident', 3, 2, 0, 1))
App.ActiveDocument.recompute()
win = Arch.makeWindow(sk)
Arch.removeComponents(win, host=wall)
App.ActiveDocument.recompute()
bp = Arch.makeBuildingPart()
# Wall visibility works when standalone
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection('ArchTest',wall.Name)
assert wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
App.ActiveDocument.recompute()
assert not wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
assert wall.Visibility
bp.Group = [wall]
App.ActiveDocument.recompute()
# Fails with OCC 7.5
# self.assertTrue(len(bp.Shape.Faces) == 16, "'{}' failed".format(operation))
# Wall visibility works when inside a BuildingPart
FreeCADGui.runCommand('Std_ToggleVisibility',0)
App.ActiveDocument.recompute()
assert not wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
assert wall.Visibility
# Wall visibility works when BuildingPart Toggled
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection('ArchTest',bp.Name)
FreeCADGui.runCommand('Std_ToggleVisibility',0)
assert not wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
assert wall.Visibility
# Wall visibiity works inside group inside BuildingPart Toggled
grp = App.ActiveDocument.addObject("App::DocumentObjectGroup","Group")
grp.Label="Group"
grp.Group = [wall]
bp.Group = [grp]
App.ActiveDocument.recompute()
assert wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
App.ActiveDocument.recompute()
assert not wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
App.ActiveDocument.recompute()
assert wall.Visibility
def testImportSH3D(self):
"""Import a SweetHome 3D file
"""
operation = "importers.importSH3D"
_msg(" Test '{}'".format(operation))
import BIM.importers.importSH3DHelper
importer = BIM.importers.importSH3DHelper.SH3DImporter(None)
importer.import_sh3d_from_string(SH3D_HOME)
assert App.ActiveDocument.Site
assert App.ActiveDocument.BuildingPart.Label == "Building"
assert App.ActiveDocument.BuildingPart001.Label == "Level"
assert App.ActiveDocument.Wall
def testViewGeneration(self):
"""Tests the whole TD view generation workflow"""
@@ -819,173 +207,11 @@ class ArchTest(unittest.TestCase):
App.ActiveDocument.recompute()
assert True
def test_addSpaceBoundaries(self):
"""Test the Arch.addSpaceBoundaries method.
Create a space and a wall that intersects it. Add the wall as a boundary to the space,
and check if the resulting space area is as expected.
"""
operation = " Add a wall face as a boundary to a space"
_msg(" Test '{}'".format(operation))
# Create the space
pl = App.Placement()
pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0)
pl.Base = App.Vector(-2000.0, -2000.0, 0.0)
rectangleBase = Draft.make_rectangle(
length=4000.0, height=4000.0, placement=pl, face=True, support=None)
App.ActiveDocument.recompute()
extr = rectangleBase.Shape.extrude(App.Vector(0,0,2000))
Part.show(extr, 'Extrusion')
space = Arch.makeSpace(App.activeDocument().getObject('Extrusion'))
App.ActiveDocument.recompute() # To calculate area
# Create the wall
trace = Part.LineSegment(App.Vector (3000.0, 1000.0, 0.0),
App.Vector (-3000.0, 1000.0, 0.0))
wp = WorkingPlane.get_working_plane()
base = App.ActiveDocument.addObject("Sketcher::SketchObject","WallTrace")
base.Placement = wp.get_placement()
base.addGeometry(trace)
wall = Arch.makeWall(base,width=200.0,height=3000.0,align="Left")
wall.Normal = wp.axis
# Add the boundary
wallBoundary = [(wall, ["Face1"])]
Arch.addSpaceBoundaries(App.ActiveDocument.Space, wallBoundary)
App.ActiveDocument.recompute() # To recalculate area
# Assert if area is as expected
expectedArea = Units.parseQuantity('12 m^2')
actualArea = Units.parseQuantity(str(space.Area))
self.assertAlmostEqual(
expectedArea.Value,
actualArea.Value,
msg = (f"Invalid area value. " +
f"Expected: {expectedArea.UserString}, actual: {actualArea.UserString}"))
def test_SpaceFromSingleWall(self):
"""Create a space from boundaries of a single wall.
"""
from FreeCAD import Units
operation = "Arch Space from single wall"
_msg(f"\n Test '{operation}'")
# Create a wall
wallInnerLength = 4000.0
wallHeight = 3000.0
wallInnerFaceArea = wallInnerLength * wallHeight
pl = App.Placement()
pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0)
pl.Base = App.Vector(0.0, 0.0, 0.0)
rectangleBase = Draft.make_rectangle(
length=wallInnerLength, height=wallInnerLength, placement=pl, face=True, support=None)
App.ActiveDocument.recompute() # To calculate rectangle area
rectangleArea = rectangleBase.Area
App.ActiveDocument.getObject(rectangleBase.Name).MakeFace = False
wall = Arch.makeWall(baseobj=rectangleBase, height=wallHeight, align="Left")
App.ActiveDocument.recompute() # To calculate face areas
# Create a space from the wall's inner faces
boundaries = [f"Face{ind+1}" for ind, face in enumerate(wall.Shape.Faces)
if round(face.Area) == round(wallInnerFaceArea)]
if App.GuiUp:
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(wall, boundaries)
space = Arch.makeSpace(FreeCADGui.Selection.getSelectionEx())
# Alternative, but test takes longer to run (~10x)
# FreeCADGui.activateWorkbench("BIMWorkbench")
# FreeCADGui.runCommand('Arch_Space', 0)
# space = App.ActiveDocument.Space
else:
# Also tests the alternative way of specifying the boundaries
# [ (<Part::PartFeature>, ["Face1", ...]), ... ]
space = Arch.makeSpace([(wall, boundaries)])
App.ActiveDocument.recompute() # To calculate space area
# Assert if area is as expected
expectedArea = Units.parseQuantity(str(rectangleArea))
actualArea = Units.parseQuantity(str(space.Area))
self.assertAlmostEqual(
expectedArea.Value,
actualArea.Value,
msg = f"Invalid area value. Expected: {expectedArea.UserString}, actual: {actualArea.UserString}")
def tearDown(self):
App.closeDocument("ArchTest")
pass
SH3D_HOME = """<?xml version='1.0'?>
<home version='7200' name='0-JoinWall.sh3d' camera='topCamera' wallHeight='250.0'>
<property name='com.eteks.sweethome3d.SweetHome3D.CatalogPaneDividerLocation' value='327'/>
<property name='com.eteks.sweethome3d.SweetHome3D.ColumnWidths' value='100,84,82,85,84'/>
<property name='com.eteks.sweethome3d.SweetHome3D.FrameHeight' value='576'/>
<property name='com.eteks.sweethome3d.SweetHome3D.FrameMaximized' value='true'/>
<property name='com.eteks.sweethome3d.SweetHome3D.FrameWidth' value='1092'/>
<property name='com.eteks.sweethome3d.SweetHome3D.FrameX' value='50'/>
<property name='com.eteks.sweethome3d.SweetHome3D.FrameY' value='87'/>
<property name='com.eteks.sweethome3d.SweetHome3D.MainPaneDividerLocation' value='441'/>
<property name='com.eteks.sweethome3d.SweetHome3D.PlanPaneDividerLocation' value='263'/>
<property name='com.eteks.sweethome3d.SweetHome3D.PlanScale' value='0.21343713'/>
<property name='com.eteks.sweethome3d.SweetHome3D.PlanViewportX' value='0'/>
<property name='com.eteks.sweethome3d.SweetHome3D.PlanViewportY' value='0'/>
<property name='com.eteks.sweethome3d.SweetHome3D.ScreenHeight' value='720'/>
<property name='com.eteks.sweethome3d.SweetHome3D.ScreenWidth' value='1366'/>
<furnitureVisibleProperty name='NAME'/>
<furnitureVisibleProperty name='WIDTH'/>
<furnitureVisibleProperty name='DEPTH'/>
<furnitureVisibleProperty name='HEIGHT'/>
<furnitureVisibleProperty name='VISIBLE'/>
<environment groundColor='FFB78744' skyColor='00CCE4FC' lightColor='00D0D0D0' ceillingLightColor='00D0D0D0' photoWidth='400' photoHeight='300' photoAspectRatio='VIEW_3D_RATIO' photoQuality='0' videoWidth='320' videoAspectRatio='RATIO_4_3' videoQuality='0' videoFrameRate='25'>
<texture attribute='skyTexture' name='Cloudy' creator='eTeks' catalogId='eTeks#cloudy' width='100.0' height='27.6' image='0'/>
</environment>
<compass x='-100.0' y='50.0' diameter='100.0' northDirection='0.0' longitude='-0.06428629' latitude='0.70511305' timeZone='Europe/Madrid'/>
<observerCamera attribute='observerCamera' lens='PINHOLE' x='50.0' y='50.0' z='170.0' yaw='5.4977875' pitch='0.19634955' fieldOfView='1.0995575' time='1729080000000'/>
<camera attribute='topCamera' lens='PINHOLE' x='1304.082' y='1936.5889' z='1526.6199' yaw='8.98363' pitch='0.7049999' fieldOfView='1.0995575' time='1729080000000'/>
<wall id='wall0' wallAtEnd='wall1' xStart='0.0' yStart='0.0' xEnd='100.0' yEnd='0.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall1' wallAtStart='wall0' wallAtEnd='wall2' xStart='100.0' yStart='0.0' xEnd='200.0' yEnd='0.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall2' wallAtStart='wall1' xStart='200.0' yStart='0.0' xEnd='300.0' yEnd='0.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall3' wallAtEnd='wall4' xStart='0.0' yStart='50.0' xEnd='100.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall4' wallAtStart='wall3' wallAtEnd='wall5' xStart='100.0' yStart='100.0' xEnd='200.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall5' wallAtStart='wall4' xStart='200.0' yStart='100.0' xEnd='300.0' yEnd='50.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall6' wallAtEnd='wall7' xStart='0.0' yStart='200.0' xEnd='100.0' yEnd='300.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall7' wallAtStart='wall6' wallAtEnd='wall8' xStart='100.0' yStart='300.0' xEnd='200.0' yEnd='300.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall8' wallAtStart='wall7' xStart='200.0' yStart='300.0' xEnd='300.0' yEnd='200.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall9' wallAtEnd='wall10' xStart='100.0' yStart='400.0' xEnd='100.0' yEnd='500.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall10' wallAtStart='wall9' wallAtEnd='wall11' xStart='100.0' yStart='500.0' xEnd='200.0' yEnd='500.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall11' wallAtStart='wall10' xStart='200.0' yStart='500.0' xEnd='200.0' yEnd='400.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall12' wallAtStart='wall14' wallAtEnd='wall13' xStart='150.0' yStart='600.0' xEnd='100.0' yEnd='700.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall13' wallAtStart='wall12' wallAtEnd='wall14' xStart='100.0' yStart='700.0' xEnd='200.0' yEnd='700.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall14' wallAtStart='wall13' wallAtEnd='wall12' xStart='200.0' yStart='700.0' xEnd='150.0' yEnd='600.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall15' wallAtEnd='wall16' xStart='400.0' yStart='150.0' xEnd='500.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall16' wallAtStart='wall15' wallAtEnd='wall17' xStart='500.0' yStart='100.0' xEnd='600.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall17' wallAtStart='wall16' xStart='600.0' yStart='100.0' xEnd='700.0' yEnd='50.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall18' wallAtEnd='wall19' xStart='400.0' yStart='400.0' xEnd='500.0' yEnd='300.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall19' wallAtStart='wall18' wallAtEnd='wall20' xStart='500.0' yStart='300.0' xEnd='600.0' yEnd='300.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall20' wallAtStart='wall19' xStart='600.0' yStart='300.0' xEnd='700.0' yEnd='200.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall21' wallAtEnd='wall22' xStart='400.0' yStart='600.0' xEnd='400.0' yEnd='500.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall22' wallAtStart='wall21' wallAtEnd='wall23' xStart='400.0' yStart='500.0' xEnd='600.0' yEnd='500.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall23' wallAtStart='wall22' xStart='600.0' yStart='500.0' xEnd='600.0' yEnd='400.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall24' wallAtEnd='wall25' xStart='600.0' yStart='800.0' xEnd='500.0' yEnd='700.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall25' wallAtStart='wall24' wallAtEnd='wall26' xStart='500.0' yStart='700.0' xEnd='600.0' yEnd='700.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall26' wallAtStart='wall25' xStart='600.0' yStart='700.0' xEnd='500.0' yEnd='600.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall27' wallAtStart='wall30' wallAtEnd='wall28' xStart='800.0' yStart='0.0' xEnd='1000.0' yEnd='0.0' height='250.0' thickness='10.0' arcExtent='1.0471976' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall28' wallAtStart='wall27' wallAtEnd='wall29' xStart='1000.0' yStart='0.0' xEnd='1000.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall29' wallAtStart='wall28' wallAtEnd='wall30' xStart='1000.0' yStart='100.0' xEnd='800.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall30' wallAtStart='wall29' wallAtEnd='wall27' xStart='800.0' yStart='100.0' xEnd='800.0' yEnd='0.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall31' wallAtEnd='wall32' xStart='800.0' yStart='400.0' xEnd='1000.0' yEnd='200.0' height='250.0' thickness='10.0' arcExtent='1.5707964' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall32' wallAtStart='wall31' wallAtEnd='wall33' xStart='1000.0' yStart='200.0' xEnd='1200.0' yEnd='400.0' height='250.0' thickness='10.0' arcExtent='1.5707964' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall33' wallAtStart='wall32' wallAtEnd='wall34' xStart='1200.0' yStart='400.0' xEnd='1000.0' yEnd='600.0' height='250.0' thickness='10.0' arcExtent='1.5707964' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall34' wallAtStart='wall33' xStart='1000.0' yStart='600.0' xEnd='800.0' yEnd='400.0' height='250.0' thickness='10.0' arcExtent='1.5707964' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall35' wallAtEnd='wall36' xStart='800.0' yStart='800.0' xEnd='900.0' yEnd='900.0' height='250.0' thickness='10.0' arcExtent='-3.1415927' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall36' wallAtStart='wall35' wallAtEnd='wall37' xStart='900.0' yStart='900.0' xEnd='1000.0' yEnd='800.0' height='250.0' thickness='10.0' arcExtent='-3.1415927' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall37' wallAtStart='wall36' wallAtEnd='wall38' xStart='1000.0' yStart='800.0' xEnd='900.0' yEnd='700.0' height='250.0' thickness='10.0' arcExtent='-3.1415927' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall38' wallAtStart='wall37' xStart='900.0' yStart='700.0' xEnd='800.0' yEnd='800.0' height='250.0' thickness='10.0' arcExtent='-3.1415927' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
</home>
"""
# Use the modules so that code checkers don't complain (flake8)
True if TestArchSpace else False
True if TestArchRoof else False
True if TestArchWall else False

226
src/Mod/BIM/TestArchGui.py Normal file
View File

@@ -0,0 +1,226 @@
#***************************************************************************
#* Copyright (c) 2013 Yorik van Havre <yorik@uncreated.net> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* FreeCAD is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with FreeCAD; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************/
# Unit test for the Arch module
import os
import unittest
import FreeCAD as App
from FreeCAD import Units
import Arch
import Draft
import Part
import Sketcher
import TechDraw
import WorkingPlane
from draftutils.messages import _msg
if App.GuiUp:
import FreeCADGui
class ArchTest(unittest.TestCase):
def setUp(self):
# setting a new document to hold the tests
if App.ActiveDocument:
if App.ActiveDocument.Name != "ArchTest":
App.newDocument("ArchTest")
else:
App.newDocument("ArchTest")
App.setActiveDocument("ArchTest")
def testRebar(self):
App.Console.PrintLog ('Checking Arch Rebar...\n')
s = Arch.makeStructure(length=2,width=3,height=5)
sk = App.ActiveDocument.addObject('Sketcher::SketchObject','Sketch')
sk.AttachmentSupport = (s,["Face6"])
sk.addGeometry(Part.LineSegment(App.Vector(-0.85,1.25,0),App.Vector(0.75,1.25,0)))
sk.addGeometry(Part.LineSegment(App.Vector(0.75,1.25,0),App.Vector(0.75,-1.20,0)))
sk.addGeometry(Part.LineSegment(App.Vector(0.75,-1.20,0),App.Vector(-0.85,-1.20,0)))
sk.addGeometry(Part.LineSegment(App.Vector(-0.85,-1.20,0),App.Vector(-0.85,1.25,0)))
sk.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1))
sk.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1))
sk.addConstraint(Sketcher.Constraint('Coincident',2,2,3,1))
sk.addConstraint(Sketcher.Constraint('Coincident',3,2,0,1))
r = Arch.makeRebar(s,sk,diameter=.1,amount=2)
self.assertTrue(r,"Arch Rebar failed")
def testBuildingPart(self):
"""Create a BuildingPart from a wall with a window and check its shape.
"""
# Also regression test for:
# https://github.com/FreeCAD/FreeCAD/issues/6178
operation = "Arch BuildingPart"
_msg(" Test '{}'".format(operation))
# Most of the code below taken from testWindow function.
line = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(3000, 0, 0))
wall = Arch.makeWall(line)
sk = App.ActiveDocument.addObject("Sketcher::SketchObject", "Sketch001")
sk.Placement.Rotation = App.Rotation(App.Vector(1, 0, 0), 90)
sk.addGeometry(Part.LineSegment(App.Vector( 500, 800, 0), App.Vector(1500, 800, 0)))
sk.addGeometry(Part.LineSegment(App.Vector(1500, 800, 0), App.Vector(1500, 2000, 0)))
sk.addGeometry(Part.LineSegment(App.Vector(1500, 2000, 0), App.Vector( 500, 2000, 0)))
sk.addGeometry(Part.LineSegment(App.Vector( 500, 2000, 0), App.Vector( 500, 800, 0)))
sk.addConstraint(Sketcher.Constraint('Coincident', 0, 2, 1, 1))
sk.addConstraint(Sketcher.Constraint('Coincident', 1, 2, 2, 1))
sk.addConstraint(Sketcher.Constraint('Coincident', 2, 2, 3, 1))
sk.addConstraint(Sketcher.Constraint('Coincident', 3, 2, 0, 1))
App.ActiveDocument.recompute()
win = Arch.makeWindow(sk)
Arch.removeComponents(win, host=wall)
App.ActiveDocument.recompute()
bp = Arch.makeBuildingPart()
# Wall visibility works when standalone
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection('ArchTest',wall.Name)
assert wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
App.ActiveDocument.recompute()
assert not wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
assert wall.Visibility
bp.Group = [wall]
App.ActiveDocument.recompute()
# Fails with OCC 7.5
# self.assertTrue(len(bp.Shape.Faces) == 16, "'{}' failed".format(operation))
# Wall visibility works when inside a BuildingPart
FreeCADGui.runCommand('Std_ToggleVisibility',0)
App.ActiveDocument.recompute()
assert not wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
assert wall.Visibility
# Wall visibility works when BuildingPart Toggled
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection('ArchTest',bp.Name)
FreeCADGui.runCommand('Std_ToggleVisibility',0)
assert not wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
assert wall.Visibility
# Wall visibiity works inside group inside BuildingPart Toggled
grp = App.ActiveDocument.addObject("App::DocumentObjectGroup","Group")
grp.Label="Group"
grp.Group = [wall]
bp.Group = [grp]
App.ActiveDocument.recompute()
assert wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
App.ActiveDocument.recompute()
assert not wall.Visibility
FreeCADGui.runCommand('Std_ToggleVisibility',0)
App.ActiveDocument.recompute()
assert wall.Visibility
def testImportSH3D(self):
"""Import a SweetHome 3D file
"""
operation = "importers.importSH3D"
_msg(" Test '{}'".format(operation))
import BIM.importers.importSH3DHelper
importer = BIM.importers.importSH3DHelper.SH3DImporter(None)
importer.import_sh3d_from_string(SH3D_HOME)
assert App.ActiveDocument.Site
assert App.ActiveDocument.BuildingPart.Label == "Building"
assert App.ActiveDocument.BuildingPart001.Label == "Level"
assert App.ActiveDocument.Wall
def tearDown(self):
App.closeDocument("ArchTest")
pass
SH3D_HOME = """<?xml version='1.0'?>
<home version='7200' name='0-JoinWall.sh3d' camera='topCamera' wallHeight='250.0'>
<property name='com.eteks.sweethome3d.SweetHome3D.CatalogPaneDividerLocation' value='327'/>
<property name='com.eteks.sweethome3d.SweetHome3D.ColumnWidths' value='100,84,82,85,84'/>
<property name='com.eteks.sweethome3d.SweetHome3D.FrameHeight' value='576'/>
<property name='com.eteks.sweethome3d.SweetHome3D.FrameMaximized' value='true'/>
<property name='com.eteks.sweethome3d.SweetHome3D.FrameWidth' value='1092'/>
<property name='com.eteks.sweethome3d.SweetHome3D.FrameX' value='50'/>
<property name='com.eteks.sweethome3d.SweetHome3D.FrameY' value='87'/>
<property name='com.eteks.sweethome3d.SweetHome3D.MainPaneDividerLocation' value='441'/>
<property name='com.eteks.sweethome3d.SweetHome3D.PlanPaneDividerLocation' value='263'/>
<property name='com.eteks.sweethome3d.SweetHome3D.PlanScale' value='0.21343713'/>
<property name='com.eteks.sweethome3d.SweetHome3D.PlanViewportX' value='0'/>
<property name='com.eteks.sweethome3d.SweetHome3D.PlanViewportY' value='0'/>
<property name='com.eteks.sweethome3d.SweetHome3D.ScreenHeight' value='720'/>
<property name='com.eteks.sweethome3d.SweetHome3D.ScreenWidth' value='1366'/>
<furnitureVisibleProperty name='NAME'/>
<furnitureVisibleProperty name='WIDTH'/>
<furnitureVisibleProperty name='DEPTH'/>
<furnitureVisibleProperty name='HEIGHT'/>
<furnitureVisibleProperty name='VISIBLE'/>
<environment groundColor='FFB78744' skyColor='00CCE4FC' lightColor='00D0D0D0' ceillingLightColor='00D0D0D0' photoWidth='400' photoHeight='300' photoAspectRatio='VIEW_3D_RATIO' photoQuality='0' videoWidth='320' videoAspectRatio='RATIO_4_3' videoQuality='0' videoFrameRate='25'>
<texture attribute='skyTexture' name='Cloudy' creator='eTeks' catalogId='eTeks#cloudy' width='100.0' height='27.6' image='0'/>
</environment>
<compass x='-100.0' y='50.0' diameter='100.0' northDirection='0.0' longitude='-0.06428629' latitude='0.70511305' timeZone='Europe/Madrid'/>
<observerCamera attribute='observerCamera' lens='PINHOLE' x='50.0' y='50.0' z='170.0' yaw='5.4977875' pitch='0.19634955' fieldOfView='1.0995575' time='1729080000000'/>
<camera attribute='topCamera' lens='PINHOLE' x='1304.082' y='1936.5889' z='1526.6199' yaw='8.98363' pitch='0.7049999' fieldOfView='1.0995575' time='1729080000000'/>
<wall id='wall0' wallAtEnd='wall1' xStart='0.0' yStart='0.0' xEnd='100.0' yEnd='0.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall1' wallAtStart='wall0' wallAtEnd='wall2' xStart='100.0' yStart='0.0' xEnd='200.0' yEnd='0.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall2' wallAtStart='wall1' xStart='200.0' yStart='0.0' xEnd='300.0' yEnd='0.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall3' wallAtEnd='wall4' xStart='0.0' yStart='50.0' xEnd='100.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall4' wallAtStart='wall3' wallAtEnd='wall5' xStart='100.0' yStart='100.0' xEnd='200.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall5' wallAtStart='wall4' xStart='200.0' yStart='100.0' xEnd='300.0' yEnd='50.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall6' wallAtEnd='wall7' xStart='0.0' yStart='200.0' xEnd='100.0' yEnd='300.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall7' wallAtStart='wall6' wallAtEnd='wall8' xStart='100.0' yStart='300.0' xEnd='200.0' yEnd='300.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall8' wallAtStart='wall7' xStart='200.0' yStart='300.0' xEnd='300.0' yEnd='200.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall9' wallAtEnd='wall10' xStart='100.0' yStart='400.0' xEnd='100.0' yEnd='500.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall10' wallAtStart='wall9' wallAtEnd='wall11' xStart='100.0' yStart='500.0' xEnd='200.0' yEnd='500.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall11' wallAtStart='wall10' xStart='200.0' yStart='500.0' xEnd='200.0' yEnd='400.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall12' wallAtStart='wall14' wallAtEnd='wall13' xStart='150.0' yStart='600.0' xEnd='100.0' yEnd='700.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall13' wallAtStart='wall12' wallAtEnd='wall14' xStart='100.0' yStart='700.0' xEnd='200.0' yEnd='700.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall14' wallAtStart='wall13' wallAtEnd='wall12' xStart='200.0' yStart='700.0' xEnd='150.0' yEnd='600.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall15' wallAtEnd='wall16' xStart='400.0' yStart='150.0' xEnd='500.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall16' wallAtStart='wall15' wallAtEnd='wall17' xStart='500.0' yStart='100.0' xEnd='600.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall17' wallAtStart='wall16' xStart='600.0' yStart='100.0' xEnd='700.0' yEnd='50.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall18' wallAtEnd='wall19' xStart='400.0' yStart='400.0' xEnd='500.0' yEnd='300.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall19' wallAtStart='wall18' wallAtEnd='wall20' xStart='500.0' yStart='300.0' xEnd='600.0' yEnd='300.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall20' wallAtStart='wall19' xStart='600.0' yStart='300.0' xEnd='700.0' yEnd='200.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall21' wallAtEnd='wall22' xStart='400.0' yStart='600.0' xEnd='400.0' yEnd='500.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall22' wallAtStart='wall21' wallAtEnd='wall23' xStart='400.0' yStart='500.0' xEnd='600.0' yEnd='500.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall23' wallAtStart='wall22' xStart='600.0' yStart='500.0' xEnd='600.0' yEnd='400.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall24' wallAtEnd='wall25' xStart='600.0' yStart='800.0' xEnd='500.0' yEnd='700.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall25' wallAtStart='wall24' wallAtEnd='wall26' xStart='500.0' yStart='700.0' xEnd='600.0' yEnd='700.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall26' wallAtStart='wall25' xStart='600.0' yStart='700.0' xEnd='500.0' yEnd='600.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall27' wallAtStart='wall30' wallAtEnd='wall28' xStart='800.0' yStart='0.0' xEnd='1000.0' yEnd='0.0' height='250.0' thickness='10.0' arcExtent='1.0471976' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall28' wallAtStart='wall27' wallAtEnd='wall29' xStart='1000.0' yStart='0.0' xEnd='1000.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall29' wallAtStart='wall28' wallAtEnd='wall30' xStart='1000.0' yStart='100.0' xEnd='800.0' yEnd='100.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall30' wallAtStart='wall29' wallAtEnd='wall27' xStart='800.0' yStart='100.0' xEnd='800.0' yEnd='0.0' height='250.0' thickness='10.0' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall31' wallAtEnd='wall32' xStart='800.0' yStart='400.0' xEnd='1000.0' yEnd='200.0' height='250.0' thickness='10.0' arcExtent='1.5707964' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall32' wallAtStart='wall31' wallAtEnd='wall33' xStart='1000.0' yStart='200.0' xEnd='1200.0' yEnd='400.0' height='250.0' thickness='10.0' arcExtent='1.5707964' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall33' wallAtStart='wall32' wallAtEnd='wall34' xStart='1200.0' yStart='400.0' xEnd='1000.0' yEnd='600.0' height='250.0' thickness='10.0' arcExtent='1.5707964' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall34' wallAtStart='wall33' xStart='1000.0' yStart='600.0' xEnd='800.0' yEnd='400.0' height='250.0' thickness='10.0' arcExtent='1.5707964' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall35' wallAtEnd='wall36' xStart='800.0' yStart='800.0' xEnd='900.0' yEnd='900.0' height='250.0' thickness='10.0' arcExtent='-3.1415927' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall36' wallAtStart='wall35' wallAtEnd='wall37' xStart='900.0' yStart='900.0' xEnd='1000.0' yEnd='800.0' height='250.0' thickness='10.0' arcExtent='-3.1415927' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall37' wallAtStart='wall36' wallAtEnd='wall38' xStart='1000.0' yStart='800.0' xEnd='900.0' yEnd='700.0' height='250.0' thickness='10.0' arcExtent='-3.1415927' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
<wall id='wall38' wallAtStart='wall37' xStart='900.0' yStart='700.0' xEnd='800.0' yEnd='800.0' height='250.0' thickness='10.0' arcExtent='-3.1415927' pattern='hatchUp' topColor='FF0000FF' leftSideColor='FF00FF00' rightSideColor='FFFF0000'/>
</home>
"""

View File

@@ -150,7 +150,10 @@ class BIM_Material:
)
buttonMergeDupes.setIcon(QtGui.QIcon(":/icons/view-refresh.svg"))
createButtonsLayout.addWidget(buttonMergeDupes, 1, 0)
self.dlg.buttonMergeDupes = buttonMergeDupes
buttonMergeDupes.clicked.connect(self.onMergeDupes)
if len(self.dlg.materials) < 2:
buttonMergeDupes.setEnabled(False)
# delete unused
buttonDeleteUnused = QtGui.QPushButton(
@@ -208,20 +211,15 @@ class BIM_Material:
first = True
for mat in self.dlg.materials:
orig = None
for om in mats:
if om.Label == mat.Label:
orig = om
break
else:
if (
mat.Label[-1].isdigit()
and mat.Label[-2].isdigit()
and mat.Label[-3].isdigit()
):
for om in self.dlg.materials:
if om.Label == mat.Label[:-3].strip():
orig = om
break
if (
mat.Label[-1].isdigit()
and mat.Label[-2].isdigit()
and mat.Label[-3].isdigit()
):
for om in self.dlg.materials:
if om.Label == mat.Label[:-3].strip():
orig = om
break
if orig:
for par in mat.InList:
for prop in par.PropertiesList:
@@ -522,6 +520,9 @@ class BIM_Material:
i.setFlags(i.flags() | QtCore.Qt.ItemIsEditable)
if o.Name == name:
self.dlg.matList.setCurrentItem(i)
if hasattr(self.dlg, "buttonMergeDupes"):
hasMultipleMaterials = len(self.dlg.materials) > 1
self.dlg.buttonMergeDupes.setEnabled(hasMultipleMaterials)
def createIcon(self, obj):
from PySide import QtCore, QtGui

View File

@@ -144,6 +144,7 @@ class BIM_ProjectManager:
import Draft
import FreeCADGui
import Part
from draftutils import params
vaxes = []
haxes = []
@@ -306,8 +307,8 @@ class BIM_ProjectManager:
outtext = Draft.make_text(
[buildingname],
FreeCAD.Vector(
Draft.getParam("textheight", 0.20) * 0.3,
-Draft.getParam("textheight", 0.20) * 1.43,
params.get_param("textheight") * 0.3,
-params.get_param("textheight") * 1.43,
0,
),
)
@@ -324,8 +325,8 @@ class BIM_ProjectManager:
axisV.Label = translate("BIM", "Vertical Axes")
axisV.ViewObject.BubblePosition = "Both"
axisV.ViewObject.LineWidth = self.form.lineWidth.value()
axisV.ViewObject.FontSize = Draft.getParam("textheight", 0.20)
axisV.ViewObject.BubbleSize = Draft.getParam("textheight", 0.20) * 1.43
axisV.ViewObject.FontSize = params.get_param("textheight")
axisV.ViewObject.BubbleSize = params.get_param("textheight") * 1.43
axisV.ViewObject.LineColor = color
axisH = None
if self.form.countHAxes.value() and distHAxes:
@@ -336,8 +337,8 @@ class BIM_ProjectManager:
axisH.ViewObject.BubblePosition = "Both"
axisH.ViewObject.NumberingStyle = "A,B,C"
axisH.ViewObject.LineWidth = self.form.lineWidth.value()
axisH.ViewObject.FontSize = Draft.getParam("textheight", 0.20)
axisH.ViewObject.BubbleSize = Draft.getParam("textheight", 0.20) * 1.43
axisH.ViewObject.FontSize = params.get_param("textheight")
axisH.ViewObject.BubbleSize = params.get_param("textheight") * 1.43
axisH.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 90)
axisH.ViewObject.LineColor = color
if axisV and axisH:

View File

@@ -0,0 +1,43 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2025 Furgo *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
"""Defines the base class for Arch module unit tests."""
import unittest
import FreeCAD
class TestArchBase(unittest.TestCase):
def setUp(self):
self.document = FreeCAD.newDocument(self.__class__.__name__)
def tearDown(self):
FreeCAD.closeDocument(self.document.Name)
def printTestMessage(self, text, prepend_text="Test ", end="\n"):
"""Write messages to the console including the line ending.
Messages will be prepended with "Test ", unless an empty string is
passed as the prepend_text argument
"""
FreeCAD.Console.PrintMessage(prepend_text + text + end)

View File

@@ -0,0 +1,150 @@
#***************************************************************************
#* Copyright (c) 2013 Yorik van Havre <yorik@uncreated.net> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* FreeCAD is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with FreeCAD; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************/
# Unit tests for the Arch wall module
import os
import unittest
import Arch
import Draft
import Part
import FreeCAD as App
from bimtests import TestArchBase
from draftutils.messages import _msg
class TestArchRoof(TestArchBase.TestArchBase):
def testRoof(self):
operation = "Checking Arch Roof..."
self.printTestMessage(operation)
r = Draft.makeRectangle(length=2,height=-1)
r.recompute() # required before calling Arch.makeRoof
ro = Arch.makeRoof(r)
self.assertTrue(ro,"Arch Roof failed")
def testRoof81Permutations(self):
"""Create 81 roofs using a range of arguments.
"""
operation = "Arch Roof testRoof81Permutations"
self.printTestMessage(operation)
pts = [App.Vector( 0, 0, 0),
App.Vector(2000, 0, 0),
App.Vector(4000, 0, 0),
App.Vector(4000, 4000, 0),
App.Vector( 0, 4000, 0)]
ptsMod = [App.Vector(2000, 0, 0),
App.Vector(2000, -1000, 0),
App.Vector(2000, 1000, 0)]
angsMod = [[60, 60], [30, 60], [60, 30]]
runsMod = [[500, 500], [400, 500], [500, 400]]
overhangsMod = [[100, 100], [100, 200], [200, 100]]
delta = 6000
pla = App.Placement()
for iY in range(9):
for iX in range(9):
pts[1] = ptsMod[iY % 3] # to get different edge angles
angsLst = angsMod[iY // 3] + [90, 90, 90]
runsLst = runsMod[iX % 3] + [0, 0, 0]
overhangsLst = overhangsMod[iX // 3] + [0, 0, 0]
pla.Base = App.Vector(iX * delta, iY * delta, 0)
wire = Draft.makeWire(pts, closed = True)
wire.MakeFace = False
wire.Placement = pla
wire.recompute() # required before calling Arch.makeRoof
roof = Arch.makeRoof(wire,
angles = angsLst,
run = runsLst,
overhang = overhangsLst)
roof.recompute()
self.assertFalse(roof.Shape.isNull(),
"'{}' failed".format(operation))
self.assertTrue(roof.Shape.isValid(),
"'{}' failed".format(operation))
def testRoofAllAngles90(self):
"""Create a roof with the angles of all segments set at 90 degrees.
This corner case results in a flat roof.
"""
operation = "Arch Roof testRoofAllAngles90"
self.printTestMessage(operation)
pts = [App.Vector( 0, 0, 0),
App.Vector(2000, 0, 0),
App.Vector(2000, 2000, 0),
App.Vector( 0, 2000, 0)]
wire = Draft.makeWire(pts, closed = True)
wire.MakeFace = False
wire.recompute() # required before calling Arch.makeRoof
roof = Arch.makeRoof(wire,
angles = [90, 90, 90, 90])
roof.recompute()
self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation))
self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation))
def testRoofApex(self):
"""Create a hipped roof that relies on apex calculation. The roof has
2 triangular segments with a single apex point.
"""
operation = "Arch Roof testRoofApex"
self.printTestMessage(operation)
rec = Draft.makeRectangle(length = 4000,
height = 3000,
face = False)
rec.recompute() # required before calling Arch.makeRoof
roof = Arch.makeRoof(rec,
angles = [30, 40, 50, 60],
run = [2000, 0, 2000, 0],
idrel = [-1, 0, -1, 0],
thickness = [50.0],
overhang = [100.0])
roof.recompute()
self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation))
self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation))
def testRoofSingleEavePoint(self):
"""Create a roof with a single triangular segment that has a single
eave point.
"""
operation = "Arch Roof testRoofSingleEavePoint"
self.printTestMessage(operation)
pts = [App.Vector( 0, 0, 0),
App.Vector( 2000, 0, 0),
App.Vector( 4000, 2000, 0),
App.Vector(-2000, 2000, 0)]
wire = Draft.makeWire(pts, closed = True)
wire.MakeFace = False
wire.recompute() # required before calling Arch.makeRoof
roof = Arch.makeRoof(wire,
angles = [45, 90, 90, 90],
run = [1000, 0, 0, 0],
overhang = [1000, 0, 0, 0])
roof.recompute()
self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation))
self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation))

View File

@@ -0,0 +1,500 @@
#***************************************************************************
#* Copyright (c) 2013 Yorik van Havre <yorik@uncreated.net> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* FreeCAD is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with FreeCAD; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************/
# Unit tests for the Arch space module
import os
import unittest
import Arch
import Draft
import Part
import FreeCAD as App
from FreeCAD import Units
from bimtests import TestArchBase
import WorkingPlane
def like(a, b):
return abs(a-b) < 0.001
def checkBB(a, b):
return like(a.XMin, b.XMin) and like(a.YMin, b.YMin) and like(a.ZMin, b.ZMin) and like(a.XMax, b.XMax) and like(a.YMax, b.YMax) and like(a.ZMax, b.ZMax)
class TestArchSpace(TestArchBase.TestArchBase):
def testSpace(self):
operation = "Checking Arch Space..."
self.printTestMessage(operation)
sb = Part.makeBox(1,1,1)
b = App.ActiveDocument.addObject('Part::Feature','Box')
b.Shape = sb
s = Arch.makeSpace([b])
self.assertTrue(s,"Arch Space failed")
def testSpaceBBox(self):
operation = "Checking Arch Space bound box..."
self.printTestMessage(operation)
shape = Part.Shape()
shape.importBrepFromString(brepArchiCAD)
bborig = shape.BoundBox
App.Console.PrintLog ("Original BB: "+str(bborig))
baseobj = App.ActiveDocument.addObject("Part::Feature","brepArchiCAD_body")
baseobj.Shape = shape
space = Arch.makeSpace(baseobj)
space.recompute()
bbnew = space.Shape.BoundBox
App.Console.PrintLog ("New BB: "+str(bbnew))
self.assertTrue(checkBB(bborig,bbnew),"Arch Space has wrong Placement")
def test_addSpaceBoundaries(self):
"""Test the Arch.addSpaceBoundaries method.
Create a space and a wall that intersects it. Add the wall as a boundary to the space,
and check if the resulting space area is as expected.
"""
operation = "Add a wall face as a boundary to a space"
self.printTestMessage(operation)
# Create the space
pl = App.Placement()
pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0)
pl.Base = App.Vector(-2000.0, -2000.0, 0.0)
rectangleBase = Draft.make_rectangle(
length=4000.0, height=4000.0, placement=pl, face=True, support=None)
App.ActiveDocument.recompute()
extr = rectangleBase.Shape.extrude(App.Vector(0,0,2000))
Part.show(extr, 'Extrusion')
space = Arch.makeSpace(App.activeDocument().getObject('Extrusion'))
App.ActiveDocument.recompute() # To calculate area
# Create the wall
trace = Part.LineSegment(App.Vector (3000.0, 1000.0, 0.0),
App.Vector (-3000.0, 1000.0, 0.0))
wp = WorkingPlane.get_working_plane()
base = App.ActiveDocument.addObject("Sketcher::SketchObject","WallTrace")
base.Placement = wp.get_placement()
base.addGeometry(trace)
wall = Arch.makeWall(base,width=200.0,height=3000.0,align="Left")
wall.Normal = wp.axis
# Add the boundary
wallBoundary = [(wall, ["Face1"])]
Arch.addSpaceBoundaries(App.ActiveDocument.Space, wallBoundary)
App.ActiveDocument.recompute() # To recalculate area
# Assert if area is as expected
expectedArea = Units.parseQuantity('12 m^2')
actualArea = Units.parseQuantity(str(space.Area))
self.assertAlmostEqual(
expectedArea.Value,
actualArea.Value,
msg = (f"Invalid area value. " +
f"Expected: {expectedArea.UserString}, actual: {actualArea.UserString}"))
def test_SpaceFromSingleWall(self):
"""Create a space from boundaries of a single wall.
"""
operation = "Arch Space from single wall"
self.printTestMessage(operation)
# Create a wall
wallInnerLength = 4000.0
wallHeight = 3000.0
wallInnerFaceArea = wallInnerLength * wallHeight
pl = App.Placement()
pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0)
pl.Base = App.Vector(0.0, 0.0, 0.0)
rectangleBase = Draft.make_rectangle(
length=wallInnerLength, height=wallInnerLength, placement=pl, face=True, support=None)
App.ActiveDocument.recompute() # To calculate rectangle area
rectangleArea = rectangleBase.Area
App.ActiveDocument.getObject(rectangleBase.Name).MakeFace = False
wall = Arch.makeWall(baseobj=rectangleBase, height=wallHeight, align="Left")
App.ActiveDocument.recompute() # To calculate face areas
# Create a space from the wall's inner faces
boundaries = [f"Face{ind+1}" for ind, face in enumerate(wall.Shape.Faces)
if round(face.Area) == round(wallInnerFaceArea)]
if App.GuiUp:
import FreeCADGui
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(wall, boundaries)
space = Arch.makeSpace(FreeCADGui.Selection.getSelectionEx())
# Alternative, but test takes longer to run (~10x)
# FreeCADGui.activateWorkbench("BIMWorkbench")
# FreeCADGui.runCommand('Arch_Space', 0)
# space = App.ActiveDocument.Space
else:
# Also tests the alternative way of specifying the boundaries
# [ (<Part::PartFeature>, ["Face1", ...]), ... ]
space = Arch.makeSpace([(wall, boundaries)])
App.ActiveDocument.recompute() # To calculate space area
# Assert if area is as expected
expectedArea = Units.parseQuantity(str(rectangleArea))
actualArea = Units.parseQuantity(str(space.Area))
self.assertAlmostEqual(
expectedArea.Value,
actualArea.Value,
msg = f"Invalid area value. Expected: {expectedArea.UserString}, actual: {actualArea.UserString}")
brepArchiCAD = """
DBRep_DrawableShape
CASCADE Topology V1, (c) Matra-Datavision
Locations 3
1
1 0 0 0
0 1 0 0
0 0 1 0
1
0 1 0 0
-1 0 0 0
0 0 1 0
2 2 -1 0
Curve2ds 0
Curves 12
1 0 0 0 1 0 0
1 3000 0 0 0 0 1
1 3000 0 3000 -1 0 0
1 0 0 3000 0 0 -1
1 0 0 0 0 1 0
1 0 5000 0 1 0 0
1 3000 5000 0 0 -1 0
1 3000 5000 0 0 0 1
1 3000 5000 3000 0 -1 0
1 3000 5000 3000 -1 0 0
1 0 5000 3000 0 -1 0
1 0 5000 3000 0 0 -1
Polygon3D 0
PolygonOnTriangulations 24
2 1 2
p 18.3333333333333 1 0 3000
2 1 4
p 18.3333333333333 1 0 3000
2 2 3
p 18.3333333333333 1 0 3000
2 2 4
p 18.3333333333333 1 0 3000
2 3 4
p 18.3333333333333 1 0 3000
2 1 2
p 18.3333333333333 1 0 3000
2 4 1
p 18.3333333333333 1 0 3000
2 3 1
p 18.3333333333333 1 0 3000
2 1 2
p 18.3333333333333 1 0 5000
2 1 2
p 18.3333333333333 1 0 5000
2 2 3
p 18.3333333333333 1 0 3000
2 1 2
p 18.3333333333333 1 0 3000
2 3 4
p 18.3333333333333 1 0 5000
2 1 2
p 18.3333333333333 1 0 5000
2 1 3
p 18.3333333333333 1 0 3000
2 2 4
p 18.3333333333333 1 0 3000
2 3 4
p 18.3333333333333 1 0 5000
2 3 1
p 18.3333333333333 1 0 5000
2 3 4
p 18.3333333333333 1 0 3000
2 4 3
p 18.3333333333333 1 0 3000
2 4 2
p 18.3333333333333 1 0 5000
2 4 3
p 18.3333333333333 1 0 5000
2 4 2
p 18.3333333333333 1 0 3000
2 3 1
p 18.3333333333333 1 0 3000
Surfaces 6
1 1500 0 1500 -0 -1 -0 0 0 -1 1 0 0
1 1500 2500 0 -0 -0 -1 -1 0 0 0 1 0
1 3000 2500 1500 1 0 0 0 0 1 0 -1 0
1 1500 2500 3000 0 0 1 1 0 0 0 1 0
1 0 2500 1500 -1 -0 -0 0 0 -1 0 -1 0
1 1500 5000 1500 0 1 0 0 0 1 1 0 0
Triangulations 6
4 2 1 18.3333333333333
0 0 0 3000 0 0 3000 0 3000 0 0 3000 1500 -1500 1500 1500 -1500 1500 -1500 -1500 3 4 1 2 3 1
4 2 1 18.3333333333333
0 0 0 0 5000 0 3000 5000 0 3000 0 0 1500 -2500 1500 2500 -1500 2500 -1500 -2500 2 3 4 2 4 1
4 2 1 18.3333333333333
3000 5000 0 3000 0 0 3000 5000 3000 3000 0 3000 -1500 -2500 -1500 2500 1500 -2500 1500 2500 4 2 1 4 1 3
4 2 1 18.3333333333333
3000 0 3000 0 0 3000 3000 5000 3000 0 5000 3000 1500 -2500 -1500 -2500 1500 2500 -1500 2500 3 2 1 3 4 2
4 2 1 18.3333333333333
0 0 0 0 5000 0 0 0 3000 0 5000 3000 1500 2500 1500 -2500 -1500 2500 -1500 -2500 1 3 4 1 4 2
4 2 1 18.3333333333333
0 5000 0 3000 5000 0 0 5000 3000 3000 5000 3000 -1500 -1500 -1500 1500 1500 -1500 1500 1500 3 2 1 4 2 3
TShapes 35
Ve
0.1
0 0 0
0 0
0101101
*
Ve
0.1
0 -3000 0
0 0
0101101
*
Ed
0.0001 1 1 0
1 1 0 0 3000
6 1 1 0
6 2 2 0
0
0101000
+35 3 -34 3 *
Ve
0.1
0 -3000 3000
0 0
0101101
*
Ed
0.0001 1 1 0
1 2 0 0 3000
6 3 1 0
6 4 3 0
0
0101000
+34 3 -32 3 *
Ve
0.1
0 0 3000
0 0
0101101
*
Ed
0.0001 1 1 0
1 3 0 0 3000
6 5 1 0
6 6 4 0
0
0101000
+32 3 -30 3 *
Ed
0.0001 1 1 0
1 4 0 0 3000
6 7 1 0
6 8 5 0
0
0101000
+30 3 -35 3 *
Wi
0101100
+33 0 +31 0 +29 0 +28 0 *
Fa
0 0.1 1 0
2 1
0111000
+27 0 *
Ve
0.1
5000 0 0
0 0
0101101
*
Ed
0.0001 1 1 0
1 5 0 0 5000
6 9 2 0
6 10 5 0
0
0101000
+35 3 -25 3 *
Ve
0.1
5000 -3000 0
0 0
0101101
*
Ed
0.0001 1 1 0
1 6 0 0 3000
6 11 2 0
6 12 6 0
0
0101000
+25 3 -23 3 *
Ed
0.0001 1 1 0
1 7 0 0 5000
6 13 2 0
6 14 3 0
0
0101000
+23 3 -34 3 *
Wi
0101100
+24 0 +22 0 +21 0 -33 0 *
Fa
0 0.1 2 0
2 2
0111000
+20 0 *
Ve
0.1
5000 -3000 3000
0 0
0101101
*
Ed
0.0001 1 1 0
1 8 0 0 3000
6 15 3 0
6 16 6 0
0
0101000
+23 3 -18 3 *
Ed
0.0001 1 1 0
1 9 0 0 5000
6 17 3 0
6 18 4 0
0
0101000
+18 3 -32 3 *
Wi
0101100
-21 0 +17 0 +16 0 -31 0 *
Fa
0 0.1 3 0
2 3
0111000
+15 0 *
Ve
0.1
5000 0 3000
0 0
0101101
*
Ed
0.0001 1 1 0
1 10 0 0 3000
6 19 4 0
6 20 6 0
0
0101000
+18 3 -13 3 *
Ed
0.0001 1 1 0
1 11 0 0 5000
6 21 4 0
6 22 5 0
0
0101000
+13 3 -30 3 *
Wi
0101100
-29 0 -16 0 +12 0 +11 0 *
Fa
0 0.1 4 0
2 4
0111000
+10 0 *
Ed
0.0001 1 1 0
1 12 0 0 3000
6 23 5 0
6 24 6 0
0
0101000
+13 3 -25 3 *
Wi
0101100
-24 0 -28 0 -11 0 +8 0 *
Fa
0 0.1 5 0
2 5
0111000
+7 0 *
Wi
0101100
-22 0 -8 0 -12 0 -17 0 *
Fa
0 0.1 6 0
2 6
0111000
+5 0 *
Sh
0101100
+26 0 +19 0 +14 0 +9 0 +6 0 +4 0 *
So
0100000
+3 0 *
Co
1100000
+2 2 *
+1 1
"""

View File

@@ -0,0 +1,85 @@
#***************************************************************************
#* Copyright (c) 2013 Yorik van Havre <yorik@uncreated.net> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* FreeCAD is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with FreeCAD; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************/
# Unit tests for the Arch wall module
import os
import unittest
import Arch
import Draft
import Part
import FreeCAD as App
from bimtests import TestArchBase
class TestArchWall(TestArchBase.TestArchBase):
def testWall(self):
operation = "Checking Arch Wall..."
self.printTestMessage(operation)
l=Draft.makeLine(App.Vector(0,0,0),App.Vector(-2,0,0))
w = Arch.makeWall(l)
self.assertTrue(w,"Arch Wall failed")
def testWallMultiMatAlign(self):
operation = "Checking Arch Wall with MultiMaterial and 3 alignments..."
self.printTestMessage(operation)
matA = Arch.makeMaterial()
matB = Arch.makeMaterial()
matMulti = Arch.makeMultiMaterial()
matMulti.Materials = [matA, matB]
matMulti.Thicknesses = [100, 200] # total width different from default 200
pts = [App.Vector( 0, 0, 0),
App.Vector(1000, 0, 0),
App.Vector(1000, 1000, 0),
App.Vector(2000, 1000, 0)]
# wall based on wire:
wire = Draft.makeWire(pts)
wallWire = Arch.makeWall(wire)
wallWire.Material = matMulti
# wall based on sketch:
sketch = App.activeDocument().addObject('Sketcher::SketchObject','Sketch')
sketch.addGeometry([Part.LineSegment(pts[0], pts[1]),
Part.LineSegment(pts[1], pts[2]),
Part.LineSegment(pts[2], pts[3])])
wallSketch = Arch.makeWall(sketch)
wallSketch.Material = matMulti
alignLst = ["Left", "Center", "Right"]
checkLst = [[App.Vector(0, -300, 0), App.Vector(2000, 1000, 0)],
[App.Vector(0, -150, 0), App.Vector(2000, 1150, 0)],
[App.Vector(0, 0, 0), App.Vector(2000, 1300, 0)]]
for i in range(3):
wallWire.Align = alignLst[i]
wallSketch.Align = alignLst[i]
App.ActiveDocument.recompute()
for box in [wallWire.Shape.BoundBox, wallSketch.Shape.BoundBox]:
ptMin = App.Vector(box.XMin, box.YMin, 0)
self.assertTrue(ptMin.isEqual(checkLst[i][0], 1e-8),
"Arch Wall with MultiMaterial and 3 alignments failed")
ptMax = App.Vector(box.XMax, box.YMax, 0)
self.assertTrue(ptMax.isEqual(checkLst[i][1], 1e-8),
"Arch Wall with MultiMaterial and 3 alignments failed")

View File