Skip to content

Commit

Permalink
Added major tick determining in timeHelpers.
Browse files Browse the repository at this point in the history
  • Loading branch information
andrew.hurskiy committed May 26, 2017
1 parent a6fba63 commit deee58f
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 223 deletions.
38 changes: 19 additions & 19 deletions samples/scales/time/line.html
Expand Up @@ -28,7 +28,7 @@
<button id="removeData">Remove Data</button>
<script>
var timeFormat = 'MM/DD/YYYY HH:mm';

function newDate(days) {
return moment().add(days, 'd').toDate();
}
Expand All @@ -46,12 +46,12 @@
type: 'line',
data: {
labels: [ // Date Objects
newDate(0),
newDate(1),
newDate(2),
newDate(3),
newDate(4),
newDate(5),
newDate(0),
newDate(1),
newDate(2),
newDate(3),
newDate(4),
newDate(5),
newDate(6)
],
datasets: [{
Expand All @@ -60,12 +60,12 @@
borderColor: window.chartColors.red,
fill: false,
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
],
}, {
Expand All @@ -74,12 +74,12 @@
borderColor: window.chartColors.blue,
fill: false,
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
],
}, {
Expand Down
19 changes: 3 additions & 16 deletions src/core/core.scale.js
@@ -1,8 +1,5 @@
'use strict';

var moment = require('moment');
moment = typeof(moment) === 'function' ? moment : window.moment;

module.exports = function(Chart) {

var helpers = Chart.helpers;
Expand Down Expand Up @@ -506,7 +503,7 @@ module.exports = function(Chart) {

var tickFontColor = helpers.getValueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
var tickFont = parseFontOptions(optionTicks);
var seniorTickFont = helpers.fontString(tickFont.size, 'bold', tickFont.family);

var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;

var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
Expand Down Expand Up @@ -654,7 +651,7 @@ module.exports = function(Chart) {
});

// Draw all of the tick labels, tick marks, and grid lines at the correct places
helpers.each(itemsToDraw, function(itemToDraw, index) {
helpers.each(itemsToDraw, function(itemToDraw) {
if (gridLines.display) {
context.save();
context.lineWidth = itemToDraw.glWidth;
Expand Down Expand Up @@ -684,17 +681,7 @@ module.exports = function(Chart) {
context.save();
context.translate(itemToDraw.labelX, itemToDraw.labelY);
context.rotate(itemToDraw.rotation);
var font = tickFont.font;
if (me.ticksAsTimestamps) {
// Add bold style to senior units
var tickMoment = moment(me.ticksAsTimestamps[index]);
var tickMomentClone = tickMoment.clone();
if (me.majorUnit &&
tickMoment.valueOf() === tickMomentClone.startOf(me.majorUnit).valueOf()) {
font = seniorTickFont;
}
}
context.font = font;
context.font = tickFont.font;
context.textBaseline = itemToDraw.textBaseline;
context.textAlign = itemToDraw.textAlign;

Expand Down
34 changes: 31 additions & 3 deletions src/helpers/helpers.time.js
Expand Up @@ -55,8 +55,20 @@ module.exports = function(Chart) {
var ticks = [];
if (options.maxTicks) {
var stepSize = options.stepSize;
ticks.push(options.min !== undefined ? options.min : niceRange.min);
var cur = moment(niceRange.min);
var startTick = options.min !== undefined ? options.min : niceRange.min;
var majorUnitStart = startTick;
if (options.majorUnit) {
majorUnitStart = moment(startTick).add(1, options.majorUnit).startOf(options.majorUnit);
}
var startRange = majorUnitStart.valueOf() - startTick;
var startFraction = startRange % (interval[options.unit].size * stepSize);
var alignedTick = startTick;
ticks.push(startTick);
if (startFraction && options.majorUnit && !options.timeOpts.round && !options.timeOpts.isoWeekday) {
alignedTick += startFraction;
ticks.push(alignedTick);
}
var cur = moment(alignedTick);
while (cur.add(stepSize, options.unit).valueOf() < niceRange.max) {
ticks.push(cur.valueOf());
}
Expand Down Expand Up @@ -128,6 +140,22 @@ module.exports = function(Chart) {
return unit;
},

/**
* Determine major unit accordingly to passed unit
* @param unit {String} relative unit
* @return {String} major unit
*/
determineMajorUnit: function(unit) {
var units = Object.keys(interval);
var majorUnit = null;
var unitIndex = units.indexOf(unit);
if (unitIndex < units.length - 1) {
majorUnit = units[unitIndex + 1];
}

return majorUnit;
},

/**
* Determines how we scale the unit
* @param min {Number} the scale minimum
Expand Down Expand Up @@ -169,7 +197,7 @@ module.exports = function(Chart) {
generateTicks: function(options, dataRange) {
var niceMin;
var niceMax;
var isoWeekday = options.isoWeekday;
var isoWeekday = options.timeOpts.isoWeekday;
if (options.unit === 'week' && isoWeekday !== false) {
niceMin = moment(dataRange.min).startOf('isoWeek').isoWeekday(isoWeekday).valueOf();
niceMax = moment(dataRange.max).startOf('isoWeek').isoWeekday(isoWeekday);
Expand Down
179 changes: 4 additions & 175 deletions src/scales/scale.time.js
Expand Up @@ -26,8 +26,8 @@ module.exports = function(Chart) {
millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
second: 'h:mm:ss a', // 11:20:01 AM
minute: 'h:mm:ss a', // 11:20:01 AM
hour: 'hA', // 5PM
day: 'MMM D', // Sep 4
hour: 'MMM D, hA', // Sept 4, 5PM
day: 'll', // Sep 4 2015
week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
month: 'MMM YYYY', // Sept 2015
quarter: '[Q]Q - YYYY', // Q3
Expand All @@ -39,161 +39,6 @@ module.exports = function(Chart) {
}
};

/**
* Helper function to parse time to a moment object
* @param axis {TimeAxis} the time axis
* @param label {Date|string|number|Moment} The thing to parse
* @return {Moment} parsed time
*/
function parseTime(axis, label) {
var timeOpts = axis.options.time;
if (typeof timeOpts.parser === 'string') {
return moment(label, timeOpts.parser);
}
if (typeof timeOpts.parser === 'function') {
return timeOpts.parser(label);
}
if (typeof label.getMonth === 'function' || typeof label === 'number') {
// Date objects
return moment(label);
}
if (label.isValid && label.isValid()) {
// Moment support
return label;
}
var format = timeOpts.format;
if (typeof format !== 'string' && format.call) {
// Custom parsing (return an instance of moment)
console.warn('options.time.format is deprecated and replaced by options.time.parser.');
return format(label);
}
// Moment format parsing
return moment(label, format);
}

/**
* Figure out which is the best unit for the scale
* @param minUnit {String} minimum unit to use
* @param min {Number} scale minimum
* @param max {Number} scale maximum
* @return {String} the unit to use
*/
function determineUnit(minUnit, min, max, maxTicks) {
var units = Object.keys(interval);
var unit;
var numUnits = units.length;

for (var i = units.indexOf(minUnit); i < numUnits; i++) {
unit = units[i];
var unitDetails = interval[unit];
var steps = (unitDetails.steps && unitDetails.steps[unitDetails.steps.length - 1]) || unitDetails.maxStep;
if (steps === undefined || Math.ceil((max - min) / (steps * unitDetails.size)) <= maxTicks) {
break;
}
}

return unit;
}

/**
* Determines how we scale the unit
* @param min {Number} the scale minimum
* @param max {Number} the scale maximum
* @param unit {String} the unit determined by the {@see determineUnit} method
* @return {Number} the axis step size as a multiple of unit
*/
function determineStepSize(min, max, unit, maxTicks) {
// Using our unit, figoure out what we need to scale as
var unitDefinition = interval[unit];
var unitSizeInMilliSeconds = unitDefinition.size;
var range = max - min;
var sizeInUnits = Math.ceil(range / unitSizeInMilliSeconds);
var multiplier = 1;

if (unitDefinition.steps) {
// Have an array of steps
var numSteps = unitDefinition.steps.length;
for (var i = 0; i < numSteps && sizeInUnits > maxTicks; i++) {
multiplier = unitDefinition.steps[i];
sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier));
}
} else {
while (sizeInUnits > maxTicks && maxTicks > 0) {
++multiplier;
sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier));
}
}

return multiplier;
}

/**
* Helper for generating axis labels.
* @param options {ITimeGeneratorOptions} the options for generation
* @param dataRange {IRange} the data range
* @param niceRange {IRange} the pretty range to display
* @return {Number[]} ticks
*/
function generateTicks(options, dataRange, niceRange) {
var ticks = [];
if (options.maxTicks) {
var stepSize = options.stepSize;
var startTick = options.min !== undefined ? options.min : niceRange.min;
var majorUnitStart = startTick;
if (options.majorUnit) {
majorUnitStart = moment(startTick).add(1, options.majorUnit).startOf(options.majorUnit);
}
var startRange = majorUnitStart.valueOf() - startTick;
var startFraction = startRange % (interval[options.unit].size * stepSize);
var alignedTick = startTick;
ticks.push(startTick);
if (startTick !== alignedTick + startFraction && options.majorUnit && !options.timeOpts.round && !options.timeOpts.isoWeekday) {
alignedTick += startFraction;
ticks.push(alignedTick);
}
var cur = moment(alignedTick);
while (cur.add(stepSize, options.unit).valueOf() < niceRange.max) {
ticks.push(cur.valueOf());
}
var realMax = options.max || niceRange.max;
if (ticks[ticks.length - 1] !== realMax) {
ticks.push(realMax);
}
}
return ticks;
}

/**
* @function Chart.Ticks.generators.time
* @param options {ITimeGeneratorOptions} the options for generation
* @param dataRange {IRange} the data range
* @return {Number[]} ticks
*/
Chart.Ticks.generators.time = function(options, dataRange) {
var niceMin;
var niceMax;
var isoWeekday = options.timeOpts.isoWeekday;
if (options.unit === 'week' && isoWeekday !== false) {
niceMin = moment(dataRange.min).startOf('isoWeek').isoWeekday(isoWeekday).valueOf();
niceMax = moment(dataRange.max).startOf('isoWeek').isoWeekday(isoWeekday);
if (dataRange.max - niceMax > 0) {
niceMax.add(1, 'week');
}
niceMax = niceMax.valueOf();
} else {
niceMin = moment(dataRange.min).startOf(options.unit).valueOf();
niceMax = moment(dataRange.max).startOf(options.unit);
if (dataRange.max - niceMax > 0) {
niceMax.add(1, options.unit);
}
niceMax = niceMax.valueOf();
}
return generateTicks(options, dataRange, {
min: niceMin,
max: niceMax
});
};

var TimeScale = Chart.Scale.extend({
initialize: function() {
if (!moment) {
Expand Down Expand Up @@ -291,12 +136,7 @@ module.exports = function(Chart) {
var maxTicks = me.getLabelCapacity(minTimestamp || dataMin);

var unit = timeOpts.unit || timeHelpers.determineUnit(timeOpts.minUnit, minTimestamp || dataMin, maxTimestamp || dataMax, maxTicks);
var units = Object.keys(interval);
var majorUnit = null;
var unitIndex = units.indexOf(unit);
if (unitIndex < units.length - 1) {
majorUnit = units[unitIndex + 1];
}
var majorUnit = timeHelpers.determineMajorUnit(unit);

me.displayFormat = timeOpts.displayFormats[unit];
me.majorDisplayFormat = timeOpts.displayFormats[majorUnit];
Expand Down Expand Up @@ -341,18 +181,7 @@ module.exports = function(Chart) {
},
// Function to format an individual tick mark
tickFormatFunction: function(tick, index, ticks) {
var formattedTick;
var tickClone = tick.clone();
if (this.majorUnit &&
this.majorDisplayFormat &&
tick.valueOf() === tickClone.startOf(this.majorUnit).valueOf()) {
// format as senior unit
formattedTick = tick.format(this.majorDisplayFormat);
} else {
// format as base unit
formattedTick = tick.format(this.displayFormat);
}

var formattedTick = tick.format(this.displayFormat);
var tickOpts = this.options.ticks;
var callback = helpers.getValueOrDefault(tickOpts.callback, tickOpts.userCallback);

Expand Down

0 comments on commit deee58f

Please sign in to comment.