opynsim
Unofficial C++ Documentation
Loading...
Searching...
No Matches
recalculate_wrap_cylinder_xyz_body_rotation_from_station_scaling_step.h
Go to the documentation of this file.
1#pragma once
2
9
11#include <OpenSim/Simulation/Model/Station.h>
12#include <OpenSim/Simulation/Wrap/WrapCylinder.h>
13#include <SimTKcommon/internal/Rotation.h>
14#include <SimTKcommon/internal/Transform.h>
15#include <SimTKcommon/internal/UnitVec.h>
16#include <SimTKcommon/SmallMatrix.h>
17
18#include <cmath>
19#include <sstream>
20#include <string>
21#include <utility>
22#include <vector>
23
24namespace OpenSim { class Model; }
25
26namespace opyn
27{
30 public:
31 OpenSim_DECLARE_PROPERTY(station_path, std::string, "Absolute path (e.g. `/componentset/some_station`) to a `Station` component in the model");
32 OpenSim_DECLARE_PROPERTY(wrap_cylinder_path, std::string, "Absolute path (e.g. `/bodyset/body/wrap_cylinder_2`) to a `WrapCylinder` component in the model");
33
35 ScalingStep{"Recalculate WrapCylinder 'xyz_body_rotation' from Station Placed Along Its Midline"}
36 {
37 setDescription("Recalculates the 'xyz_body_rotation' of a `WrapCylinder` component, located at `wrap_cylinder_path` such that the cylinder's +Z direction (midline) points toward the Station component located at `station_path`");
38 constructProperty_station_path("");
39 constructProperty_wrap_cylinder_path("");
40 }
41 private:
42 std::vector<ScalingStepValidationMessage> implValidate(
44 const ScalingParameters&,
45 const OpenSim::Model& model) const final
46 {
47 std::vector<ScalingStepValidationMessage> messages;
48
49 // Ensure `station_path` exists in the model (and is a `Station`)
50 const auto* station = FindComponent<OpenSim::Station>(model, get_station_path());
51 if (not station) {
52 std::stringstream msg;
53 msg << get_station_path() << ": Cannot find `station_path` in the source model (or it isn't a Station).";
54 messages.emplace_back(ScalingStepValidationState::Error, std::move(msg).str());
55 }
56
57 // Ensure `wrap_cylinder_path` exists in the model (and is a `WrapCylinder`)
58 const auto* wrapCylinder = FindComponent<OpenSim::WrapCylinder>(model, get_wrap_cylinder_path());
59 if (not wrapCylinder) {
60 std::stringstream msg;
61 msg << get_wrap_cylinder_path() << ": Cannot find 'wrap_cylinder_path' in the source model (or it isn't a `WrapCylinder`).";
62 messages.emplace_back(ScalingStepValidationState::Error, std::move(msg).str());
63 }
64
65 return messages;
66 }
67
70 const ScalingParameters&,
71 const OpenSim::Model&,
72 OpenSim::Model& resultModel) const final
73 {
74 using std::acos;
75 using std::abs;
76
77 const auto* station = FindComponent<OpenSim::Station>(resultModel, get_station_path());
78 OSC_ASSERT_ALWAYS(station && "could not find a station in the model");
79 auto* wrapCylinder = FindComponentMut<OpenSim::WrapCylinder>(resultModel, get_wrap_cylinder_path());
80 OSC_ASSERT_ALWAYS(wrapCylinder && "could not find a wrap cylinder in the model");
81
82 // Re-express the station in the cylinder's reference frame
83 const SimTK::Transform cylinder2cylinderFrame = wrapCylinder->getTransform();
84 const SimTK::Transform cylinderFrame2ground = wrapCylinder->getFrame().getTransformInGround(resultModel.getWorkingState());
85 const SimTK::Transform ground2cylinder = (cylinderFrame2ground * cylinder2cylinderFrame).invert();
86 const SimTK::Vec3 stationPosInCylinder = ground2cylinder * station->getLocationInGround(resultModel.getWorkingState());
87
88 // Calculate the source/target cylinder midline directions.
89 const SimTK::UnitVec3 cylinderDirectionInCylinder{SimTK::CoordinateAxis::ZCoordinateAxis{}};
90 const SimTK::UnitVec3 stationDirectionInCylinder{stationPosInCylinder};
91
92 // Edge-case: If the station is pointing along the Z axis, no reorientation is necessary
93 const auto cosRotationAngle = SimTK::dot(cylinderDirectionInCylinder, stationDirectionInCylinder);
94 if (abs(cosRotationAngle) >= 1.0 - SimTK::Eps) {
95 return; // Nothing to do
96 }
97
98 // Else: If the station isn't pointing along the Z axis (usual case), calculate a rotation that
99 // rotates the cylinder's coordinate space so that +Z points toward toward the station.
100 const auto rotationAngle = acos(cosRotationAngle);
101 const SimTK::UnitVec3 rotationAxis{SimTK::cross(cylinderDirectionInCylinder, stationDirectionInCylinder)};
102 const SimTK::Rotation rotation{rotationAngle, rotationAxis};
103
104 // Compose the additional rotation with the original one to calculate the necessary overall rotation
105 const SimTK::Rotation newRotation = cylinder2cylinderFrame.R() * rotation;
106
107 // Write the new rotation to the `WrapCylinder`'s `xyz_body_rotation` property as Euler angles
108 wrapCylinder->set_xyz_body_rotation(newRotation.convertRotationToBodyFixedXYZ());
109 InitializeModel(resultModel);
110 InitializeState(resultModel);
111 }
112 };
113}
#define OSC_ASSERT_ALWAYS(expr)
Definition assertions.h:20
Definition recalculate_wrap_cylinder_xyz_body_rotation_from_station_scaling_step.h:28
std::vector< ScalingStepValidationMessage > implValidate(ScalingCache &, const ScalingParameters &, const OpenSim::Model &model) const final
Definition recalculate_wrap_cylinder_xyz_body_rotation_from_station_scaling_step.h:42
void implApplyScalingStep(ScalingCache &, const ScalingParameters &, const OpenSim::Model &, OpenSim::Model &resultModel) const final
Definition recalculate_wrap_cylinder_xyz_body_rotation_from_station_scaling_step.h:68
Definition scaling_cache.h:30
Definition scaling_parameters.h:14
Definition scaling_step.h:25
Definition static_component_registries.h:3
Definition component_registry.h:14
void InitializeModel(OpenSim::Model &)
SimTK::State & InitializeState(OpenSim::Model &)