Drawing

Reference

Drawing

The contracts for chart drawing tools: the tool interface and base class, the time/price anchor model, the per-frame render context, hit-test geometry helpers, the discovery attributes/descriptors, and the line/placement enums. All types live under SabrTrader.Pipeline.Drawing (Fibonacci helpers under SabrTrader.Pipeline.Drawing.Tools).

Drawing tools — SabrTrader.Pipeline.Drawing

IDrawingTool INTERFACE

One placed instance of a drawing tool (trend line, ray, horizontal line, …). State, render, and hit-test all live on the tool; the chart's drawing layer owns the list and coordinates the lifecycle. Most authors derive from DrawingToolBase rather than implementing this directly.

Member Description
Guid Id { get; } Process-unique identity, generated at construction.
string ToolName { get; } Display-only name (e.g. "Trend Line"); sourced from DrawingToolMetadataAttribute.
string PanelId { get; set; } Panel the tool belongs to. DrawingToolPanelIds.Main = main candle panel; any other value matches a subpanel id. Render + hit-test filter by this.
bool IsSelected { get; set; } True when selected (handles drawn, draggable, deletable).
bool IsPlaced { get; set; } True once the user has finalised the tool (all anchors placed).
int AnchorCount { get; } Currently-placed anchor count.
int MaxAnchorCount { get; } Upper bound on anchor count. Fixed tools: equals AnchorCount; variable tools: a cap or int.MaxValue.
DrawingPlacementMode PlacementMode { get; } How the placement state machine gathers anchors.
bool CanAddAnchor { get; } True when another anchor can still be appended (AnchorCount < MaxAnchorCount).
IReadOnlyList<DrawingAnchor> Anchors { get; } Currently-set anchors; uninitialised slots are zero-valued.
ChartColor Color { get; set; } Stroke colour.
double LineWidthPx { get; set; } Line width in pixels (> 0).
LineStyle LineStyle { get; set; } Stroke style.
DrawingScope Scope { get; set; } Whether the drawing shows on this chart only (default) or on every open chart of the same symbol. See DrawingScope.
void SetAnchor(int index, DrawingAnchor anchor) Set a single anchor by index; validates against AnchorCount.
void AddAnchor(DrawingAnchor anchor) Append an anchor (open-ended / drag-to-paint). Throws InvalidOperationException when CanAddAnchor is false.
bool RemoveLastAnchor() Remove the most-recently-appended anchor; false when nothing to remove.
void Move(TimeSpan dTime, double dPrice) Move all anchors by a (time, price) delta. Constrained tools override.
void OnRender(IDrawingToolRenderContext ctx) Render the tool; called every repaint frame.
bool HitTest(double pixelX, double pixelY, IDrawingToolRenderContext ctx, double tolerancePx = 6) True when the pixel point hits the tool within tolerance; drives hover-highlight + click-to-select.
IDrawingTool.cspublic interface IDrawingTool
{
    Guid Id { get; }
    string ToolName { get; }
    string PanelId { get; set; }
    bool IsSelected { get; set; }
    bool IsPlaced { get; set; }
    int AnchorCount { get; }
    int MaxAnchorCount { get; }
    DrawingPlacementMode PlacementMode { get; }
    bool CanAddAnchor { get; }
    IReadOnlyList<DrawingAnchor> Anchors { get; }
    ChartColor Color { get; set; }
    double LineWidthPx { get; set; }
    LineStyle LineStyle { get; set; }
    DrawingScope Scope { get; set; }
    void SetAnchor(int index, DrawingAnchor anchor);
    void AddAnchor(DrawingAnchor anchor);
    bool RemoveLastAnchor();
    void Move(TimeSpan dTime, double dPrice);
    void OnRender(IDrawingToolRenderContext ctx);
    bool HitTest(double pixelX, double pixelY, IDrawingToolRenderContext ctx, double tolerancePx = 6);
}

DrawingToolBase

Default IDrawingTool base. Anchor storage, identity, selection, default styling (Color / LineWidth / LineStyle), and reading the metadata attribute for ToolName all live in the base — a subclass only implements OnRender + HitTest. Two construction modes: fixed-anchor (base(anchorCount: N)) pre-allocates N slots and runs DrawingPlacementMode.FixedAnchors; variable-anchor (base(mode, maxAnchorCount, initialAnchorCount)) starts at the initial count and grows via AddAnchor.

