mesh/io/icq.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief Gaskell's implicitly connected quadrilateral (ICQ) shape models: reading, writing and | ||
| 3 | /// triangulating them. | ||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "terraingen/mesh/mesh.h" | ||
| 7 | #include "terraingen/util/radial_shape.h" | ||
| 8 | |||
| 9 | #include <cstddef> | ||
| 10 | #include <filesystem> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | namespace terraingen { | ||
| 14 | |||
| 15 | /// A shape model in Gaskell's implicitly connected quadrilateral (ICQ) format, in metres: the | ||
| 16 | /// vertices v(I, J, F) labelled as grid points on the six faces of a cube, I and J from 0 to Q and | ||
| 17 | /// F from 1 to 6, the faces centred on +z, -y, -x, +y, +x and -z. Vertices on the cube's edges and | ||
| 18 | /// corners are stored once per face, as in the files. Format: R. W. Gaskell, "The Implicitly | ||
| 19 | /// Connected Quadrilateral Format", NASA PDS Small Bodies Node, | ||
| 20 | /// https://sbnarchive.psi.edu/pds3/dawn/fc/DWNCSPC_4_01/DOCUMENT/ICQMODEL.ASC | ||
| 21 | struct IcqModel { | ||
| 22 | ✗ | std::size_t q = 0; ///< Q, the vertex grid's intervals per face edge. | |
| 23 | ✗ | std::vector<Vec3d> vertices; ///< 6 (Q + 1)^2 vertices in file order: F, then J, then I. | |
| 24 | |||
| 25 | /// The index in `vertices` of v(`i`, `j`, `face`), with `face` from 1 to 6. | ||
| 26 | 77415 | std::size_t Index(std::size_t i, std::size_t j, std::size_t face) const { | |
| 27 | 77415 | return ((face - 1) * (q + 1) + j) * (q + 1) + i; | |
| 28 | } | ||
| 29 | |||
| 30 | ✗ | bool operator==(const IcqModel&) const = default; | |
| 31 | }; | ||
| 32 | |||
| 33 | /// Whether `path` ends in .icq, in any case. | ||
| 34 | bool IsIcqPath(const std::filesystem::path& path); | ||
| 35 | |||
| 36 | /// The unit direction of label (`i`, `j`, `face`) of a cube with `q` intervals per edge, at | ||
| 37 | /// equiangular coordinates, with the faces oriented as in Gaskell's models: on face 1 (+z) I runs | ||
| 38 | /// along +x and J along -y; on faces 2 to 5 (-y, -x, +y, +x) J runs along -z; on face 6 (-z) I runs | ||
| 39 | /// along +x and J along +y. Labels the format shares between faces get the same direction exactly. | ||
| 40 | /// @throws std::invalid_argument unless `q` > 0, `i` and `j` <= `q` and 1 <= `face` <= 6. | ||
| 41 | Vec3d IcqDirection(std::size_t q, std::size_t i, std::size_t j, std::size_t face); | ||
| 42 | |||
| 43 | /// Reads an ICQ file: Q on the first line, then 6 (Q + 1)^2 lines of x, y and z (extra columns, | ||
| 44 | /// such as albedo, are ignored). | ||
| 45 | /// @param metresPerUnit Metres per file unit; Gaskell's models are in kilometres. | ||
| 46 | /// @throws std::runtime_error if the file cannot be read, or holds a bad Q, too few vertices or a | ||
| 47 | /// value that is not a finite number. | ||
| 48 | IcqModel ReadIcq(const std::filesystem::path& path, double metresPerUnit = 1000.0); | ||
| 49 | |||
| 50 | /// Writes `model` as an ICQ file. | ||
| 51 | /// @param metresPerUnit Metres per file unit; Gaskell's models are in kilometres. | ||
| 52 | /// @throws std::invalid_argument for a Q of 0, a vertex count other than 6 (Q + 1)^2 or a vertex | ||
| 53 | /// that is not finite. | ||
| 54 | /// @throws std::runtime_error if the file cannot be written. | ||
| 55 | void WriteIcq(const std::filesystem::path& path, const IcqModel& model, | ||
| 56 | double metresPerUnit = 1000.0); | ||
| 57 | |||
| 58 | /// The model as 12 Q^2 triangles over its 6 Q^2 + 2 distinct vertices, as Gaskell converts it: | ||
| 59 | /// each shared edge and corner vertex taken from one face, each quadrilateral split along its | ||
| 60 | /// shorter diagonal, triangles wound outward. | ||
| 61 | /// @throws std::invalid_argument as `WriteIcq`. | ||
| 62 | TriangleMesh IcqToMesh(const IcqModel& model); | ||
| 63 | |||
| 64 | /// The model as a radial shape of the same Q: each vertex's distance from the origin when every | ||
| 65 | /// vertex lies along its label's `IcqDirection`, as in the ICQ files TerrainGen writes; otherwise, | ||
| 66 | /// as in Gaskell's models, whose vertices are relaxed off those directions, the model resampled | ||
| 67 | /// along rays from the origin (`ShapeModelFromMesh` of `IcqToMesh`). | ||
| 68 | /// @throws std::invalid_argument as `WriteIcq`. | ||
| 69 | /// @throws std::runtime_error as `ShapeModelFromMesh`. | ||
| 70 | RadialShape ToRadialShape(const IcqModel& model); | ||
| 71 | |||
| 72 | /// The shape as an ICQ model: each label at its direction times its radius. | ||
| 73 | IcqModel ToIcq(const RadialShape& shape); | ||
| 74 | |||
| 75 | } // namespace terraingen | ||
| 76 |