in src/core/prototypes/plot_segments/axis.ts [499:927]
public renderLine(
x: number,
y: number,
angle: number,
side: number,
offset?: number
): Group {
const g = makeGroup([]);
const style = this.style;
const rangeMin = this.rangeMin;
const rangeMax = this.rangeMax;
const tickSize = style.tickSize;
const lineStyle: Style = {
strokeLinecap: "square",
strokeColor: style.lineColor,
};
AxisRenderer.textMeasurer.setFontFamily(style.fontFamily);
AxisRenderer.textMeasurer.setFontSize(style.fontSize);
if (this.oppositeSide) {
side = -side;
}
//shift axis for scrollbar space
if (this.scrollRequired && this.shiftAxis) {
if (angle === 90) {
x += side * AxisRenderer.SCROLL_BAR_SIZE;
}
if (angle === 0) {
y += -side * AxisRenderer.SCROLL_BAR_SIZE;
}
}
const cos = Math.cos(Geometry.degreesToRadians(angle));
const sin = Math.sin(Geometry.degreesToRadians(angle));
const x1 = x + rangeMin * cos;
const y1 = y + rangeMin * sin;
const x2 = x + rangeMax * cos;
const y2 = y + rangeMax * sin;
// Base line
if (style.showBaseline) {
g.elements.push(makeLine(x1, y1, x2, y2, lineStyle));
}
// Ticks
const ticksData = this.ticks.map((x) => x.position);
const visibleTicks = ticksData.concat([rangeMin, rangeMax]);
if (style.showTicks) {
for (const tickPosition of visibleTicks) {
const tx = x + tickPosition * cos;
const ty = y + tickPosition * sin;
const dx = side * tickSize * sin;
const dy = -side * tickSize * cos;
g.elements.push(makeLine(tx, ty, tx + dx, ty + dy, lineStyle));
}
}
// Tick texts
const ticks = this.ticks.map((x) => {
return {
position: x.position,
label: x.label,
measure: AxisRenderer.textMeasurer.measure(x.label),
};
});
let maxTextWidth = 0;
let maxTickDistance = 0;
for (let i = 0; i < ticks.length; i++) {
maxTextWidth = Math.max(maxTextWidth, ticks[i].measure.width);
if (i > 0) {
maxTickDistance = Math.max(
maxTickDistance,
Math.abs(ticks[i - 1].position - ticks[i].position)
);
}
}
for (const tick of ticks) {
const tx = x + tick.position * cos,
ty = y + tick.position * sin;
const offset = 3;
const dx = side * (tickSize + offset) * sin,
dy = -side * (tickSize + offset) * cos;
if (Math.abs(cos) < 0.5) {
if (
style.wordWrap ||
(typeof tick.label === "string" &&
splitStringByNewLine(tick.label).length > 1)
) {
let textContent: string[] = splitByWidth(
replaceSymbolByTab(replaceSymbolByNewLine(tick.label)),
maxTickDistance,
10000,
style.fontFamily,
style.fontSize
);
textContent = textContent.flatMap((line) =>
splitStringByNewLine(line)
);
const lines: Graphics.Element[] = [];
for (let index = 0; index < textContent.length; index++) {
const [px, py] = TextMeasurer.ComputeTextPosition(
0,
0,
AxisRenderer.textMeasurer.measure(textContent[index]),
style.verticalText ? "middle" : side * sin < 0 ? "right" : "left",
style.verticalText
? side * sin < 0
? "bottom"
: "top"
: "middle",
0
);
const text = makeText(
px,
py -
style.fontSize * index +
(side * cos > 0
? 0
: (textContent.length * style.fontSize - style.fontSize) / 2),
textContent[index],
style.fontFamily,
style.fontSize,
{
fillColor: style.tickColor,
backgroundColor: style.tickTextBackgroudColor,
backgroundColorId: style.tickTextBackgroudColorId,
}
);
lines.push(text);
}
const gText = makeGroup(lines);
gText.transform = {
x: tx + dx,
y: ty + dy,
angle: style.verticalText ? angle : 0,
};
g.elements.push(gText);
} else {
// 60 ~ 120 degree
const [px, py] = TextMeasurer.ComputeTextPosition(
0,
0,
tick.measure,
side * sin < 0 ? "right" : "left",
"middle",
0
);
const gText = makeGroup([
makeText(
px,
py,
tick.label,
style.fontFamily,
style.fontSize,
{
fillColor: style.tickColor,
backgroundColor: style.tickTextBackgroudColor,
backgroundColorId: style.tickTextBackgroudColorId,
},
this.plotSegment && this.dataFlow
? {
enableSelection: this.data.enableSelection,
glyphIndex: 1,
rowIndices: applySelectionFilter(
this.data,
this.plotSegment.table,
ticks.indexOf(tick),
this.dataFlow
),
plotSegment: this.plotSegment,
}
: undefined
),
]);
gText.transform = {
x: tx + dx,
y: ty + dy,
angle: style.verticalText ? (sin > 0 ? angle - 90 : angle + 90) : 0,
};
g.elements.push(gText);
}
} else if (Math.abs(cos) < Math.sqrt(3) / 2) {
const [px, py] = TextMeasurer.ComputeTextPosition(
0,
0,
tick.measure,
side * sin < 0 ? "right" : "left",
"middle",
0
);
const gText = makeGroup([
makeText(
px,
py,
tick.label,
style.fontFamily,
style.fontSize,
{
fillColor: style.tickColor,
backgroundColor: style.tickTextBackgroudColor,
backgroundColorId: style.tickTextBackgroudColorId,
},
this.plotSegment && this.dataFlow
? {
enableSelection: this.data.enableSelection,
glyphIndex: 1,
rowIndices: applySelectionFilter(
this.data,
this.plotSegment.table,
ticks.indexOf(tick),
this.dataFlow
),
plotSegment: this.plotSegment,
}
: undefined
),
]);
gText.transform = {
x: tx + dx,
y: ty + dy,
angle: style.verticalText ? (sin > 0 ? angle - 90 : angle + 90) : 0,
};
g.elements.push(gText);
} else {
if (
!style.wordWrap &&
maxTextWidth > maxTickDistance &&
typeof tick.label === "string" &&
splitStringByNewLine(tick.label).length === 1
) {
const [px, py] = TextMeasurer.ComputeTextPosition(
0,
0,
tick.measure,
style.verticalText
? side * cos > 0
? "right"
: "left"
: style.wordWrap
? "middle"
: side * cos > 0
? "right"
: "left",
style.verticalText
? "middle"
: style.wordWrap
? "middle"
: side * cos > 0
? "top"
: "bottom",
0
);
const gText = makeGroup([
makeText(
px,
py,
tick.label,
style.fontFamily,
style.fontSize,
{
fillColor: style.tickColor,
backgroundColor: style.tickTextBackgroudColor,
backgroundColorId: style.tickTextBackgroudColorId,
},
this.plotSegment && this.dataFlow
? {
enableSelection: this.data.enableSelection,
glyphIndex: 1,
rowIndices: applySelectionFilter(
this.data,
this.plotSegment.table,
ticks.indexOf(tick),
this.dataFlow
),
plotSegment: this.plotSegment,
}
: undefined
),
]);
gText.transform = {
x: tx + dx,
y: ty + dy,
angle: style.verticalText
? cos > 0
? 90 + angle
: 90 + angle - 180
: cos > 0
? 36 + angle
: 36 + angle - 180,
};
g.elements.push(gText);
} else {
if (
style.wordWrap ||
(typeof tick.label === "string" &&
splitStringByNewLine(tick.label).length > 1)
) {
let textContent = [
replaceSymbolByTab(replaceSymbolByNewLine(tick.label)),
];
if (style.wordWrap) {
textContent = splitByWidth(
replaceSymbolByTab(replaceSymbolByNewLine(tick.label)),
maxTickDistance,
10000,
style.fontFamily,
style.fontSize
);
}
textContent = textContent.flatMap((line) =>
splitStringByNewLine(line)
);
const lines: Graphics.Element[] = [];
for (let index = 0; index < textContent.length; index++) {
const [px, py] = TextMeasurer.ComputeTextPosition(
0,
0,
AxisRenderer.textMeasurer.measure(textContent[index]),
style.wordWrap ? "middle" : side * cos > 0 ? "right" : "left",
side * cos > 0 ? "top" : "bottom",
0
);
const text = makeText(
px,
py -
style.fontSize * index +
(side * cos > 0 ? 0 : textContent.length * style.fontSize),
textContent[index],
style.fontFamily,
style.fontSize,
{
fillColor: style.tickColor,
backgroundColor: style.tickTextBackgroudColor,
backgroundColorId: style.tickTextBackgroudColorId,
},
this.plotSegment && this.dataFlow
? {
enableSelection: this.data.enableSelection,
glyphIndex: 1,
rowIndices: applySelectionFilter(
this.data,
this.plotSegment.table,
ticks.indexOf(tick),
this.dataFlow
),
plotSegment: this.plotSegment,
}
: undefined
);
lines.push(text);
}
const gText = makeGroup(lines);
gText.transform = {
x: tx + dx,
y: ty + dy,
angle: style.verticalText
? style.wordWrap
? 0
: cos > 0
? 90 + angle
: 90 + angle - 180
: style.wordWrap
? 0
: cos > 0
? 36 + angle
: 36 + angle - 180,
};
g.elements.push(gText);
} else {
const [px, py] = TextMeasurer.ComputeTextPosition(
0,
0,
tick.measure,
style.verticalText
? side * cos > 0
? "right"
: "left"
: "middle",
style.verticalText ? "middle" : side * cos > 0 ? "top" : "bottom",
0
);
const gText = makeGroup([
makeText(
px,
py,
tick.label,
style.fontFamily,
style.fontSize,
{
fillColor: style.tickColor,
backgroundColor: style.tickTextBackgroudColor,
backgroundColorId: style.tickTextBackgroudColorId,
},
this.plotSegment && this.dataFlow
? {
enableSelection: this.data.enableSelection,
glyphIndex: 1,
rowIndices: applySelectionFilter(
this.data,
this.plotSegment.table,
ticks.indexOf(tick),
this.dataFlow
),
plotSegment: this.plotSegment,
}
: undefined
),
]);
gText.transform = {
x: tx + dx,
y: ty + dy,
angle: style.verticalText ? 90 + angle : 0,
};
g.elements.push(gText);
}
}
}
}
if (offset) {
g.transform = {
x: angle == 90 ? offset : 0,
y: angle == 90 ? 0 : offset,
angle: 0,
};
}
return g;
}