216 lines
9.3 KiB
Python
216 lines
9.3 KiB
Python
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
# /****************************************************************************
|
|
# *
|
|
# Copyright (c) 2023 Ondsel <development@ondsel.com> *
|
|
# *
|
|
# 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/>. *
|
|
# *
|
|
# ***************************************************************************/
|
|
|
|
import FreeCAD as App
|
|
import Part
|
|
import unittest
|
|
|
|
import UtilsAssembly
|
|
import JointObject
|
|
|
|
|
|
def _msg(text, end="\n"):
|
|
"""Write messages to the console including the line ending."""
|
|
App.Console.PrintMessage(text + end)
|
|
|
|
|
|
class TestCore(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
"""setUpClass()...
|
|
This method is called upon instantiation of this test class. Add code and objects here
|
|
that are needed for the duration of the test() methods in this class. In other words,
|
|
set up the 'global' test environment here; use the `setUp()` method to set up a 'local'
|
|
test environment.
|
|
This method does not have access to the class `self` reference, but it
|
|
is able to call static methods within this same class.
|
|
"""
|
|
pass
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
"""tearDownClass()...
|
|
This method is called prior to destruction of this test class. Add code and objects here
|
|
that cleanup the test environment after the test() methods in this class have been executed.
|
|
This method does not have access to the class `self` reference. This method
|
|
is able to call static methods within this same class.
|
|
"""
|
|
pass
|
|
|
|
# Setup and tear down methods called before and after each unit test
|
|
def setUp(self):
|
|
"""setUp()...
|
|
This method is called prior to each `test()` method. Add code and objects here
|
|
that are needed for multiple `test()` methods.
|
|
"""
|
|
doc_name = self.__class__.__name__
|
|
if App.ActiveDocument:
|
|
if App.ActiveDocument.Name != doc_name:
|
|
App.newDocument(doc_name)
|
|
else:
|
|
App.newDocument(doc_name)
|
|
App.setActiveDocument(doc_name)
|
|
self.doc = App.ActiveDocument
|
|
|
|
self.assembly = App.ActiveDocument.addObject("Assembly::AssemblyObject", "Assembly")
|
|
if self.assembly:
|
|
self.jointgroup = self.assembly.newObject("Assembly::JointGroup", "Joints")
|
|
|
|
_msg(" Temporary document '{}'".format(self.doc.Name))
|
|
|
|
def tearDown(self):
|
|
"""tearDown()...
|
|
This method is called after each test() method. Add cleanup instructions here.
|
|
Such cleanup instructions will likely undo those in the setUp() method.
|
|
"""
|
|
App.closeDocument(self.doc.Name)
|
|
|
|
def test_create_assembly(self):
|
|
"""Create an assembly."""
|
|
operation = "Create Assembly Object"
|
|
_msg(" Test '{}'".format(operation))
|
|
self.assertTrue(self.assembly, "'{}' failed".format(operation))
|
|
|
|
def test_create_jointGroup(self):
|
|
"""Create a joint group in an assembly."""
|
|
operation = "Create JointGroup Object"
|
|
_msg(" Test '{}'".format(operation))
|
|
self.assertTrue(self.jointgroup, "'{}' failed".format(operation))
|
|
|
|
def test_create_joint(self):
|
|
"""Create a joint in an assembly."""
|
|
operation = "Create Joint Object"
|
|
_msg(" Test '{}'".format(operation))
|
|
|
|
joint = self.jointgroup.newObject("App::FeaturePython", "testJoint")
|
|
self.assertTrue(joint, "'{}' failed (FeaturePython creation failed)".format(operation))
|
|
JointObject.Joint(joint, 0)
|
|
|
|
self.assertTrue(hasattr(joint, "JointType"), "'{}' failed".format(operation))
|
|
|
|
def test_create_grounded_joint(self):
|
|
"""Create a grounded joint in an assembly."""
|
|
operation = "Create Grounded Joint Object"
|
|
_msg(" Test '{}'".format(operation))
|
|
|
|
groundedjoint = self.jointgroup.newObject("App::FeaturePython", "testJoint")
|
|
self.assertTrue(
|
|
groundedjoint, "'{}' failed (FeaturePython creation failed)".format(operation)
|
|
)
|
|
|
|
box = self.assembly.newObject("Part::Box", "Box")
|
|
|
|
JointObject.GroundedJoint(groundedjoint, box)
|
|
|
|
self.assertTrue(
|
|
hasattr(groundedjoint, "ObjectToGround"),
|
|
"'{}' failed: No attribute 'ObjectToGround'".format(operation),
|
|
)
|
|
self.assertTrue(
|
|
groundedjoint.ObjectToGround == box,
|
|
"'{}' failed: ObjectToGround not set correctly.".format(operation),
|
|
)
|
|
|
|
def test_find_placement(self):
|
|
"""Test find placement of joint."""
|
|
operation = "Find placement"
|
|
_msg(" Test '{}'".format(operation))
|
|
|
|
joint = self.jointgroup.newObject("App::FeaturePython", "testJoint")
|
|
JointObject.Joint(joint, 0)
|
|
|
|
L = 2
|
|
W = 3
|
|
H = 7
|
|
box = self.assembly.newObject("Part::Box", "Box")
|
|
box.Length = L
|
|
box.Width = W
|
|
box.Height = H
|
|
box.Placement = App.Placement(App.Vector(10, 20, 30), App.Rotation(15, 25, 35))
|
|
|
|
# Step 0 : box with placement. No element selected
|
|
ref = [self.assembly, [box.Name + ".", box.Name + "."]]
|
|
plc = joint.Proxy.findPlacement(joint, ref)
|
|
targetPlc = App.Placement(App.Vector(), App.Rotation())
|
|
self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 0".format(operation))
|
|
|
|
# Step 1 : box with placement. Face + Vertex
|
|
ref = [self.assembly, [box.Name + ".Face6", box.Name + ".Vertex7"]]
|
|
plc = joint.Proxy.findPlacement(joint, ref)
|
|
targetPlc = App.Placement(App.Vector(L, W, H), App.Rotation())
|
|
self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 1".format(operation))
|
|
|
|
# Step 2 : box with placement. Edge + Vertex
|
|
ref = [self.assembly, [box.Name + ".Edge8", box.Name + ".Vertex8"]]
|
|
plc = joint.Proxy.findPlacement(joint, ref)
|
|
targetPlc = App.Placement(App.Vector(L, W, 0), App.Rotation(0, -90, 270))
|
|
self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 2".format(operation))
|
|
|
|
# Step 3 : box with placement. Vertex
|
|
ref = [self.assembly, [box.Name + ".Vertex3", box.Name + ".Vertex3"]]
|
|
plc = joint.Proxy.findPlacement(joint, ref)
|
|
targetPlc = App.Placement(App.Vector(0, W, H), App.Rotation())
|
|
_msg(" plc '{}'".format(plc))
|
|
_msg(" targetPlc '{}'".format(targetPlc))
|
|
self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 3".format(operation))
|
|
|
|
# Step 4 : box with placement. Face
|
|
ref = [self.assembly, [box.Name + ".Face2", box.Name + ".Face2"]]
|
|
plc = joint.Proxy.findPlacement(joint, ref)
|
|
targetPlc = App.Placement(App.Vector(L, W / 2, H / 2), App.Rotation(0, -90, 180))
|
|
_msg(" plc '{}'".format(plc))
|
|
_msg(" targetPlc '{}'".format(targetPlc))
|
|
self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 4".format(operation))
|
|
|
|
def test_solve_assembly(self):
|
|
"""Test solving an assembly."""
|
|
operation = "Solve assembly"
|
|
_msg(" Test '{}'".format(operation))
|
|
|
|
box = self.assembly.newObject("Part::Box", "Box")
|
|
box.Length = 10
|
|
box.Width = 10
|
|
box.Height = 10
|
|
box.Placement = App.Placement(App.Vector(10, 20, 30), App.Rotation(15, 25, 35))
|
|
|
|
box2 = self.assembly.newObject("Part::Box", "Box")
|
|
box2.Length = 10
|
|
box2.Width = 10
|
|
box2.Height = 10
|
|
box2.Placement = App.Placement(App.Vector(40, 50, 60), App.Rotation(45, 55, 65))
|
|
|
|
ground = self.jointgroup.newObject("App::FeaturePython", "GroundedJoint")
|
|
JointObject.GroundedJoint(ground, box2)
|
|
|
|
joint = self.jointgroup.newObject("App::FeaturePython", "testJoint")
|
|
JointObject.Joint(joint, 0)
|
|
|
|
refs = [
|
|
[self.assembly, [box2.Name + ".Face6", box2.Name + ".Vertex7"]],
|
|
[self.assembly, [box.Name + ".Face6", box.Name + ".Vertex7"]],
|
|
]
|
|
|
|
joint.Proxy.setJointConnectors(joint, refs)
|
|
|
|
self.assertTrue(box.Placement.isSame(box2.Placement, 1e-6), "'{}'".format(operation))
|