Member Description
protected DrawingToolBase(int anchorCount) Fixed-anchor ctor: pre-allocates anchorCount default anchors; mode = FixedAnchors. Throws if anchorCount < 1.
protected DrawingToolBase(DrawingPlacementMode mode, int maxAnchorCount = int.MaxValue, int initialAnchorCount = 0) Variable-anchor ctor. Throws ArgumentException if mode == FixedAnchors (use the int ctor).
int MinAnchorsForFinalize { get; protected set; } Minimum anchors before a finalize gesture is accepted (open-ended / drag-to-paint). Defaults to 2.
ChartColor Color { get; set; } Stroke colour; annotated [DrawingToolProperty]. Defaults to DodgerBlue.
LineStyle LineStyle { get; set; } Stroke pattern; annotated [DrawingToolProperty]. Defaults to Solid.
double LineWidthPx { get; set; } Stroke thickness in px; annotated [DrawingToolProperty]. Setter throws ArgumentOutOfRangeException when <= 0.
DrawingScope Scope { get; set; } Attach-to setting; annotated [DrawingToolProperty] (Category "Behavior"). Defaults to ThisChart.
protected void DrawLine(IDrawingToolRenderContext ctx, double x0, double y0, double x1, double y1) Convenience stroke through the renderer using the tool's own Color, LineWidthPx and LineStyle.
protected void DrawLine(IDrawingToolRenderContext ctx, double x0, double y0, double x1, double y1, ChartColor color, double widthPx) Same, with an explicit colour and width (still applies the tool's LineStyle).
public abstract void OnRender(IDrawingToolRenderContext ctx) Implement to paint the tool.
public abstract bool HitTest(double pixelX, double pixelY, IDrawingToolRenderContext ctx, double tolerancePx = 6) Implement the hit-test.
public virtual void Move(TimeSpan dTime, double dPrice) Shifts every anchor by the delta; override for constrained tools.

The interface members (Id, ToolName, PanelId, IsSelected, IsPlaced, AnchorCount, MaxAnchorCount, PlacementMode, CanAddAnchor, Anchors, SetAnchor, AddAnchor, RemoveLastAnchor) are all implemented by the base.

Rectangle.csusing SabrTrader.Pipeline.Drawing;

[DrawingToolMetadata("Rectangle", Category = "Shapes", AnchorCount = 2)]
public sealed class Rectangle : DrawingToolBase
{
    public Rectangle() : base(anchorCount: 2) { }

    public override void OnRender(IDrawingToolRenderContext ctx)
    {
        var p1 = ctx.AnchorToPixel(Anchors[0]);
        var p2 = ctx.AnchorToPixel(Anchors[1]);
        // ... draw the outline via ctx.Renderer ...
        if (ctx.IsSelected) { ctx.DrawHandle(p1.X, p1.Y); ctx.DrawHandle(p2.X, p2.Y); }
    }

    public override bool HitTest(double px, double py, IDrawingToolRenderContext ctx, double tol = 6)
    {
        var p1 = ctx.AnchorToPixel(Anchors[0]);
        var p2 = ctx.AnchorToPixel(Anchors[1]);
        return DrawingGeometry.DistanceToRectangleOutline(px, py, p1.X, p1.Y, p2.X, p2.Y) <= tol;
    }
}

DrawingAnchor

A single anchor point: a (time, price) pair in the bar series' canonical timeline. Anchors are bar-index-independent, so a tool survives live bar updates, scroll, replays, and bar-type changes. Time-only tools ignore Price; price-only tools ignore Time — the unused coordinate is stored as a sentinel and never read.

DrawingAnchor.cspublic readonly record struct DrawingAnchor(DateTime Time, double Price)
{
    public static DrawingAnchor PriceOnly(double price);   // Time = DateTime.MinValue
    public static DrawingAnchor TimeOnly(DateTime time);   // Price = double.NaN
}

DrawingGeometry

Static 2-D geometry helpers for hit tests. All inputs are pixel coordinates, so call them after converting anchors via IDrawingToolRenderContext.AnchorToPixel. Outline helpers return a non-negative distance — compare against the tool's pixel tolerance.

Signature Description
double DistanceToSegment(px, py, x1, y1, x2, y2) Perpendicular distance to a finite line segment.
double DistanceToRay(px, py, x1, y1, x2, y2) Distance to a ray from (x1,y1) through (x2,y2) extending to infinity.
double DistanceToHorizontalLine(py, lineY) Distance to an infinite horizontal line at y = lineY.
double DistanceToVerticalLine(px, lineX) Distance to an infinite vertical line at x = lineX.
double DistanceToHorizontalRay(px, py, lineY, startX, endX) Distance to a horizontal ray from startX (to endX or +∞).
double DistanceToRectangleOutline(px, py, x1, y1, x2, y2) Distance to the closest edge of an axis-aligned rectangle (outline only).
double DistanceToClosedPolyline(px, py, ReadOnlySpan<(double X, double Y)> points) Distance to a closed polyline (last point auto-connects to first).
double DistanceToOpenPolyline(px, py, ReadOnlySpan<(double X, double Y)> points) Distance to an open polyline.
double DistanceToCircleOutline(px, py, cx, cy, radius) Absolute distance to a circle outline.
double DistanceToEllipseOutline(px, py, x1, y1, x2, y2) Distance to an axis-aligned ellipse outline (64-sample approximation).
(double X, double Y) QuadraticBezier((double X, double Y) p0, (double X, double Y) p1, (double X, double Y) p2, double t) Evaluate a quadratic Bézier at t ∈ [0, 1].

IDrawingToolRenderContext INTERFACE

Per-frame context handed to OnRender and HitTest. Encapsulates the renderer, viewport, plot-area rectangle, selection state, and time→pixel conversion. All the plumbing lives here so tool authors write almost-trivial render/hit-test methods. The instrument-metadata members default to null/identity so headless and test shims need not supply them.

Member Description
IChartRenderer Renderer { get; } Active renderer for the current frame.
ChartViewport Viewport { get; } Bar↔pixel + price↔pixel transforms for the current paint.
PlotAreaRect PlotArea { get; } Pixel rectangle of the main candle pane (excludes axis strips).
bool IsSelected { get; } True when the tool being rendered/hit-tested is selected.
double TimeToBarIndex(DateTime time) Convert an anchor time to a (possibly fractional) bar index.
(double X, double Y) AnchorToPixel(DrawingAnchor anchor) Convenience: anchor → pixel (X, Y), combining TimeToBarIndex + viewport transforms.
void DrawHandle(double x, double y, ChartColor? fill = null, double sizePx = 7) Draw a small selection handle at a pixel point.
IBarSeries? Bars { get; } Bar series driving the chart, when the host has one. Default null.
double? TickSize { get; } Instrument minimum price increment, when known. Default null.
double? PointValue { get; } Currency value of a 1.0 price move per contract, when known. Default null.
double? PipSize { get; } Instrument pip size (forex-style quoting), when known. Default null.
QuantityUnit QuantityUnit { get; } Unit the instrument's quantity is counted in. Default QuantityUnit.Contract.
double? QuantityStep { get; } Smallest tradable quantity increment, when known. Default null.
IOrderFlowWindowSource? OrderFlowWindows { get; } Host seam building volume-at-price profiles for arbitrary windows. Default null.

Discovery — attributes & descriptors

DrawingToolMetadataAttribute

Marks a class as a discoverable drawing tool — read at assembly-scan time to populate the toolbar menu, hotkeys, icons, and anchor-count requirements. One per tool class; no registration boilerplate. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)].

