Skip to content

Commit db9c8ea

Browse files
committed
Add decorative path point interface to AbstractGeometryPath and downsample Scholz2015GeometryPath decorative curve points
1 parent 448a807 commit db9c8ea

7 files changed

Lines changed: 221 additions & 43 deletions

File tree

OpenSim/Simulation/Model/AbstractGeometryPath.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include <OpenSim/Simulation/Model/ForceApplier.h>
2727
#include <OpenSim/Simulation/Model/Model.h>
2828

29+
#include <optional>
30+
2931
using namespace OpenSim;
3032

3133
//=============================================================================
@@ -91,3 +93,71 @@ void AbstractGeometryPath::setPreScaleLength(const SimTK::State&,
9193
{
9294
_preScaleLength = preScaleLength;
9395
}
96+
97+
void AbstractGeometryPath::forEachDecorativePathPoint(
98+
const SimTK::State& state,
99+
const std::function<void(const DecorativePathPoint&)>& callback) const
100+
{
101+
implForEachDecorativePathPoint(state, callback);
102+
}
103+
104+
std::vector<AbstractGeometryPath::DecorativePathPoint>
105+
AbstractGeometryPath::getDecorativePathPoints(const SimTK::State& state) const
106+
{
107+
std::vector<AbstractGeometryPath::DecorativePathPoint> rv;
108+
forEachDecorativePathPoint(state,
109+
[&rv](const DecorativePathPoint& dp) { rv.push_back(dp); });
110+
return rv;
111+
}
112+
113+
void AbstractGeometryPath::generateDecorations(bool fixed,
114+
const ModelDisplayHints& hints, const SimTK::State& s,
115+
SimTK::Array_<SimTK::DecorativeGeometry>& geoms) const
116+
{
117+
if (fixed) {
118+
return;
119+
}
120+
if (not get_Appearance().get_visible()) {
121+
// Don't render a path that's hidden
122+
// (ComputationalBiomechanicsLab/opensim-creator#1166)
123+
return;
124+
}
125+
126+
const bool showPathPoints = hints.get_show_path_points();
127+
const SimTK::Vec3 color = getColor(s);
128+
const double opacity = get_Appearance().get_opacity();
129+
const auto representation = get_Appearance().get_representation();
130+
131+
int index = 0;
132+
std::optional<DecorativePathPoint> previous;
133+
forEachDecorativePathPoint(s, [&](const DecorativePathPoint& dpp)
134+
{
135+
if (previous) {
136+
// Emit line between points
137+
const SimTK::Vec3& p1 = previous->getLocationInGround();
138+
const SimTK::Vec3& p2 = dpp.getLocationInGround();
139+
geoms.push_back(SimTK::DecorativeLine(p1, p2)
140+
.setLineThickness(4)
141+
.setScaleFactors(SimTK::Vec3{1.0})
142+
.setColor(color)
143+
.setOpacity(opacity)
144+
.setRepresentation(representation)
145+
.setBodyId(0)
146+
.setIndexOnBody(index++)
147+
);
148+
}
149+
if (showPathPoints) {
150+
geoms.push_back(SimTK::DecorativeSphere(0.005)
151+
.setTransform(dpp.getLocationInGround())
152+
.setScaleFactors(SimTK::Vec3{1.0})
153+
.setColor(color)
154+
.setOpacity(opacity)
155+
.setRepresentation(representation)
156+
.setBodyId(0)
157+
.setIndexOnBody(index++)
158+
);
159+
}
160+
161+
previous = dpp;
162+
});
163+
}

OpenSim/Simulation/Model/AbstractGeometryPath.h

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
#include <OpenSim/Simulation/Model/ModelComponent.h>
2828
#include <OpenSim/Simulation/Model/Appearance.h>
2929

30+
#include <functional>
31+
#include <vector>
32+
3033
#ifdef SWIG
3134
#ifdef OSIMSIMULATION_API
3235
#undef OSIMSIMULATION_API
@@ -229,8 +232,86 @@ OpenSim_DECLARE_ABSTRACT_OBJECT(AbstractGeometryPath, ModelComponent);
229232
*/
230233
double getPreScaleLength(const SimTK::State& s) const;
231234
void setPreScaleLength(const SimTK::State& s, double preScaleLength);
232-
235+
236+
/**
237+
* Represents a decorative (i.e. visualization-only) point of an
238+
* `AbstractGeometryPath`. Can be used by UI implementations to
239+
* display the path.
240+
*/
241+
class DecorativePathPoint final {
242+
public:
243+
explicit DecorativePathPoint(
244+
const SimTK::Vec3& locationInGround,
245+
const Component* associatedComponent = nullptr) :
246+
m_locationInGround{locationInGround},
247+
m_maybeAssociatedComponent{associatedComponent}
248+
{}
249+
250+
SimTK::Vec3 getLocationInGround() const { return m_locationInGround; }
251+
void setLocationInGround(const SimTK::Vec3& p) {
252+
m_locationInGround = p;
253+
}
254+
const Component* getAssociatedComponent() const {
255+
return m_maybeAssociatedComponent;
256+
}
257+
private:
258+
SimTK::Vec3 m_locationInGround;
259+
const Component* m_maybeAssociatedComponent;
260+
};
261+
262+
/**
263+
* Calls `callback` with each point of a decorative representation of the
264+
* path, if such a representation is available.
265+
*
266+
* @param s Current simulation state, used to read the state of this
267+
* path.
268+
* @param callback A callback that should be called with each point of the
269+
* decorative path.
270+
*/
271+
void forEachDecorativePathPoint(const SimTK::State& state,
272+
const std::function<void(const DecorativePathPoint&)>& callback) const;
273+
274+
/**
275+
* Get a vector of decorative path points.
276+
*
277+
* @param s Current simulation state, used to read the state of this path.
278+
*/
279+
std::vector<DecorativePathPoint> getDecorativePathPoints(
280+
const SimTK::State& s) const;
281+
282+
protected:
283+
/**
284+
* Generates default decorations for this `AbstractGeometryPath`, based on
285+
* the decorative points emitted by `implForEachDecorativePathPoint`.
286+
*
287+
* Derived classes may override this to provide different display behavior
288+
* for the path.
289+
*/
290+
void generateDecorations(bool fixed, const ModelDisplayHints&,
291+
const SimTK::State&,
292+
SimTK::Array_<SimTK::DecorativeGeometry>&) const override;
293+
233294
private:
295+
/**
296+
* Implementors should call `callback` with each point of a decorative
297+
* representation of the path.
298+
*
299+
* The decorative representation does not necessarily need to be the same
300+
* as the computational representation: it only needs to be a "suitable"
301+
* visual representation of the path (if any).
302+
*
303+
* @param s Current simulation state, used to read the state of this
304+
* path.
305+
* @param callback A callback that should be called with each point of the
306+
* decorative path.
307+
*/
308+
virtual void implForEachDecorativePathPoint(const SimTK::State& s,
309+
const std::function<void(const DecorativePathPoint&)>& callback) const
310+
{
311+
// No-op: implementations are also permitted to provide no decorative
312+
// point representation at all.
313+
}
314+
234315
// Used by `(get|set)PreLengthScale`. Used during `extend(Pre|Post)Scale` by
235316
// downstream users of AbstractGeometryPath to cache the length of the path
236317
// before scaling.

OpenSim/Simulation/Model/GeometryPath.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,35 @@ void GeometryPath::extendFinalizeFromProperties()
177177
}
178178
}
179179

