Skip to content

Commit

Permalink
Add angular force visualization
Browse files Browse the repository at this point in the history
  • Loading branch information
adamkewley committed Jul 25, 2024
1 parent 5403ae4 commit e796c98
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 33 deletions.
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Upcoming Release]

- The model editor's/simulator's 3D visualizers now have an experimental `Show > Forces' Linear Component`
visualization option, which adds arrows indicating how each `OpenSim::Force` adds its
linear force component to each body in the model. This can be useful for debugging model
creation or `ExternalForce`s

- The model editor's/simulator's 3D visualizers now have an experimental
`Show > Forces' Linear/Rotational Component` visualization option, which adds arrows indicating
how each `OpenSim::Force` in the model applies its linear/angular force component to each
body in the model. This can be useful for debugging model creation or `ExternalForce`s
- The model editor UI now has experimental support for viewing `OpenSim::Function` curves. This
is currently exposed as an eye icon in the property editor panel (#695)
- Selecting an `OpenSim::Joint` that has `OpenSim::PhysicalOffsetFrame`s for both its parent
Expand Down
88 changes: 61 additions & 27 deletions src/OpenSimCreator/Graphics/OpenSimDecorationGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,12 +318,15 @@ namespace
};

// OSC-specific decoration handler that adds arrows to forces
void HandleForceArrows(
void HandleLinearForceArrows(
RendererState& rs,
const OpenSim::Force& force)
{
if (!rs.getOptions().getShouldShowForceLinearComponent()) {
return;
const bool showLinearForces = rs.getOptions().getShouldShowForceLinearComponent();
const bool showAngularForces = rs.getOptions().getShouldShowForceAngularComponent();

if (not showLinearForces and not showAngularForces) {
return; // nothing to draw
}

// this is a very heavy-handed way of getting the relevant information, because
Expand All @@ -333,7 +336,7 @@ namespace
const SimTK::SimbodyMatterSubsystem& matter = rs.getMatterSubsystem();
const SimTK::State& state = rs.getState();

OpenSim::ForceAdapter adapter{force};
const OpenSim::ForceAdapter adapter{force};
SimTK::Vector_<SimTK::SpatialVec> bodyForces(matter.getNumBodies(), SimTK::SpatialVec{});
SimTK::Vector_<SimTK::Vec3> particleForces(matter.getNumParticles(), SimTK::Vec3{}); // (unused)
SimTK::Vector mobilityForces(matter.getNumMobilities(), 0.0); // (unused)
Expand All @@ -346,29 +349,60 @@ namespace
);

for (SimTK::MobilizedBodyIndex bodyIdx{0}; bodyIdx < bodyForces.size(); ++bodyIdx) {
const SimTK::Vec3 translationalForce = bodyForces[bodyIdx][1];
if (equal_within_scaled_epsilon(translationalForce.normSqr(), 0.0)) {
continue; // no translational force applied
}

// else: `bodyForce` was applied to the `SimTK::MobilizedBody` at index `bodyIdx`
const SimTK::MobilizedBody& mobod = matter.getMobilizedBody(bodyIdx);
const SimTK::Transform mobod2ground = mobod.getBodyTransform(state);
// if applicable, handle drawing the linear component of force as a yellow arrow
if (showLinearForces) {
const SimTK::Vec3 linearForce = bodyForces[bodyIdx][1];
if (equal_within_scaled_epsilon(linearForce.normSqr(), 0.0)) {
continue; // no translational force applied
}

const float fixupScaleFactor = rs.getFixupScaleFactor();
// else: `bodyForce` was applied to the `SimTK::MobilizedBody` at index `bodyIdx`
const SimTK::MobilizedBody& mobod = matter.getMobilizedBody(bodyIdx);
const SimTK::Transform mobod2ground = mobod.getBodyTransform(state);

const float fixupScaleFactor = rs.getFixupScaleFactor();

const ArrowProperties props = {
.worldspace_start = ToVec3(mobod2ground * SimTK::Vec3{}),
.worldspace_end = ToVec3(mobod2ground * (fixupScaleFactor * 0.005 * linearForce)),
.tip_length = (fixupScaleFactor*0.015f),
.neck_thickness = (fixupScaleFactor*0.006f),
.head_thickness = (fixupScaleFactor*0.01f),
.color = Color::yellow(),
};
draw_arrow(rs.updMeshCache(), props, [&force, &rs](SceneDecoration&& decoration)
{
rs.consume(force, std::move(decoration));
});
}

const ArrowProperties props = {
.worldspace_start = ToVec3(mobod2ground * SimTK::Vec3{}),
.worldspace_end = ToVec3(mobod2ground * (fixupScaleFactor * 0.005 * translationalForce)),
.tip_length = (fixupScaleFactor*0.015f),
.neck_thickness = (fixupScaleFactor*0.006f),
.head_thickness = (fixupScaleFactor*0.01f),
.color = Color::yellow(),
};
draw_arrow(rs.updMeshCache(), props, [&force, &rs](SceneDecoration&& decoration)
{
rs.consume(force, std::move(decoration));
});
// if applicable, handle drawing the angular component of force as an orange arrow
if (showAngularForces) {
const SimTK::Vec3 angularForce = bodyForces[bodyIdx][0];
if (equal_within_scaled_epsilon(angularForce.normSqr(), 0.0)) {
continue; // no translational force applied
}

// else: `bodyForce` was applied to the `SimTK::MobilizedBody` at index `bodyIdx`
const SimTK::MobilizedBody& mobod = matter.getMobilizedBody(bodyIdx);
const SimTK::Transform mobod2ground = mobod.getBodyTransform(state);

const float fixupScaleFactor = rs.getFixupScaleFactor();

const ArrowProperties props = {
.worldspace_start = ToVec3(mobod2ground * SimTK::Vec3{}),
.worldspace_end = ToVec3(mobod2ground * (fixupScaleFactor * 0.005 * angularForce)),
.tip_length = (fixupScaleFactor*0.015f),
.neck_thickness = (fixupScaleFactor*0.006f),
.head_thickness = (fixupScaleFactor*0.01f),
.color = Color::orange(),
};
draw_arrow(rs.updMeshCache(), props, [&force, &rs](SceneDecoration&& decoration)
{
rs.consume(force, std::move(decoration));
});
}
}
}

Expand Down Expand Up @@ -1060,7 +1094,7 @@ void osc::GenerateSubcomponentDecorations(
}
else if (const auto* const p2p = dynamic_cast<const OpenSim::PointToPointSpring*>(&c); p2p && opts.getShouldShowPointToPointSprings())
{
HandleForceArrows(rendererState, *p2p);
HandleLinearForceArrows(rendererState, *p2p);
HandlePointToPointSpring(rendererState, *p2p);
}
else if (typeid(c) == typeid(OpenSim::Station))
Expand All @@ -1074,7 +1108,7 @@ void osc::GenerateSubcomponentDecorations(
}
else if (const auto* const hcf = dynamic_cast<const OpenSim::HuntCrossleyForce*>(&c))
{
HandleForceArrows(rendererState, *hcf);
HandleLinearForceArrows(rendererState, *hcf);
HandleHuntCrossleyForce(rendererState, *hcf);
}
else if (const auto* const geom = dynamic_cast<const OpenSim::Geometry*>(&c))
Expand All @@ -1087,7 +1121,7 @@ void osc::GenerateSubcomponentDecorations(
}
else if (const auto* const force = dynamic_cast<const OpenSim::Force*>(&c))
{
HandleForceArrows(rendererState, *force);
HandleLinearForceArrows(rendererState, *force);
rendererState.emitGenericDecorations(c, c);
}
else
Expand Down
8 changes: 7 additions & 1 deletion src/OpenSimCreator/Graphics/OpenSimDecorationOptionFlags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,13 @@ namespace
{
"show_force_linear_component",
"Forces' Linear Component (EXPERIMENTAL)",
"Tries to draw the linear component applies by each `OpenSim::Force` in the model.\n\nEXPERIMENTAL: this currently iterates through all the forces and extracts their linear component w.r.t. the body frame, it's probably slow, and probably noisy, but also probably still useful to know (e.g. if you're debugging weird model behavior)",
"Tries to draw the linear component applied by each `OpenSim::Force` in the model.\n\nEXPERIMENTAL: this currently iterates through all the forces and extracts their linear component w.r.t. the body frame, it's probably slow, and probably noisy, but also probably still useful to know (e.g. if you're debugging weird model behavior)",
},
OpenSimDecorationOptionMetadata
{
"show_force_angular_component",
"Forces' Angular Component (EXPERIMENTAL)",
"Tries to draw the angular component applied by each `OpenSim::Force` in the model.\n\nEXPERIMENTAL: this currently iterates through all the forces and extracts their angular component w.r.t. the body frame, it's probably slow, and probably noisy, but also probably still useful to know (e.g. if you're debugging weird model behavior)",
},
});

