plot.xts() is based on quantmod::chart_Series(). The internal structures have a lot of implicit assumptions and some duplicated code, which makes the code harder to reason about and change. For example:
There are no explicit "panels", i.e. what is added when addSeries() is called with on = NA. Instead, there is one list of "frames" that contains header and series frames. The odd numbered list elements are the header frames and the even numbered elements are the series frames.
Target: store the header and series frames in a panel object.
Everything that gets rendered is in one list of "actions". Where the action is rendered is stored in the "frame" attribute of the action.
Target: frames should contain actions that are rendered in the the frame.
There are separate lists for the ylim and x/y aspect ratio for each frame.
Target: frames should contain their ylim and x/y aspect ratio.
There are 5 separate code blocks to add y-axis grid lines and labels.
Target: the panel should control the y-axis settings.
Some functions are always called, even though they're only necessary in certain situations. For example, get_ylim() is always called but only needed when multi.panel = TRUE.
The x-axis action is part of the first series frame.
Target: the x-axis is the same for the entire plot, so it should be part of the plot object.
The main purpose for this refactor is to make it easier to implement y-axis log scaling (#103).
Refactor target design
The target design attempts to organize the plot components into their respective parts: the plot window, the window panels, and the panel data/actions.
The plot object contains the plot title/header (and optional timespan), the x-axis labels and tick marks, and a list of 'panel' objects. The main plot object contains:
Env: an environment to hold all the plot information
add_main_header(): add the main plot header
add_main_xaxis(): add the x-axis labels and ticks to the main plot
new_panel(): create a new panel and add it to the plot
get_xcoords(): get the x-coordinate values for the plot
get_panel(): get a specific panel
get_last_action_panel(): get the panel that had the last rendered action
new_environment(): create a new environment with Env as its parent
Functions that aren't intended to be called externally:
update_panels(): re-calculate the x-axis and y-axis values
render_panels(): render all the plot panels
x_grid_lines(): plot the x-axis grid lines
create_ylim(): create y-axis max/min, to handle when max(x) == min(x).
The panel object contains:
id: the numeric index of the panel in the plot's list of panels
asp: the x/y aspect ratio for the panel (relative vertical size)
ylim: the ylim of the panel when it was created
ylim_render: the ylim of the panel to use when rendering
use_fixed_ylim: when TRUE, do not update the panel ylim based on all panel series
header: the panel title
actions: a list of expressions used to render the panel
add_action(): add an action to the actions list
yaxis_expr the expression to render the y-axis min/max values, labels, and grid lines/ticks. It also contains the x-axis grid expression because we need the y-axis min/max values to know where to draw the x-axis grid lines on the panel.
Original design
plot.xts()
is based onquantmod::chart_Series()
. The internal structures have a lot of implicit assumptions and some duplicated code, which makes the code harder to reason about and change. For example:addSeries()
is called withon = NA
. Instead, there is one list of "frames" that contains header and series frames. The odd numbered list elements are the header frames and the even numbered elements are the series frames.get_ylim()
is always called but only needed whenmulti.panel = TRUE
.The main purpose for this refactor is to make it easier to implement y-axis log scaling (#103).
Refactor target design
The target design attempts to organize the plot components into their respective parts: the plot window, the window panels, and the panel data/actions.
The plot object contains the plot title/header (and optional timespan), the x-axis labels and tick marks, and a list of 'panel' objects. The main plot object contains:
Env
: an environment to hold all the plot informationadd_main_header()
: add the main plot headeradd_main_xaxis()
: add the x-axis labels and ticks to the main plotnew_panel()
: create a new panel and add it to the plotget_xcoords()
: get the x-coordinate values for the plotget_panel()
: get a specific panelget_last_action_panel()
: get the panel that had the last rendered actionnew_environment()
: create a new environment withEnv
as its parentupdate_panels()
: re-calculate the x-axis and y-axis valuesrender_panels()
: render all the plot panelsx_grid_lines()
: plot the x-axis grid linescreate_ylim()
: create y-axis max/min, to handle whenmax(x) == min(x)
.The panel object contains:
id
: the numeric index of the panel in the plot's list of panelsasp
: the x/y aspect ratio for the panel (relative vertical size)ylim
: the ylim of the panel when it was createdylim_render
: the ylim of the panel to use when renderinguse_fixed_ylim
: whenTRUE
, do not update the panel ylim based on all panel seriesheader
: the panel titleactions
: a list of expressions used to render the paneladd_action()
: add an action to the actions listyaxis_expr
the expression to render the y-axis min/max values, labels, and grid lines/ticks. It also contains the x-axis grid expression because we need the y-axis min/max values to know where to draw the x-axis grid lines on the panel.