180+
void GeometryPath::implForEachDecorativePathPoint(
181+
const SimTK::State& state,
182+
const std::function<void(const DecorativePathPoint&)>& callback) const
183+
{
184+
const Array<AbstractPathPoint*>& pathPoints = getCurrentPath(state);
185+
186+
for (int i = 0; i < pathPoints.size(); ++i) {
187+
const AbstractPathPoint& p = *pathPoints[i];
188+
189+
if (auto* pwp = dynamic_cast<const PathWrapPoint*>(&p)) {
190+
// A `PathWrapPoint`'s surface points are expressed w.r.t. the wrap
191+
// surface's body frame. Ensure they're transformed to ground.
192+
const SimTK::Transform& X_BG =
193+
pwp->getParentFrame().getTransformInGround(state);
194+
195+
// A `PathWrapPoint`'s surface points should be emitted, but not
196+
// associated to a component in the model (they are synthetic).
197+
const Array<Vec3>& surfacePoints = pwp->getWrapPath(state);
198+
199+
for (int j = 0; j < surfacePoints.getSize(); ++j) {
200+
callback(DecorativePathPoint{X_BG * surfacePoints[j]});
201+
}
202+
}
203+
else { // Otherwise, it's a regular `PathPoint`, so just emit it.
204+
callback(DecorativePathPoint{p.getLocationInGround(state), &p});
205+
}
206+
}
207+
}
208+
180209
void GeometryPath::extendConnectToModel(Model& aModel)
181210
{
182211
Super::extendConnectToModel(aModel);

OpenSim/Simulation/Model/GeometryPath.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ OpenSim_DECLARE_CONCRETE_OBJECT(GeometryPath, AbstractGeometryPath);
204204
void extendFinalizeFromProperties() override;
205205

206206
private:
207+
void implForEachDecorativePathPoint(const SimTK::State&,
208+
const std::function<void(const DecorativePathPoint&)>&) const override;
207209

208210
void computePath(const SimTK::State& s ) const;
209211
void computeLengtheningSpeed(const SimTK::State& s) const;

OpenSim/Simulation/Model/Scholz2015GeometryPath.cpp

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
#include <OpenSim/Simulation/SimbodyEngine/Coordinate.h>
2929
#include <OpenSim/Simulation/Model/Model.h>
3030

31-
#include <optional>
32-
3331
using namespace OpenSim;
3432

3533
//=============================================================================
@@ -189,6 +187,15 @@ int Scholz2015GeometryPath::getNumPathElements() const {
189187
return getProperty_path_elements().size();
190188
}
191189

190+
void Scholz2015GeometryPath::setNumDecorativeCurvePoints(
191+
int numDecorativeCurvePoints) {
192+
set_num_decorative_curve_points(numDecorativeCurvePoints);
193+
}
194+
195+
int Scholz2015GeometryPath::getNumDecorativeCurvePoints() const {
196+
return get_num_decorative_curve_points();
197+
}
198+
192199
//=============================================================================
193200
// ABSTRACT PATH INTERFACE
194201
//=============================================================================
@@ -212,6 +219,16 @@ double Scholz2015GeometryPath::computeMomentArm(const SimTK::State& s,
212219
return _maSolver->solve(s, coord, *this);
213220
}
214221

222+
void Scholz2015GeometryPath::implForEachDecorativePathPoint(
223+
const SimTK::State& state,
224+
const std::function<void(const DecorativePathPoint&)>& callback) const
225+
{
226+
getCableSpan().calcResampledDecorativePathPoints(
227+
state, get_num_decorative_curve_points(),
228+
[&callback](SimTK::Vec3 p) { callback(DecorativePathPoint{p});
229+
});
230+
}
231+
215232
void Scholz2015GeometryPath::produceForces(const SimTK::State& state,
216233
double tension, ForceConsumer& forceConsumer) const {
217234

@@ -329,47 +346,12 @@ void Scholz2015GeometryPath::extendAddToSystem(
329346
_index = cable.getIndex();
330347
}
331348

332-
void Scholz2015GeometryPath::generateDecorations(
333-
bool fixed,
334-
const ModelDisplayHints& hints,
335-
const SimTK::State& s,
336-
SimTK::Array_<SimTK::DecorativeGeometry>& geoms) const {
337-
338-
if (fixed) { return; }
339-
const bool showPathPoints = hints.get_show_path_points();
340-
const SimTK::Vec3 color = getColor(s);
341-
int index = 0;
342-
std::optional<SimTK::Vec3> previous;
343-
getCableSpan().calcDecorativePathPoints(s, [&](SimTK::Vec3 x_G) {
344-
if (previous) {
345-
// Emit line between points
346-
geoms.push_back(SimTK::DecorativeLine(*previous, x_G)
347-
.setLineThickness(4)
348-
.setScaleFactors(SimTK::Vec3{1.0})
349-
.setColor(color)
350-
.setBodyId(0)
351-
.setIndexOnBody(index++)
352-
);
353-
}
354-
if (showPathPoints) {
355-
geoms.push_back(SimTK::DecorativeSphere(0.005)
356-
.setTransform(x_G)
357-
.setScaleFactors(SimTK::Vec3{1.0})
358-
.setColor(color)
359-
.setBodyId(0)
360-
.setIndexOnBody(index++)
361-
);
362-
}
363-
364-
previous = x_G;
365-
});
366-
}
367-
368349
//=============================================================================
369350
// CONVENIENCE METHODS
370351
//=============================================================================
371352
void Scholz2015GeometryPath::constructProperties() {
372353
constructProperty_path_elements();
354+
constructProperty_num_decorative_curve_points(5);
373355
}
374356

375357
const Scholz2015GeometryPathObstacle* Scholz2015GeometryPath::getObstacle(

OpenSim/Simulation/Model/Scholz2015GeometryPath.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,18 @@ OpenSim_DECLARE_CONCRETE_OBJECT(Scholz2015GeometryPath, AbstractGeometryPath);
421421
*/
422422
int getNumPathElements() const;
423423

424+
/**
425+
* Set the number of decorative path points per curve segment used when
426+
* generating path decorations.
427+
*/
428+
void setNumDecorativeCurvePoints(int numDecorativeCurvePoints);
429+
430+
/**
431+
* Get the number of decorative path points per curve segment used when
432+
* generating path decorations (default: 5).
433+
*/
434+
int getNumDecorativeCurvePoints() const;
435+
424436
// @}
425437

426438
//** @name `AbstractGeometryPath` interface */
@@ -430,6 +442,8 @@ OpenSim_DECLARE_CONCRETE_OBJECT(Scholz2015GeometryPath, AbstractGeometryPath);
430442
double computeMomentArm(const SimTK::State& s,
431443
const Coordinate& coord) const override;
432444
bool isVisualPath() const override { return true; }
445+
void implForEachDecorativePathPoint(const SimTK::State&,
446+
const std::function<void(const DecorativePathPoint&)>&) const override;
433447
// @}
434448

435449
//** @name `ForceProducer` interface */
@@ -442,13 +456,13 @@ OpenSim_DECLARE_CONCRETE_OBJECT(Scholz2015GeometryPath, AbstractGeometryPath);
442456
// PROPERTIES
443457
OpenSim_DECLARE_LIST_PROPERTY(path_elements, Scholz2015GeometryPathElement,
444458
"The list of elements (path points or obstacles) defining the path.");
459+
OpenSim_DECLARE_PROPERTY(num_decorative_curve_points, int,
460+
"The number of decorative path points per curve segment used when "
461+
"generating path decorations (default: 5).");
445462

446463
// MODEL COMPONENT INTERFACE
447464
void extendConnectToModel(Model& model) override;
448465
void extendAddToSystem(SimTK::MultibodySystem& system) const override;
449-
void generateDecorations(bool fixed, const ModelDisplayHints& hints,
450-
const SimTK::State& s,
451-
SimTK::Array_<SimTK::DecorativeGeometry>& geoms) const override;
452466

453467
// CONVENIENCE METHODS
454468
void constructProperties();

dependencies/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ AddDependency(NAME ezc3d
185185
AddDependency(NAME simbody
186186
DEFAULT ON
187187
GIT_URL https://github.com/simbody/simbody.git
188-
GIT_TAG 7f35622b3c5daac919fc39a2865498c03c553e53
188+
GIT_TAG 8a62e27882838708ae98e72c6902704f836aacb9
189189
CMAKE_ARGS -DBUILD_EXAMPLES:BOOL=OFF
190190
-DBUILD_TESTING:BOOL=OFF
191191
${SIMBODY_EXTRA_CMAKE_ARGS})

0 commit comments

Comments
 (0)