diff --git a/src/main/scala/scalismo/io/statisticalmodel/StatismoDomainIO.scala b/src/main/scala/scalismo/io/statisticalmodel/StatismoDomainIO.scala index 3825e84b..3694eca5 100644 --- a/src/main/scala/scalismo/io/statisticalmodel/StatismoDomainIO.scala +++ b/src/main/scala/scalismo/io/statisticalmodel/StatismoDomainIO.scala @@ -75,8 +75,8 @@ object StatismoDomainIO { override def createDomainWithCells(points: IndexedSeq[Point[_2D]], cellArray: Option[NDArray[Int]] ): Try[TriangleMesh[_2D]] = { - cellArray match { - case None => Failure(new Throwable("Triangle cells missing")) + val triangleList = cellArray match { + case None => Success(TriangleList(IndexedSeq())) case Some(c) => val cellMatrix = ndIntArrayToIntMatrix(c) if (cellMatrix.cols != 3) Failure(new Exception("Representer cells are not triangles")) @@ -84,10 +84,10 @@ object StatismoDomainIO { val cells = for (i <- 0 until cellMatrix.rows) yield { TriangleCell(PointId(cellMatrix(i, 0)), PointId(cellMatrix(i, 1)), PointId(cellMatrix(i, 2))) } - Success(TriangleMesh2D(UnstructuredPoints(points), TriangleList(cells))) + Success(TriangleList(cells)) } } - + triangleList.map(triangles => TriangleMesh2D(UnstructuredPoints(points), triangles)) } override def cellsToArray(mesh: TriangleMesh[_2D]): NDArray[Int] = { @@ -103,20 +103,19 @@ object StatismoDomainIO { override def createDomainWithCells(points: IndexedSeq[Point[_3D]], cellArray: Option[NDArray[Int]] ): Try[TriangleMesh[_3D]] = { - cellArray match { - case None => Failure(new Throwable("Triangle cells missing")) + val triangleList = cellArray match { + case None => Success(TriangleList(IndexedSeq())) case Some(c) => val cellMatrix = ndIntArrayToIntMatrix(c) if (cellMatrix.cols != 3) Failure(new Exception("Representer cells are not triangles")) else { - val cells = - for (i <- 0 until cellMatrix.rows) - yield { - TriangleCell(PointId(cellMatrix(i, 0)), PointId(cellMatrix(i, 1)), PointId(cellMatrix(i, 2))) - } - Success(TriangleMesh3D(UnstructuredPoints(points), TriangleList(cells))) + val cells = for (i <- 0 until cellMatrix.rows) yield { + TriangleCell(PointId(cellMatrix(i, 0)), PointId(cellMatrix(i, 1)), PointId(cellMatrix(i, 2))) + } + Success(TriangleList(cells)) } } + triangleList.map(triangles => TriangleMesh3D(UnstructuredPoints(points), triangles)) } override def cellsToArray(mesh: TriangleMesh[_3D]): NDArray[Int] = { @@ -132,8 +131,8 @@ object StatismoDomainIO { override def createDomainWithCells(points: IndexedSeq[Point[_3D]], cellArray: Option[NDArray[Int]] ): Try[TetrahedralMesh[_3D]] = { - cellArray match { - case None => Failure(new Throwable("Tetrahedral cells missing")) + val tetrahedralList = cellArray match { + case None => Success(TetrahedralList(IndexedSeq())) case Some(c) => val cellMatrix = ndIntArrayToIntMatrix(c) if (cellMatrix.cols != 4) Failure(new Exception("Representer cells are not tetrahedrons")) @@ -147,9 +146,10 @@ object StatismoDomainIO { PointId(cellMatrix(i, 3)) ) } - Success(TetrahedralMesh3D(UnstructuredPoints(points), TetrahedralList(cells))) + Success(TetrahedralList(cells)) } } + tetrahedralList.map(tetrahedrons => TetrahedralMesh3D(UnstructuredPoints(points), tetrahedrons)) } override def cellsToArray(mesh: TetrahedralMesh[_3D]): NDArray[Int] = { @@ -168,8 +168,8 @@ object StatismoDomainIO { override def createDomainWithCells(points: IndexedSeq[Point[_2D]], cellArray: Option[NDArray[Int]] ): Try[LineMesh[_2D]] = { - cellArray match { - case None => Failure(new Throwable("Line cells missing")) + val lineList = cellArray match { + case None => Success(LineList(IndexedSeq())) case Some(c) => val cellMatrix = ndIntArrayToIntMatrix(c) if (cellMatrix.cols != 2) Failure(new Exception("Representer cells are not lines")) @@ -179,9 +179,10 @@ object StatismoDomainIO { yield { LineCell(PointId(cellMatrix(i, 0)), PointId(cellMatrix(i, 1))) } - Try(LineMesh2D(UnstructuredPoints(points), LineList(cells))) + Try(LineList(cells)) } } + lineList.map(lines => LineMesh2D(UnstructuredPoints(points), lines)) } override def cellsToArray(mesh: LineMesh[_2D]): NDArray[Int] = { @@ -197,8 +198,8 @@ object StatismoDomainIO { override def createDomainWithCells(points: IndexedSeq[Point[_3D]], cellArray: Option[NDArray[Int]] ): Try[LineMesh[_3D]] = { - cellArray match { - case None => Failure(new Throwable("Line cells missing")) + val lineList = cellArray match { + case None => Success(LineList(IndexedSeq())) case Some(c) => val cellMatrix = ndIntArrayToIntMatrix(c) if (cellMatrix.cols != 2) Failure(new Exception("Representer cells are not lines")) @@ -208,9 +209,10 @@ object StatismoDomainIO { yield { LineCell(PointId(cellMatrix(i, 0)), PointId(cellMatrix(i, 1))) } - Success(LineMesh3D(UnstructuredPoints(points), LineList(cells))) + Success(LineList(cells)) } } + lineList.map(lines => LineMesh3D(UnstructuredPoints(points), lines)) } override def cellsToArray(mesh: LineMesh[_3D]): NDArray[Int] = { diff --git a/src/main/scala/scalismo/io/statisticalmodel/StatismoIO.scala b/src/main/scala/scalismo/io/statisticalmodel/StatismoIO.scala index a68a3522..4cc2fbb2 100644 --- a/src/main/scala/scalismo/io/statisticalmodel/StatismoIO.scala +++ b/src/main/scala/scalismo/io/statisticalmodel/StatismoIO.scala @@ -290,8 +290,8 @@ object StatismoIO { points <- Try( for (i <- 0 until pointsMatrix.cols) yield vectorizer.unvectorize(pointsMatrix(::, i).copy).toPoint ) - cells <- readStandardConnectiveityRepresenterGroup(h5file, modelPath) - domain <- typeHelper.createDomainWithCells(points, Option(cells)) + cells = readStandardConnectiveityRepresenterGroup(h5file, modelPath).toOption + domain <- typeHelper.createDomainWithCells(points, cells) } yield domain } @@ -307,7 +307,7 @@ object StatismoIO { val cells = if (h5file.exists(HDFPath(modelPath, "/representer/cells"))) h5file.readNDArrayInt(HDFPath(modelPath, "/representer/cells")) - else Failure(new Throwable("No cells found in representer")) + else Failure[NDArray[Int]](new Throwable("No cells found in representer")) cells } diff --git a/src/test/scala/scalismo/io/statisticalmodel/StatismoDomainIOTests.scala b/src/test/scala/scalismo/io/statisticalmodel/StatismoDomainIOTests.scala index 22970295..4af41fc0 100644 --- a/src/test/scala/scalismo/io/statisticalmodel/StatismoDomainIOTests.scala +++ b/src/test/scala/scalismo/io/statisticalmodel/StatismoDomainIOTests.scala @@ -44,6 +44,21 @@ class StatismoDomainIOTests extends ScalismoTestSuite { t.get } + it("can convert a 2D TriangleMesh without triangles") { + val unstructuredPoints = + CreateUnstructuredPoints2D.create(IndexedSeq(Point2D(0, 0), Point2D(1, 0), Point2D(1, 1))) + val topology = TriangleList(IndexedSeq()) + val input = TriangleMesh2D(unstructuredPoints, topology) + val t = for { + output <- StatismoDomainIO.domainIOTriangleMesh2D.createDomainWithCells(unstructuredPoints.points.toIndexedSeq, + None + ) + } yield { + assert(input == output) + } + t.get + } + it("can convert a 3D TriangleMesh") { val unstructuredPoints = CreateUnstructuredPoints3D.create(IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0))) @@ -60,6 +75,21 @@ class StatismoDomainIOTests extends ScalismoTestSuite { t.get } + it("can convert a 3D TriangleMesh without triangles") { + val unstructuredPoints = + CreateUnstructuredPoints3D.create(IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0))) + val topology = TriangleList(IndexedSeq()) + val input = TriangleMesh3D(unstructuredPoints, topology) + val t = for { + output <- StatismoDomainIO.domainIOTriangleMesh3D.createDomainWithCells(unstructuredPoints.points.toIndexedSeq, + None + ) + } yield { + assert(input == output) + } + t.get + } + it("can convert a 3D TetrahedralMesh") { val unstructuredPoints = CreateUnstructuredPoints3D.create( IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0), Point3D(1, 1, 1)) @@ -76,6 +106,21 @@ class StatismoDomainIOTests extends ScalismoTestSuite { t.get } + it("can convert a 3D TetrahedralMesh without tetrahedrons") { + val unstructuredPoints = CreateUnstructuredPoints3D.create( + IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0), Point3D(1, 1, 1)) + ) + val topology = TetrahedralList(IndexedSeq()) + val input = TetrahedralMesh3D(unstructuredPoints, topology) + val t = for { + output <- StatismoDomainIO.domainIOTetrahedralMesh3D + .createDomainWithCells(unstructuredPoints.points.toIndexedSeq, None) + } yield { + assert(input == output) + } + t.get + } + it("can convert a 2D LineMesh") { val unstructuredPoints = CreateUnstructuredPoints2D.create(IndexedSeq(Point2D(0, 0), Point2D(1, 0))) val topology = LineList(IndexedSeq(LineCell(PointId(0), PointId(1)))) @@ -91,6 +136,20 @@ class StatismoDomainIOTests extends ScalismoTestSuite { t.get } + it("can convert a 2D LineMesh without lines") { + val unstructuredPoints = CreateUnstructuredPoints2D.create(IndexedSeq(Point2D(0, 0), Point2D(1, 0))) + val topology = LineList(IndexedSeq()) + val input = LineMesh2D(unstructuredPoints, topology) + val t = for { + output <- StatismoDomainIO.domainIOLineMesh2D.createDomainWithCells(unstructuredPoints.points.toIndexedSeq, + None + ) + } yield { + assert(input == output) + } + t.get + } + it("can convert a 3D LineMesh") { val unstructuredPoints = CreateUnstructuredPoints3D.create(IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0))) @@ -106,5 +165,20 @@ class StatismoDomainIOTests extends ScalismoTestSuite { } t.get } + + it("can convert a 3D LineMesh without lines") { + val unstructuredPoints = + CreateUnstructuredPoints3D.create(IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0))) + val topology = LineList(IndexedSeq()) + val input = LineMesh3D(unstructuredPoints, topology) + val t = for { + output <- StatismoDomainIO.domainIOLineMesh3D.createDomainWithCells(unstructuredPoints.points.toIndexedSeq, + None + ) + } yield { + assert(input == output) + } + t.get + } } } diff --git a/src/test/scala/scalismo/io/statisticalmodel/StatismoIOTests.scala b/src/test/scala/scalismo/io/statisticalmodel/StatismoIOTests.scala new file mode 100644 index 00000000..f93d9904 --- /dev/null +++ b/src/test/scala/scalismo/io/statisticalmodel/StatismoIOTests.scala @@ -0,0 +1,65 @@ +package scalismo.io.statisticalmodel + +import org.scalatest.PrivateMethodTester.* +import scalismo.geometry.{_3D, Point3D} +import scalismo.io.statisticalmodel.StatismoIO +import scalismo.io.StatisticalModelIO +import scalismo.ScalismoTestSuite +import scalismo.common.{UnstructuredPoints, UnstructuredPointsDomain} +import scalismo.hdf5json.HDFPath +import scalismo.mesh.{TriangleList, TriangleMesh, TriangleMesh3D} +import scalismo.statisticalmodel.StatisticalMeshModel + +import java.io.File +import java.net.URLDecoder +import java.nio.file.Files +import scala.util.Try + +class StatismoIOTests extends ScalismoTestSuite { + def assertModelAlmostEqual[D](m1: UnstructuredPointsDomain[D], m2: UnstructuredPointsDomain[D]): Unit = { + m1.pointSet.pointSequence.zip(m2.pointSet.pointSequence).foreach { case (a, b) => + assert((a - b).norm < 1e-5) + } + } + + describe("the StatismoIO methods") { + + it("can write and read UnstructuredPointsDomain") { + val tmpDir = Files.createTempDirectory("test-StatismoIO") + tmpDir.toFile.deleteOnExit() + val tmpFile = new File(tmpDir.toFile, "UnstructuredPointsDomain.h5.json") + tmpFile.deleteOnExit() + + val dataToWrite = UnstructuredPointsDomain( + IndexedSeq( + Point3D(0, 1, 2) + ) + ) + + val modelPath = HDFPath("/") + val representerPath = HDFPath(modelPath, "representer") + + // helper to test / call private methods + val writerMethod = PrivateMethod[Try[Unit]](Symbol("writeRepresenterStatismov090")) + val readerMethod = PrivateMethod[Try[UnstructuredPointsDomain[_3D]]](Symbol("readStandardMeshRepresentation")) + val ndSpace = scalismo.geometry.Dim.ThreeDSpace + val domainIO = scalismo.io.StatismoDomainIO.domainIOUnstructuredPoints3D + val vectorizer = scalismo.geometry.EuclideanVector.Vector_3DVectorizer + + val t = for { + h5Out <- StatisticalModelIOUtils.openFileForWriting(tmpFile) + t <- StatismoIO invokePrivate writerMethod(h5Out, representerPath, dataToWrite, modelPath, ndSpace, domainIO) + _ <- h5Out.write() + _ <- Try { + h5Out.close() + } + h5In <- StatisticalModelIOUtils.openFileForReading(tmpFile) + loaded <- StatismoIO invokePrivate readerMethod(h5In, modelPath, ndSpace, domainIO, vectorizer) + } yield { + assertModelAlmostEqual(dataToWrite, loaded) + } + + t.get + } + } +}