Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 74 additions & 45 deletions packages/viewer-charts/src/ts/axis/bar-axis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ export type BarCategoryAxis =
| { mode: "category"; domain: CategoricalDomain }
| { mode: "numeric"; domain: AxisDomain; ticks: number[] };

export type BarValueAxis =
| { mode: "category"; domain: CategoricalDomain }
| { mode: "numeric"; domain: AxisDomain; ticks: number[] };

/**
* Render a numeric date-aware axis along the bottom of the plot. Aliases
* the bar-axis bottom variant so heatmap can share the implementation.
Expand Down Expand Up @@ -190,13 +194,11 @@ export interface BarAxesFormatters {
export function renderBarAxesChrome(
canvas: Canvas2D,
catAxis: BarCategoryAxis,
valueDomain: AxisDomain,
valueTicks: number[],
valueAxis: BarValueAxis,
layout: PlotLayout,
theme: Theme,
dpr: number,
altDomain?: AxisDomain,
altTicks?: number[],
altAxis: BarValueAxis | undefined,
isHorizontal = false,
formatters: BarAxesFormatters = {},
): void {
Expand All @@ -212,7 +214,7 @@ export function renderBarAxesChrome(
ctx.moveTo(plot.x, plot.y);
ctx.lineTo(plot.x, plot.y + plot.height);
ctx.lineTo(plot.x + plot.width, plot.y + plot.height);
if (altDomain) {
if (altAxis) {
if (isHorizontal) {
ctx.moveTo(plot.x, plot.y);
ctx.lineTo(plot.x + plot.width, plot.y);
Expand All @@ -238,31 +240,49 @@ export function renderBarAxesChrome(
);
}

drawNumericXAxis(
ctx,
layout,
valueDomain,
valueTicks,
"bottom",
theme,
formatters.value,
);
if (altDomain && altTicks) {
const origMin = layout.paddedXMin;
const origMax = layout.paddedXMax;
layout.paddedXMin = altDomain.min;
layout.paddedXMax = altDomain.max;
if (valueAxis.mode === "category") {
// Categorical value axis on the bottom: reuse the X
// categorical painter. Slot indices on the layout's X
// domain already place each category at its slot pixel.
renderCategoricalXTicks(ctx, layout, valueAxis.domain, theme);
} else {
drawNumericXAxis(
ctx,
layout,
altDomain,
altTicks,
"top",
valueAxis.domain,
valueAxis.ticks,
"bottom",
theme,
formatters.alt,
formatters.value,
);
layout.paddedXMin = origMin;
layout.paddedXMax = origMax;
}

if (altAxis) {
// Alt-axis painter expects the layout's X domain to match
// the alt domain — temporarily swap in `altDomain.min/max`
// for the duration of the call. Categorical alt has no
// top-side painter; render with the bottom-side painter
// (visual overlap with the primary side — documented
// limitation; user-pinned categorical alt is rare).
if (altAxis.mode === "category") {
renderCategoricalXTicks(ctx, layout, altAxis.domain, theme);
} else {
const origMin = layout.paddedXMin;
const origMax = layout.paddedXMax;
layout.paddedXMin = altAxis.domain.min;
layout.paddedXMax = altAxis.domain.max;
drawNumericXAxis(
ctx,
layout,
altAxis.domain,
altAxis.ticks,
"top",
theme,
formatters.alt,
);
layout.paddedXMin = origMin;
layout.paddedXMax = origMax;
}
}
} else {
if (catAxis.mode === "category") {
Expand All @@ -278,31 +298,40 @@ export function renderBarAxesChrome(
);
}

drawYAxis(
ctx,
layout,
valueDomain,
valueTicks,
"left",
theme,
formatters.value,
);
if (altDomain && altTicks) {
const origMin = layout.paddedYMin;
const origMax = layout.paddedYMax;
layout.paddedYMin = altDomain.min;
layout.paddedYMax = altDomain.max;
if (valueAxis.mode === "category") {
renderCategoricalYTicks(ctx, layout, valueAxis.domain, theme);
} else {
drawYAxis(
ctx,
layout,
altDomain,
altTicks,
"right",
valueAxis.domain,
valueAxis.ticks,
"left",
theme,
formatters.alt,
formatters.value,
);
layout.paddedYMin = origMin;
layout.paddedYMax = origMax;
}

if (altAxis) {
if (altAxis.mode === "category") {
renderCategoricalYTicks(ctx, layout, altAxis.domain, theme);
} else {
const origMin = layout.paddedYMin;
const origMax = layout.paddedYMax;
layout.paddedYMin = altAxis.domain.min;
layout.paddedYMax = altAxis.domain.max;
drawYAxis(
ctx,
layout,
altAxis.domain,
altAxis.ticks,
"right",
theme,
formatters.alt,
);
layout.paddedYMin = origMin;
layout.paddedYMax = origMax;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,13 +272,15 @@ export function renderCandlestickChromeOverlay(chart: CandlestickChart): void {
renderBarAxesChrome(
chart._chromeCanvas,
catAxis,
chart._lastYDomain,
chart._lastYTicks,
{
mode: "numeric",
domain: chart._lastYDomain,
ticks: chart._lastYTicks,
},
chart._lastLayout,
theme,
chart._glManager?.dpr ?? 1,
undefined,
undefined,
false,
{
value: chart.getColumnFormatter(valueColumn, "tick"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,23 @@ interface WickCache {
u_color: WebGLUniformLocation | null;
u_resolution: WebGLUniformLocation | null;
u_line_width: WebGLUniformLocation | null;

/**
* `line-uniform` was extended to support the Y-Line `interpolate`
* feature with a per-segment alpha multiplier driven by
* `a_real_start * a_real_end` and `u_interp_alpha`. Wicks are
* always "real" data — every segment renders fully — so we hold
* these locations to neutralize them at draw time (uniform = 1.0,
* constant attribute values = 1.0). Without that, an unset uniform
* defaults to 0 and the fragment alpha collapses to 0, rendering
* the wicks invisible.
*/
u_interp_alpha: WebGLUniformLocation | null;
a_corner: number;
a_start: number;
a_end: number;
a_real_start: number;
a_real_end: number;
}

