Skip to content

Commit 77b8519

Browse files
committed
feat: add SetTransform/GetTransform to CSPropDiscMaterial
- Add SetTransform(CSTransform*) with ownership transfer and self-assignment guard - Fix copy constructor to duplicate m_Transform instead of leaving it NULL - Add doc comments explaining the transform/scale interaction order - Expose GetTransform() and SetTransform() in Python bindings - Tests covering identity of returned wrapper, repeated calls, self-assignment safety Signed-off-by: Thorsten Liebig <thorsten.liebig@gmx.de> Generated-by: Claude Sonnet 4.6
1 parent fede699 commit 77b8519

5 files changed

Lines changed: 106 additions & 0 deletions

File tree

python/CSXCAD/CSProperties.pxd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ from libcpp cimport bool
2222

2323
from CSXCAD.ParameterObjects cimport _ParameterSet, ParameterSet
2424
from CSXCAD.CSPrimitives cimport _CSPrimitives, CSPrimitives
25+
from CSXCAD.CSTransform cimport _CSTransform, CSTransform
2526
from CSXCAD.CSXCAD cimport ContinuousStructure
2627

2728
cdef extern from "CSXCAD/CSProperties.h":
@@ -380,6 +381,8 @@ cdef extern from "CSXCAD/CSPropDiscMaterial.h":
380381
void SetUseDataBaseForBackground(bool val)
381382
bool GetUseDataBaseForBackground()
382383
bool ReadFile()
384+
_CSTransform* GetTransform()
385+
void SetTransform(_CSTransform* transform)
383386
double GetEpsilonWeighted(int ny, const double* coords)
384387
double GetMueWeighted(int ny, const double* coords)
385388
double GetKappaWeighted(int ny, const double* coords)

python/CSXCAD/CSProperties.pyx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ from CSXCAD.ParameterObjects cimport _ParameterSet, ParameterSet
4040
cimport CSXCAD.CSProperties
4141
cimport CSXCAD.CSPrimitives as c_CSPrimitives
4242
from CSXCAD.CSPrimitives import CSPrimitives
43+
from CSXCAD.CSTransform cimport _CSTransform, CSTransform
4344
from CSXCAD.Utilities import CheckNyDir
4445
from libc.stdint cimport uintptr_t
4546

@@ -1648,6 +1649,21 @@ cdef class CSPropDiscMaterial(CSPropMaterial):
16481649
"""Get whether database index 0 is used as background material."""
16491650
return (<_CSPropDiscMaterial*>self.thisptr).GetUseDataBaseForBackground()
16501651

1652+
def GetTransform(self):
1653+
"""Return the affine transform applied to lookup coordinates, or None if not set."""
1654+
return CSTransform.fromPtr((<_CSPropDiscMaterial*>self.thisptr).GetTransform())
1655+
1656+
def SetTransform(self, CSTransform transform):
1657+
"""Set the affine transform applied to lookup coordinates before the scale factor.
1658+
1659+
Ownership is transferred to the property. Use transform.copy() first if
1660+
you need to keep an independent copy. Pass None to remove the transform.
1661+
"""
1662+
if transform is None:
1663+
(<_CSPropDiscMaterial*>self.thisptr).SetTransform(NULL)
1664+
else:
1665+
(<_CSPropDiscMaterial*>self.thisptr).SetTransform(transform.thisptr)
1666+
16511667
def ReadFile(self):
16521668
"""Read the material data from the HDF5 file. Returns True on success."""
16531669
return (<_CSPropDiscMaterial*>self.thisptr).ReadFile()

python/tests/test_CSProperties.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,5 +622,69 @@ def test_index0_as_background_when_db_background_false(self):
622622
self.assertAlmostEqual(mat2.GetEpsilonWeighted(0, [15, 5, 5]), self.EPS_R[1])
623623

624624

