CMDimData.EasyPlot Plotting interface
March 25, 2021 · View on GitHub
- Description
- Main plot objects
- Creating plots
- Supported backends
- Julia display stack: Adding a default backend
- Configuration/defaults
- Known limitations
Description
CMDimData.EasyPlot provides a high-level abstraction to describe plots. The interface is optimized to write compact extraction routines for investigative (circuit) design work.
The goal of the CMDimData.EasyPlot interface is to let the user focus on analyzing measurement/simulation results (extracting relevant circuit performance) by keeping the necessary plotting code to a strict minimum. Also, to keep things portable, an EasyPlot.Plot object can be rendered on different backends.
That being said, the CMDimData.EasyPlot interface is relatively generic and should be adequate for many scientific fields.
Main plot objects
EasyPlot.PlotCollection: A collection of plots to display simultaneously.EasyPlot.Plot: A single plot.EasyPlot.Waveform: Stores input data and attributes specifying how do display.EasyPlot.LineAttributes: How to draw lines.EasyPlot.GlyphAttributes: How to display glyphs (aka markers/symbols).
Note that objects are constructed using the cons(...) API to minimize namespace pollution (see Creating Plots).
Attributes & AttributeChangeSpec
To simultaneously set multiple object attributes in a single command, EasyPlot uses AttributeChangeSpec objects. These objecs are created with the attributes() function, for example:
lin_log = EasyPlot.attributes(
xyaxes = set(xscale=:lin, yscale=:log)
)
In practice however, users should call the cons(:attribute_list, ...) function or one of its aliases (:a, :attr) created to reduce namespace pollution (objects are not all exported):
#Aliases:
lin_log = cons(:a, xyaxes = set(xscale=:lin, yscale=:log) )
lin_log = cons(:attr, xyaxes = set(xscale=:lin, yscale=:log) )
lin_log = cons(:attribute_list, xyaxes = set(xscale=:lin, yscale=:log) )
#Direct call:
lin_log = EasyPlot.attributes( xyaxes = set(xscale=:lin, yscale=:log) )
Use Julia's help system for more examples on how to create AttributeChangeSpecs:
help?> cons
help?> EasyPlot.attributes
help?> EasyPlot.Plot
help?> EasyPlot.PlotCollection
help?> EasyPlot.Waveform
Axis scale identifiers
X/Y-axis scales are specified using one of the following Symbols:
:lin:log10,:log(=:log10):ln,:log2(not yet supported):dB20,:dB10:reciprocal
Supported linestyles
:none,:solid,:dash,:dot,:dashdot
Supported glyphs (aka markers/symbols)
:none,:square,:diamond:uarrow,:darrow,:larrow,:rarrow(usually triangles):+,:cross,:x,:diagcross:o,:circle,:*,:star
TODO: :star4, :star5, ...??
Creating plots
There are two steps to plotting with EasyPlot:
- Create the
Plot/PlotCollectionobject - Display the
Plot/PlotCollectionobject
Note that you must choose a particular plotting backend on which the plot is to be displayed.
Plot creation example: A simple plot
The following illustrates how to create plots with EasyPlot:
using CMDimData
using MDDatasets #Create multi-dim data (DataF1)
using CMDimData.EasyPlot
CMDimData.@includepkg EasyPlotInspect #Display plots with InspectDR
#Generate some data:
x = DataF1(-2:.1:2)
#`cons`truct a backend-agnositc EasyPlot.Plot object:
plot = cons(:plot, title="Simple plot", legend=true,
labels = set(xaxis="x-value", yaxis="y-value")
)
#Add some data:
push!(plot,
cons(:wfrm, x^2, label="x^2"),
cons(:wfrm, x^3, label="x^3"),
)
#Display plot on selected backend:
EasyPlot.displaygui(:InspectDR, plot)
Plot creation example: A stacked, multi-strip plot
EasyPlot.Plot supports "multi-strip" plots with a common x-axis and multiple stacked y-axes.
"Multi-strip" plots are ideal for plotting data with widely differing y-value ranges.
using CMDimData
using MDDatasets #Create multi-dim data (DataF1)
using CMDimData.EasyPlot
CMDimData.@includepkg EasyPlotInspect #Display plots with InspectDR
#Generate data with differing y-value ranges:
T = 1/60; ΔT = T/20 #sec
C = 480e-6 #F
t = DataF1(0:ΔT:8T) #sec
v = (120*sqrt(2)) * sin(t*(2π/T)) #V
dv_dt = (2π/T) * cos(t*(2π/T)) #Calculate (more exact)
i = C * dv_dt
#Create a multi-strip plot:
plot = cons(:plot, nstrips=2, title="Multi-strip plot", legend=true,
ystrip1 = set(axislabel="Voltage [V]"),
ystrip2 = set(axislabel="Current [A]"),
xaxis = set(label="Time [s]"),
)
#Add some data:
push!(plot,
cons(:wfrm, v, label="cap", strip=1),
cons(:wfrm, i, label="cap", strip=2),
)
#Display plot on selected backend:
EasyPlot.displaygui(:InspectDR, plot)
Plot creation example: Bode plot (Another multi-strip plot)
# ...
#Create a multi-strip plot:
plot = cons(:plot, nstrips=2, title="Bode plot", legend=true,
ystrip1 = set(axislabel="|G(s)| [dB]"),
ystrip2 = set(axislabel="∠G(s) [°]"),
xaxis = set(label="Frequency [Hz]"),
)
# ...
Plot creation example: A multi-plot collection
pcoll = cons(:plot_collection, title="A multiplot object", ncolumns=2)
push!(pcoll,
cons(:plot, title="1st plot"),
cons(:plot, title="2nd plot"),
)
EasyPlot.displaygui(:InspectDR, pcoll)
Supported backends (link)
Julia display stack: Adding a default backend
It is also possible to use the Julia display stack to specify an default backend for rendering plots. Simply push an instance of the desired backend's <:AbstractPlotDisplay object to the top of Julia's display stack:
using CMDimData
using CMDimData.EasyPlot
CMDimData.@includepkg EasyPlotInspect
pushdisplay(EasyPlot.GUIDisplay(:InspectDR)); #Semicolon: inhibit object dump to REPL
NOTE: EasyPlot.GUIDisplay(:InspectDR, args...; kwargs...) returns a GUI-enabled <:AbstractPlotDisplay/plot builder for the InspectDR backend.
With an <:AbstractPlotDisplay object on Julia's display stack, it is no longer necessary to use EasyPlot.displaygui():
#Display plot using the top-most (most recently pushed) `AbstractPlotDisplay` object:
display(plot)
Actually, the Julia REPL, implicitly calls display() if you evaluate plot:
julia> plot
Known limitations
EasyPlotsupports mostly x/y graphs & basic plot attributes at the moment.EasyPlotdoes not supportDataTimeorDataFreq.- The
CMDimData.@includepkg [backend]macro evaluates the interfacing code at run time to circumvent having to add backends as dependencies ofCMDimData. They consequenly do not take advantage of Julia's pre-compilation cache.
Important note on @includepkg
@includepkg includes the module code that implements the EasyPlot interface (ex: EasyPlotInspect) in whichever module it is called. It also imports the plotting backend module (ex: InspectDR).
These modules will therefore only be accessible from within that scope. Consequently, your Julia environment must ensure it can resolve where the backend module resides. This can be done by adding the backend module to the active julia "project". For example, you can add the InspectDR backend to the active Julia project with the package add command:
]add InspectDR