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
11
12#include <filesystem>
13#include <optional>
14#include <sstream>
15#include <string>
16#include <utility>
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() / get_destination_mesh_file();
59 not std::filesystem::exists(destinationMeshPath)) {
60
61 std::stringstream msg;
62 msg << destinationMeshPath.string() << ": Cannot find `destination_mesh_file` on filesystem";
63 messages.emplace_back(ScalingStepValidationState::Error, std::move(msg).str());
64 }
65
66 // Ensure `source_mesh_component_path` exists in the model
67 const auto* sourceMesh = FindComponent<OpenSim::Mesh>(sourceModel, get_source_mesh_component_path());
68 if (not sourceMesh) {
69 std::stringstream msg;
70 msg << get_landmarks_frame() << ": Cannot find Mesh 'source_mesh_component_path' in the source model (or it isn't a Mesh).";
71 messages.emplace_back(ScalingStepValidationState::Error, std::move(msg).str());
72 }
73
74 return messages;
75 }
76
78 ScalingCache& cache,
79 const ScalingParameters& parameters,
80 const OpenSim::Model& sourceModel,
81 OpenSim::Model& resultModel) const final
82 {
83 // Lookup/validate warping inputs.
84 const auto commonParams = calcTPSScalingStepCommonParams(parameters, sourceModel, resultModel);
85
86 // Lookup/validate warping inputs.
87 const std::optional<std::filesystem::path> modelFilesystemLocation = TryFindInputFile(resultModel);
88 OSC_ASSERT_ALWAYS(modelFilesystemLocation && "The source model has no filesystem location");
89
90 OSC_ASSERT_ALWAYS(not get_destination_mesh_file().empty());
91 const std::filesystem::path destinationMeshPath = modelFilesystemLocation->parent_path() / get_destination_mesh_file();
92
93 OSC_ASSERT_ALWAYS(not get_source_mesh_component_path().empty());
94 const auto* sourceMesh = FindComponent<OpenSim::Geometry>(resultModel, get_source_mesh_component_path());
95 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");
96 OSC_ASSERT_ALWAYS(sourceMesh && "could not find `source_mesh_component_path` in the model");
97
98 // TODO: everything below is a hack because manipulating models
99 // via OpenSim is like pulling teeth, underwater, from a shark,
100 // with rabies, using your bare hands.
101
102 const SimTK::Transform t = cache.lookupTPSAffineTransformWithoutScaling(commonParams.tpsInputs);
103 const SimTK::Vec3 newScaleFactors =
104 (commonParams.tpsInputs.destinationLandmarksPrescale/commonParams.tpsInputs.sourceLandmarksPrescale) * sourceMesh->get_scale_factors();
105
106 // Find existing mesh
107 auto* destinationMesh = FindComponentMut<OpenSim::Geometry>(resultModel, get_source_mesh_component_path());
108 OSC_ASSERT_ALWAYS(destinationMesh && "could not find `source_mesh_component_path` in the result model");
109 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");
110 const std::string existingMeshName = destinationMesh->getName();
111 // Figure out existing mesh's frame
112 auto* oldParentFrame = FindComponentMut<OpenSim::PhysicalFrame>(resultModel, destinationMesh->getFrame().getAbsolutePath());
113 OSC_ASSERT_ALWAYS(oldParentFrame);
114 // Delete existing mesh from model
115 OSC_ASSERT_ALWAYS(TryDeleteComponentFromModel(resultModel, *destinationMesh));
116 InitializeModel(resultModel);
117 InitializeState(resultModel);
118
119 // Add new frame + mesh to the model
120 auto* newFrame = new OpenSim::PhysicalOffsetFrame{*oldParentFrame, t.invert()};
121 newFrame->updSocket("parent").setConnecteePath(oldParentFrame->getAbsolutePathString());
122 resultModel.addComponent(newFrame);
123
124 auto newMesh = std::make_unique<OpenSim::Mesh>();
125 newMesh->setName(existingMeshName);
126 newMesh->set_scale_factors(newScaleFactors);
127 newMesh->set_mesh_file(destinationMeshPath.string());
128 newMesh->updSocket("frame").setConnecteePath(newFrame->getAbsolutePathString());
129 resultModel.addComponent(newMesh.release());
130
131 InitializeModel(resultModel);
132 InitializeState(resultModel);
133 }
134 };
135}
#define OSC_ASSERT_ALWAYS(expr)
Definition assertions.h:20
Definition in_memory_mesh.h:15
Definition scaling_cache.h:30
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:77
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:28
std::vector< ScalingStepValidationMessage > implValidate(ScalingCache &, const ScalingParameters &, const OpenSim::Model &sourceModel) const override
Definition thin_plate_spline_scaling_step.h:71
CommonParameters calcTPSScalingStepCommonParams(const ScalingParameters &parameters, const OpenSim::Model &sourceModel, const OpenSim::Model &resultModel) const
Definition thin_plate_spline_scaling_step.h:122
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 &)