interface ProgramCache {
Expand Down Expand Up @@ -124,8 +138,14 @@ export class BodyWickGlyph {
"line-uniform",
lineVert,
lineFrag,
["u_projection", "u_color", "u_resolution", "u_line_width"],
["a_corner", "a_start", "a_end"],
[
"u_projection",
"u_color",
"u_resolution",
"u_line_width",
"u_interp_alpha",
],
["a_corner", "a_start", "a_end", "a_real_start", "a_real_end"],
);
const wick: WickCache = {
...wickPartial,
Expand Down Expand Up @@ -375,6 +395,20 @@ function drawWicks(
gl.uniform2f(cache.u_resolution, gl.canvas.width, gl.canvas.height);
gl.uniform1f(cache.u_line_width, chart._pluginConfig.wick_width_px * dpr);

// `line-uniform` was extended for the Y-Line interpolate feature
// with a per-segment alpha multiplier; neutralize it here.
// Constant attribute values (used when the array is disabled) and
// uniform are stable for every draw, so set once after
// `useProgram`. Disabling the arrays first guards against a prior
// Y-Line draw that left them enabled at the same attribute index
// (locations are shared because both programs link from the same
// source).
gl.disableVertexAttribArray(cache.a_real_start);
gl.disableVertexAttribArray(cache.a_real_end);
gl.vertexAttrib1f(cache.a_real_start, 1.0);
gl.vertexAttrib1f(cache.a_real_end, 1.0);
gl.uniform1f(cache.u_interp_alpha, 1.0);

const instancing = getInstancing(glManager);
const { setDivisor } = instancing;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,23 @@ interface OHLCCache {
u_color: WebGLUniformLocation | null;
u_resolution: WebGLUniformLocation | null;
u_line_width: WebGLUniformLocation | null;

/**
* `line-uniform` was extended to support the Y-Line `interpolate`
* feature with a per-segment alpha multiplier driven by
* `a_real_start * a_real_end` and `u_interp_alpha`. The OHLC glyph
* doesn't need that — every segment is "real" — so we hold these
* locations to neutralize them at draw time (uniform = 1.0,
* constant attribute values = 1.0). Without that, an unset uniform
* defaults to 0 and the fragment alpha collapses to 0, rendering
* the entire OHLC glyph invisible.
*/
u_interp_alpha: WebGLUniformLocation | null;
a_corner: number;
a_start: number;
a_end: number;
a_real_start: number;
a_real_end: number;
}

/**
Expand Down Expand Up @@ -76,8 +90,14 @@ export class OHLCGlyph {
"line-uniform",
lineVert,
lineFrag,
["u_projection", "u_color", "u_resolution", "u_line_width"],
["a_corner", "a_start", "a_end"],
[
"u_projection",
"u_color",
"u_resolution",
"u_line_width",
"u_interp_alpha",
],
["a_corner", "a_start", "a_end", "a_real_start", "a_real_end"],
);
this._program = {
...partial,
Expand Down Expand Up @@ -229,6 +249,20 @@ export class OHLCGlyph {
chart._pluginConfig.ohlc_line_width_px * dpr,
);

// `line-uniform` was extended for the Y-Line interpolate
// feature with a per-segment alpha multiplier; neutralize it
// here. Constant attribute values (used when the array is
// disabled) and uniform are stable for every draw, so set
// once after `useProgram`. Disabling the arrays first guards
// against a prior Y-Line draw that left them enabled at the
// same attribute index (locations are shared because both
// programs link from the same source).
gl.disableVertexAttribArray(cache.a_real_start);
gl.disableVertexAttribArray(cache.a_real_end);
gl.vertexAttrib1f(cache.a_real_start, 1.0);
gl.vertexAttrib1f(cache.a_real_end, 1.0);
gl.uniform1f(cache.u_interp_alpha, 1.0);

const instancing = getInstancing(glManager);
const { setDivisor } = instancing;

Expand Down
Loading
Loading