Member Description
string Name { get; } Display name (ctor argument; required, non-blank).
string Category { get; init; } Toolbar submenu group. Default "Lines".
string? Hotkey { get; init; } Optional global hotkey (e.g. "Alt+T").
string? IconKey { get; init; } Key into the icon-resource dictionary.
int AnchorCount { get; init; } Anchor points the user clicks before finalising. Default 2.
int Order { get; init; } Display order within the category (lower first). Default 0.
string? Description { get; init; } Tooltip text.

DrawingToolPropertyAttribute

Marks a tool property as user-editable in the generic properties dialog. Opt-in: infrastructure properties stay hidden. DrawingToolBase already annotates Color / LineWidth / LineStyle, so every subclass surfaces those three by default; decorate your own for extra editors. Constraints are advisory (the dialog enforces them; programmatic writes are not clamped). [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)].

Member Description
string? DisplayName { get; set; } Editor label (defaults to property name).
string? Description { get; set; } One-line tooltip.
string Category { get; set; } Group bucket. Default "General".
int Order { get; set; } Order within the category (lower first).
object? MinValue { get; set; } Advisory numeric lower bound. Null = none.
object? MaxValue { get; set; } Advisory numeric upper bound. Null = none.
object? Step { get; set; } Spinner increment. Null = editor decides.

DrawingToolPropertyDescriptor

