opynsim
Unofficial C++ Documentation
Loading...
Searching...
No Matches
thin_plate_spline_mesh_substitution_scaling_step.h
Go to the documentation of this file.
1#pragma once
2
9
12
13#include <filesystem>
14#include <format>
15#include <optional>
16#include <string>
17#include <vector>
18
19namespace OpenSim { class Model; }
20
21namespace opyn
22{
23 // A `ThinPlateSpline` scaling step that substitutes a single new mesh in place of the
24 // original one by using the TPS translation + rotation terms to reorient/scale the mesh
25 // correctly.
28 public:
29 OpenSim_DECLARE_PROPERTY(source_mesh_component_path, std::string, "Absolute path (e.g. `/bodyset/body/geom_1`) to a mesh component in the model that should be substituted.");
30 OpenSim_DECLARE_PROPERTY(destination_mesh_file, std::string, "Filesystem path, potentially relative to the model file, to a mesh file that should be warped + substitute 'source_mesh_component_path'");
31
33 ThinPlateSplineScalingStep{"Substitute Mesh via Inverse Thin-Plate Spline (TPS) Affine Transform"}
34 {
35 setDescription("Substitutes the source mesh in the model with a new mesh file. The mesh's affine rotation and translation (i.e. frame) will be computed from the inverse of the Thin-Plate Spline (TPS) between the source and destination landmarks (i.e. it'll use them to figure out how to go _from_ the destination mesh coordinate system _to_ the source mesh coordinate system in a way that doesn't rescale or warp the destination mesh).");
36 constructProperty_source_mesh_component_path("");
37 constructProperty_destination_mesh_file("");
38 }
39
40 std::vector<ScalingStepValidationMessage> implValidate(
41 ScalingCache& scalingCache,
42 const ScalingParameters& parameters,
43 const OpenSim::Model& sourceModel) const final
44 {
45 auto messages = ThinPlateSplineScalingStep::implValidate(scalingCache, parameters, sourceModel);
46
47 // Ensure the model has a filesystem location (prerequisite).
48 const auto modelFilesystemLocation = TryFindInputFile(sourceModel);
49 if (not modelFilesystemLocation) {
50 messages.emplace_back(ScalingStepValidationState::Error, "The source model has no filesystem location.");
51 return messages;
52 }
53
54 // Ensure the `destination_mesh_filepath` can be found (relative to the model osim).
55 if (get_destination_mesh_file().empty()) {
56 messages.emplace_back(ScalingStepValidationState::Error, "`destination_mesh_file` is empty.");
57 }
58 else if (const auto destinationMeshPath = modelFilesystemLocation->parent_path() / osc::read_windows_or_unix_path_string(get_destination_mesh_file());
59 not std::filesystem::exists(destinationMeshPath)) {
60
61 messages.emplace_back(
63 std::format("{}: : Cannot find `destination_mesh_file` on filesystem", destinationMeshPath.string())
64 );
65 }
66
67 // Ensure `source_mesh_component_path` exists in the model
68 const auto* sourceMesh = FindComponent<OpenSim::Mesh>(sourceModel, get_source_mesh_component_path());
69 if (not sourceMesh) {
70 messages.emplace_back(
72 std::format("{}: Cannot find Mesh 'source_mesh_component_path' in the source model (or it isn't a Mesh).", get_source_mesh_component_path())
73 );
74 }
75
76 return messages;
77 }
78
80 ScalingCache& cache,
81 const ScalingParameters& parameters,
82 const OpenSim::Model& sourceModel,
83 OpenSim::Model& resultModel) const final
84 {
85 // Lookup/validate warping inputs.
86 const auto commonParams = calcTPSScalingStepCommonParams(parameters, sourceModel, resultModel);
87
88 // Lookup/validate warping inputs.
89 const std::optional<std::filesystem::path> modelFilesystemLocation = TryFindInputFile(resultModel);
90 OSC_ASSERT_ALWAYS(modelFilesystemLocation && "The source model has no filesystem location");
91
92 OSC_ASSERT_ALWAYS(not get_destination_mesh_file().empty());
93 const std::filesystem::path destinationMeshPath = modelFilesystemLocation->parent_path() / osc::read_windows_or_unix_path_string(get_destination_mesh_file());
94
95 OSC_ASSERT_ALWAYS(not get_source_mesh_component_path().empty());
96 const auto* sourceMesh = FindComponent<OpenSim::Geometry>(resultModel, get_source_mesh_component_path());
97 OSC_ASSERT_ALWAYS((dynamic_cast<const OpenSim::Mesh*>(sourceMesh) or dynamic_cast<const InMemoryMesh*>(sourceMesh)) && "'source_mesh_component_path' exists in the model but isn't mesh-like");
98 OSC_ASSERT_ALWAYS(sourceMesh && "could not find `source_mesh_component_path` in the model");
99
100 // TODO: everything below is a hack because manipulating models
101 // via OpenSim is like pulling teeth, underwater, from a shark,
102 // with rabies, using your bare hands.
103
104 const SimTK::Transform t = cache.lookupTPSAffineTransformWithoutScaling(commonParams.tpsInputs);
105 const SimTK::Vec3 newScaleFactors =
106 (commonParams.tpsInputs.destinationLandmarksPrescale/commonParams.tpsInputs.sourceLandmarksPrescale) * sourceMesh->get_scale_factors();
107
108 // Find existing mesh
109 auto* destinationMesh = FindComponentMut<OpenSim::Geometry>(resultModel, get_source_mesh_component_path());
110 OSC_ASSERT_ALWAYS(destinationMesh && "could not find `source_mesh_component_path` in the result model");
111 OSC_ASSERT_ALWAYS((dynamic_cast<const OpenSim::Mesh*>(destinationMesh) or dynamic_cast<const InMemoryMesh*>(destinationMesh)) && "'source_mesh_component_path' exists in the model but isn't mesh-like");
112 const std::string existingMeshName = destinationMesh->getName();
113 // Figure out existing mesh's frame
114 auto* oldParentFrame = FindComponentMut<OpenSim::PhysicalFrame>(resultModel, destinationMesh->getFrame().getAbsolutePath());
115 OSC_ASSERT_ALWAYS(oldParentFrame);
116 // Delete existing mesh from model
117 OSC_ASSERT_ALWAYS(TryDeleteComponentFromModel(resultModel, *destinationMesh));
118 InitializeModel(resultModel);
119 InitializeState(resultModel);
120
121 // Add new frame + mesh to the model
122 auto* newFrame = new OpenSim::PhysicalOffsetFrame{*oldParentFrame, t.invert()};
123 newFrame->updSocket("parent").setConnecteePath(oldParentFrame->getAbsolutePathString());
124 resultModel.addComponent(newFrame);
125
126 auto newMesh = std::make_unique<OpenSim::Mesh>();
127 newMesh->setName(existingMeshName);
128 newMesh->set_scale_factors(newScaleFactors);
129 newMesh->set_mesh_file(destinationMeshPath.string());
130 newMesh->updSocket("frame").setConnecteePath(newFrame->getAbsolutePathString());
131 resultModel.addComponent(newMesh.release());
132
133 InitializeModel(resultModel);
134 InitializeState(resultModel);
135 }
136 };
137}
#define OSC_ASSERT_ALWAYS(expr)
Definition assertions.h:20
Definition in_memory_mesh.h:15
Definition scaling_cache.h:31
Definition scaling_parameters.h:14
Definition thin_plate_spline_mesh_substitution_scaling_step.h:26
void implApplyScalingStep(ScalingCache &cache, const ScalingParameters &parameters, const OpenSim::Model &sourceModel, OpenSim::Model &resultModel) const final
Definition thin_plate_spline_mesh_substitution_scaling_step.h:79
std::vector< ScalingStepValidationMessage > implValidate(ScalingCache &scalingCache, const ScalingParameters &parameters, const OpenSim::Model &sourceModel) const final
Definition thin_plate_spline_mesh_substitution_scaling_step.h:40
Definition thin_plate_spline_scaling_step.h:29
std::vector< ScalingStepValidationMessage > implValidate(ScalingCache &, const ScalingParameters &, const OpenSim::Model &sourceModel) const override
Definition thin_plate_spline_scaling_step.h:72
CommonParameters calcTPSScalingStepCommonParams(const ScalingParameters &parameters, const OpenSim::Model &sourceModel, const OpenSim::Model &resultModel) const
Definition thin_plate_spline_scaling_step.h:126
Definition static_component_registries.h:3
Definition component_registry.h:14
void InitializeModel(OpenSim::Model &)
std::optional< std::filesystem::path > TryFindInputFile(const OpenSim::Model &)
bool TryDeleteComponentFromModel(OpenSim::Model &, OpenSim::Component &)
bool empty(const OpenSim::Set< T, C > &s)
Definition open_sim_helpers.h:149
SimTK::State & InitializeState(OpenSim::Model &)
std::filesystem::path read_windows_or_unix_path_string(const std::string &)