Compare commits

...

42 Commits

Author SHA1 Message Date
Shefali Joshi
9f1e38bacd Merge branch 'master' into log-plots-2/d3-ticks 2022-04-04 09:10:42 -07:00
Joshi
de8240e189 Merge branch 'log-plots-2/d3-ticks' of https://github.com/nasa/openmct into log-plots-2/d3-ticks 2022-03-31 09:22:38 -07:00
Joshi
c369b7332e Merge branch 'master' of https://github.com/nasa/openmct into log-plots-2/d3-ticks 2022-03-31 09:22:31 -07:00
Joe Pea
504939d64a fix incorrect logMode state during initialization 2022-03-30 21:32:20 -07:00
Joe Pea
11c8a39758 Merge branch 'master' into log-plots-2/d3-ticks 2022-03-30 15:33:11 -07:00
Joshi
ff8708b269 Merge branch 'master' of https://github.com/nasa/openmct into log-plots-2/d3-ticks 2022-03-30 13:44:38 -07:00
Shefali Joshi
b6c74e784e Merge branch 'master' into log-plots-2/d3-ticks 2022-03-30 10:48:09 -07:00
Joe Pea
224d20fba9 Merge branch 'master' into log-plots-2/d3-ticks 2022-03-29 18:44:34 -07:00
Joe Pea
68c044fa1e Merge branch 'type-annotations' into log-plots-2/d3-ticks 2022-03-28 15:41:38 -07:00
Joe Pea
a6d86d470f Merge branch 'master' into type-annotations 2022-03-28 15:14:34 -07:00
Joe Pea
6a01ce0c2d lint fix 2022-03-28 11:16:05 -07:00
John Hill
064fa80fdc Merge branch 'master' into type-annotations 2022-03-25 14:00:11 -07:00
John Hill
08e84c9ad3 Merge branch 'master' into type-annotations 2022-03-22 17:50:23 -07:00
Joe Pea
f5d4e75c52 undo the changes to MctChart, improve it later 2022-03-22 11:34:14 -07:00
Joe Pea
5f816179d6 Merge branch 'master' into type-annotations 2022-03-22 10:37:12 -07:00
Joe Pea
817f8411f1 Merge branch 'master' into type-annotations 2022-03-15 13:02:05 -07:00
Joe Pea
6a77fe6f03 improve plot series reset as per Shefali, and adjust displayRange on logMode change 2022-03-07 16:55:26 -08:00
Joe Pea
e7727bc9ad add log mode status to UI in non-edit mode 2022-03-07 16:35:48 -08:00
Joshi
116aee9c5f Add d3 axis with symlog 2022-02-17 20:05:05 -08:00
Joe Pea
f7a0c030fa Add UI to toggle log mode.
This almost works: ticks don't redraw until a zoom or pan is performed.
2022-02-16 23:39:49 -08:00
John Hill
c87c9f48fd Merge branch 'master' into type-annotations 2022-02-16 14:45:12 -08:00
Joe Pea
0d6de7dfdb Merge branch 'type-annotations' into log-plots 2022-02-15 13:20:17 -08:00
Joe Pea
f05e895e3a update types, avoid runtime behavior in type definition that breaks SeriesCollection 2022-02-15 13:20:03 -08:00
Joe Pea
429ca484ed further simplify plot canvas creation 2022-02-15 12:39:27 -08:00
Joe Pea
56a2e63600 further simplify plot canvas creation 2022-02-15 12:36:56 -08:00
Joe Pea
e6c2a118f7 Merge branch 'type-annotations' into log-plots 2022-02-15 11:31:40 -08:00
Joe Pea
c917914183 Merge branch 'master' into type-annotations 2022-02-15 11:31:28 -08:00
Joe Pea
fb1d6c0187 use a symlog function to cover negative values 2022-02-08 16:24:06 -08:00
Joe Pea
3b42490883 use getLogTicks instead of getLogTicks2. It is currently more useful, especially when zooming out 2022-02-02 20:21:58 -08:00
Joe Pea
5b756d3588 make unevenly spread (but numericaly even) ticks 2022-02-02 16:30:52 -08:00
Joe Pea
63e8fb53f8 add more ticks to log plot axis (needs more work, but it works) 2022-02-02 16:18:53 -08:00
Joe Pea
72aea12f68 Merge branch 'master' into type-annotations 2022-01-31 12:46:22 -08:00
unlikelyzero
c9d96565fa Add karma and jasmine, too 2022-01-27 06:50:56 -08:00
unlikelyzero
fe447a0d4c add mocha types 2022-01-27 06:47:38 -08:00
Joe Pea
d21adc6f69 WIP log plot values and ticks experiment 2022-01-26 12:02:24 -08:00
Joe Pea
607acf9626 Merge branch 'master' into type-annotations 2022-01-26 12:02:00 -08:00
Joe Pea
4251274174 Merge branch 'master' into type-annotations 2022-01-26 11:46:37 -08:00
Joe Pea
ffaeea3d31 more type annotations and small tweaks to make types show 2022-01-26 11:28:11 -08:00
Joe Pea
84693b008e more type annotations and a few small tweaks 2022-01-20 19:19:44 -08:00
Joe Pea
896571e20e some more type defs and small code tweaks while getting familiar with plots 2022-01-11 21:57:08 -08:00
Joe Pea
4ff150caf4 Merge branch 'master' into log-plots 2022-01-11 12:29:36 -08:00
Joe Pea
ddf45a18b0 add some types to XAxisModel 2022-01-11 11:37:02 -08:00
16 changed files with 425 additions and 58 deletions

