To use an analogy from databases, we may say that PlotJuggler works with column-oriented series.
Given a message called "position" with theses fields:
The following timeseries will be created when multiple messages are received at time t:
Field | Value |
---|---|
X | ... |
Y | ... |
Z | ... |
position/x | t1; x1 | t2; x2 | t3; x3 | ... |
position/y | t1; y1 | t2; y2 | t3; y3 | ... |
position/x | t1; x1 | t2; x2 | t3; x3 | ... |
position/z | t1; z1 | t2; z2 | t3; z3 | ... |
Considered this hypothetical "Trajectory2D" message, that contains an array of X/Y positions.
In this case, we may talk about row-oriented series.
Any single message should create an entire ScatterXY series, using:
Field | Value |
---|---|
node.0/pos/x | ... |
node.0/pos/y | ... |
node.1/pos/x | ... |
node.1/pos/y | ... |
node.2/pos/x | ... |
node.2/pos/y | ... |
node.3/pos/x | ... |
node.3/pos/y | ... |
... | .... |
"Reactive scrips" are Lua script which are executed every time:
Inside these scripts, you can:
-- Get the handle to access a timeseries called "trajectory/node.0/position/x"
series_x = TimeseriesView.find( "trajectory/node.0/position/x" )
-- Very often, you may want to do something like this
node_num = 0
str = string.format("trajectory/node.%d/position/x", node_num)
series_x = TimeseriesView.find( str )
-- If the series doesn't exist, nil is retuned
if series_x == nil then
print("nope")
end
-- Get the number of elements
n = series_x:size()
-- Access time/value pairs at index i, where the first index is 0
t, x = series_x:at(i)
-- More often, it is useful to find a value by timestamp, instead of index
x = series:atTime(t)
-- Create a new series or clear its points, if the series exists already
new_series = ScatterXY.new("trajectory_XY")
-- Add data
new_series:push_back(x, y)
-- Clear previous data
new_series:clear()
-- Count number of elements
n = new_series:size()
-- Access x/y value pairs at index i, where the first index is 0
x, y = new_series:at(i)
--- Create a new series or overwite the previous one.
--- Do this once, outside the function
new_series = ScatterXY.new("trajectory_XY")
function( tracker_time )
index = 0
while(true) do
str = string.format("trajectory/node.%d/pos/x", index) -- X
series_x = TimeseriesView.find( str )
str = string.format("trajectory/node.%d/pos/y", index) -- Y
series_y = TimeseriesView.find( str )
-- stop the loop if any of those series can't be found
if series_x == nil or series_y == nil then break end
--- Append points to new_series
x = series_x:atTime(tracker_time)
y = series_y:atTime(tracker_time)
new_series:push_back(x,y)
index = index + 1
end -- end loop
end
To save you time, PlotJuggler allows you to create a "library" of reusable helper functions, pre-loaded by all your scripts