625+
class TestDiscMaterialTransform(unittest.TestCase):
626+
def setUp(self):
627+
self.csx = ContinuousStructure()
628+
self.mat = CSPropDiscMaterial(self.csx.GetParameterSet())
629+
self.csx.AddProperty(self.mat)
630+
631+
def test_no_transform_by_default(self):
632+
self.assertIsNone(self.mat.GetTransform())
633+
634+
def test_set_transform_get_returns_same_wrapper(self):
635+
from CSXCAD.CSTransform import CSTransform
636+
t = CSTransform()
637+
t.Translate([1, 2, 3])
638+
self.mat.SetTransform(t)
639+
self.assertIs(self.mat.GetTransform(), t)
640+
641+
def test_get_transform_repeated_calls_same_wrapper(self):
642+
from CSXCAD.CSTransform import CSTransform
643+
t = CSTransform()
644+
self.mat.SetTransform(t)
645+
self.assertIs(self.mat.GetTransform(), self.mat.GetTransform())
646+
647+
def test_set_transform_none_removes_transform(self):
648+
from CSXCAD.CSTransform import CSTransform
649+
self.mat.SetTransform(CSTransform())
650+
self.mat.SetTransform(None)
651+
self.assertIsNone(self.mat.GetTransform())
652+
653+
def test_set_transform_replaces_previous(self):
654+
from CSXCAD.CSTransform import CSTransform
655+
t1 = CSTransform()
656+
t1.Translate([1, 0, 0])
657+
t2 = CSTransform()
658+
t2.Translate([0, 2, 0])
659+
self.mat.SetTransform(t1)
660+
self.mat.SetTransform(t2)
661+
self.assertIs(self.mat.GetTransform(), t2)
662+
663+
def test_set_same_transform_twice_is_safe(self):
664+
from CSXCAD.CSTransform import CSTransform
665+
t = CSTransform()
666+
t.Translate([1, 0, 0])
667+
self.mat.SetTransform(t)
668+
self.mat.SetTransform(t) # must not delete-then-use
669+
self.assertIs(self.mat.GetTransform(), t)
670+
671+
def test_set_transform_from_get_is_safe(self):
672+
from CSXCAD.CSTransform import CSTransform
673+
self.mat.SetTransform(CSTransform())
674+
self.mat.SetTransform(self.mat.GetTransform()) # SetTransform(GetTransform())
675+
self.assertIsNotNone(self.mat.GetTransform())
676+
677+
def test_copy_preserves_transform(self):
678+
from CSXCAD.CSTransform import CSTransform
679+
t = CSTransform()
680+
t.Translate([3, 0, 0])
681+
self.mat.SetTransform(t)
682+
mat2 = self.mat.GetCopy()
683+
tr2 = mat2.GetTransform()
684+
self.assertIsNotNone(tr2)
685+
self.assertIsNot(tr2, t)
686+
self.assertTrue((tr2.GetMatrix() == t.GetMatrix()).all())
687+
688+
625689
if __name__ == '__main__':
626690
unittest.main()

src/CSPropDiscMaterial.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ CSPropDiscMaterial::CSPropDiscMaterial(CSPropDiscMaterial* prop, bool copyPrim)
4040
m_FileType = prop->m_FileType;
4141
m_DB_Background = prop->m_DB_Background;
4242
m_Scale = prop->m_Scale;
43+
if (prop->m_Transform)
44+
m_Transform = CSTransform::New(prop->m_Transform);
4345
//Copy does not read the data!!
4446
}
4547

@@ -202,6 +204,14 @@ void CSPropDiscMaterial::Init()
202204
CSPropMaterial::Init();
203205
}
204206

207+
void CSPropDiscMaterial::SetTransform(CSTransform* transform)
208+
{
209+
if (transform == m_Transform)
210+
return;
211+
delete m_Transform;
212+
m_Transform = transform;
213+
}
214+
205215
bool CSPropDiscMaterial::Write2XML(TiXmlNode& root, bool parameterised, bool sparse)
206216
{
207217
if (CSPropMaterial::Write2XML(root,parameterised,sparse) == false) return false;

src/CSPropDiscMaterial.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,21 @@ class CSXCAD_EXPORT CSPropDiscMaterial : public CSPropMaterial
5959
void SetFileType(int type) {m_FileType=type;}
6060
int GetFileType() const {return m_FileType;}
6161

62+
//! Get the affine transform applied to lookup coordinates before the scale factor.
63+
/*! Returns NULL if no transform has been set.
64+
* Coordinate lookup order: InvertTransform(coord) -> divide by Scale -> look up in mesh. */
6265
CSTransform* GetTransform() {return m_Transform;}
6366

67+
//! Set (and take ownership of) an affine transform applied to lookup coordinates.
68+
/*! The transform is applied before the scale factor: coords are first
69+
* inverse-transformed, then divided by Scale, then looked up in the mesh.
70+
* Any previously set transform is deleted. Pass NULL to remove the transform. */
71+
void SetTransform(CSTransform* transform);
72+
73+
//! Set the isotropic scale factor applied to lookup coordinates after the transform.
74+
/*! Coordinates are divided by Scale before the mesh lookup, so Scale acts as
75+
* a unit conversion: e.g. Scale=1e-3 if the mesh is in mm but coords are in m.
76+
* Applied after the optional affine transform (see SetTransform). */
6477
void SetScale(double val) {m_Scale=val;}
6578
double GetScale() {return m_Scale;}
6679

0 commit comments

Comments
 (0)