1 /* -------------------------------------------------------------------------- *
2  *                               Simbody(tm)                                  *
3  * -------------------------------------------------------------------------- *
4  * This is part of the SimTK biosimulation toolkit originating from           *
5  * Simbios, the NIH National Center for Physics-Based Simulation of           *
6  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
7  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
8  *                                                                            *
9  * Portions copyright (c) 2010-14 Stanford University and the Authors.        *
10  * Authors: Peter Eastman, Michael Sherman                                    *
11  * Contributors:                                                              *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 #include "simbody/internal/common.h"
25 #include "simbody/internal/MobilizedBody.h"
26 #include "simbody/internal/MultibodySystem.h"
27 #include "simbody/internal/SimbodyMatterSubsystem.h"
28 #include "simbody/internal/Visualizer.h"
29 #include "simbody/internal/Visualizer_InputListener.h"
30 
31 #include "VisualizerGeometry.h"
32 #include "VisualizerProtocol.h"
33 
34 #include <cstdlib>
35 #include <cstdio>
36 #include <string>
37 #include <ctime>
38 #include <iostream>
39 #include <limits>
40 #include <condition_variable>
41 
42 using namespace SimTK;
43 using namespace std;
44 
45 static void drawingThreadMain(Visualizer::Impl& vizImpl);
46 
47 static const long long UsToNs = 1000LL;          // ns = us * UsToNs
48 static const long long MsToNs = 1000LL * UsToNs; // ns = ms * MsToNs
49 
50 static const Real      DefaultFrameRateFPS             = 30;
51 static const Real      DefaultDesiredBufferLengthInSec = Real(0.15); // 150ms
52 
53 // These are not currently overrideable.
54 static const long long DefaultAllowableFrameJitterInNs      = 5 * MsToNs; //5ms
55 static const Real      DefaultSlopAsFractionOfFrameInterval = Real(0.05); //5%
56 
57 namespace { // local classes
58 //==============================================================================
59 //                              VISUALIZER IMPL
60 //==============================================================================
61 /* This is the private implementation object contained in a Visualizer handle.
62 See the "implementation notes" section of the Visualizer class documentation
63 for some information about how this works. */
64 
65 // If we are buffering frames, this is the object that represents a frame
66 // in the queue. It consists of a copy of a reported State, and a desired
67 // draw time for the frame, in adjusted real time (AdjRT).
68 struct Frame {
Frame__anonab20b5810111::Frame69     Frame() : desiredDrawTimeAdjRT(-1LL) {}
Frame__anonab20b5810111::Frame70     Frame(const State& state, const long long& desiredDrawTimeAdjRT)
71     :   state(state), desiredDrawTimeAdjRT(desiredDrawTimeAdjRT) {}
72     // default copy constructor, copy assignment, destructor
73 
clear__anonab20b5810111::Frame74     void clear() {desiredDrawTimeAdjRT = -1LL;}
isValid__anonab20b5810111::Frame75     bool isValid() const {return desiredDrawTimeAdjRT >= 0LL;}
76 
77     State       state;
78     long long   desiredDrawTimeAdjRT; // in adjusted real time
79 };
80 
81 // This holds the specs for rubber band lines that are added directly
82 // to the Visualizer.
83 class RubberBandLine {
84 public:
RubberBandLine(MobilizedBodyIndex b1,const Vec3 & station1,MobilizedBodyIndex b2,const Vec3 & station2,const DecorativeLine & line)85     RubberBandLine(MobilizedBodyIndex b1, const Vec3& station1,
86                    MobilizedBodyIndex b2, const Vec3& station2,
87                    const DecorativeLine& line)
88     :   b1(b1), station1(station1), b2(b2), station2(station2), line(line) {}
89 
90     MobilizedBodyIndex  b1, b2;
91     Vec3                station1, station2;
92     DecorativeLine      line;
93 };
94 } // end local namespace
95 
96 // Implementation of the Visualizer.
97 class Visualizer::Impl {
98 public:
99     // Create a Visualizer and put it in PassThrough mode.
Impl(Visualizer * owner,const MultibodySystem & system,const Array_<String> & searchPath)100     Impl(Visualizer* owner, const MultibodySystem& system,
101          const Array_<String>& searchPath)
102     :   m_system(system), m_protocol(*owner, searchPath),
103         m_shutdownWhenDestructed(false), m_upDirection(YAxis), m_groundHeight(0),
104         m_mode(PassThrough), m_frameRateFPS(DefaultFrameRateFPS),
105         m_simTimeUnitsPerSec(1),
106         m_desiredBufferLengthInSec(DefaultDesiredBufferLengthInSec),
107         m_timeBetweenFramesInNs(secToNs(1/DefaultFrameRateFPS)),
108         m_allowableFrameJitterInNs(DefaultAllowableFrameJitterInNs),
109         m_allowableFrameTimeSlopInNs(
110             secToNs(DefaultSlopAsFractionOfFrameInterval/DefaultFrameRateFPS)),
111         m_adjustedRealTimeBase(realTimeInNs()),
112         m_prevFrameSimTime(-1), m_nextFrameDueAdjRT(-1),
113         m_oldest(0),m_nframe(0),
114         m_drawThreadIsRunning(false), m_drawThreadShouldSuicide(false),
115         m_refCount(0)
116     {
117         setMode(PassThrough);
118         clearStats();
119 
120         m_protocol.setMaxFrameRate(m_frameRateFPS);
121         m_protocol.setBackgroundColor(White);
122         m_protocol.setBackgroundType(system.getUseUniformBackground()
123                                         ? SolidColor : GroundAndSky);
124         m_protocol.setSystemUpDirection(system.getUpDirection());
125     }
126 
~Impl()127     ~Impl() {
128         if (m_mode==RealTime && m_pool.size()) {
129             killDrawThreadIfNecessary();
130         }
131         for (unsigned i = 0; i < m_controllers.size(); i++)
132             delete m_controllers[i];
133         for (unsigned i = 0; i < m_listeners.size(); i++)
134             delete m_listeners[i];
135         for (unsigned i = 0; i < m_generators.size(); i++)
136             delete m_generators[i];
137 
138         if (m_shutdownWhenDestructed) {
139             try {
140                 // This throws an exception if the pipe is broken (e.g., if the
141                 // simbody-visualizer has already been shut down).
142                 m_protocol.shutdownGUI();
143             } catch (...) {}
144         }
145     }
146 
setShutdownWhenDestructed(bool shouldShutdown)147     void setShutdownWhenDestructed(bool shouldShutdown)
148     {   m_shutdownWhenDestructed = shouldShutdown; }
149 
getShutdownWhenDestructed() const150     bool getShutdownWhenDestructed() const
151     {   return m_shutdownWhenDestructed; }
152 
153     // Call from simulation thread.
startDrawThread()154     void startDrawThread() {
155         SimTK_ASSERT_ALWAYS(!m_drawThreadIsRunning,
156             "Tried to start the draw thread when it was already running.");
157         m_drawThreadShouldSuicide = false;
158         m_drawThread = std::thread(drawingThreadMain, std::ref(*this));
159         m_drawThreadIsRunning = true;
160     }
161 
162     // Call from simulation thread.
killDrawThread()163     void killDrawThread() {
164         SimTK_ASSERT_ALWAYS(m_drawThreadIsRunning,
165             "Tried to kill the draw thread when it wasn't running.");
166         m_drawThreadShouldSuicide = true;
167         // The draw thread might be waiting on an empty queue, in which
168         // case we have to wake it up (see getOldestFrameInQueue()).
169         // We'll do it twice 100ms apart to avoid a timing issue where
170         // we signal just before the thread waits.
171         m_queueNotEmpty.notify_one(); // wake it if necessary
172         sleepInSec(0.1);
173         m_queueNotEmpty.notify_one();
174         m_drawThread.join(); // wait for death
175         m_drawThreadIsRunning = m_drawThreadShouldSuicide = false;
176     }
177 
startDrawThreadIfNecessary()178     void startDrawThreadIfNecessary()
179     {   if (!m_drawThreadIsRunning) startDrawThread(); }
180 
killDrawThreadIfNecessary()181     void killDrawThreadIfNecessary()
182     {   if (m_drawThreadIsRunning) killDrawThread(); }
183 
184     // Whenever a "set" method is called that may change one of the
185     // interrelated time quantities, set all of them. We expect
186     // the mode to have been set already.
resetTimeRelatedQuantities(Real framesPerSec,Real timeScale,Real desiredBufLengthSec)187     void resetTimeRelatedQuantities(Real framesPerSec,
188         Real timeScale, Real desiredBufLengthSec)
189     {
190         if (framesPerSec <= 0) framesPerSec = DefaultFrameRateFPS;
191         if (timeScale <= 0)    timeScale = 1;
192         if (desiredBufLengthSec < 0)
193             desiredBufLengthSec = DefaultDesiredBufferLengthInSec;
194 
195         // Frame rate.
196         m_frameRateFPS               = framesPerSec;
197         m_timeBetweenFramesInNs      = secToNs(1/m_frameRateFPS);
198         m_allowableFrameTimeSlopInNs =
199             secToNs(DefaultSlopAsFractionOfFrameInterval/m_frameRateFPS);
200         m_allowableFrameJitterInNs   = DefaultAllowableFrameJitterInNs;
201 
202         // Time scale.
203         m_simTimeUnitsPerSec = timeScale;
204 
205         // Realtime buffer.
206         m_desiredBufferLengthInSec = desiredBufLengthSec;
207 
208         int numFrames =
209             (int)(m_desiredBufferLengthInSec/nsToSec(m_timeBetweenFramesInNs)
210                   + 0.5);
211         if (numFrames==0 && m_desiredBufferLengthInSec > 0)
212             numFrames = 1;
213 
214         // If we're in RealTime mode and we have changed the number of
215         // frames in the buffer, reallocate the pool and kill or start
216         // the draw thread if necessary.
217         if (m_mode == RealTime && numFrames != m_pool.size()) {
218             if (m_pool.size()) {
219                 // draw thread isn't needed if we get rid of the buffer
220                 if (numFrames == 0)
221                     killDrawThreadIfNecessary();
222                 initializePool(numFrames);
223             } else {
224                 // draw thread is needed if we don't have one
225                 initializePool(numFrames);
226                 startDrawThreadIfNecessary();
227             }
228         }
229 
230         clearStats();
231 
232         // Note that the next frame we see is the first one and we'll need
233         // to initialize adjusted real time then.
234         m_nextFrameDueAdjRT = -1LL; // i.e., now
235     }
236 
setMode(Visualizer::Mode newMode)237     void setMode(Visualizer::Mode newMode) {
238         // If we're not changing modes we just clear the stats and invalidate
239         // the next expected frame time so that we'll take the first one that
240         // shows up.
241         if (newMode==m_mode) {
242             resetTimeRelatedQuantities(m_frameRateFPS,
243                                        m_simTimeUnitsPerSec,
244                                        m_desiredBufferLengthInSec);
245             return;
246         }
247 
248         // Mode is changing. If it was buffered RealTime before we have
249         // to clean up first.
250         if (m_mode == RealTime && m_pool.size()) {
251             killDrawThreadIfNecessary();
252             initializePool(0);  // clear the buffer
253         }
254 
255         m_mode = newMode; // change mode
256         resetTimeRelatedQuantities(m_frameRateFPS,
257                                    m_simTimeUnitsPerSec,
258                                    m_desiredBufferLengthInSec);
259     }
260 
setDesiredFrameRate(Real framesPerSec)261     void setDesiredFrameRate(Real framesPerSec) {
262         resetTimeRelatedQuantities(framesPerSec,
263                                    m_simTimeUnitsPerSec,
264                                    m_desiredBufferLengthInSec);
265         // Make sure the GUI doesn't try to outrace us when it generates
266         // its own frames.
267         m_protocol.setMaxFrameRate(framesPerSec);
268     }
269 
setDesiredBufferLengthInSec(Real bufferLengthInSec)270     void setDesiredBufferLengthInSec(Real bufferLengthInSec) {
271         resetTimeRelatedQuantities(m_frameRateFPS,
272                                    m_simTimeUnitsPerSec,
273                                    bufferLengthInSec);
274     }
275 
setRealTimeScale(Real simTimePerRealSec)276     void setRealTimeScale(Real simTimePerRealSec)  {
277         resetTimeRelatedQuantities(m_frameRateFPS,
278                                    simTimePerRealSec,
279                                    m_desiredBufferLengthInSec);
280     }
281 
getDesiredBufferLengthInSec() const282     Real getDesiredBufferLengthInSec() const
283     {   return m_desiredBufferLengthInSec; }
284 
getActualBufferLengthInFrames() const285     int getActualBufferLengthInFrames() const {return m_pool.size();}
getActualBufferLengthInSec() const286     Real getActualBufferLengthInSec() const
287     {   return (Real)nsToSec(getActualBufferLengthInFrames()
288                              *m_timeBetweenFramesInNs); }
289 
290     // Generate this frame and send it immediately to the renderer without
291     // thinking too hard about it.
292     void drawFrameNow(const State& state);
293 
294     // In RealTime mode we have a frame to draw and a desired draw time in
295     // AdjRT. Draw it when the time comes, and adjust AdjRT if necessary.
296     void drawRealtimeFrameWhenReady
297        (const State& state, const long long& desiredDrawTimeAdjRT);
298 
299     // Queuing is used only in RealTime mode.
300 
301     // Called from the simulation thread when it wants to report a frame
302     // and we are in RealTime mode.
303     void reportRealtime(const State& state);
304 
305     // Set the maximum number of frames in the buffer.
initializePool(int sz)306     void initializePool(int sz) {
307         std::lock_guard<std::mutex> lock(m_queueMutex);
308         m_pool.resize(sz);m_oldest=m_nframe=0;
309     }
310 
getNFramesInQueue() const311     int getNFramesInQueue() const {return m_nframe;}
312 
313     // Queing is enabled if the pool was allocated.
queuingIsEnabled() const314     bool queuingIsEnabled() const {return m_pool.size() != 0;}
queueIsFull() const315     bool queueIsFull() const {return m_nframe==m_pool.size();}
queueIsEmpty() const316     bool queueIsEmpty() const {return m_nframe==0;}
317 
318     // Called from simulation thread. Blocks until there is room in
319     // the queue, then inserts this state unconditionally, with the indicated
320     // desired rendering time in adjusted real time. We then update the
321     // "time of next queue slot" to be one ideal frame interval later than
322     // the desired draw time.
addFrameToQueueWithWait(const State & state,const long long & desiredDrawTimeAdjRT)323     void addFrameToQueueWithWait(const State& state,
324                                  const long long& desiredDrawTimeAdjRT)
325     {
326         std::unique_lock<std::mutex> lock(m_queueMutex);
327         ++numReportedFramesThatWereQueued;
328         if (queueIsFull()) {
329             ++numQueuedFramesThatHadToWait;
330             // atomic: unlock, long wait, relock
331             // Only wake up if queue is not full (ignore spurious wakeups).
332             m_queueNotFull.wait(lock, [&] {return !queueIsFull();});
333         }
334 
335         // There is room in the queue now. We're holding the lock.
336         Frame& frame = m_pool[(m_oldest+m_nframe)%m_pool.size()];
337         frame.state  = state;
338         frame.desiredDrawTimeAdjRT = desiredDrawTimeAdjRT;
339 
340         // Record the frame time.
341         m_prevFrameSimTime = state.getTime();
342 
343         // Set the expected next frame time (in AdjRT).
344         m_nextFrameDueAdjRT = desiredDrawTimeAdjRT + m_timeBetweenFramesInNs;
345 
346         if (++m_nframe == 1)
347             // wake up rendering thread on first frame
348             m_queueNotEmpty.notify_one();
349 
350         lock.unlock();
351     }
352 
353     // Call from simulation thread to allow the drawing thread to flush
354     // any frames currently in the queue.
waitUntilQueueIsEmpty()355     void waitUntilQueueIsEmpty() {
356         if (   !queuingIsEnabled() || m_nframe==0
357             || !m_drawThreadIsRunning || m_drawThreadShouldSuicide)
358             return;
359         std::unique_lock<std::mutex> lock(m_queueMutex);
360         m_queueIsEmpty.wait(lock, [&] {return m_nframe == 0;});
361         lock.unlock();
362     }
363 
364     // The drawing thread uses this to find the oldest frame in the buffer.
365     // It may then at its leisure use the contained State to generate a screen
366     // image. There is no danger of the simulation thread modifying this
367     // frame; once it has been put in it stays there until the drawing thread
368     // takes it out. When done it should return the frame to the pool.
369     // Returns true if it gets a frame (which will always happen in normal
370     // operation since it waits until one is available), false if the draw
371     // thread should quit.
getOldestFrameInQueue(const Frame ** fp)372     bool getOldestFrameInQueue(const Frame** fp) {
373         std::unique_lock<std::mutex> lock(m_queueMutex);
374         if (m_nframe == 0 && !m_drawThreadShouldSuicide) {
375             ++numTimesDrawThreadBlockedOnEmptyQueue;
376             // atomic: unlock, long wait, relock; ignore spurious wakeups.
377             m_queueNotEmpty.wait(lock,
378                     [&] {return m_nframe || m_drawThreadShouldSuicide;});
379         } else {
380             sumOfQueueLengths        += double(m_nframe);
381             sumSquaredOfQueueLengths += double(square(m_nframe));
382         }
383         // There is at least one frame available now, unless we're supposed
384         // to quit. We are holding the lock.
385         lock.unlock();
386         if (m_drawThreadShouldSuicide) {*fp=0; return false;}
387         else {*fp=&m_pool[m_oldest]; return true;} // sim thread won't change oldest
388     }
389 
390     // Drawing thread uses this to note that it is done with the oldest
391     // frame which may now be reused by the simulation thread. The queueNotFull
392     // condition is posted if there is a reasonable amount of room in the pool
393     // now.
noteThatOldestFrameIsNowAvailable()394     void noteThatOldestFrameIsNowAvailable() {
395         std::unique_lock<std::mutex> lock(m_queueMutex);
396         m_oldest = (m_oldest+1)%m_pool.size(); // move to next-oldest
397         --m_nframe; // there is now one fewer frame in use
398         if (m_nframe == 0)
399             m_queueIsEmpty.notify_one(); // in case we're flushing
400         // Start the simulation again when the pool is about half empty.
401         if (m_nframe <= m_pool.size()/2+1)
402             m_queueNotFull.notify_one();
403         lock.unlock();
404     }
405 
406     // Given a time t in simulation time units, return the equivalent time r in
407     // seconds of real time. That is the amount of real time that should have
408     // elapsed since t=0 if this simulation were running at exactly the desired
409     // real time rate.
convertSimTimeToNs(const double & t)410     long long convertSimTimeToNs(const double& t)
411     {   return secToNs(t / m_simTimeUnitsPerSec); }
412 
413     // same as ns; that's what AdjRT tries to be
convertSimTimeToAdjRT(const double & t)414     long long convertSimTimeToAdjRT(const double& t)
415     {   return convertSimTimeToNs(t); }
416 
convertAdjRTtoSimTime(const long long & a)417     double convertAdjRTtoSimTime(const long long& a)
418     {   return nsToSec(a) * m_simTimeUnitsPerSec; }
419 
convertRTtoAdjRT(const long long & r)420     long long convertRTtoAdjRT(const long long& r)
421     {   return r - m_adjustedRealTimeBase; }
422 
convertAdjRTtoRT(const long long & a)423     long long convertAdjRTtoRT(const long long& a)
424     {   return a + m_adjustedRealTimeBase; }
425 
426 
427     // Adjust the real time base by a given signed offset in nanoseconds. We're
428     // seeing incorrect adjusted realtime a* = r - r0*, but we know the actual
429     // value is a. Pass in the error e=(a*-a), then we want to calculate a
430     // new base adjustment r0 such that a = r - r0. So:
431     //      a = r - r0* - e => r0=r0*+e.
readjustAdjustedRealTimeBy(const long long & e)432     void readjustAdjustedRealTimeBy(const long long& e) {
433         m_adjustedRealTimeBase += e;
434         ++numAdjustmentsToRealTimeBase;
435     }
436 
getAdjustedRealTime()437     long long getAdjustedRealTime()
438     {   return realTimeInNs() - m_adjustedRealTimeBase; }
439 
440     // Increment the reference count and return its new value.
incrRefCount() const441     int incrRefCount() const {return ++m_refCount;}
442 
443     // Decrement the reference count and return its new value.
decrRefCount() const444     int decrRefCount() const {return --m_refCount;}
445 
446     // Get the current value of the reference counter.
getRefCount() const447     int getRefCount() const {return m_refCount;}
448 
449     const MultibodySystem&                  m_system;
450     VisualizerProtocol                      m_protocol;
451     bool                                    m_shutdownWhenDestructed;
452 
453     Array_<DecorativeGeometry>              m_addedGeometry;
454     Array_<RubberBandLine>                  m_lines;
455     Array_<DecorationGenerator*>            m_generators;
456     Array_<Visualizer::InputListener*>      m_listeners;
457     Array_<Visualizer::FrameController*>    m_controllers;
458 
459     CoordinateDirection                     m_upDirection;
460     Real                                    m_groundHeight;
461 
462     // User control of Visualizer behavior.
463     Visualizer::Mode    m_mode;
464     Real    m_frameRateFPS;       // in frames/sec if > 0, else use default
465     Real    m_simTimeUnitsPerSec; // ratio of sim time units to real seconds
466     Real    m_desiredBufferLengthInSec; // RT only: how much delay (<0 => default)
467 
468     // How many nanoseconds between frames?
469     long long m_timeBetweenFramesInNs;
470     // How much accuracy should we require from sleep()?
471     long long m_allowableFrameJitterInNs;
472     // How much slop is allowed in matching the time of a simulation frame
473     // to the real time at which its frame is drawn?
474     long long m_allowableFrameTimeSlopInNs;
475 
476     // The offset r0 to subtract from the interval timer reading to produce
477     // the adjusted real time a that we expect to match the current simulation
478     // time t in ns. That is a = realTimeInNs()-r0. This base is adjusted by
479     // the drawing thread when we see what time we actually were able to
480     // deliver a frame.
481     long long m_adjustedRealTimeBase; // r0
482 
483     // This is when we would like the simulation to send us another frame.
484     // It is optimistically set to one frame interval later than the desired
485     // draw time of the most recent frame to be put in the queue. This is
486     // also used in non-RealTime mode where AdjRT==RT.
487     long long m_nextFrameDueAdjRT;
488 
489     // In RealTime mode we remember the simulated time in the previous
490     // supplied frame so that we can tell if we see an earlier frame,
491     // meaning (most likely) that we are starting a new simulation or
492     // seeing a playback of an old one.
493     double m_prevFrameSimTime;
494 
495     // The frame buffer:
496     Array_<Frame,int> m_pool; // fixed size, old to new order but circular
497     int m_oldest, m_nframe;   // oldest is index into pool, nframe is #valid entries
498     std::mutex              m_queueMutex;
499     std::condition_variable m_queueNotFull;  // these must use m_queueMutex
500     std::condition_variable m_queueNotEmpty;
501     std::condition_variable m_queueIsEmpty;
502 
503     std::thread         m_drawThread;    // the rendering thread
504     bool                m_drawThreadIsRunning;
505     bool                m_drawThreadShouldSuicide;
506 
507     mutable int         m_refCount; // how many Visualizer handles reference
508                                     //   this Impl object?
509 
510     // Statistics
511     int numFramesReportedBySimulation;
512     int   numReportedFramesThatWereIgnored;
513     int   numReportedFramesThatHadToWait;
514     int   numReportedFramesThatSkippedAhead;
515     int   numReportedFramesThatArrivedTooLate;
516     int   numReportedFramesThatWereQueued;
517     int     numQueuedFramesThatHadToWait;
518 
519     int numFramesSentToRenderer;
520     int   numFramesDelayedByRenderer;
521     int numTimesDrawThreadBlockedOnEmptyQueue;
522     int numAdjustmentsToRealTimeBase;
523 
524     double sumOfAllJitter;        // updated at time sent to renderer (ms)
525     double sumSquaredOfAllJitter; // ms^2
526 
527     // These are updated by the drawing thread each time it looks at the
528     // queue to pull off a frame.
529     double sumOfQueueLengths; // for computing the average length
530     double sumSquaredOfQueueLengths; // for std deviation
531 
532 
clearStats()533     void clearStats() {
534         numFramesReportedBySimulation=0;
535           numReportedFramesThatWereIgnored=0;
536           numReportedFramesThatHadToWait=0;
537           numReportedFramesThatSkippedAhead=0;
538           numReportedFramesThatArrivedTooLate=0;
539           numReportedFramesThatWereQueued=0;
540             numQueuedFramesThatHadToWait=0;
541 
542         numFramesSentToRenderer=0;
543           numFramesDelayedByRenderer=0;
544         numTimesDrawThreadBlockedOnEmptyQueue=0;
545         numAdjustmentsToRealTimeBase=0;
546 
547         sumOfAllJitter        = 0;
548         sumSquaredOfAllJitter = 0;
549 
550         sumOfQueueLengths = 0;
551         sumSquaredOfQueueLengths = 0;
552     }
553 
dumpStats(std::ostream & o) const554     void dumpStats(std::ostream& o) const {
555         o << "Visualizer stats:\n";
556         o << "  Mode: ";
557         switch(m_mode) {
558         case PassThrough: o << "PassThrough\n"; break;
559         case Sampling: o << "Sampling\n"; break;
560         case RealTime:
561             o << "RealTime, TimeScale=" << m_simTimeUnitsPerSec
562               << " sim time units/real second\n";
563             o << "  Desired/actual buffer length(s): "
564               << getDesiredBufferLengthInSec() << "/"
565               << getActualBufferLengthInSec() << " ("
566               << getActualBufferLengthInFrames() << " frames)\n";
567             break;
568         };
569         o << "  Desired frame rate: " << m_frameRateFPS << endl;
570         o << "  reported frames: " << numFramesReportedBySimulation << endl;
571         o << "  |       ignored: " << numReportedFramesThatWereIgnored << endl;
572         o << "  |   had to wait: " << numReportedFramesThatHadToWait << endl;
573         o << "  | skipped ahead: " << numReportedFramesThatSkippedAhead << endl;
574         o << "  | came too late: " << numReportedFramesThatArrivedTooLate << endl;
575         o << "  | were buffered: " << numReportedFramesThatWereQueued << endl;
576         o << "  | | full buffer: " << numQueuedFramesThatHadToWait << endl;
577         o << "  frames sent to renderer: " << numFramesSentToRenderer << endl;
578         o << "  | delayed by renderer  : " << numFramesDelayedByRenderer << endl;
579         if (numReportedFramesThatWereQueued && numFramesSentToRenderer) {
580             const double avg = sumOfQueueLengths/numFramesSentToRenderer;
581             o << "  | average buffer length (frames): " << avg << endl;
582             o << "  | std dev buffer length (frames): "
583               << std::sqrt(std::max(0.,
584                               sumSquaredOfQueueLengths/numFramesSentToRenderer
585                               - square(avg))) << endl;
586         }
587         o << "  draw blocked for empty buffer: "
588           << numTimesDrawThreadBlockedOnEmptyQueue << endl;
589         o << "  adjustments to real time base: "
590           << numAdjustmentsToRealTimeBase << endl;
591         if (numFramesSentToRenderer > 0) {
592             const double avg = sumOfAllJitter/numFramesSentToRenderer;
593             o << "  average jitter (ms): " << avg << endl;
594             o << "  jitter std dev (ms): "
595               << std::sqrt(sumSquaredOfAllJitter/numFramesSentToRenderer
596                  - square(avg)) << endl;
597         }
598     }
599 
600 };
601 
602 // Generate geometry for the given state and send it to the visualizer using
603 // the VisualizerProtocol object. In buffered mode this is called from the
604 // rendering thread; otherwise, this is just the main simulation thread.
drawFrameNow(const State & state)605 void Visualizer::Impl::drawFrameNow(const State& state) {
606     m_system.realize(state, Stage::Position);
607 
608     // Collect up the geometry that constitutes this scene.
609     Array_<DecorativeGeometry> geometry;
610     for (Stage stage = Stage::Topology; stage <= state.getSystemStage(); ++stage)
611         m_system.calcDecorativeGeometryAndAppend(state, stage, geometry);
612     for (unsigned i = 0; i < m_generators.size(); i++)
613         m_generators[i]->generateDecorations(state, geometry);
614 
615     // Execute frame controls (e.g. camera positioning).
616     for (unsigned i = 0; i < m_controllers.size(); ++i)
617         m_controllers[i]->generateControls(Visualizer(this), state, geometry);
618 
619     // Calculate the spatial pose of all the geometry and send it to the
620     // renderer.
621     m_protocol.beginScene(state.getTime());
622     VisualizerGeometry geometryCreator
623         (m_protocol, m_system.getMatterSubsystem(), state);
624     for (unsigned i = 0; i < geometry.size(); ++i)
625         geometry[i].implementGeometry(geometryCreator);
626     for (unsigned i = 0; i < m_addedGeometry.size(); ++i)
627         m_addedGeometry[i].implementGeometry(geometryCreator);
628     const SimbodyMatterSubsystem& matter = m_system.getMatterSubsystem();
629     for (unsigned i = 0; i < m_lines.size(); ++i) {
630         const RubberBandLine& line = m_lines[i];
631         const MobilizedBody& B1 = matter.getMobilizedBody(line.b1);
632         const MobilizedBody& B2 = matter.getMobilizedBody(line.b2);
633         const Transform&  X_GB1 = B1.getBodyTransform(state);
634         const Transform&  X_GB2 = B2.getBodyTransform(state);
635         const Vec3 end1 = X_GB1*line.station1;
636         const Vec3 end2 = X_GB2*line.station2;
637         const Real thickness = line.line.getLineThickness() == -1
638                                ? 1 : line.line.getLineThickness();
639         m_protocol.drawLine(end1, end2,
640             VisualizerGeometry::getColor(line.line), thickness);
641     }
642     m_protocol.finishScene();
643 
644     ++numFramesSentToRenderer;
645 }
646 
647 // This is called from the drawing thread if we're buffering, otherwise
648 // directly from the simulation thread.
drawRealtimeFrameWhenReady(const State & state,const long long & desiredDrawTimeAdjRT)649 void Visualizer::Impl::drawRealtimeFrameWhenReady
650    (const State& state, const long long& desiredDrawTimeAdjRT)
651 {
652     const long long earliestDrawTimeAdjRT =
653         desiredDrawTimeAdjRT - m_allowableFrameJitterInNs;
654     const long long latestDrawTimeAdjRT =
655         desiredDrawTimeAdjRT + m_allowableFrameJitterInNs;
656 
657     // Wait for the next frame time, allowing for a little jitter
658     // since we can't expect sleep to wake us up at the exact time.
659     long long now = getAdjustedRealTime();
660     if (now < earliestDrawTimeAdjRT) {
661         ++numFramesDelayedByRenderer;
662         do {sleepInNs(desiredDrawTimeAdjRT-now);}
663         while ((now=getAdjustedRealTime()) < earliestDrawTimeAdjRT);
664 
665         // Keep stats on the jitter situation.
666         const long long jitterInNs = now - desiredDrawTimeAdjRT;
667         const double jitterInMs = jitterInNs*1e-6;
668         sumOfAllJitter        += jitterInMs;
669         sumSquaredOfAllJitter += square(jitterInMs);
670     }
671 
672     // timingError is signed with + meaning we sent the frame late.
673     const long long timingError = now - desiredDrawTimeAdjRT;
674 
675     // If we sent this frame more than one frame time late we're going to
676     // admit we're not making real time and adjust the
677     // AdjRT base to  match.
678     if (timingError > m_timeBetweenFramesInNs)
679         readjustAdjustedRealTimeBy(now - desiredDrawTimeAdjRT);
680 
681     // It is time to render the frame.
682     drawFrameNow(state);
683 }
684 
685 // Attempt to report a frame while we're in realtime mode.
reportRealtime(const State & state)686 void Visualizer::Impl::reportRealtime(const State& state) {
687     // If we see a simulation time that is earlier than the last one,
688     // we are probably starting a new simulation or playback. Flush the
689     // old one. Note that we're using actual simulation time; we don't
690     // want to get tricked by adjustments to the real time base.
691     if (state.getTime() < m_prevFrameSimTime) {
692         waitUntilQueueIsEmpty();
693         m_nextFrameDueAdjRT = -1; // restart time base
694     }
695 
696     // scale, convert to ns (doesn't depend on real time base)
697     const long long t = convertSimTimeToAdjRT(state.getTime());
698 
699     // If this is the first frame, or first since last setMode(), then
700     // we synchronize Adjusted Real Time to match. Readjustments will occur
701     // if the simulation doesn't keep up with real time.
702     if (m_nextFrameDueAdjRT < 0 || t == 0) {
703         m_adjustedRealTimeBase = realTimeInNs() - t;
704         // now getAdjustedRealTime()==t
705         m_nextFrameDueAdjRT = t; // i.e., now
706     }
707 
708     // "timeSlop" is the amount we'll allow a frame's simulation time to
709     // deviate from the real time at which we draw it. That is, if we're
710     // expecting a frame at time t_f and the simulator instead delivers a
711     // frame at t_s, we'll consider that a match if |t_s-t_f|<=slop.
712     // The reason for this is that we prefer to issue frames at regular
713     // intervals, so if the frame time and sim time match closely enough
714     // we won't reschedule the frames. Otherwise, a sim frame whose time
715     // is too early (t_s<t_f-slop) gets thrown away (or used in desperation
716     // if we're not keeping up with real time), and a sim frame
717     // whose time is too late (t_s>t_f+slop) causes us to delay drawing
718     // that frame until real time catches up with what's in it. Typically
719     // timeSlop is set to a small fraction of the frame time, like 5%.
720     const long long timeSlop = m_allowableFrameTimeSlopInNs;
721     const long long next     = m_nextFrameDueAdjRT;
722     const long long earliest = next - timeSlop;
723     const long long latest   = next + timeSlop;
724 
725     if (t < earliest) {
726         ++numReportedFramesThatWereIgnored; // we don't need this one
727         return;
728     }
729 
730     long long desiredDrawTimeAdjRT = next;
731     if (t > latest) {
732         ++numReportedFramesThatSkippedAhead;
733         desiredDrawTimeAdjRT = t;
734     }
735 
736     // If buffering is enabled, push this onto the queue. Note that we
737     // might have to wait until the queue has some room.
738     if (queuingIsEnabled()) {
739         // This also sets expectations for the next frame.
740         addFrameToQueueWithWait(state, desiredDrawTimeAdjRT);
741         return;
742     }
743 
744     // There is no buffer. We'll just render this frame as soon as its
745     // drawing time arrives. No need to copy the state here. Note that
746     // the simulation thread is doing the drawing as well as the simulating.
747     // This method will also readjust adjusted real time if the frame came
748     // too late.
749     drawRealtimeFrameWhenReady(state, desiredDrawTimeAdjRT);
750 
751     // Now set expectations for the next frame.
752     m_nextFrameDueAdjRT = t + m_timeBetweenFramesInNs;
753 }
754 
755 
756 
757 //==============================================================================
758 //                                VISUALIZER
759 //==============================================================================
Visualizer(Visualizer::Impl * srcImpl)760 Visualizer::Visualizer(Visualizer::Impl* srcImpl) : impl(srcImpl) {
761     if (impl) impl->incrRefCount();
762 }
763 
Visualizer(const MultibodySystem & system)764 Visualizer::Visualizer(const MultibodySystem& system) : impl(0) {
765     impl = new Impl(this, system, Array_<String>());
766     impl->incrRefCount();
767 }
768 
Visualizer(const MultibodySystem & system,const Array_<String> & searchPath)769 Visualizer::Visualizer(const MultibodySystem& system,
770                        const Array_<String>& searchPath) : impl(0) {
771     impl = new Impl(this, system, searchPath);
772     impl->incrRefCount();
773 }
774 
Visualizer(const Visualizer & source)775 Visualizer::Visualizer(const Visualizer& source) : impl(0) {
776     if (source.impl) {
777         impl = source.impl;
778         impl->incrRefCount();
779     }
780 }
781 
operator =(const Visualizer & source)782 Visualizer& Visualizer::operator=(const Visualizer& source) {
783     if (impl != source.impl) {
784         if (impl&& impl->decrRefCount()==0) delete impl;
785         impl = source.impl;
786         impl->incrRefCount();
787     }
788     return *this;
789 }
790 
~Visualizer()791 Visualizer::~Visualizer() {
792     if (impl && impl->decrRefCount()==0)
793         delete impl;
794 }
795 
shutdown()796 void Visualizer::shutdown()
797 {   updImpl().m_protocol.shutdownGUI(); }
798 
setShutdownWhenDestructed(bool shouldShutdown)799 Visualizer& Visualizer::setShutdownWhenDestructed(bool shouldShutdown)
800 {   updImpl().setShutdownWhenDestructed(shouldShutdown); return *this; }
801 
getShutdownWhenDestructed() const802 bool Visualizer::getShutdownWhenDestructed() const
803 {   return getImpl().getShutdownWhenDestructed(); }
804 
getRefCount() const805 int Visualizer::getRefCount() const
806 {   return impl ? impl->getRefCount() : 0; }
807 
808        // Frame drawing methods
809 
drawFrameNow(const State & state) const810 void Visualizer::drawFrameNow(const State& state) const
811 {   const_cast<Visualizer*>(this)->updImpl().drawFrameNow(state); }
812 
flushFrames() const813 void Visualizer::flushFrames() const
814 {   const_cast<Visualizer*>(this)->updImpl().waitUntilQueueIsEmpty(); }
815 
816 // The simulation thread normally delivers frames here. Handling is dispatched
817 // according the current visualization mode.
report(const State & state) const818 void Visualizer::report(const State& state) const {
819     Visualizer::Impl& rep = const_cast<Visualizer*>(this)->updImpl();
820 
821     ++rep.numFramesReportedBySimulation;
822     if (rep.m_mode == RealTime) {
823         rep.reportRealtime(state);
824         return;
825     }
826 
827     // We're in Sampling or PassThrough mode. AdjRT and RT are the same in
828     // these modes; they are just real time as determined by realTimeInNs(),
829     // the current value of the interval counter. We don't care at all what
830     // time the simulation thinks it is.
831 
832     // If this is the first frame, or first since last setMode(), then
833     // we set our expected next frame arrival time to now.
834     if (rep.m_nextFrameDueAdjRT < 0LL)
835         rep.m_nextFrameDueAdjRT = realTimeInNs(); // now
836 
837     // If someone asked for an infinite frame rate just send this along now.
838     if (rep.m_timeBetweenFramesInNs == 0LL) {
839         drawFrameNow(state);
840         return;
841     }
842 
843     const long long earliestDrawTime = rep.m_nextFrameDueAdjRT
844                                        - rep.m_allowableFrameJitterInNs;
845     long long now = realTimeInNs();
846     if (now < earliestDrawTime) {
847         // Too early to draw this frame. In Sampling mode that means we
848         // just ignore it.
849         if (rep.m_mode == Sampling) {
850             ++rep.numReportedFramesThatWereIgnored;
851             return;
852         }
853 
854         // We're in PassThrough mode.
855         ++rep.numReportedFramesThatHadToWait;
856         do {sleepInNs(rep.m_nextFrameDueAdjRT - now);}
857         while ((now=realTimeInNs()) < earliestDrawTime);
858 
859         // We're not going to wake up exactly when we wanted to; keep stats.
860         const double jitterInMs = (now - rep.m_nextFrameDueAdjRT)*1e-6;
861         rep.sumOfAllJitter        += jitterInMs;
862         rep.sumSquaredOfAllJitter += square(jitterInMs);
863     }
864 
865     // Frame time reached in Sampling or PassThrough modes. Draw the frame.
866     drawFrameNow(state);
867 
868     // This frame might have been on time or late; we'll schedule the next
869     // time for one ideal frame interval later to keep the maximum rate down
870     // to the specified rate. Otherwise a late frame could be followed by lots
871     // of fast frames playing catch-up.
872     if (now-rep.m_nextFrameDueAdjRT <= rep.m_timeBetweenFramesInNs)
873         rep.m_nextFrameDueAdjRT += rep.m_timeBetweenFramesInNs;
874     else { // a late frame; delay the next one
875         rep.m_nextFrameDueAdjRT = now + rep.m_timeBetweenFramesInNs;
876         ++rep.numAdjustmentsToRealTimeBase;
877     }
878 }
879 
880         // Visualizer display options
881 
setBackgroundType(BackgroundType type)882 Visualizer& Visualizer::setBackgroundType(BackgroundType type)
883 {   updImpl().m_protocol.setBackgroundType(type); return *this; }
884 
setBackgroundColor(const Vec3 & color) const885 const Visualizer& Visualizer::setBackgroundColor(const Vec3& color) const
886 {   getImpl().m_protocol.setBackgroundColor(color); return *this; }
887 
setShowShadows(bool showShadows) const888 const Visualizer& Visualizer::setShowShadows(bool showShadows) const
889 {   getImpl().m_protocol.setShowShadows(showShadows); return *this; }
890 
setShowFrameRate(bool showFrameRate) const891 const Visualizer& Visualizer::setShowFrameRate(bool showFrameRate) const
892 {   getImpl().m_protocol.setShowFrameRate(showFrameRate); return *this; }
893 
setShowSimTime(bool showSimTime) const894 const Visualizer& Visualizer::setShowSimTime(bool showSimTime) const
895 {   getImpl().m_protocol.setShowSimTime(showSimTime); return *this; }
896 
setShowFrameNumber(bool showFrameNumber) const897 const Visualizer& Visualizer::setShowFrameNumber(bool showFrameNumber) const
898 {   getImpl().m_protocol.setShowFrameNumber(showFrameNumber); return *this; }
899 
setWindowTitle(const String & title) const900 const Visualizer& Visualizer::setWindowTitle(const String& title) const
901 {   getImpl().m_protocol.setWindowTitle(title); return *this; }
902 
903         // Visualizer options
904 
setSystemUpDirection(const CoordinateDirection & upDir)905 Visualizer& Visualizer::setSystemUpDirection(const CoordinateDirection& upDir)
906 {   updImpl().m_upDirection = upDir;
907     updImpl().m_protocol.setSystemUpDirection(upDir); return *this; }
getSystemUpDirection() const908 CoordinateDirection Visualizer::getSystemUpDirection() const
909 {   return getImpl().m_upDirection; }
910 
911 
setGroundHeight(Real height)912 Visualizer& Visualizer::setGroundHeight(Real height) {
913     updImpl().m_groundHeight = height;
914     updImpl().m_protocol.setGroundHeight(height); return *this;
915 }
getGroundHeight() const916 Real Visualizer::getGroundHeight() const
917 {   return getImpl().m_groundHeight; }
918 
setMode(Visualizer::Mode mode)919 Visualizer& Visualizer::setMode(Visualizer::Mode mode)
920 {   updImpl().setMode(mode); return *this; }
getMode() const921 Visualizer::Mode Visualizer::getMode() const {return getImpl().m_mode;}
922 
setDesiredFrameRate(Real fps)923 Visualizer& Visualizer::setDesiredFrameRate(Real fps)
924 {   updImpl().setDesiredFrameRate(std::max(fps, Real(0))); return *this; }
getDesiredFrameRate() const925 Real Visualizer::getDesiredFrameRate() const
926 {   return getImpl().m_frameRateFPS; }
927 
setRealTimeScale(Real simTimePerRealSec)928 Visualizer& Visualizer::setRealTimeScale(Real simTimePerRealSec)
929 {   updImpl().setRealTimeScale(simTimePerRealSec); return *this; }
getRealTimeScale() const930 Real Visualizer::getRealTimeScale() const
931 {   return getImpl().m_simTimeUnitsPerSec; }
932 
setDesiredBufferLengthInSec(Real bufferLengthInSec)933 Visualizer& Visualizer::setDesiredBufferLengthInSec(Real bufferLengthInSec)
934 {   updImpl().setDesiredBufferLengthInSec(bufferLengthInSec); return *this; }
getDesiredBufferLengthInSec() const935 Real Visualizer::getDesiredBufferLengthInSec() const
936 {   return getImpl().getDesiredBufferLengthInSec(); }
getActualBufferLengthInFrames() const937 int Visualizer::getActualBufferLengthInFrames() const
938 {   return getImpl().getActualBufferLengthInFrames(); }
getActualBufferLengthInSec() const939 Real Visualizer::getActualBufferLengthInSec() const
940 {   return getImpl().getActualBufferLengthInSec(); }
941 
942 
addInputListener(Visualizer::InputListener * listener)943 int Visualizer::addInputListener(Visualizer::InputListener* listener) {
944     Impl& impl = updImpl();
945     const int nxt = (int)impl.m_listeners.size();
946     impl.m_listeners.push_back(listener);
947     return nxt;
948 }
getNumInputListeners() const949 int Visualizer::getNumInputListeners() const
950 {   return (int)getImpl().m_listeners.size(); }
getInputListener(int i) const951 const Visualizer::InputListener& Visualizer::getInputListener(int i) const
952 {   return *getImpl().m_listeners[i]; }
updInputListener(int i)953 Visualizer::InputListener& Visualizer::updInputListener(int i)
954 {   return *updImpl().m_listeners[i]; }
955 
addFrameController(Visualizer::FrameController * fc)956 int Visualizer::addFrameController(Visualizer::FrameController* fc) {
957     Impl& impl = updImpl();
958     const int nxt = (int)impl.m_controllers.size();
959     impl.m_controllers.push_back(fc);
960     return nxt;
961 }
getNumFrameControllers() const962 int Visualizer::getNumFrameControllers() const
963 {   return (int)getImpl().m_controllers.size(); }
getFrameController(int i) const964 const Visualizer::FrameController& Visualizer::getFrameController(int i) const
965 {   return *getImpl().m_controllers[i]; }
updFrameController(int i)966 Visualizer::FrameController& Visualizer::updFrameController(int i)
967 {   return *updImpl().m_controllers[i]; }
968 
969 
970         // Scene-building methods
971 
972 Visualizer& Visualizer::
addMenu(const String & title,int menuId,const Array_<pair<String,int>> & items)973 addMenu(const String& title, int menuId,
974         const Array_<pair<String, int> >& items)
975 {
976     SimTK_ERRCHK2_ALWAYS(menuId >= 0, "Visualizer::addMenu()",
977         "Assigned menu ids must be nonnegative, but an attempt was made to create"
978         " a menu %s with id %d.", title.c_str(), menuId);
979 
980     updImpl().m_protocol.addMenu(title, menuId, items);
981     return *this;
982 }
983 
984 Visualizer& Visualizer::
addSlider(const String & title,int sliderId,Real minVal,Real maxVal,Real value)985 addSlider(const String& title, int sliderId,
986           Real minVal, Real maxVal, Real value)
987 {
988     SimTK_ERRCHK2_ALWAYS(sliderId >= 0, "Visualizer::addSlider()",
989         "Assigned slider ids must be nonnegative, but an attempt was made to create"
990         " a slider %s with id %d.", title.c_str(), sliderId);
991     SimTK_ERRCHK4_ALWAYS(minVal <= value && value <= maxVal, "Visualizer::addSlider()",
992         "Initial slider value %g for slider %s was outside the specified range [%g,%g].",
993         value, title.c_str(), minVal, maxVal);
994 
995     updImpl().m_protocol.addSlider(title, sliderId, minVal, maxVal, value);
996     return *this;
997 }
998 
999 int Visualizer::
addDecoration(MobilizedBodyIndex mobodIx,const Transform & X_BD,const DecorativeGeometry & geom)1000 addDecoration(MobilizedBodyIndex mobodIx, const Transform& X_BD,
1001               const DecorativeGeometry& geom)
1002 {
1003     Array_<DecorativeGeometry>& addedGeometry = updImpl().m_addedGeometry;
1004     const int nxt = (int)addedGeometry.size();
1005     addedGeometry.push_back(geom);
1006     DecorativeGeometry& geomCopy = addedGeometry.back();
1007     geomCopy.setBodyId((int)mobodIx);
1008     geomCopy.setTransform(X_BD * geomCopy.getTransform());
1009     return nxt;
1010 }
getNumDecorations() const1011 int Visualizer::getNumDecorations() const
1012 {   return (int)getImpl().m_addedGeometry.size(); }
getDecoration(int i) const1013 const DecorativeGeometry& Visualizer::getDecoration(int i) const
1014 {   return getImpl().m_addedGeometry[i]; }
updDecoration(int i) const1015 DecorativeGeometry& Visualizer::updDecoration(int i) const
1016 {   return const_cast<Visualizer*>(this)->updImpl().m_addedGeometry[i]; }
1017 
1018 int Visualizer::
addRubberBandLine(MobilizedBodyIndex b1,const Vec3 & station1,MobilizedBodyIndex b2,const Vec3 & station2,const DecorativeLine & line)1019 addRubberBandLine(MobilizedBodyIndex b1, const Vec3& station1,
1020                   MobilizedBodyIndex b2, const Vec3& station2,
1021                   const DecorativeLine& line)
1022 {
1023     Impl& impl = updImpl();
1024     const int nxt = (int)impl.m_lines.size();
1025     impl.m_lines.push_back(RubberBandLine(b1,station1, b2,station2, line));
1026     return nxt;
1027 }
getNumRubberBandLines() const1028 int Visualizer::getNumRubberBandLines() const
1029 {   return (int)getImpl().m_lines.size(); }
getRubberBandLine(int i) const1030 const DecorativeLine& Visualizer::getRubberBandLine(int i) const
1031 {   return getImpl().m_lines[i].line; }
updRubberBandLine(int i) const1032 DecorativeLine& Visualizer::updRubberBandLine(int i) const
1033 {   return const_cast<Visualizer*>(this)->updImpl().m_lines[i].line; }
1034 
1035 int Visualizer::
addDecorationGenerator(DecorationGenerator * generator)1036 addDecorationGenerator(DecorationGenerator* generator)
1037 {
1038     Impl& impl = updImpl();
1039     const int nxt = (int)impl.m_generators.size();
1040     impl.m_generators.push_back(generator);
1041     return nxt;
1042 }
1043 int Visualizer::
getNumDecorationGenerators() const1044 getNumDecorationGenerators() const
1045 {   return (int)getImpl().m_generators.size(); }
1046 const DecorationGenerator& Visualizer::
getDecorationGenerator(int i) const1047 getDecorationGenerator(int i) const
1048 {   return *getImpl().m_generators[i]; }
1049 DecorationGenerator& Visualizer::
updDecorationGenerator(int i)1050 updDecorationGenerator(int i)
1051 {   return *updImpl().m_generators[i]; }
1052 
1053         // Frame control methods
1054 const Visualizer& Visualizer::
setCameraTransform(const Transform & transform) const1055 setCameraTransform(const Transform& transform) const
1056 {   getImpl().m_protocol.setCameraTransform(transform); return *this; }
1057 
zoomCameraToShowAllGeometry() const1058 const Visualizer& Visualizer::zoomCameraToShowAllGeometry() const
1059 {   getImpl().m_protocol.zoomCamera(); return *this; }
1060 
1061 const Visualizer& Visualizer::
pointCameraAt(const Vec3 & point,const Vec3 & upDirection) const1062 pointCameraAt(const Vec3& point, const Vec3& upDirection) const
1063 {   getImpl().m_protocol.lookAt(point, upDirection); return *this; }
1064 
setCameraFieldOfView(Real fov) const1065 const Visualizer& Visualizer::setCameraFieldOfView(Real fov) const
1066 {   getImpl().m_protocol.setFieldOfView(fov); return *this; }
1067 
1068 const Visualizer& Visualizer::
setCameraClippingPlanes(Real nearPlane,Real farPlane) const1069 setCameraClippingPlanes(Real nearPlane, Real farPlane) const
1070 {   getImpl().m_protocol.setClippingPlanes(nearPlane, farPlane);
1071     return *this; }
1072 
1073 
setSliderValue(int slider,Real newValue) const1074 const Visualizer& Visualizer::setSliderValue(int slider, Real newValue) const
1075 {   getImpl().m_protocol.setSliderValue(slider, newValue); return *this; }
1076 
1077 const Visualizer& Visualizer::
setSliderRange(int slider,Real newMin,Real newMax) const1078 setSliderRange(int slider, Real newMin, Real newMax) const
1079 {   getImpl().m_protocol.setSliderRange(slider, newMin, newMax); return *this; }
1080 
1081         // Debugging and statistics
dumpStats(std::ostream & o) const1082 void Visualizer::dumpStats(std::ostream& o) const {getImpl().dumpStats(o);}
clearStats()1083 void Visualizer::clearStats() {updImpl().clearStats();}
1084 
1085         // Internal use only
getInputListeners() const1086 const Array_<Visualizer::InputListener*>& Visualizer::getInputListeners() const
1087 {   return getImpl().m_listeners; }
getFrameControllers() const1088 const Array_<Visualizer::FrameController*>& Visualizer::getFrameControllers() const
1089 {   return getImpl().m_controllers; }
getSystem() const1090 const MultibodySystem& Visualizer::getSystem() const {return getImpl().m_system;}
1091 
1092 
1093 //==============================================================================
1094 //                             BODY FOLLOWER
1095 //==============================================================================
BodyFollower(const MobilizedBody & mobodB,const Vec3 & stationPinB,const Vec3 & offset,const UnitVec3 & upDirection)1096 Visualizer::BodyFollower::BodyFollower(
1097         const MobilizedBody& mobodB,
1098         const Vec3&          stationPinB,
1099         const Vec3&          offset,
1100         const UnitVec3&      upDirection)
1101     :   m_mobodB(mobodB), m_stationPinB(stationPinB), m_offset(offset),
1102         m_upDirection(upDirection) {}
1103 
generateControls(const Visualizer & viz,const State & state,Array_<DecorativeGeometry> & geometry)1104 void Visualizer::BodyFollower::generateControls(
1105         const Visualizer&             viz,
1106         const State&                  state,
1107         Array_< DecorativeGeometry >& geometry)
1108 {
1109     // Offset.
1110     Vec3 offset(m_offset);
1111     if (m_offset.isNaN()) {
1112         // Default: offset is based on system up direction and ground height.
1113         offset = Vec3(1, 1, 1);
1114         offset[viz.getSystemUpDirection().getAxis()] += viz.getGroundHeight();
1115     }
1116 
1117     // Up direction. Default: use System up direction.
1118     const UnitVec3& upDirection = m_upDirection.isNaN() ?
1119         UnitVec3(viz.getSystemUpDirection()) : m_upDirection;
1120 
1121     const Vec3 P = m_mobodB.findStationLocationInGround(state, m_stationPinB);
1122     // Position of camera (C) from ground origin (G), expressed in ground.
1123     const Vec3 p_GC = P + offset;
1124     // Rotation of camera frame (C) in ground frame (G).
1125     // To get the camera to point at P, we require the camera's z direction
1126     // (which points "back") to be parallel to the offset. We also want the
1127     // camera's y direction (which points to the top of the screen) to be as
1128     // closely aligned with the provided up direction as is possible.
1129     const Rotation R_GC(UnitVec3(offset), ZAxis, upDirection, YAxis);
1130     viz.setCameraTransform(Transform(R_GC, p_GC));
1131 }
1132 
1133 
1134 //==============================================================================
1135 //                             THE DRAWING THREAD
1136 //==============================================================================
1137 /* When we're in RealTime mode, we run a separate thread that actually sends
1138 frames to the renderer. It pulls frames off the back (oldest) end of the
1139 frame buffer queue, while the simulation thread is pushing frames onto the
1140 front (newest) end of the queue. The rendering thread is created whenever
1141 the Visualizer enters RealTime mode, and canceled whenever it leaves
1142 RealTime mode or is destructed.
1143 
1144 This is the main function for the buffered drawing thread. Its job is to
1145 pull the oldest frames off the queue and send them to the renderer at
1146 the right real times. We use adjusted real time (AdjRT) which should match
1147 the simulation time kept with the frame as its desired draw time. If the
1148 frame is ahead of AdjRT, we'll sleep to let real time catch up. If the
1149 frame is substantially behind, we'll render it now and then adjust the AdjRT
1150 base to acknowledge that we have irretrievably slipped from real time and need
1151 to adjust our expectations for the future. */
drawingThreadMain(Visualizer::Impl & vizImpl)1152 static void drawingThreadMain(Visualizer::Impl& vizImpl) {
1153 
1154     do {
1155         // Grab the oldest frame in the queue.
1156         // This will wait if necessary until a frame is available.
1157         const Frame* framep;
1158         if (vizImpl.getOldestFrameInQueue(&framep)) {
1159             // Draw this frame as soon as its draw time arrives, and readjust
1160             // adjusted real time if necessary.
1161             vizImpl.drawRealtimeFrameWhenReady
1162                (framep->state, framep->desiredDrawTimeAdjRT);
1163 
1164             // Return the now-rendered frame to circulation in the pool. This may
1165             // wake up the simulation thread if it was waiting for space.
1166             vizImpl.noteThatOldestFrameIsNowAvailable();
1167         }
1168     } while (!vizImpl.m_drawThreadShouldSuicide);
1169 
1170     // Attempt to wake up the simulation thread if it is waiting for
1171     // the draw thread since there won't be any more notices!
1172     vizImpl.m_queueNotFull.notify_one(); // wake up if waiting for queue space
1173     vizImpl.m_queueIsEmpty.notify_one(); // wake up if flushing
1174 }
1175