UNPKG

webglimpse

Version:

Webglimpse is a data visualization library for the web.

688 lines 135 kB
/* * Copyright (c) 2014, Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import { black } from '../color'; import { Side, solid_FRAGSHADER } from '../misc'; import { Program, Uniform1f, Uniform2f, UniformColor, Attribute } from '../shader'; import { edgeMarks_VERTSHADER } from '../plot/edge_axis_painter'; import { newDynamicBuffer } from '../buffer'; import { newTextTextureCache } from '../text'; import { TextureRenderer } from '../texture'; import { ensureCapacityFloat32, GL, hasval, parseTime_PMILLIS, clamp } from '../util/util'; import { minutesToMillis, millisToDays, hoursToMillis, daysToMillis, millisToHours, secondsToMillis } from '../time'; import { Axis1D, getTickInterval, getTickCount, getTickPositions } from '../plot/axis'; import moment from 'moment'; // XXX: Much of this is duplicated from edge_axis_painter export class FormatOptions { constructor(tick, prefix) { this.tickFormat = tick; this.prefixFormat = prefix; } } export function newTimeAxisPainter(timeAxis, labelSide, displayTimeZone, tickTimeZone, options) { const tickSpacing = (hasval(options) && hasval(options.tickSpacing) ? options.tickSpacing : 60); const font = (hasval(options) && hasval(options.font) ? options.font : '11px verdana,sans-serif'); const textColor = (hasval(options) && hasval(options.textColor) ? options.textColor : black); const tickColor = (hasval(options) && hasval(options.tickColor) ? options.tickColor : black); const tickSize = (hasval(options) && hasval(options.tickSize) ? options.tickSize : 6); const labelAlign = (hasval(options) && hasval(options.labelAlign) ? options.labelAlign : 0.5); const referenceDate_PMILLIS = (hasval(options) && hasval(options.referenceDate) ? parseTime_PMILLIS(options.referenceDate) : undefined); const isFuturePositive = (hasval(options) && hasval(options.isFuturePositive) ? options.isFuturePositive : true); const timeAxisFormat = (hasval(options) && hasval(options.timeAxisFormat) ? options.timeAxisFormat : undefined); const marksProgram = new Program(edgeMarks_VERTSHADER(labelSide), solid_FRAGSHADER); const marksProgram_u_VMin = new Uniform1f(marksProgram, 'u_VMin'); const marksProgram_u_VSize = new Uniform1f(marksProgram, 'u_VSize'); const marksProgram_u_ViewportSize = new Uniform2f(marksProgram, 'u_ViewportSize'); const marksProgram_u_MarkSize = new Uniform1f(marksProgram, 'u_MarkSize'); const marksProgram_u_Color = new UniformColor(marksProgram, 'u_Color'); const marksProgram_a_VCoord = new Attribute(marksProgram, 'a_VCoord'); let markCoords = new Float32Array(0); const markCoordsBuffer = newDynamicBuffer(); const textTextures = newTextTextureCache(font, textColor); const textureRenderer = new TextureRenderer(); const hTickLabels = textTextures.value('-0123456789:.').h; const isVerticalAxis = (labelSide === Side.LEFT || labelSide === Side.RIGHT); return function (gl, viewport) { const sizePixels = isVerticalAxis ? viewport.h : viewport.w; if (sizePixels === 0) { return; } const tickTimes_PMILLIS = getTickTimes_PMILLIS(timeAxis, sizePixels, tickSpacing, tickTimeZone, referenceDate_PMILLIS); const tickInterval_MILLIS = getTickInterval_MILLIS(tickTimes_PMILLIS); const tickCount = tickTimes_PMILLIS.length; // Tick marks // marksProgram.use(gl); marksProgram_u_VMin.setData(gl, timeAxis.vMin); marksProgram_u_VSize.setData(gl, timeAxis.vSize); marksProgram_u_ViewportSize.setData(gl, viewport.w, viewport.h); marksProgram_u_MarkSize.setData(gl, tickSize); marksProgram_u_Color.setData(gl, tickColor); markCoords = ensureCapacityFloat32(markCoords, 4 * tickCount); for (let n = 0; n < tickCount; n++) { const v = timeAxis.vAtTime(tickTimes_PMILLIS[n]); markCoords[(4 * n + 0)] = v; markCoords[(4 * n + 1)] = 0; markCoords[(4 * n + 2)] = v; markCoords[(4 * n + 3)] = 1; } markCoordsBuffer.setData(markCoords.subarray(0, 4 * tickCount)); marksProgram_a_VCoord.setDataAndEnable(gl, markCoordsBuffer, 2, GL.FLOAT); // IE does not support lineWidths other than 1, so make sure all browsers use lineWidth of 1 gl.lineWidth(1); gl.drawArrays(GL.LINES, 0, 2 * tickCount); marksProgram_a_VCoord.disable(gl); marksProgram.endUse(gl); gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA); gl.enable(GL.BLEND); // Tick labels // const ticks = getTickDisplayData(tickInterval_MILLIS, referenceDate_PMILLIS, displayTimeZone, isFuturePositive, timeAxisFormat); textTextures.resetTouches(); textureRenderer.begin(gl, viewport); for (let n = 0; n < tickCount; n++) { const tickTime_PMILLIS = tickTimes_PMILLIS[n]; const tFrac = timeAxis.tFrac(tickTime_PMILLIS); if (tFrac < 0 || tFrac >= 1) { continue; } const tickLabel = ticks.tickFormat(tickTime_PMILLIS); const textTexture = textTextures.value(tickLabel); let xFrac; let yFrac; if (labelSide === Side.LEFT || labelSide === Side.RIGHT) { const yAnchor = textTexture.yAnchor(0.43); const j0 = (tFrac * viewport.h) - yAnchor * textTexture.h; const j = clamp(0, viewport.h - textTexture.h, j0); yFrac = j / viewport.h; if (labelSide === Side.LEFT) { xFrac = (viewport.w - tickSize - 2 - textTexture.w) / viewport.w; } else { xFrac = (tickSize + 2) / viewport.w; } } else { const xAnchor = 0.45; const i0 = (tFrac * viewport.w) - xAnchor * (textTexture.w); const i = clamp(0, viewport.w - textTexture.w, i0); xFrac = i / viewport.w; if (labelSide === Side.BOTTOM) { yFrac = (viewport.h - tickSize - 2 - hTickLabels) / viewport.h; } else { yFrac = (tickSize + 2) / viewport.h; } } textureRenderer.draw(gl, textTexture, xFrac, yFrac, { xAnchor: 0, yAnchor: 0 }); } // Axis label // if (ticks.timeStructFactory) { const timeStructs = createTimeStructs(timeAxis, ticks.timeStructFactory, tickTimeZone, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign); for (let n = 0; n < timeStructs.length; n++) { const timeStruct = timeStructs[n]; const text = ticks.prefixFormat(timeStruct); const textTexture = textTextures.value(text); const halfTextFrac = 0.5 * textTexture.w / viewport.w; const minFrac = timeAxis.tFrac(timeStruct.start_PMILLIS) - halfTextFrac; const maxFrac = timeAxis.tFrac(timeStruct.end_PMILLIS) + halfTextFrac; const tFrac = clamp(minFrac, maxFrac, timeAxis.tFrac(timeStruct.textCenter_PMILLIS)); if (tFrac - halfTextFrac < 0 || tFrac + halfTextFrac > 1) { continue; } let xFrac; let yFrac; let textOpts; if (labelSide === Side.LEFT || labelSide === Side.RIGHT) { // Using hTickLabels here works out about right, even though the tick-label text is horizontal const xFrac0 = 0.5 * (viewport.w - tickSize - 2 - hTickLabels) / viewport.w; xFrac = (labelSide === Side.LEFT ? xFrac0 : 1 - xFrac0); yFrac = tFrac; textOpts = { xAnchor: textTexture.yAnchor(0.5), yAnchor: 0.5, rotation_CCWRAD: 0.5 * Math.PI }; } else { const yFrac0 = 0.5 * (viewport.h - tickSize - 2 - hTickLabels) / viewport.h; yFrac = (labelSide === Side.BOTTOM ? yFrac0 : 1 - yFrac0); xFrac = tFrac; textOpts = { xAnchor: 0.5, yAnchor: textTexture.yAnchor(0.5), rotation_CCWRAD: 0 }; } textureRenderer.draw(gl, textTexture, xFrac, yFrac, textOpts); } } // Finish up // textureRenderer.end(gl); textTextures.retainTouched(); }; } function getTickDisplayData(tickInterval_MILLIS, referenceDate_PMILLIS, displayTimeZone, isFuturePositive, timeAxisFormat) { if (hasval(referenceDate_PMILLIS)) { return getTickDisplayDataRelative(tickInterval_MILLIS, referenceDate_PMILLIS, isFuturePositive); } else { return getTickDisplayDataAbsolute(tickInterval_MILLIS, displayTimeZone, timeAxisFormat); } } function getTickDisplayDataRelative(tickInterval_MILLIS, referenceDate_PMILLIS, isFuturePositive) { let tickFormat = null; let prefixFormat = null; let timeStructFactory = null; if (tickInterval_MILLIS <= minutesToMillis(1)) { tickFormat = function (tickTime_PMILLIS) { const elapsedTime_MILLIS = Math.abs(tickTime_PMILLIS - referenceDate_PMILLIS); const elapsedTime_DAYS = millisToDays(elapsedTime_MILLIS); const elapsedTime_DAYS_WHOLE = Math.floor(elapsedTime_DAYS); const elapsedTime_HOURS = (elapsedTime_DAYS - elapsedTime_DAYS_WHOLE) * 24; const elapsedTime_HOURS_WHOLE = Math.floor(elapsedTime_HOURS); const elapsedTime_MIN = (elapsedTime_HOURS - elapsedTime_HOURS_WHOLE) * 60; let elapsedTime_MIN_WHOLE = Math.floor(elapsedTime_MIN); const elapsedTime_SEC = (elapsedTime_MIN - elapsedTime_MIN_WHOLE) * 60; // use round() here instead of floor() because we always expect ticks to be on even second // boundaries but rounding error will cause us to be somewhat unpredictably above or below // the nearest even second boundary let elapsedTime_SEC_WHOLE = Math.round(elapsedTime_SEC); // however the above fails when we round up to a whole minute, so special case that if (elapsedTime_SEC_WHOLE >= 60) { elapsedTime_SEC_WHOLE -= 60; elapsedTime_MIN_WHOLE += 1; } if (elapsedTime_MIN_WHOLE >= 60) { elapsedTime_MIN_WHOLE = 0; } const min = elapsedTime_MIN_WHOLE < 10 ? '0' + elapsedTime_MIN_WHOLE : '' + elapsedTime_MIN_WHOLE; const sec = elapsedTime_SEC_WHOLE < 10 ? '0' + elapsedTime_SEC_WHOLE : '' + elapsedTime_SEC_WHOLE; return min + ':' + sec; }; prefixFormat = function (timeStruct) { const center_PMILLIS = (timeStruct.end_PMILLIS - timeStruct.start_PMILLIS) / 2 + timeStruct.start_PMILLIS; let elapsedTime_MILLIS = center_PMILLIS - referenceDate_PMILLIS; const negative = (elapsedTime_MILLIS < 0); const signString = (negative && isFuturePositive) || (!negative && !isFuturePositive) ? '-' : ''; elapsedTime_MILLIS = Math.abs(elapsedTime_MILLIS); const elapsedTime_DAYS = millisToDays(elapsedTime_MILLIS); const elapsedTime_DAYS_WHOLE = Math.floor(elapsedTime_DAYS); const elapsedTime_HOURS = (elapsedTime_DAYS - elapsedTime_DAYS_WHOLE) * 24; const elapsedTime_HOURS_WHOLE = Math.floor(elapsedTime_HOURS); return 'Day ' + signString + elapsedTime_DAYS_WHOLE + ' Hour ' + signString + elapsedTime_HOURS_WHOLE; }; timeStructFactory = function () { return new TimeStruct(); }; } else if (tickInterval_MILLIS <= hoursToMillis(12)) { tickFormat = function (tickTime_PMILLIS) { const elapsedTime_MILLIS = Math.abs(tickTime_PMILLIS - referenceDate_PMILLIS); const elapsedTime_DAYS = millisToDays(elapsedTime_MILLIS); const elapsedTime_DAYS_WHOLE = Math.floor(elapsedTime_DAYS); const elapsedTime_HOURS = (elapsedTime_DAYS - elapsedTime_DAYS_WHOLE) * 24; let elapsedTime_HOURS_WHOLE = Math.floor(elapsedTime_HOURS); const elapsedTime_MIN = (elapsedTime_HOURS - elapsedTime_HOURS_WHOLE) * 60; // use round() here instead of floor() because we always expect ticks to be on even minute // boundaries but rounding error will cause us to be somewhat unpredictably above or below // the nearest even minute boundary let elapsedTime_MIN_WHOLE = Math.round(elapsedTime_MIN); // however the above fails when we round up to a whole hour, so special case that if (elapsedTime_MIN_WHOLE >= 60) { elapsedTime_MIN_WHOLE -= 60; elapsedTime_HOURS_WHOLE += 1; } if (elapsedTime_HOURS_WHOLE >= 24) { elapsedTime_HOURS_WHOLE = 0; } const hour = elapsedTime_HOURS_WHOLE < 10 ? '0' + elapsedTime_HOURS_WHOLE : '' + elapsedTime_HOURS_WHOLE; const min = elapsedTime_MIN_WHOLE < 10 ? '0' + elapsedTime_MIN_WHOLE : '' + elapsedTime_MIN_WHOLE; return hour + ':' + min; }; prefixFormat = function (timeStruct) { const center_PMILLIS = (timeStruct.end_PMILLIS - timeStruct.start_PMILLIS) / 2 + timeStruct.start_PMILLIS; let elapsedTime_MILLIS = center_PMILLIS - referenceDate_PMILLIS; const negative = (elapsedTime_MILLIS < 0); const signString = (negative && isFuturePositive) || (!negative && !isFuturePositive) ? '-' : ''; elapsedTime_MILLIS = Math.abs(elapsedTime_MILLIS); const elapsedTime_DAYS = Math.floor(millisToDays(elapsedTime_MILLIS)); return 'Day ' + signString + elapsedTime_DAYS; }; timeStructFactory = function () { return new TimeStruct(); }; } else { tickFormat = function (tickTime_PMILLIS) { let elapsedTime_MILLIS = tickTime_PMILLIS - referenceDate_PMILLIS; const negative = (elapsedTime_MILLIS < 0); const signString = (negative && isFuturePositive) || (!negative && !isFuturePositive) ? '-' : ''; elapsedTime_MILLIS = Math.abs(elapsedTime_MILLIS); const elapsedTime_DAYS = Math.floor(millisToDays(elapsedTime_MILLIS)); return elapsedTime_DAYS === 0 ? '' + elapsedTime_DAYS : signString + elapsedTime_DAYS; }; } return { prefixFormat: prefixFormat, tickFormat: tickFormat, timeStructFactory: timeStructFactory }; } function getTickDisplayDataAbsolute(tickInterval_MILLIS, displayTimeZone, timeAxisFormat) { let tickFormat = null; let prefixFormat = null; let timeStructFactory = null; const defaultTickFormat = function (format) { return function (tickTime_PMILLIS) { return moment(tickTime_PMILLIS).zone(displayTimeZone).format(format); }; }; const defaultPrefixFormat = function (format) { return function (timeStruct) { return moment(timeStruct.textCenter_PMILLIS).zone(displayTimeZone).format(format); }; }; if (tickInterval_MILLIS <= minutesToMillis(1)) { const formatOptions = new FormatOptions('mm:ss', 'D MMM HH:00'); const hour = (hasval(timeAxisFormat) && hasval(timeAxisFormat.hour) ? timeAxisFormat.hour : formatOptions); tickFormat = defaultTickFormat((hasval(hour.tickFormat) && hour.tickFormat !== '' ? hour.tickFormat : formatOptions.tickFormat)); prefixFormat = defaultPrefixFormat((hasval(hour.prefixFormat) && hour.prefixFormat !== '' ? hour.prefixFormat : formatOptions.prefixFormat)); timeStructFactory = function () { return new HourStruct(); }; } else if (tickInterval_MILLIS <= hoursToMillis(12)) { const formatOptions = new FormatOptions('HH:mm', 'D MMM YYYY'); const day = (hasval(timeAxisFormat) && hasval(timeAxisFormat.day) ? timeAxisFormat.day : formatOptions); tickFormat = defaultTickFormat((hasval(day.tickFormat) && day.tickFormat !== '' ? day.tickFormat : formatOptions.tickFormat)); prefixFormat = defaultPrefixFormat((hasval(day.prefixFormat) && day.prefixFormat !== '' ? day.prefixFormat : formatOptions.prefixFormat)); timeStructFactory = function () { return new DayStruct(); }; } else if (tickInterval_MILLIS <= daysToMillis(10)) { const formatOptions = new FormatOptions('D', 'MMM YYYY'); const month = (hasval(timeAxisFormat) && hasval(timeAxisFormat.month) ? timeAxisFormat.month : formatOptions); tickFormat = defaultTickFormat((hasval(month.tickFormat) && month.tickFormat !== '' ? month.tickFormat : formatOptions.tickFormat)); prefixFormat = defaultPrefixFormat((hasval(month.prefixFormat) && month.prefixFormat !== '' ? month.prefixFormat : formatOptions.prefixFormat)); timeStructFactory = function () { return new MonthStruct(); }; } else if (tickInterval_MILLIS <= daysToMillis(60)) { const formatOptions = new FormatOptions('MMM', 'YYYY'); const year = (hasval(timeAxisFormat) && hasval(timeAxisFormat.year) ? timeAxisFormat.year : formatOptions); tickFormat = defaultTickFormat((hasval(year.tickFormat) && year.tickFormat !== '' ? year.tickFormat : formatOptions.tickFormat)); prefixFormat = defaultPrefixFormat((hasval(year.prefixFormat) && year.prefixFormat !== '' ? year.prefixFormat : formatOptions.prefixFormat)); timeStructFactory = function () { return new YearStruct(); }; } else { tickFormat = defaultTickFormat('YYYY'); } return { prefixFormat: prefixFormat, tickFormat: tickFormat, timeStructFactory: timeStructFactory }; } class TimeStruct { setTime(time_PMILLIS, timeZone) { return moment(time_PMILLIS).zone(timeZone); } incrementTime(m) { } } class YearStruct extends TimeStruct { setTime(time_PMILLIS, timeZone) { const m = moment(time_PMILLIS).zone(timeZone); m.month(0); m.date(0); m.hours(0); m.minutes(0); m.seconds(0); return m; } incrementTime(m) { m.add('years', 1); } } class MonthStruct extends TimeStruct { setTime(time_PMILLIS, timeZone) { const m = moment(time_PMILLIS).zone(timeZone); m.date(0); m.hours(0); m.minutes(0); m.seconds(0); return m; } incrementTime(m) { m.add('months', 1); } } class DayStruct extends TimeStruct { setTime(time_PMILLIS, timeZone) { const m = moment(time_PMILLIS).zone(timeZone); m.hours(0); m.minutes(0); m.seconds(0); return m; } incrementTime(m) { m.add('days', 1); } } class HourStruct extends TimeStruct { setTime(time_PMILLIS, timeZone) { const m = moment(time_PMILLIS).zone(timeZone); m.minutes(0); m.seconds(0); return m; } incrementTime(m) { m.add('hours', 1); } } function createTimeStructs(timeAxis, factory, timeZone, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign) { if (hasval(referenceDate_PMILLIS)) { const tickInterval_MILLIS = getTickInterval_MILLIS(tickTimes_PMILLIS); if (tickInterval_MILLIS <= minutesToMillis(1)) { return createTimeStructsRelativeHours(timeAxis, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign); } else { return createTimeStructsRelativeDays(timeAxis, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign); } } else { return createTimeStructsAbsolute(timeAxis, factory, timeZone, tickTimes_PMILLIS, labelAlign); } } function createTimeStructsRelativeHours(timeAxis, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign) { const dMin_PMILLIS = timeAxis.tMin_PMILLIS; const dMax_PMILLIS = timeAxis.tMax_PMILLIS; const timeStructs = []; let maxViewDuration_MILLIS = Number.NEGATIVE_INFINITY; let previous_HOURS = null; let previous_SIGN = null; for (let n = 0; n < tickTimes_PMILLIS.length; n++) { let elapsedTime_MILLIS = tickTimes_PMILLIS[n] - referenceDate_PMILLIS; const negative = (elapsedTime_MILLIS < 0); const signString = (negative && isFuturePositive) || (!negative && !isFuturePositive) ? '-' : ''; elapsedTime_MILLIS = Math.abs(elapsedTime_MILLIS); const elapsedTime_HOURS = millisToHours(elapsedTime_MILLIS); const elapsedTime_HOURS_WHOLE = Math.floor(elapsedTime_HOURS); if (hasval(previous_HOURS) && elapsedTime_HOURS_WHOLE === previous_HOURS && negative === previous_SIGN) { continue; } previous_HOURS = elapsedTime_HOURS_WHOLE; previous_SIGN = negative; const timeStruct = new TimeStruct(); if (negative) { timeStruct.end_PMILLIS = hoursToMillis(-elapsedTime_HOURS_WHOLE) + referenceDate_PMILLIS; timeStruct.start_PMILLIS = timeStruct.end_PMILLIS - hoursToMillis(1); } else { timeStruct.start_PMILLIS = hoursToMillis(elapsedTime_HOURS_WHOLE) + referenceDate_PMILLIS; timeStruct.end_PMILLIS = timeStruct.start_PMILLIS + hoursToMillis(1); } timeStruct.viewStart_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMin_PMILLIS); timeStruct.viewEnd_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMax_PMILLIS); maxViewDuration_MILLIS = Math.max(maxViewDuration_MILLIS, timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS); timeStructs.push(timeStruct); } setTimeStructTextCenter(timeStructs, labelAlign, maxViewDuration_MILLIS); return timeStructs; } function createTimeStructsRelativeDays(timeAxis, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign) { const dMin_PMILLIS = timeAxis.tMin_PMILLIS; const dMax_PMILLIS = timeAxis.tMax_PMILLIS; const timeStructs = []; let maxViewDuration_MILLIS = Number.NEGATIVE_INFINITY; let previous_DAYS = null; let previous_SIGN = null; for (let n = 0; n < tickTimes_PMILLIS.length; n++) { let elapsedTime_MILLIS = tickTimes_PMILLIS[n] - referenceDate_PMILLIS; const negative = (elapsedTime_MILLIS < 0); const signString = (negative && isFuturePositive) || (!negative && !isFuturePositive) ? '-' : ''; elapsedTime_MILLIS = Math.abs(elapsedTime_MILLIS); const elapsedTime_DAYS = millisToDays(elapsedTime_MILLIS); const elapsedTime_DAYS_WHOLE = Math.floor(elapsedTime_DAYS); if (hasval(previous_DAYS) && elapsedTime_DAYS_WHOLE === previous_DAYS && negative === previous_SIGN) { continue; } previous_DAYS = elapsedTime_DAYS_WHOLE; previous_SIGN = negative; const timeStruct = new TimeStruct(); if (negative) { timeStruct.end_PMILLIS = daysToMillis(-elapsedTime_DAYS_WHOLE) + referenceDate_PMILLIS; timeStruct.start_PMILLIS = timeStruct.end_PMILLIS - daysToMillis(1); } else { timeStruct.start_PMILLIS = daysToMillis(elapsedTime_DAYS_WHOLE) + referenceDate_PMILLIS; timeStruct.end_PMILLIS = timeStruct.start_PMILLIS + daysToMillis(1); } timeStruct.viewStart_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMin_PMILLIS); timeStruct.viewEnd_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMax_PMILLIS); maxViewDuration_MILLIS = Math.max(maxViewDuration_MILLIS, timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS); timeStructs.push(timeStruct); } setTimeStructTextCenter(timeStructs, labelAlign, maxViewDuration_MILLIS); return timeStructs; } function createTimeStructsAbsolute(timeAxis, factory, timeZone, tickTimes_PMILLIS, labelAlign) { const dMin_PMILLIS = timeAxis.tMin_PMILLIS; const dMax_PMILLIS = timeAxis.tMax_PMILLIS; const timeStructs = []; let maxViewDuration_MILLIS = Number.NEGATIVE_INFINITY; let previous_PMILLIS = null; for (let n = 0; n < tickTimes_PMILLIS.length; n++) { const tickTime_PMILLIS = tickTimes_PMILLIS[n]; const timeStruct = factory(); const m = timeStruct.setTime(tickTime_PMILLIS, timeZone); const start_PMILLIS = m.valueOf(); // XXX: Floating-point comparison can be unintuitive if (hasval(previous_PMILLIS) && start_PMILLIS === previous_PMILLIS) { continue; } previous_PMILLIS = start_PMILLIS; timeStruct.start_PMILLIS = start_PMILLIS; timeStruct.incrementTime(m); timeStruct.end_PMILLIS = m.valueOf(); timeStruct.viewStart_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMin_PMILLIS); timeStruct.viewEnd_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMax_PMILLIS); maxViewDuration_MILLIS = Math.max(maxViewDuration_MILLIS, timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS); timeStructs.push(timeStruct); } setTimeStructTextCenter(timeStructs, labelAlign, maxViewDuration_MILLIS); return timeStructs; } function setTimeStructTextCenter(timeStructs, labelAlign, maxViewDuration_MILLIS) { for (let n = 0; n < timeStructs.length; n++) { const timeStruct = timeStructs[n]; const duration_MILLIS = timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS; const midpoint_PMILLIS = timeStruct.viewStart_PMILLIS + labelAlign * duration_MILLIS; const edge_PMILLIS = (timeStruct.viewStart_PMILLIS === timeStruct.start_PMILLIS ? timeStruct.viewEnd_PMILLIS : timeStruct.viewStart_PMILLIS); const edginess = 1 - clamp(0, 1, duration_MILLIS / maxViewDuration_MILLIS); timeStruct.textCenter_PMILLIS = midpoint_PMILLIS + edginess * (edge_PMILLIS - midpoint_PMILLIS); } } export function getTickTimes_PMILLIS(timeAxis, sizePixels, tickSpacing, timeZone, referenceDate_PMILLIS) { if (hasval(referenceDate_PMILLIS)) { return getTickTimesRelative_PMILLIS(timeAxis, sizePixels, tickSpacing, referenceDate_PMILLIS); } else { return getTickTimesAbsolute_PMILLIS(timeAxis, sizePixels, tickSpacing, timeZone); } } function getTickTimesRelative_PMILLIS(timeAxis, sizePixels, tickSpacing, referenceDate_PMILLIS) { const dMin_PMILLIS = timeAxis.tMin_PMILLIS; const dMax_PMILLIS = timeAxis.tMax_PMILLIS; const approxTickInterval_MILLIS = tickSpacing * (dMax_PMILLIS - dMin_PMILLIS) / sizePixels; if (approxTickInterval_MILLIS < daysToMillis(1)) { return getHourTickTimesRelative_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, referenceDate_PMILLIS); } else { return getDayTickTimesRelative_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, sizePixels, tickSpacing, referenceDate_PMILLIS); } } function getHourTickTimesRelative_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, referenceDate_PMILLIS) { const tickTimes = getHourTickTimes_PMILLIS(dMin_PMILLIS - referenceDate_PMILLIS, dMax_PMILLIS - referenceDate_PMILLIS, approxTickInterval_MILLIS, 0); for (let n = 0; n < tickTimes.length; n++) { tickTimes[n] = tickTimes[n] + referenceDate_PMILLIS; } return tickTimes; } function getDayTickTimesRelative_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, sizePixels, tickSpacing, referenceDate_PMILLIS) { const axis = new Axis1D(millisToDays(dMin_PMILLIS - referenceDate_PMILLIS), millisToDays(dMax_PMILLIS - referenceDate_PMILLIS)); const approxNumTicks = sizePixels / tickSpacing; const tickInterval = getTickInterval(axis, approxNumTicks); const tickCount = getTickCount(axis, tickInterval); const tickPositions = new Float32Array(tickCount); getTickPositions(axis, tickInterval, tickCount, tickPositions); const tickTimes_PMILLIS = []; for (let n = 0; n < tickCount; n++) { tickTimes_PMILLIS.push(daysToMillis(tickPositions[n]) + referenceDate_PMILLIS); } return tickTimes_PMILLIS; } function getTickTimesAbsolute_PMILLIS(timeAxis, sizePixels, tickSpacing, timeZone) { const dMin_PMILLIS = timeAxis.tMin_PMILLIS; const dMax_PMILLIS = timeAxis.tMax_PMILLIS; // NOTE: moment.js reports time zone offset reversed from Java Calendar, thus the negative sign const mMin = moment(dMin_PMILLIS).zone(timeZone); const zoneOffset_MILLIS = -minutesToMillis(mMin.zone()); const approxTickInterval_MILLIS = tickSpacing * (dMax_PMILLIS - dMin_PMILLIS) / sizePixels; if (approxTickInterval_MILLIS > daysToMillis(60)) { return getYearTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone); } else if (approxTickInterval_MILLIS > daysToMillis(10)) { return getMonthTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone); } else if (approxTickInterval_MILLIS > daysToMillis(1)) { return getDayTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone); } else { return getHourTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, zoneOffset_MILLIS); } } function getYearTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone) { const m = moment(dMin_PMILLIS).zone(timeZone); const currentYear = m.year(); const daysPerYear = 365.25; // assume 365.25 days in every year as a heuristic const approxTickInterval_YEARS = millisToDays(approxTickInterval_MILLIS) / daysPerYear; const yearOrderFactor = 6.0; const stepYears = getYearStep(approxTickInterval_YEARS * yearOrderFactor); const startYear = getRoundedYear(currentYear, stepYears); m.year(startYear).month(0).date(1).hours(0).minutes(0).seconds(0).milliseconds(0); const tickTimes_PMILLIS = []; while (m.valueOf() <= dMax_PMILLIS) { tickTimes_PMILLIS.push(m.valueOf()); m.add('years', stepYears); } return tickTimes_PMILLIS; } function getYearStep(spanYears) { const log10Span = Math.log(spanYears) / Math.LN10; let orderSpan = Math.floor(log10Span); if ((log10Span - orderSpan) > (1.0 - 1e-12)) { orderSpan++; } return Math.max(1, Math.pow(10, orderSpan)); } function getRoundedYear(currentYear, yearStep) { const numSteps = Math.floor(currentYear / yearStep); return numSteps * yearStep; } function getMonthTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone) { const m = moment(dMin_PMILLIS).zone(timeZone).date(1).hours(0).minutes(0).seconds(0).milliseconds(0); const tickTimes_PMILLIS = []; while (m.valueOf() <= dMax_PMILLIS) { tickTimes_PMILLIS.push(m.valueOf()); m.add('months', 1); } return tickTimes_PMILLIS; } function getDayTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone) { const tickInterval_DAYS = calculateTickInterval_DAYS(approxTickInterval_MILLIS); // initialize calendar off start time and reset fields less than month const m = moment(dMin_PMILLIS).zone(timeZone).date(1).hours(0).minutes(0).seconds(0).milliseconds(0); const endTime_PMILLIS = dMax_PMILLIS + daysToMillis(tickInterval_DAYS); let currentMonth = m.month(); const tickTimes_PMILLIS = []; while (m.valueOf() <= endTime_PMILLIS) { // ensure ticks always fall on the first day of the month const newMonth = m.month(); if (newMonth !== currentMonth) { m.date(1); currentMonth = newMonth; } // prevent display of ticks too close to the end of the month const maxDom = m.clone().endOf('month').date(); const dom = m.date(); if (maxDom - dom + 1 >= tickInterval_DAYS / 2) { tickTimes_PMILLIS.push(m.valueOf()); } m.add('days', tickInterval_DAYS); } return tickTimes_PMILLIS; } function getHourTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, zoneOffset_MILLIS) { const tickInterval_MILLIS = calculateTickInterval_MILLIS(approxTickInterval_MILLIS); const ticksSince1970 = Math.floor((dMin_PMILLIS + zoneOffset_MILLIS) / tickInterval_MILLIS); const firstTick_PMILLIS = (ticksSince1970 * tickInterval_MILLIS) - zoneOffset_MILLIS; const numTicks = Math.ceil(1 + (dMax_PMILLIS - firstTick_PMILLIS) / tickInterval_MILLIS); const tickTimes_PMILLIS = []; for (let n = 0; n < numTicks; n++) { tickTimes_PMILLIS.push(firstTick_PMILLIS + n * tickInterval_MILLIS); } return tickTimes_PMILLIS; } const tickIntervalRungs_DAYS = [2, 3, 4, 5, 8, 10]; function calculateTickInterval_DAYS(approxTickInterval_MILLIS) { const approxTickInterval_DAYS = millisToDays(approxTickInterval_MILLIS); for (let n = 0; n < tickIntervalRungs_DAYS.length; n++) { if (approxTickInterval_DAYS <= tickIntervalRungs_DAYS[n]) { return tickIntervalRungs_DAYS[n]; } } return 10; } const tickIntervalRungs_MILLIS = [secondsToMillis(1), secondsToMillis(2), secondsToMillis(5), secondsToMillis(10), secondsToMillis(15), secondsToMillis(20), secondsToMillis(30), minutesToMillis(1), minutesToMillis(2), minutesToMillis(5), minutesToMillis(10), minutesToMillis(15), minutesToMillis(20), minutesToMillis(30), hoursToMillis(1), hoursToMillis(2), hoursToMillis(3), hoursToMillis(6), hoursToMillis(12), daysToMillis(1)]; function calculateTickInterval_MILLIS(approxTickInterval_MILLIS) { for (let n = 0; n < tickIntervalRungs_MILLIS.length; n++) { if (approxTickInterval_MILLIS <= tickIntervalRungs_MILLIS[n]) { return tickIntervalRungs_MILLIS[n]; } } return daysToMillis(1); } function getTickInterval_MILLIS(tickTimes_PMILLIS) { if (hasval(tickTimes_PMILLIS) && tickTimes_PMILLIS.length > 1) { return (tickTimes_PMILLIS[1] - tickTimes_PMILLIS[0]); } else { return secondsToMillis(1); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZV9heGlzX3BhaW50ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvd2ViZ2xpbXBzZS90aW1lbGluZS90aW1lX2F4aXNfcGFpbnRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTRCRztBQUNILE9BQU8sRUFBUyxLQUFLLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFFeEMsT0FBTyxFQUFFLElBQUksRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUVqRCxPQUFPLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLFNBQVMsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNuRixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNqRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFFN0MsT0FBTyxFQUFpQixtQkFBbUIsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUM3RCxPQUFPLEVBQUUsZUFBZSxFQUFzQixNQUFNLFlBQVksQ0FBQztBQUVqRSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsRUFBRSxFQUFFLE1BQU0sRUFBRSxpQkFBaUIsRUFBRSxLQUFLLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDM0YsT0FBTyxFQUFFLGVBQWUsRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsZUFBZSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3JILE9BQU8sRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUFFLFlBQVksRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUN2RixPQUFPLE1BQWtCLE1BQU0sUUFBUSxDQUFDO0FBRXhDLHlEQUF5RDtBQUV6RCxNQUFNLE9BQU8sYUFBYTtJQUd0QixZQUFZLElBQVksRUFBRSxNQUFjO1FBQ3BDLElBQUksQ0FBQyxVQUFV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