/// <summary>
 /// Phase: render-components.
 /// </summary>
 /// <param name="ls">Layout state.</param>
 protected void Phase_RenderComponents(LayoutState ls)
 {
     foreach (IRequireRender irr in Render_Components.Items /*Components.Where((cc2) => !(cc2 is IChartAxis) && (cc2 is IRequireRender) && !(cc2 is IRequireRenderPostAxesFinalized))*/)
     {
         var ctx = ls.RenderFor(irr as ChartComponent, Surface, Components, DataContext);
         irr.Render(ctx);
     }
 }
 /// <summary>
 /// Phase: render post-axes-finalized.
 /// </summary>
 /// <param name="ls">Layout state.</param>
 protected void Phase_RenderPostAxesFinalized(LayoutState ls)
 {
     foreach (IRequireRender irr in Render_Components_PostAxis.Items /*Components.Where((cc2) => !(cc2 is IChartAxis) && (cc2 is IRequireRender) && (cc2 is IRequireRenderPostAxesFinalized))*/)
     {
         var ctx = ls.RenderFor(irr as ChartComponent, Surface, Components, DataContext);
         _trace.Verbose($"render-post-axes-finalized {(irr as ChartComponent).Name} rect:{ctx.Area}");
         irr.Render(ctx);
     }
 }
 /// <summary>
 /// Phase: transforms.
 /// </summary>
 /// <param name="ls">Layout state.</param>
 protected void Phase_Transforms(LayoutState ls)
 {
     foreach (IRequireTransforms irt in Transforms_All.Items /*Components.Where((cc2) => cc2 is IRequireTransforms)*/)
     {
         var ctx = ls.RenderFor(irt as ChartComponent, Surface, Components, DataContext);
         _trace.Verbose($"transforms {irt} {ctx.Area}");
         irt.Transforms(ctx);
     }
 }
 /// <summary>
 /// Phase: after-axes-finalized.
 /// </summary>
 /// <param name="ls">Layout state.</param>
 protected void Phase_AxesFinalized(LayoutState ls)
 {
     foreach (var iraaf in AfterAxesFinalized.Items)
     {
         var cc  = iraaf as ChartComponent;
         var ctx = ls.RenderFor(cc, Surface, Components, DataContext);
         _trace.Verbose($"axes-finalized {cc.Name} rect:{ctx.Area}");
         iraaf.AxesFinalized(ctx);
     }
 }
        /// <summary>
        /// Process incremental updates to a <see cref="DataSource"/>.
        /// </summary>
        /// <param name="ncca">The operation.  Only <see cref="NotifyCollectionChangedAction.Add"/> and <see cref="NotifyCollectionChangedAction.Remove"/> are supported.</param>
        /// <param name="ls">Current layout state.</param>
        /// <param name="ds">The <see cref="DataSource"/> that changed.</param>
        /// <param name="startIndex">Starting index of update.</param>
        /// <param name="items">Item(s) involved in the update.</param>
        protected void IncrementalUpdate(NotifyCollectionChangedAction ncca, LayoutState ls, DataSource ds, int startIndex, IList items)
        {
            _trace.Verbose($"incr-update {ncca} '{ds.Name}' {ds} @{startIndex}+{items.Count}");
            ls.Type = RenderType.Incremental;
            // Phase I: reset axes
            Phase_ResetAxes();
            // Phase II: Phase_Layout (skipped)
            // Phase III: this loop comprises the DSRP
            foreach (var irdsu in DataSourceUpdates_All.Items.Where(irdsu2 => irdsu2.UpdateSourceName == ds.Name) /* Components.Where(xx => xx is IRequireDataSourceUpdates irdsu2 && irdsu2.UpdateSourceName == ds.Name)*/)
            {
                var cc = irdsu as ChartComponent;
                _trace.Verbose($"incr {ncca} '{cc.Name}' {cc}");
                var ctx = ls.RenderFor(cc, Surface, Components, DataContext);
                switch (ncca)
                {
                case NotifyCollectionChangedAction.Add:
                    irdsu.Add(ctx, startIndex, items);
                    break;

                case NotifyCollectionChangedAction.Remove:
                    irdsu.Remove(ctx, startIndex, items);
                    break;
                }
            }
            // TODO above stage MAY generate additional update events, e.g. to ISeriesItemValues, that MUST be collected and distributed
            // TODO do it here and not allow things to directly connect to each other, so render pipeline stays under control
            Phase_AxisLimits(cc2 => cc2 is IRequireDataSourceUpdates irdsu2 && irdsu2.UpdateSourceName == ds.Name && cc2 is IProvideValueExtents);
            // Phase IV: render non-axis components (IRequireRender)
            // trigger render on other components since values they track may have changed
            foreach (IRequireRender irr in Render_NotAnAxis.Items.Where(cc2 => !(cc2 is IRequireDataSourceUpdates irdsu2 && irdsu2.UpdateSourceName == ds.Name)) /*Components.Where((cc2) => !(cc2 is IChartAxis) && !(cc2 is IRequireDataSourceUpdates irdsu2 && irdsu2.UpdateSourceName == ds.Name) && (cc2 is IRequireRender))*/)
            {
                var ctx = ls.RenderFor(irr as ChartComponent, Surface, Components, DataContext);
                irr.Render(ctx);
            }
            Phase_AxisLimits(cc2 => !(cc2 is IRequireDataSourceUpdates irdsu2 && irdsu2.UpdateSourceName == ds.Name) && cc2 is IProvideValueExtents);
            // Phase V: axis-finalized
            Phase_AxesFinalized(ls);
            // Phase VI: render axes
            Phase_RenderAxes(ls);
            // Phase VII: transforms
            Phase_Transforms(ls);
        }
 /// <summary>
 /// Phase: axes have seen all values let them render (IRequireRender).
 /// </summary>
 /// <param name="ls">Layout state.</param>
 protected void Phase_RenderAxes(LayoutState ls)
 {
     foreach (var irr in Render_AllAxes.Items /*Axes.Where((cc2) => cc2 is IRequireRender)*/)
     {
         var ctx = ls.RenderFor(irr as ChartComponent, Surface, Components, DataContext);
         _trace.Verbose(() => {
             var axis = irr as IChartAxis;
             return($"limits {(irr as ChartComponent).Name} ({axis.Minimum},{axis.Maximum}) r:{axis.Range} rect:{ctx.Area}");
         });
         irr.Render(ctx);
     }
 }