Reflection metadata for one editable property, consumed by the properties dialog to render editors and write input back onto the live tool. Use Read / Write rather than reflecting yourself so the dialog and headless paths share the same value-coercion contract. (The constructor is internal — instances are produced by the host's discovery.)

Member Description
string Name { get; } CLR property name on the tool type.
string DisplayName { get; } Human-readable label.
string Description { get; } Tooltip; empty when none.
string Category { get; } Group bucket.
int Order { get; } Sort order within the category.
Type PropertyType { get; } Underlying property type (e.g. double, LineStyle, ChartColor).
object? MinValue { get; } Advisory min. Null = none.
object? MaxValue { get; } Advisory max. Null = none.
object? Step { get; } Spinner increment. Null = editor decides.
object? Read(IDrawingTool tool) Read the current value off a live tool.
void Write(IDrawingTool tool, object? value) Write a value back; the value must be assignable to PropertyType.

DrawingToolDescriptor

Frozen description of a discovered tool type: its metadata plus a factory delegate that builds a fresh instance. A sealed record returned by the host's drawing-tool catalog.

DrawingToolDescriptor.cspublic sealed record DrawingToolDescriptor(
    Type ToolType,
    string Name,
    string Category,
    string? Hotkey,
    string? IconKey,
    int AnchorCount,
    int Order,
    string? Description,
    Func<IDrawingTool> Factory);

DrawingToolPanelIds

Well-known panel-id constants for IDrawingTool.PanelId.

Member Value / Meaning
const string Main "main" — the chart's main candle/price panel.

Enums

LineStyle

Stroke style for drawing-tool segments. Mirrors PlotStyle's line variants.

Value Meaning
Solid Continuous stroke.
Dashed Dashed stroke.
Dotted Dotted stroke.

DrawingPlacementMode

How a tool gathers its anchors during placement. The chart's interaction state machine dispatches on this value; each mode has a distinct user gesture.

Value Meaning
FixedAnchors Click once per anchor; placement completes when AnchorCount clicks land. Default for line / rectangle / triangle / arrow.
OpenEnded Click to append anchors, double-click or Escape to finalize (must meet MinAnchorsForFinalize). Used by Path, Polyline.
DragToPaint Mouse-down + drag samples points; mouse-up finalizes. Used by Brush, Highlighter.

DrawingScope

Where a placed drawing is shown. Surfaced on IDrawingTool.Scope as an "Attach to" [DrawingToolProperty]; each member's [Description] is the dropdown label the editor renders in place of the raw name.

Value Meaning
ThisChart "This chart" — the drawing lives only on the chart it was placed on (default).
AllCharts "All charts (same symbol)" — the drawing is shared and kept in sync across every open chart of the same symbol.

ElliottDegree

Wave degree for Elliott-wave labelling tools, largest to smallest. Each member's [Description] is its dropdown label (with the notation shown in parentheses). ElliottDegrees.Label(ElliottDegree degree, string key) formats a single wave label for a degree and key.

Value Notation
GrandSupercycle [I] / [a]
Supercycle (I) / (a)
Cycle I / a
Primary [1] / [A]
Intermediate (1) / (A)
Minor 1 / A
Minute i / a
Minuette (i) / (a)
Subminuette i / a
Micro 1 / A
Submicro (1) / (A)
Miniscule 1 / A

Fibonacci levels — SabrTrader.Pipeline.Drawing.Tools

FibLevels

Standard Fibonacci ratios used by the Fib retracement / extension / channel / wedge / fan family. Centralised so changing a default updates every tool at once. Static fields are the raw ratio ladders; the Default*Set() factories build a ready-to-edit FibLevelSet.

Member Description
static readonly double[] StandardRetracements { 0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0 } — classic retracement ladder.
static readonly double[] StandardExtensions { 1.272, 1.414, 1.618, 2.0, 2.618, 3.618, 4.236 } — extension ratios (> 1.0).
static readonly double[] StandardRetracementsAndExtensions { 0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0, 1.272, 1.618, 2.0, 2.618 }.
static readonly double[] StandardSpeedRatios { 0.382, 0.5, 0.618 } — speed-resistance fan / arc ratios.
static readonly int[] StandardTimeZones { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 } — time-zone multiples.
static readonly (int Price, int Time)[] GannAngles Gann fan slopes from 1×8 to 8×1 (1×1 = central reference).
static FibLevelSet DefaultRetracementSet() Visible retracement ladder paired with default colours.
static FibLevelSet DefaultExtensionSet() Visible retracements + extensions with the extension palette.
static FibLevelSet DefaultRetracementSetInheritColor() Retracement ladder, every level inherits the tool's stroke colour.
static FibLevelSet DefaultSpeedRatioSetInheritColor() Three speed ratios, inherit stroke colour.
static FibLevelSet DefaultCircleRatioSetInheritColor() Fib-circle ring set, inherit stroke colour.

FibLevel — one configurable level (a sealed class):

Member Description
double Ratio { get; set; } Ratio applied to the tool's anchor range.
ChartColor? Color { get; set; } Per-level stroke override; null = inherit the tool's stroke colour.
bool IsVisible { get; set; } Render flag. False hides the level without removing it. Default true.
FibLevel(double ratio, ChartColor? color = null, bool visible = true) Convenience constructor (also has a parameterless ctor).
FibLevel Clone() Deep copy.

FibLevelSet — ordered, mutable collection of levels; inherits ObservableCollection<FibLevel> so the WPF list editor binds directly:

Member Description
FibLevelSet() / FibLevelSet(IEnumerable<FibLevel> items) Empty or seeded from an item sequence.
FibLevelSet Clone() Deep copy — every level is cloned, not shared.
void ReplaceWith(IEnumerable<FibLevel> other) Replace contents in-place (Clear+Add) so change notifications fire.