Mercurial > hg > extraction-interface
diff geotemco/lib/simile/timeplot/scripts/geometry.js @ 0:b12c99b7c3f0
commit for previous development
| author | Zoe Hong <zhong@mpiwg-berlin.mpg.de> |
|---|---|
| date | Mon, 19 Jan 2015 17:13:49 +0100 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/geotemco/lib/simile/timeplot/scripts/geometry.js Mon Jan 19 17:13:49 2015 +0100 @@ -0,0 +1,879 @@ +/** + * Geometries + * + * @fileOverview Geometries + * @name Geometries + */ + +/** + * This is the constructor for the default value geometry. + * A value geometry is what regulates mapping of the plot values to the screen y coordinate. + * If two plots share the same value geometry, they will be drawn using the same scale. + * If "min" and "max" parameters are not set, the geometry will stretch itself automatically + * so that the entire plot will be drawn without overflowing. The stretching happens also + * when a geometry is shared between multiple plots, the one with the biggest range will + * win over the others. + * + * @constructor + */ +Timeplot.DefaultValueGeometry = function(params) { + if (!params) params = {}; + this._id = ("id" in params) ? params.id : "g" + Math.round(Math.random() * 1000000); + this._axisColor = ("axisColor" in params) ? ((typeof params.axisColor == "string") ? new Timeplot.Color(params.axisColor) : params.axisColor) : new Timeplot.Color("#606060"), + this._gridColor = ("gridColor" in params) ? ((typeof params.gridColor == "string") ? new Timeplot.Color(params.gridColor) : params.gridColor) : null, + this._gridLineWidth = ("gridLineWidth" in params) ? params.gridLineWidth : 0.5; + this._axisLabelsPlacement = ("axisLabelsPlacement" in params) ? params.axisLabelsPlacement : "right"; + this._gridSpacing = ("gridSpacing" in params) ? params.gridStep : 50; + this._gridType = ("gridType" in params) ? params.gridType : "short"; + this._gridShortSize = ("gridShortSize" in params) ? params.gridShortSize : 10; + this._minValue = ("min" in params) ? params.min : null; + this._maxValue = ("max" in params) ? params.max : null; + this._linMap = { + direct: function(v) { + return v; + }, + inverse: function(y) { + return y; + } + } + this._map = this._linMap; + this._labels = []; + this._grid = []; +} + +Timeplot.DefaultValueGeometry.prototype = { + + /** + * Since geometries can be reused across timeplots, we need to call this function + * before we can paint using this geometry. + */ + setTimeplot: function(timeplot) { + this._timeplot = timeplot; + this._canvas = timeplot.getCanvas(); + this.reset(); + }, + + /** + * Called by all the plot layers this geometry is associated with + * to update the value range. Unless min/max values are specified + * in the parameters, the biggest value range will be used. + */ + setRange: function(range) { + if ((this._minValue == null) || ((this._minValue != null) && (range.min < this._minValue))) { + this._minValue = range.min; + } + if ((this._maxValue == null) || ((this._maxValue != null) && (range.max * 1.05 > this._maxValue))) { + this._maxValue = range.max * 1.05; // get a little more head room to avoid hitting the ceiling + } + + this._updateMappedValues(); + + if (!(this._minValue == 0 && this._maxValue == 0)) { + this._grid = this._calculateGrid(); + } + }, + + /** + * Called after changing ranges or canvas size to reset the grid values + */ + reset: function() { + this._clearLabels(); + this._updateMappedValues(); + this._grid = this._calculateGrid(); + }, + + /** + * Map the given value to a y screen coordinate. + */ + toScreen: function(value) { + if (this._canvas && this._maxValue) { + var v = value - this._minValue; + return this._canvas.height * (this._map.direct(v)) / this._mappedRange; + } else { + return -50; + } + }, + + /** + * Map the given y screen coordinate to a value + */ + fromScreen: function(y) { + if (this._canvas) { + return this._map.inverse(this._mappedRange * y / this._canvas.height) + this._minValue; + } else { + return 0; + } + }, + + /** + * Each geometry is also a painter and paints the value grid and grid labels. + */ + paint: function() { + if (this._timeplot) { + var ctx = this._canvas.getContext('2d'); + + ctx.lineJoin = 'miter'; + + // paint grid + if (this._gridColor) { + var gridGradient = ctx.createLinearGradient(0,0,0,this._canvas.height); + gridGradient.addColorStop(0, this._gridColor.toHexString()); + gridGradient.addColorStop(0.3, this._gridColor.toHexString()); + gridGradient.addColorStop(1, "rgba(255,255,255,0.5)"); + + ctx.lineWidth = this._gridLineWidth; + ctx.strokeStyle = gridGradient; + + for (var i = 0; i < this._grid.length; i++) { + var tick = this._grid[i]; + var y = Math.floor(tick.y) + 0.5; + if (typeof tick.label != "undefined") { + if (this._axisLabelsPlacement == "left") { + var div = this._timeplot.putText(this._id + "-" + i, tick.label,"timeplot-grid-label",{ + left: 4, + bottom: y + 2, + color: this._gridColor.toHexString(), + visibility: "hidden" + }); + this._labels.push(div); + } else if (this._axisLabelsPlacement == "right") { + var div = this._timeplot.putText(this._id + "-" + i, tick.label, "timeplot-grid-label",{ + right: 4, + bottom: y + 2, + color: this._gridColor.toHexString(), + visibility: "hidden" + }); + this._labels.push(div); + } + if (y + div.clientHeight < this._canvas.height + 10) { + div.style.visibility = "visible"; // avoid the labels that would overflow + } + } + + // draw grid + ctx.beginPath(); + if (this._gridType == "long" || tick.label == 0) { + ctx.moveTo(0, y); + ctx.lineTo(this._canvas.width, y); + } else if (this._gridType == "short") { + if (this._axisLabelsPlacement == "left") { + ctx.moveTo(0, y); + ctx.lineTo(this._gridShortSize, y); + } else if (this._axisLabelsPlacement == "right") { + ctx.moveTo(this._canvas.width, y); + ctx.lineTo(this._canvas.width - this._gridShortSize, y); + } + } + ctx.stroke(); + } + } + + // paint axis + var axisGradient = ctx.createLinearGradient(0,0,0,this._canvas.height); + axisGradient.addColorStop(0, this._axisColor.toString()); + axisGradient.addColorStop(0.5, this._axisColor.toString()); + axisGradient.addColorStop(1, "rgba(255,255,255,0.5)"); + + ctx.lineWidth = 1; + ctx.strokeStyle = axisGradient; + + // left axis + ctx.beginPath(); + ctx.moveTo(0,this._canvas.height); + ctx.lineTo(0,0); + ctx.stroke(); + + // right axis + ctx.beginPath(); + ctx.moveTo(this._canvas.width,0); + ctx.lineTo(this._canvas.width,this._canvas.height); + ctx.stroke(); + } + }, + + /** + * Removes all the labels that were added by this geometry + */ + _clearLabels: function() { + for (var i = 0; i < this._labels.length; i++) { + var l = this._labels[i]; + var parent = l.parentNode; + if (parent) parent.removeChild(l); + } + }, + + /* + * This function calculates the grid spacing that it will be used + * by this geometry to draw the grid in order to reduce clutter. + */ + _calculateGrid: function() { + var grid = []; + + if (!this._canvas || this._valueRange == 0) return grid; + + var power = 0; + if (this._valueRange > 1) { + while (Math.pow(10,power) < this._valueRange) { + power++; + } + power--; + } else { + while (Math.pow(10,power) > this._valueRange) { + power--; + } + } + + var unit = Math.pow(10,power); + var inc = unit; + while (true) { + var dy = this.toScreen(this._minValue + inc); + + while (dy < this._gridSpacing) { + inc += unit; + dy = this.toScreen(this._minValue + inc); + } + + if (dy > 2 * this._gridSpacing) { // grids are too spaced out + unit /= 10; + inc = unit; + } else { + break; + } + } + + var v = 0; + var y = this.toScreen(v); + if (this._minValue >= 0) { + while (y < this._canvas.height) { + if (y > 0) { + grid.push({ y: y, label: v }); + } + v += inc; + y = this.toScreen(v); + } + } else if (this._maxValue <= 0) { + while (y > 0) { + if (y < this._canvas.height) { + grid.push({ y: y, label: v }); + } + v -= inc; + y = this.toScreen(v); + } + } else { + while (y < this._canvas.height) { + if (y > 0) { + grid.push({ y: y, label: v }); + } + v += inc; + y = this.toScreen(v); + } + v = -inc; + y = this.toScreen(v); + while (y > 0) { + if (y < this._canvas.height) { + grid.push({ y: y, label: v }); + } + v -= inc; + y = this.toScreen(v); + } + } + + return grid; + }, + + /* + * Update the values that are used by the paint function so that + * we don't have to calculate them at every repaint. + */ + _updateMappedValues: function() { + this._valueRange = Math.abs(this._maxValue - this._minValue); + this._mappedRange = this._map.direct(this._valueRange); + } + +} + +// -------------------------------------------------- + +/** + * This is the constructor for a Logarithmic value geometry, which + * is useful when plots have values in different magnitudes but + * exhibit similar trends and such trends want to be shown on the same + * plot (here a cartesian geometry would make the small magnitudes + * disappear). + * + * NOTE: this class extends Timeplot.DefaultValueGeometry and inherits + * all of the methods of that class. So refer to that class. + * + * @constructor + */ +Timeplot.LogarithmicValueGeometry = function(params) { + Timeplot.DefaultValueGeometry.apply(this, arguments); + this._logMap = { + direct: function(v) { + return Math.log(v + 1) / Math.log(10); + }, + inverse: function(y) { + return Math.exp(Math.log(10) * y) - 1; + } + } + this._mode = "log"; + this._map = this._logMap; + this._calculateGrid = this._logarithmicCalculateGrid; +}; + +Timeplot.LogarithmicValueGeometry.prototype._linearCalculateGrid = Timeplot.DefaultValueGeometry.prototype._calculateGrid; + +Object.extend(Timeplot.LogarithmicValueGeometry.prototype,Timeplot.DefaultValueGeometry.prototype); + +/* + * This function calculates the grid spacing that it will be used + * by this geometry to draw the grid in order to reduce clutter. + */ +Timeplot.LogarithmicValueGeometry.prototype._logarithmicCalculateGrid = function() { + var grid = []; + + if (!this._canvas || this._valueRange == 0) return grid; + + var v = 1; + var y = this.toScreen(v); + while (y < this._canvas.height || isNaN(y)) { + if (y > 0) { + grid.push({ y: y, label: v }); + } + v *= 10; + y = this.toScreen(v); + } + + return grid; +}; + +/** + * Turn the logarithmic scaling off. + */ +Timeplot.LogarithmicValueGeometry.prototype.actLinear = function() { + this._mode = "lin"; + this._map = this._linMap; + this._calculateGrid = this._linearCalculateGrid; + this.reset(); +} + +/** + * Turn the logarithmic scaling on. + */ +Timeplot.LogarithmicValueGeometry.prototype.actLogarithmic = function() { + this._mode = "log"; + this._map = this._logMap; + this._calculateGrid = this._logarithmicCalculateGrid; + this.reset(); +} + +/** + * Toggle logarithmic scaling seeting it to on if off and viceversa. + */ +Timeplot.LogarithmicValueGeometry.prototype.toggle = function() { + if (this._mode == "log") { + this.actLinear(); + } else { + this.actLogarithmic(); + } +} + +// ----------------------------------------------------- + +/** + * This is the constructor for the default time geometry. + * + * @constructor + */ +Timeplot.DefaultTimeGeometry = function(params) { + if (!params) params = {}; + this._id = ("id" in params) ? params.id : "g" + Math.round(Math.random() * 1000000); + this._locale = ("locale" in params) ? params.locale : "en"; + this._timeZone = ("timeZone" in params) ? params.timeZone : SimileAjax.DateTime.getTimezone(); + this._labeler = ("labeller" in params) ? params.labeller : null; + this._axisColor = ("axisColor" in params) ? ((params.axisColor == "string") ? new Timeplot.Color(params.axisColor) : params.axisColor) : new Timeplot.Color("#606060"), + this._gridColor = ("gridColor" in params) ? ((params.gridColor == "string") ? new Timeplot.Color(params.gridColor) : params.gridColor) : null, + this._gridLineWidth = ("gridLineWidth" in params) ? params.gridLineWidth : 0.5; + this._axisLabelsPlacement = ("axisLabelsPlacement" in params) ? params.axisLabelsPlacement : "bottom"; + this._gridStep = ("gridStep" in params) ? params.gridStep : 100; + this._gridStepRange = ("gridStepRange" in params) ? params.gridStepRange : 20; + this._min = ("min" in params) ? params.min : null; + this._max = ("max" in params) ? params.max : null; + this._timeValuePosition =("timeValuePosition" in params) ? params.timeValuePosition : "bottom"; + this._unit = ("unit" in params) ? params.unit : SimileAjax.NativeDateUnit; + this._linMap = { + direct: function(t) { + return t; + }, + inverse: function(x) { + return x; + } + } + this._map = this._linMap; + if (!this._labeler) + this._labeler = (this._unit && ("createLabeller" in this._unit)) ? this._unit.createLabeller(this._locale, this._timeZone) : new Timeline.GregorianDateLabeller(this._locale, this._timeZone); + var dateParser = this._unit.getParser("iso8601"); + if (this._min && !this._min.getTime) { + this._min = dateParser(this._min); + } + if (this._max && !this._max.getTime) { + this._max = dateParser(this._max); + } + this._labels = []; + this._grid = []; +} + +Timeplot.DefaultTimeGeometry.prototype = { + + /** + * Since geometries can be reused across timeplots, we need to call this function + * before we can paint using this geometry. + */ + setTimeplot: function(timeplot) { + this._timeplot = timeplot; + this._canvas = timeplot.getCanvas(); + this.reset(); + }, + + /** + * Called by all the plot layers this geometry is associated with + * to update the time range. Unless min/max values are specified + * in the parameters, the biggest range will be used. + */ + setRange: function(range) { + if (this._min) { + this._earliestDate = this._min; + } else if (range.earliestDate && ((this._earliestDate == null) || ((this._earliestDate != null) && (range.earliestDate.getTime() < this._earliestDate.getTime())))) { + this._earliestDate = range.earliestDate; + } + + if (this._max) { + this._latestDate = this._max; + } else if (range.latestDate && ((this._latestDate == null) || ((this._latestDate != null) && (range.latestDate.getTime() > this._latestDate.getTime())))) { + this._latestDate = range.latestDate; + } + + if (!this._earliestDate && !this._latestDate) { + this._grid = []; + } else { + this.reset(); + } + }, + + /** + * Called after changing ranges or canvas size to reset the grid values + */ + reset: function() { + this._updateMappedValues(); + if (this._canvas) this._grid = this._calculateGrid(); + }, + + /** + * Map the given date to a x screen coordinate. + */ + toScreen: function(time) { + if (this._canvas && this._latestDate) { + var t = time - this._earliestDate.getTime(); + var fraction = (this._mappedPeriod > 0) ? this._map.direct(t) / this._mappedPeriod : 0; + return this._canvas.width * fraction; + } else { + return -50; + } + }, + + /** + * Map the given x screen coordinate to a date. + */ + fromScreen: function(x) { + if (this._canvas) { + return this._map.inverse(this._mappedPeriod * x / this._canvas.width) + this._earliestDate.getTime(); + } else { + return 0; + } + }, + + /** + * Get a period (in milliseconds) this time geometry spans. + */ + getPeriod: function() { + return this._period; + }, + + /** + * Return the labeler that has been associated with this time geometry + */ + getLabeler: function() { + return this._labeler; + }, + + /** + * Return the time unit associated with this time geometry + */ + getUnit: function() { + return this._unit; + }, + + /** + * Each geometry is also a painter and paints the value grid and grid labels. + */ + paint: function() { + if (this._canvas) { + var unit = this._unit; + var ctx = this._canvas.getContext('2d'); + + var gradient = ctx.createLinearGradient(0,0,0,this._canvas.height); + + ctx.strokeStyle = gradient; + ctx.lineWidth = this._gridLineWidth; + ctx.lineJoin = 'miter'; + + // paint grid + if (this._gridColor) { + gradient.addColorStop(0, this._gridColor.toString()); + gradient.addColorStop(1, "rgba(255,255,255,0.9)"); + + for (var i = 0; i < this._grid.length; i++) { + var tick = this._grid[i]; + var x = Math.floor(tick.x) + 0.5; + if (this._axisLabelsPlacement == "top") { + var div = this._timeplot.putText(this._id + "-" + i, tick.label,"timeplot-grid-label",{ + left: x + 4, + top: 2, + visibility: "hidden" + }); + this._labels.push(div); + } else if (this._axisLabelsPlacement == "bottom") { + var div = this._timeplot.putText(this._id + "-" + i, tick.label, "timeplot-grid-label",{ + left: x + 4, + bottom: 2, + visibility: "hidden" + }); + this._labels.push(div); + } + if (x + div.clientWidth < this._canvas.width + 10) { + div.style.visibility = "visible"; // avoid the labels that would overflow + } + + // draw separator + ctx.beginPath(); + ctx.moveTo(x,0); + ctx.lineTo(x,this._canvas.height); + ctx.stroke(); + } + } + + // paint axis + gradient.addColorStop(0, this._axisColor.toString()); + gradient.addColorStop(1, "rgba(255,255,255,0.5)"); + + ctx.lineWidth = 1; + gradient.addColorStop(0, this._axisColor.toString()); + + ctx.beginPath(); + ctx.moveTo(0,0); + ctx.lineTo(this._canvas.width,0); + ctx.stroke(); + } + }, + + /* + * This function calculates the grid spacing that it will be used + * by this geometry to draw the grid in order to reduce clutter. + */ + _calculateGrid: function() { + var grid = []; + + var time = SimileAjax.DateTime; + var u = this._unit; + var p = this._period; + + if (p == 0) return grid; + + // find the time units nearest to the time period + if (p > time.gregorianUnitLengths[time.MILLENNIUM]) { + unit = time.MILLENNIUM; + } else { + for (var unit = time.MILLENNIUM; unit > 0; unit--) { + if (time.gregorianUnitLengths[unit-1] <= p && p < time.gregorianUnitLengths[unit]) { + unit--; + break; + } + } + } + + var t = u.cloneValue(this._earliestDate); + + do { + time.roundDownToInterval(t, unit, this._timeZone, 1, 0); + var x = this.toScreen(u.toNumber(t)); + switch (unit) { + case time.SECOND: + var l = t.toLocaleTimeString(); + break; + case time.MINUTE: + var m = t.getMinutes(); + var l = t.getHours() + ":" + ((m < 10) ? "0" : "") + m; + break; + case time.HOUR: + var l = t.getHours() + ":00"; + break; + case time.DAY: + case time.WEEK: + case time.MONTH: + var l = t.toLocaleDateString(); + break; + case time.YEAR: + case time.DECADE: + case time.CENTURY: + case time.MILLENNIUM: + var l = t.getUTCFullYear(); + break; + } + if (x > 0) { + grid.push({ x: x, label: l }); + } + time.incrementByInterval(t, unit, this._timeZone); + } while (t.getTime() < this._latestDate.getTime()); + + return grid; + }, + + /* + * Clear labels generated by this time geometry. + */ + _clearLabels: function() { + for (var i = 0; i < this._labels.length; i++) { + var l = this._labels[i]; + var parent = l.parentNode; + if (parent) parent.removeChild(l); + } + }, + + /* + * Update the values that are used by the paint function so that + * we don't have to calculate them at every repaint. + */ + _updateMappedValues: function() { + if (this._latestDate && this._earliestDate) { + this._period = this._latestDate.getTime() - this._earliestDate.getTime(); + this._mappedPeriod = this._map.direct(this._period); + } else { + this._period = 0; + this._mappedPeriod = 0; + } + } + +} + +// -------------------------------------------------------------- + +/** + * This is the constructor for the magnifying time geometry. + * Users can interact with this geometry and 'magnify' certain areas of the + * plot to see the plot enlarged and resolve details that would otherwise + * get lost or cluttered with a linear time geometry. + * + * @constructor + */ +Timeplot.MagnifyingTimeGeometry = function(params) { + Timeplot.DefaultTimeGeometry.apply(this, arguments); + + var g = this; + this._MagnifyingMap = { + direct: function(t) { + if (t < g._leftTimeMargin) { + var x = t * g._leftRate; + } else if ( g._leftTimeMargin < t && t < g._rightTimeMargin ) { + var x = t * g._expandedRate + g._expandedTimeTranslation; + } else { + var x = t * g._rightRate + g._rightTimeTranslation; + } + return x; + }, + inverse: function(x) { + if (x < g._leftScreenMargin) { + var t = x / g._leftRate; + } else if ( g._leftScreenMargin < x && x < g._rightScreenMargin ) { + var t = x / g._expandedRate + g._expandedScreenTranslation; + } else { + var t = x / g._rightRate + g._rightScreenTranslation; + } + return t; + } + } + + this._mode = "lin"; + this._map = this._linMap; +}; + +Object.extend(Timeplot.MagnifyingTimeGeometry.prototype,Timeplot.DefaultTimeGeometry.prototype); + +/** + * Initialize this geometry associating it with the given timeplot and + * register the geometry event handlers to the timeplot so that it can + * interact with the user. + */ +Timeplot.MagnifyingTimeGeometry.prototype.initialize = function(timeplot) { + Timeplot.DefaultTimeGeometry.prototype.initialize.apply(this, arguments); + + if (!this._lens) { + this._lens = this._timeplot.putDiv("lens","timeplot-lens"); + } + + var period = 1000 * 60 * 60 * 24 * 30; // a month in the magnifying lens + + var geometry = this; + + var magnifyWith = function(lens) { + var aperture = lens.clientWidth; + var loc = geometry._timeplot.locate(lens); + geometry.setMagnifyingParams(loc.x + aperture / 2, aperture, period); + geometry.actMagnifying(); + geometry._timeplot.paint(); + } + + var canvasMouseDown = function(elmt, evt, target) { + geometry._canvas.startCoords = SimileAjax.DOM.getEventRelativeCoordinates(evt,elmt); + geometry._canvas.pressed = true; + } + + var canvasMouseUp = function(elmt, evt, target) { + geometry._canvas.pressed = false; + var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt,elmt); + if (Timeplot.Math.isClose(coords,geometry._canvas.startCoords,5)) { + geometry._lens.style.display = "none"; + geometry.actLinear(); + geometry._timeplot.paint(); + } else { + geometry._lens.style.cursor = "move"; + magnifyWith(geometry._lens); + } + } + + var canvasMouseMove = function(elmt, evt, target) { + if (geometry._canvas.pressed) { + var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt,elmt); + if (coords.x < 0) coords.x = 0; + if (coords.x > geometry._canvas.width) coords.x = geometry._canvas.width; + geometry._timeplot.placeDiv(geometry._lens, { + left: geometry._canvas.startCoords.x, + width: coords.x - geometry._canvas.startCoords.x, + bottom: 0, + height: geometry._canvas.height, + display: "block" + }); + } + } + + var lensMouseDown = function(elmt, evt, target) { + geometry._lens.startCoords = SimileAjax.DOM.getEventRelativeCoordinates(evt,elmt);; + geometry._lens.pressed = true; + } + + var lensMouseUp = function(elmt, evt, target) { + geometry._lens.pressed = false; + } + + var lensMouseMove = function(elmt, evt, target) { + if (geometry._lens.pressed) { + var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt,elmt); + var lens = geometry._lens; + var left = lens.offsetLeft + coords.x - lens.startCoords.x; + if (left < geometry._timeplot._paddingX) left = geometry._timeplot._paddingX; + if (left + lens.clientWidth > geometry._canvas.width - geometry._timeplot._paddingX) left = geometry._canvas.width - lens.clientWidth + geometry._timeplot._paddingX; + lens.style.left = left; + magnifyWith(lens); + } + } + + if (!this._canvas.instrumented) { + SimileAjax.DOM.registerEvent(this._canvas, "mousedown", canvasMouseDown); + SimileAjax.DOM.registerEvent(this._canvas, "mousemove", canvasMouseMove); + SimileAjax.DOM.registerEvent(this._canvas, "mouseup" , canvasMouseUp); + SimileAjax.DOM.registerEvent(this._canvas, "mouseup" , lensMouseUp); + this._canvas.instrumented = true; + } + + if (!this._lens.instrumented) { + SimileAjax.DOM.registerEvent(this._lens, "mousedown", lensMouseDown); + SimileAjax.DOM.registerEvent(this._lens, "mousemove", lensMouseMove); + SimileAjax.DOM.registerEvent(this._lens, "mouseup" , lensMouseUp); + SimileAjax.DOM.registerEvent(this._lens, "mouseup" , canvasMouseUp); + this._lens.instrumented = true; + } +} + +/** + * Set the Magnifying parameters. c is the location in pixels where the Magnifying + * center should be located in the timeplot, a is the aperture in pixel of + * the Magnifying and b is the time period in milliseconds that the Magnifying + * should span. + */ +Timeplot.MagnifyingTimeGeometry.prototype.setMagnifyingParams = function(c,a,b) { + a = a / 2; + b = b / 2; + + var w = this._canvas.width; + var d = this._period; + + if (c < 0) c = 0; + if (c > w) c = w; + + if (c - a < 0) a = c; + if (c + a > w) a = w - c; + + var ct = this.fromScreen(c) - this._earliestDate.getTime(); + if (ct - b < 0) b = ct; + if (ct + b > d) b = d - ct; + + this._centerX = c; + this._centerTime = ct; + this._aperture = a; + this._aperturePeriod = b; + + this._leftScreenMargin = this._centerX - this._aperture; + this._rightScreenMargin = this._centerX + this._aperture; + this._leftTimeMargin = this._centerTime - this._aperturePeriod; + this._rightTimeMargin = this._centerTime + this._aperturePeriod; + + this._leftRate = (c - a) / (ct - b); + this._expandedRate = a / b; + this._rightRate = (w - c - a) / (d - ct - b); + + this._expandedTimeTranslation = this._centerX - this._centerTime * this._expandedRate; + this._expandedScreenTranslation = this._centerTime - this._centerX / this._expandedRate; + this._rightTimeTranslation = (c + a) - (ct + b) * this._rightRate; + this._rightScreenTranslation = (ct + b) - (c + a) / this._rightRate; + + this._updateMappedValues(); +} + +/* + * Turn magnification off. + */ +Timeplot.MagnifyingTimeGeometry.prototype.actLinear = function() { + this._mode = "lin"; + this._map = this._linMap; + this.reset(); +} + +/* + * Turn magnification on. + */ +Timeplot.MagnifyingTimeGeometry.prototype.actMagnifying = function() { + this._mode = "Magnifying"; + this._map = this._MagnifyingMap; + this.reset(); +} + +/* + * Toggle magnification. + */ +Timeplot.MagnifyingTimeGeometry.prototype.toggle = function() { + if (this._mode == "Magnifying") { + this.actLinear(); + } else { + this.actMagnifying(); + } +} +