Expand Down
3 changes: 2 additions & 1 deletion src/OpenSimCreator/Graphics/OpenSimDecorationOptionFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ namespace osc
ShouldShowPointToPointSprings = 1<<6,
ShouldShowContactForces = 1<<7,
ShouldShowForceLinearComponent = 1<<8,
NUM_FLAGS = 9,
ShouldShowForceAngularComponent = 1<<9,
NUM_FLAGS = 10,

Default = ShouldShowPointToPointSprings,
};
Expand Down
10 changes: 10 additions & 0 deletions src/OpenSimCreator/Graphics/OpenSimDecorationOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,16 @@ void osc::OpenSimDecorationOptions::setShouldShowForceLinearComponent(bool v)
SetOption(m_Flags, OpenSimDecorationOptionFlags::ShouldShowForceLinearComponent, v);
}

bool osc::OpenSimDecorationOptions::getShouldShowForceAngularComponent() const
{
return m_Flags & OpenSimDecorationOptionFlags::ShouldShowForceAngularComponent;
}

void osc::OpenSimDecorationOptions::getShouldShowForceAngularComponent(bool v)
{
SetOption(m_Flags, OpenSimDecorationOptionFlags::ShouldShowForceAngularComponent, v);
}

void osc::OpenSimDecorationOptions::forEachOptionAsAppSettingValue(const std::function<void(std::string_view, const Variant&)>& callback) const
{
callback("muscle_decoration_style", GetMuscleDecorationStyleMetadata(m_MuscleDecorationStyle).id);
Expand Down
3 changes: 3 additions & 0 deletions src/OpenSimCreator/Graphics/OpenSimDecorationOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ namespace osc
bool getShouldShowForceLinearComponent() const;
void setShouldShowForceLinearComponent(bool);

bool getShouldShowForceAngularComponent() const;
void getShouldShowForceAngularComponent(bool);

void forEachOptionAsAppSettingValue(const std::function<void(std::string_view, const Variant&)>&) const;
void tryUpdFromValues(std::string_view keyPrefix, const std::unordered_map<std::string, Variant>&);

Expand Down

0 comments on commit e796c98

Please sign in to comment.