@patricksurry/g3
Version:
A flexible Javascript framework for building steam gauge instrument panels that display live external metrics from flight (or other) simulators like XPlane or MS FS2020
186 lines (170 loc) • 6.09 kB
JavaScript
import * as d3 from 'd3';
import { d3sel } from './common.js';
import { stylable, appendId, identity } from './mixin.js';
function tickvals(vs, step, start, g) {
if (typeof vs !== 'undefined') return vs;
var values = vs;
if (typeof step === 'number') {
let domain = g.measure().domain();
const range = g.measure().range();
domain.sort((a, b) => a - b);
values = [];
for (var v = start ?? Math.ceil(domain[0]/step)*step; v <= domain[1]; v += step)
values.push(v);
if (
g.kind() == 'circular'
&& ((range[0] - range[1]) % 360 == 0)
&& values.includes(domain[0])
&& values.includes(domain[1])
) values.pop();
} else {
values = g.measure().ticks();
}
return values;
}
export function axisSector(vs) {
var values = vs ? vs.slice(): null,
size = 5,
inset = 0;
function sector(sel, g) {
let _ = d3sel(sel)
.append('path')
.attr('d', g.sectorpath(...(values || g.measure().domain()), size, inset));
sector.stylable(_);
}
sector.size = function(_) {
return arguments.length ? (size = _, sector) : size;
}
sector.inset = function(_) {
return arguments.length ? (inset = _, sector) : inset;
}
return stylable(sector).class('g3-axis-sector');
}
export function axisLine() {
return axisSector().size(0).class('g3-axis-line');
}
export function axisTicks(vs) {
var shape = 'tick',
size = 10,
width = 1,
inset = 0,
values = vs && vs.slice(),
step, start;
function ticks(sel, g) {
let vs = tickvals(values, step, start, g);
let _ = d3sel(sel).append('g');
ticks.class('g3-axis-ticks-' + shape).stylable(_);
_ = _.selectAll(null)
.data(vs)
.enter().append('g')
.attr('transform', d => g.marktransform(d, inset));
switch(shape) {
case 'dot':
_.append('circle').attr('r', size);
break;
case 'wedge':
_.append('path').attr('d', `M 0,${size} L ${width/2},0 L ${-width/2},0 z`)
break;
case 'rect':
_.append('rect').attr('width', width).attr('height', size).attr('x', -width/2);
break;
default:
_.append('path').attr('d', d3.line()([[0, 0], [0, size]]));
}
}
ticks.step = function(_) {
return arguments.length ? (step = _, ticks) : step;
}
ticks.start = function(_) {
return arguments.length ? (start = _, ticks) : start;
}
ticks.shape = function(_) {
return arguments.length ? (shape = _, ticks) : shape;
}
ticks.size = function(_) {
return arguments.length ? (size = _, ticks) : size;
}
ticks.width = function(_) {
return arguments.length ? (width = _, ticks) : width;
}
ticks.inset = function(_) {
return arguments.length ? (inset = _, ticks) : inset;
}
return stylable(ticks).class('g3-axis-ticks');
}
export function axisLabels(vs) {
const isMap = typeof vs === 'object' && !Array.isArray(vs),
orientations = ['fixed', 'relative', 'upward', 'clockwise', 'counterclockwise'];
var orient = 'fixed',
size = 20,
inset = 25,
rotate = 0,
values = isMap ? Object.keys(vs) : vs,
format = isMap ? v => vs[v] : identity,
step, start;
function labels(sel, g) {
const vs = tickvals(values, step, start, g),
circPath = orient.endsWith('clockwise'),
pathId = circPath ? appendId('axis-label-path-') : undefined;
let _ = d3sel(sel).append('g');
labels.stylable(_);
_ = _.selectAll(null)
.data(vs)
.enter().append('g');
if (circPath) {
const r = g.r() - inset,
cw = orient == 'clockwise' ? 1: 0;
_.append('path')
.attr('id', (d, i) => `${pathId}-${i}`)
.attr('d', `M 0,${r} A ${r},${r},0,1,${cw},0,-${r} A ${r},${r},0,1,${cw},0,${r}`)
.attr('style', 'visibility: hidden')
.attr('transform', d => g.metrictransform(d));
}
_ = _.append('text')
.attr('font-size', size);
if (circPath) {
_ = _.append('textPath')
.attr('startOffset', '50%')
.attr('href', (d, i) => `#${pathId}-${i}`);
} else {
_.attr('transform', d => {
let xform = g.marktransform(d, inset),
rot = rotate;
if (g.kind() == 'circular') {
if (orient == 'fixed') xform += ' ' + g.metrictransform(d, true);
else if (orient == 'upward') {
const v = ((g.measure()(d + rot) % 360) + 360) % 360;
if (90 < v && v < 270) rot += 180;
}
}
if (rot) xform += ` rotate(${rot})`;
return `${xform}`;
});
}
_.text(format);
}
labels.step = function(_) {
return arguments.length ? (step = _, labels) : step;
}
labels.start = function(_) {
return arguments.length ? (start = _, labels) : start;
}
labels.orient = function(_) {
if (_ && !orientations.includes(_))
throw `g3.axisLabels().orient() unknown orientation '${_}'`
return arguments.length ? (orient = _, labels) : orient;
}
labels.size = function(_) {
return arguments.length ? (size = _, labels) : size;
}
labels.inset = function(_) {
return arguments.length ? (inset = _, labels) : inset;
}
labels.rotate = function(_) {
return arguments.length ? (rotate = _, labels) : rotate;
}
labels.format = function(_) {
return arguments.length ? (format = _, labels) : format;
}
return stylable(labels).class('g3-axis-labels');
}