View File

@@ -22,7 +22,7 @@
"cross-env": "7.0.3",
"css-loader": "4.0.0",
"d3-axis": "1.0.x",
"d3-scale": "1.0.x",
"d3-scale": "3.3.0",
"d3-selection": "1.3.x",
"eslint": "8.11.0",
"eslint-plugin-compat": "4.0.2",

View File

@@ -30,8 +30,8 @@
class="gl-plot-tick-wrapper"
>
<div
v-for="tick in ticks"
:key="tick.value"
v-for="(tick, i) in ticks"
:key="i"
class="gl-plot-tick gl-plot-x-tick-label"
:style="{
left: (100 * (tick.value - min) / interval) + '%'
@@ -46,8 +46,8 @@
class="gl-plot-tick-wrapper"
>
<div
v-for="tick in ticks"
:key="tick.value"
v-for="(tick, i) in ticks"
:key="i"
class="gl-plot-tick gl-plot-y-tick-label"
:style="{ top: (100 * (max - tick.value) / interval) + '%' }"
:title="tick.fullText || tick.text"
@@ -59,8 +59,8 @@
<!-- grid lines follow -->
<template v-if="position === 'right'">
<div
v-for="tick in ticks"
:key="tick.value"
v-for="(tick, i) in ticks"
:key="i"
class="gl-plot-hash hash-v"
:style="{
right: (100 * (max - tick.value) / interval) + '%',
@@ -71,8 +71,8 @@
</template>
<template v-if="position === 'bottom'">
<div
v-for="tick in ticks"
:key="tick.value"
v-for="(tick, i) in ticks"
:key="i"
class="gl-plot-hash hash-h"
:style="{ bottom: (100 * (tick.value - min) / interval) + '%', width: '100%' }"
>
@@ -83,7 +83,7 @@
<script>
import eventHelpers from "./lib/eventHelpers";
import { ticks, getFormattedTicks } from "./tickUtils";
import { ticks, getLogTicks, getFormattedTicks } from "./tickUtils";
import configStore from "./configuration/ConfigStore";
export default {
@@ -96,6 +96,13 @@ export default {
},
required: true
},
// Make it a prop, then later we can allow user to change it via UI input
tickCount: {
type: Number,
default() {
return 6;
}
},
position: {
required: true,
type: String,
@@ -118,7 +125,6 @@ export default {
this.axis = this.getAxisFromConfig();
this.tickCount = 4;
this.tickUpdate = false;
this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this);
this.listenTo(this.axis, 'change:format', this.updateTicks, this);
@@ -184,7 +190,12 @@ export default {
}, this);
}
return ticks(range.min, range.max, number);
if (this.axisType === 'yAxis' && this.axis.get('logMode')) {
return getLogTicks(range.min, range.max, number, 4);
// return getLogTicks2(range.min, range.max, number);
} else {
return ticks(range.min, range.max, number);
}
},
updateTicksForceRegeneration() {

View File

@@ -52,23 +52,33 @@
</select>
<mct-ticks
v-if="logMode === false"
:axis-type="'yAxis'"
class="gl-plot-ticks"
:position="'top'"
@plotTickWidth="onTickWidthChange"
/>
<d3-axis
v-if="bounds && logMode === true"
class="gl-plot-ticks"
:bounds="bounds"
:content-height="height"
/>
</div>
</template>
<script>
import MctTicks from "../MctTicks.vue";
import D3Axis from "../../../ui/components/d3-ticks/D3Axis.vue";
import configStore from "../configuration/ConfigStore";
import eventHelpers from "../lib/eventHelpers";
export default {
components: {
MctTicks
MctTicks,
D3Axis
},
inject: ['openmct', 'domainObject'],
inject: ['openmct', 'domainObject', 'path'],
props: {
singleSeries: {
type: Boolean,
@@ -92,15 +102,53 @@ export default {
data() {
return {
yAxisLabel: 'none',
loaded: false
loaded: false,
bounds: undefined,
height: undefined,
logMode: false
};
},
mounted() {
eventHelpers.extend(this);
this.parentElement = this.$el.parentElement;
this.yAxis = this.getYAxisFromConfig();
this.logMode = this.yAxis.get('logMode');
this.setBoundsAndOptions();
this.setDimensions = this.setDimensions.bind(this);
this.setDimensions();
this.loaded = true;
this.setUpYAxisOptions();
this.listenTo(this.yAxis, 'change:displayRange', this.setBounds);
this.listenTo(this.yAxis, 'change:logMode', this.changeToLogTicks);
this.plotResizeObserver = new ResizeObserver(this.setDimensions);
this.plotResizeObserver.observe(this.parentElement);
},
beforeDestroy() {
if (this.plotResizeObserver) {
this.plotResizeObserver.unobserve(this.parentElement);
}
},
methods: {
setDimensions() {
//TODO: Handle resizing
const dimensions = this.parentElement.getBoundingClientRect();
this.height = Math.round(dimensions.height);
},
setBoundsAndOptions() {
this.setBounds();
this.setUpYAxisOptions();
},
setBounds() {
const bounds = this.yAxis.get('displayRange');
if (bounds) {
this.bounds = {
start: bounds.min,
end: bounds.max
};
}
},
changeToLogTicks(logMode) {
this.logMode = logMode;
},
getYAxisFromConfig() {
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
let config = configStore.get(configId);

View File

@@ -23,7 +23,7 @@
import MCTChartSeriesElement from './MCTChartSeriesElement';
export default class MCTChartLineStepAfter extends MCTChartSeriesElement {
removePoint(point, index, count) {
removePoint(index) {
if (index > 0 && index / 2 < this.count) {
this.buffer[index + 1] = this.buffer[index - 1];
}

View File

@@ -85,11 +85,10 @@ export default class MCTChartSeriesElement {
this.removeSegments(removalPoint, vertexCount);
this.removePoint(
this.makePoint(point, series),
removalPoint,
vertexCount
);
// TODO useless makePoint call?
this.makePoint(point, series);
this.removePoint(removalPoint);
this.count -= (vertexCount / 2);
}
@@ -109,11 +108,7 @@ export default class MCTChartSeriesElement {
const insertionPoint = this.startIndexForPointAtIndex(index);
this.growIfNeeded(pointsRequired);
this.makeInsertionPoint(insertionPoint, pointsRequired);
this.addPoint(
this.makePoint(point, series),
insertionPoint,
pointsRequired
);
this.addPoint(this.makePoint(point, series), insertionPoint);
this.count += (pointsRequired / 2);
}

View File

@@ -279,7 +279,7 @@ export default {
// Have to throw away the old canvas elements and replace with new
// canvas elements in order to get new drawing contexts.
const div = document.createElement('div');
div.innerHTML = this.canvasTemplate + this.canvasTemplate;
div.innerHTML = this.TEMPLATE;
const mainCanvas = div.querySelectorAll("canvas")[1];
const overlayCanvas = div.querySelectorAll("canvas")[0];
this.canvas.parentNode.replaceChild(mainCanvas, this.canvas);

View File

@@ -71,6 +71,7 @@ export default class Model extends EventEmitter {
}
/**
* @abstract
* @param {ModelOptions<T, O>} options
*/
initialize(options) {

View File

@@ -23,6 +23,7 @@ import _ from 'lodash';
import Model from "./Model";
import { MARKER_SHAPES } from '../draw/MarkerShapes';
import configStore from "../configuration/ConfigStore";
import { symlog } from '../mathUtils';
/**
* Plot series handle interpreting telemetry metadata for a single telemetry
@@ -63,6 +64,8 @@ import configStore from "../configuration/ConfigStore";
* @extends {Model<PlotSeriesModelType, PlotSeriesModelOptions>}
*/
export default class PlotSeries extends Model {
logMode = false;
/**
@param {import('./Model').ModelOptions<PlotSeriesModelType, PlotSeriesModelOptions>} options
*/
@@ -70,6 +73,8 @@ export default class PlotSeries extends Model {
super(options);
this.logMode = options.collection.plot.model.yAxis.logMode;
this.listenTo(this, 'change:xKey', this.onXKeyChange, this);
this.listenTo(this, 'change:yKey', this.onYKeyChange, this);
this.persistedConfig = options.persistedConfig;
@@ -229,6 +234,7 @@ export default class PlotSeries extends Model {
this.getXVal = format.parse.bind(format);
}
}
/**
* Update y formatter on change, default to stepAfter interpolation if
* y range is an enumeration.
@@ -251,7 +257,12 @@ export default class PlotSeries extends Model {
return this.limitEvaluator.evaluate(datum, valueMetadata);
}.bind(this);
const format = this.formats[newKey];
this.getYVal = format.parse.bind(format);
this.getYVal = (value) => {
const scale = 1; // TODO get from UI, positive number above 0
const y = format.parse(value);
return this.logMode ? scale * symlog(y, 10) : y;
};
}
formatX(point) {
@@ -519,7 +530,8 @@ export default class PlotSeries extends Model {
/**
* Update the series data with the given value.
* @returns {Array<{
* This return type definition is totally wrong, only covers sinwave generator. It needs to be generic.
* @return-example {Array<{
cos: number
sin: number
mctLimitState: {

View File

@@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import _ from 'lodash';
import { antisymlog, symlog } from '../mathUtils';
import Model from './Model';
/**
@@ -31,7 +31,7 @@ import Model from './Model';
*
* `autoscale`: boolean, whether or not to autoscale.
* `autoscalePadding`: float, percent of padding to display in plots.
* `displayRange`: the current display range for the x Axis.
* `displayRange`: the current display range for the axis.
* `format`: the formatter for the axis.
* `frozen`: boolean, if true, displayRange will not be updated automatically.
* Used to temporarily disable automatic updates during user interaction.
@@ -54,6 +54,7 @@ export default class YAxisModel extends Model {
this.listenTo(this, 'change:stats', this.calculateAutoscaleExtents, this);
this.listenTo(this, 'change:autoscale', this.toggleAutoscale, this);
this.listenTo(this, 'change:autoscalePadding', this.updatePadding, this);
this.listenTo(this, 'change:logMode', this.onLogModeChange, this);
this.listenTo(this, 'change:frozen', this.toggleFreeze, this);
this.listenTo(this, 'change:range', this.updateDisplayRange, this);
this.updateDisplayRange(this.get('range'));
@@ -173,13 +174,38 @@ export default class YAxisModel extends Model {
this.set('displayRange', this.get('range'));
}
}
/** @param {boolean} logMode */
onLogModeChange(logMode) {
const range = this.get('displayRange');
const scale = 1; // TODO get from UI, positive number above 0
if (logMode) {
range.min = scale * symlog(range.min, 10);
range.max = scale * symlog(range.max, 10);
} else {
range.min = antisymlog(range.min / scale, 10);
range.max = antisymlog(range.max / scale, 10);
}
this.set('displayRange', range);
this.resetSeries();
}
resetSeries() {
this.plot.series.forEach((plotSeries) => {
plotSeries.logMode = this.get('logMode');
plotSeries.reset(plotSeries.getSeriesData());
});
// Update the series collection labels and formatting
this.updateFromSeries(this.seriesCollection);
}
/**
* Update yAxis format, values, and label from known series.
* @param {import('./SeriesCollection').default} seriesCollection
*/
updateFromSeries(seriesCollection) {
const plotModel = this.plot.get('domainObject');
const label = _.get(plotModel, 'configuration.yAxis.label');
const label = plotModel.configuration?.yAxis?.label;
const sampleSeries = seriesCollection.first();
if (!sampleSeries) {
if (!label) {
@@ -192,7 +218,14 @@ export default class YAxisModel extends Model {
const yKey = sampleSeries.get('yKey');
const yMetadata = sampleSeries.metadata.value(yKey);
const yFormat = sampleSeries.formats[yKey];
this.set('format', yFormat.format.bind(yFormat));
const scale = 1; // TODO get from UI, positive number above 0
if (this.get('logMode')) {
this.set('format', (n) => yFormat.format(antisymlog(n / scale, 10)));
} else {
this.set('format', (n) => yFormat.format(n));
}
this.set('values', yMetadata.values);
if (!label) {
const labelName = seriesCollection.map(function (s) {
@@ -246,6 +279,7 @@ export default class YAxisModel extends Model {
return {
frozen: false,
autoscale: true,
logMode: options.model?.logMode ?? false,
autoscalePadding: 0.1
};
}
@@ -256,6 +290,7 @@ export default class YAxisModel extends Model {
/**
@typedef {import('./XAxisModel').AxisModelType & {
autoscale: boolean
logMode: boolean
autoscalePadding: number
stats: import('./XAxisModel').NumberRange
values: Array<TODO>

View File

@@ -48,11 +48,19 @@
<li class="grid-row">
<div
class="grid-cell label"
title="Automatically scale the Y axis to keep all values in view."
>Autoscale</div>
title="Enable log mode."
>Log mode</div>
<div class="grid-cell value">
{{ autoscale ? "Enabled: " : "Disabled" }}
{{ autoscale ? autoscalePadding : "" }}
{{ logMode ? "Enabled" : "Disabled" }}
</div>
</li>
<li class="grid-row">
<div
class="grid-cell label"
title="Automatically scale the Y axis to keep all values in view."
>Auto scale</div>
<div class="grid-cell value">
{{ autoscale ? "Enabled: " + autoscalePadding : "Disabled" }}
</div>
</li>
<li
@@ -142,6 +150,7 @@ export default {
config: {},
label: '',
autoscale: '',
logMode: false,
autoscalePadding: '',
rangeMin: '',
rangeMax: '',
@@ -172,6 +181,7 @@ export default {
initConfiguration() {
this.label = this.config.yAxis.get('label');
this.autoscale = this.config.yAxis.get('autoscale');
this.logMode = this.config.yAxis.get('logMode');
this.autoscalePadding = this.config.yAxis.get('autoscalePadding');
const range = this.config.yAxis.get('range');
if (range) {

View File

@@ -14,9 +14,22 @@
@change="updateForm('label')"
></div>
</li>
</ul>
<ul class="l-inspector-part">
<h2>Y Axis Scaling</h2>
<li class="grid-row">
<div
class="grid-cell label"
title="Enable log mode."
>
Log mode
</div>
<div class="grid-cell value">
<!-- eslint-disable-next-line vue/html-self-closing -->
<input
v-model="logMode"
type="checkbox"
@change="updateForm('logMode')"
/>
</div>
</li>
<li class="grid-row">
<div
class="grid-cell label"
@@ -88,7 +101,7 @@
</template>
<script>
import { objectPath, validate, coerce } from "./formUtil";
import { objectPath, validate } from "./formUtil";
import _ from "lodash";
export default {
@@ -105,6 +118,7 @@ export default {
return {
label: '',
autoscale: '',
logMode: false,
autoscalePadding: '',
rangeMin: '',
rangeMax: '',
@@ -117,23 +131,23 @@ export default {
},
methods: {
initialize: function () {
this.fields = [
{
modelProp: 'label',
this.fields = {
label: {
objectPath: 'configuration.yAxis.label'
},
{
modelProp: 'autoscale',
autoscale: {
coerce: Boolean,
objectPath: 'configuration.yAxis.autoscale'
},
{
modelProp: 'autoscalePadding',
autoscalePadding: {
coerce: Number,
objectPath: 'configuration.yAxis.autoscalePadding'
},
{
modelProp: 'range',
logMode: {
coerce: Boolean,
objectPath: 'configuration.yAxis.logMode'
},
range: {
objectPath: 'configuration.yAxis.range',
coerce: function coerceRange(range) {
if (!range) {
@@ -186,11 +200,12 @@ export default {
return true;
}
}
];
};
},
initFormValues() {
this.label = this.yAxis.get('label');
this.autoscale = this.yAxis.get('autoscale');
this.logMode = this.yAxis.get('logMode');
this.autoscalePadding = this.yAxis.get('autoscalePadding');
const range = this.yAxis.get('range');
if (!range) {
@@ -212,8 +227,8 @@ export default {
newVal = this[formKey];
}
const oldVal = this.yAxis.get(formKey);
const formField = this.fields.find((field) => field.modelProp === formKey);
let oldVal = this.yAxis.get(formKey);
const formField = this.fields[formKey];
const path = objectPath(formField.objectPath);
const validationResult = validate(newVal, this.yAxis, formField.validate);
@@ -225,13 +240,17 @@ export default {
return;
}
if (!_.isEqual(coerce(newVal, formField.coerce), coerce(oldVal, formField.coerce))) {
this.yAxis.set(formKey, coerce(newVal, formField.coerce));
newVal = formField.coerce?.(newVal) ?? newVal;
oldVal = formField.coerce?.(oldVal) ?? oldVal;
if (!_.isEqual(newVal, oldVal)) {
// TODO: Why do we mutate yAxis twice, once directly, once via objects.mutate?
this.yAxis.set(formKey, newVal);
if (path) {
this.openmct.objects.mutate(
this.domainObject,
path(this.domainObject, this.yAxis),
coerce(newVal, formField.coerce)
newVal
);
}
}

View File

@@ -0,0 +1,44 @@
/** The natural number `e`. */
export const e = Math.exp(1);
/**
Returns the logarithm of a number, using the given base or the natural number
`e` as base if not specified.
@param {number} n
@param {number=} base log base, defaults to e
*/
export function log(n, base = e) {
if (base === e) {
return Math.log(n);
}
return Math.log(n) / Math.log(base);
}
/**
Returns the inverse of the logarithm of a number, using the given base or the
natural number `e` as base if not specified.
@param {number} n
@param {number=} base log base, defaults to e
*/
export function antilog(n, base = e) {
return Math.pow(base, n);
}
/**
A symmetric logarithm function. See https://github.com/nasa/openmct/issues/2297#issuecomment-1032914258
@param {number} n
@param {number=} base log base, defaults to e
*/
export function symlog(n, base = e) {
return Math.sign(n) * log(Math.abs(n) + 1, base);
}
/**
An inverse symmetric logarithm function. See https://github.com/nasa/openmct/issues/2297#issuecomment-1032914258
@param {number} n
@param {number=} base log base, defaults to e
*/
export function antisymlog(n, base = e) {
return Math.sign(n) * (antilog(Math.abs(n), base) - 1);
}

View File

@@ -1,3 +1,5 @@
import { antisymlog, symlog } from "./mathUtils";
const e10 = Math.sqrt(50);
const e5 = Math.sqrt(10);
const e2 = Math.sqrt(2);
@@ -40,6 +42,50 @@ function getPrecision(step) {
return precision;
}
export function getLogTicks(start, stop, mainTickCount = 8, secondaryTickCount = 6) {
// log()'ed values
const mainLogTicks = ticks(start, stop, mainTickCount);
// original values
const scale = 1; // TODO get from UI, positive number above 0
const mainTicks = mainLogTicks.map(n => antisymlog(n / scale, 10));
const result = [];
let i = 0;
for (const logTick of mainLogTicks) {
result.push(logTick);
if (i === mainLogTicks.length - 1) {
break;
}
const tick = mainTicks[i];
const nextTick = mainTicks[i + 1];
const rangeBetweenMainTicks = nextTick - tick;
const secondaryLogTicks = ticks(
tick + rangeBetweenMainTicks / (secondaryTickCount + 1),
nextTick - rangeBetweenMainTicks / (secondaryTickCount + 1),
secondaryTickCount - 2
)
.map(n => scale * symlog(n, 10));
result.push(...secondaryLogTicks);
i++;
}
return result;
}
export function getLogTicks2(start, stop, count = 8) {
const scale = 1; // TODO get from UI, positive number above 0
return ticks(antisymlog(start / scale, 10), antisymlog(stop / scale, 10), count)
.map(n => scale * symlog(n, 10));
}
/**
* Linear tick generation from d3-array.
*/

View File

@@ -0,0 +1,118 @@
<template>
<div
ref="axisHolder"
class="c-d3-axis"
>
</div>
</template>
<script>
import * as d3Selection from 'd3-selection';
import * as d3Axis from 'd3-axis';
import * as d3Scale from 'd3-scale';
//TODO: UI direction needed for the following property values
const PADDING = 1;
const RESIZE_POLL_INTERVAL = 200;
// const PIXELS_PER_TICK = 100;
// const PIXELS_PER_TICK_WIDE = 200;
//This offset needs to be re-considered
export default {
inject: ['openmct', 'domainObject'],
props: {
bounds: {
type: Object,
default() {
return {};
}
},
contentHeight: {
type: Number,
default() {
return 0;
}
},
offset: {
type: Number,
default() {
return 0;
}
}
},
watch: {
bounds(newBounds) {
this.drawAxis(newBounds);
}
},
mounted() {
this.container = d3Selection.select(this.$refs.axisHolder);
this.svgElement = this.container.append("svg:svg");
// draw x axis with labels. CSS is used to position them.
this.axisElement = this.svgElement.append("g")
.attr("class", "axis")
.attr('font-size', '1.3em')
.attr("transform", "translate(25,0)");
this.setDimensions();
this.drawAxis(this.bounds);
this.resizeTimer = setInterval(this.resize, RESIZE_POLL_INTERVAL);
},
destroyed() {
clearInterval(this.resizeTimer);
},
methods: {
resize() {
if (this.$refs.axisHolder.clientHeight !== this.height) {
this.setDimensions();
this.drawAxis(this.bounds);
}
},
setDimensions() {
const axisHolder = this.$refs.axisHolder;
this.width = axisHolder.clientWidth;
this.height = axisHolder.clientHeight;
this.offsetHeight = this.height - this.offset;
this.svgElement.attr("width", this.width);
this.svgElement.attr("height", this.height);
},
drawAxis(bounds) {
let viewBounds = Object.assign({}, bounds);
this.setScale(viewBounds);
this.setAxis(viewBounds);
this.axisElement.call(this.xAxis);
},
setScale(bounds) {
if (!this.height) {
return;
}
this.xScale = d3Scale.scaleSymlog();
if (bounds.start < 0) {
this.xScale.domain(
[bounds.end, bounds.start]
);
} else {
this.xScale.domain(
[bounds.start, bounds.end]
);
}
this.xScale.range([PADDING, this.offsetHeight - PADDING * 2]);
},
setAxis() {
this.xAxis = d3Axis.axisLeft(this.xScale);
//
// if (this.height > 1800) {
// this.xAxis.ticks(this.offsetHeight / PIXELS_PER_TICK_WIDE);
// } else {
// this.xAxis.ticks(this.offsetHeight / PIXELS_PER_TICK);
// }
}
}
};
</script>

View File

@@ -0,0 +1,28 @@
.c-d3-axis {
$w: 30px;
height: $w;
svg {
$lineC: rgba($colorBodyFg, 0.3) !important;
text-rendering: geometricPrecision;
width: 100%;
height: 100%;
.domain {
stroke: $lineC;
}
.tick {
line {
stroke: $lineC;
}
text {
// Tick labels
fill: $colorBodyFg;
paint-order: stroke;
font-weight: bold;
}
}
}
}

View File

@@ -44,7 +44,7 @@ const config = {
"bourbon": "bourbon.scss",
"plotly-basic": "plotly.js-basic-dist",
"plotly-gl2d": "plotly.js-gl2d-dist",
"d3-scale": path.join(__dirname, "node_modules/d3-scale/build/d3-scale.min.js"),
"d3-scale": path.join(__dirname, "node_modules/d3-scale/dist/d3-scale.min.js"),
"printj": path.join(__dirname, "node_modules/printj/dist/printj.min.js"),
"styles": path.join(__dirname, "src/styles"),
"MCT": path.join(__dirname, "src/MCT"),