267 lines
8.1 KiB
JavaScript
267 lines
8.1 KiB
JavaScript
/*
|
|
|
|
This Object object is responsible for defining the coordinate system in a TimeLine.
|
|
|
|
The scale on the X axis depends on the time.
|
|
The scale on the Y axis depends on the data that wants to be plotted. Usually we need the Max amount on that data to generate a proper scale.
|
|
|
|
*/
|
|
|
|
function newCoordinateSystem () {
|
|
let thisObject = {
|
|
min: undefined,
|
|
max: undefined,
|
|
maxHeight: undefined,
|
|
maxWidth: undefined,
|
|
scale: undefined,
|
|
eventHandler: undefined,
|
|
autoMinXScale: true,
|
|
autoMaxXScale: true,
|
|
autoMinYScale: true,
|
|
autoMaxYScale: true,
|
|
physics: physics,
|
|
reportXValue: reportXValue,
|
|
reportYValue: reportYValue,
|
|
zoomX: zoomX,
|
|
zoomY: zoomY,
|
|
recalculateScale: recalculateScale,
|
|
transformThisPoint: transformThisPoint,
|
|
transformThisPoint2: transformThisPoint2,
|
|
unInverseTransform: unInverseTransform,
|
|
inverseTransformUncappedY: inverseTransformUncappedY,
|
|
initializeX: initializeX,
|
|
initializeY: initializeY,
|
|
initialize: initialize,
|
|
finalize: finalize
|
|
}
|
|
|
|
thisObject.min = {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
thisObject.max = {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
thisObject.scale = {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
|
|
thisObject.eventHandler = newEventHandler()
|
|
thisObject.eventHandler.name = 'Coordinate System'
|
|
|
|
let newXMax = -VERY_LARGE_NUMBER
|
|
let newXMin = VERY_LARGE_NUMBER
|
|
let noXValueReported = true
|
|
|
|
let newYMax = -VERY_LARGE_NUMBER
|
|
let newYMin = VERY_LARGE_NUMBER
|
|
let noYValueReported = true
|
|
|
|
return thisObject
|
|
|
|
function finalize () {
|
|
thisObject.eventHandler.finalize()
|
|
thisObject.eventHandler = undefined
|
|
}
|
|
|
|
function initialize (minValue, maxValue, pMaxWidth, pMaxHeight) {
|
|
/* Defines the min and max value of rate that we are going to transport to the available screen at the center position. */
|
|
thisObject.min.x = minValue.x
|
|
thisObject.max.x = maxValue.x
|
|
|
|
thisObject.min.y = minValue.y
|
|
thisObject.max.y = maxValue.y
|
|
|
|
thisObject.maxWidth = pMaxWidth
|
|
thisObject.maxHeight = pMaxHeight
|
|
|
|
recalculateScale()
|
|
}
|
|
|
|
function physics () {
|
|
let mustRecalculate = false
|
|
|
|
if ((thisObject.autoMinXScale === true || thisObject.autoMaxXScale === true) && noXValueReported === false) {
|
|
if (thisObject.autoMaxXScale === true && thisObject.max.x !== newXMax) {
|
|
thisObject.max.x = newXMax
|
|
mustRecalculate = true
|
|
}
|
|
|
|
if (thisObject.autoMaxXScale === true) {
|
|
newXMax = -VERY_LARGE_NUMBER
|
|
}
|
|
|
|
if (thisObject.autoMinXScale === true && thisObject.min.x !== newXMin) {
|
|
thisObject.min.x = newXMin
|
|
newXMin = VERY_LARGE_NUMBER
|
|
mustRecalculate = true
|
|
}
|
|
|
|
if (thisObject.autoMinXScale === true) {
|
|
newXMin = VERY_LARGE_NUMBER
|
|
}
|
|
|
|
/* Reseting this to start over at each cycle. */
|
|
noXValueReported = true
|
|
|
|
if (mustRecalculate === true) {
|
|
thisObject.eventHandler.raiseEvent('X-Range Changed')
|
|
}
|
|
}
|
|
|
|
if ((thisObject.autoMinYScale === true || thisObject.autoMaxYScale === true) && noYValueReported === false) {
|
|
if (thisObject.autoMaxYScale === true && thisObject.max.y !== newYMax) {
|
|
thisObject.max.y = newYMax
|
|
mustRecalculate = true
|
|
}
|
|
|
|
if (thisObject.autoMaxYScale === true) {
|
|
newYMax = -VERY_LARGE_NUMBER
|
|
}
|
|
|
|
if (thisObject.autoMinYScale === true && thisObject.min.y !== newYMin) {
|
|
thisObject.min.y = newYMin
|
|
newYMin = VERY_LARGE_NUMBER
|
|
mustRecalculate = true
|
|
}
|
|
|
|
if (thisObject.autoMinYScale === true) {
|
|
newYMin = VERY_LARGE_NUMBER
|
|
}
|
|
|
|
/* Reseting this to start over at each cycle. */
|
|
noYValueReported = true
|
|
}
|
|
|
|
if (mustRecalculate === true) {
|
|
recalculateScale()
|
|
}
|
|
}
|
|
|
|
function reportXValue (value) {
|
|
if (thisObject.autoMinXScale === true) {
|
|
if (value < newXMin) { newXMin = value }
|
|
noXValueReported = false
|
|
}
|
|
if (thisObject.autoMaxXScale === true) {
|
|
if (value > newXMax) { newXMax = value }
|
|
noXValueReported = false
|
|
}
|
|
}
|
|
|
|
function reportYValue (value) {
|
|
if (thisObject.autoMinYScale === true) {
|
|
if (value < newYMin) { newYMin = value }
|
|
noYValueReported = false
|
|
}
|
|
if (thisObject.autoMaxYScale === true) {
|
|
if (value > newYMax) { newYMax = value }
|
|
noYValueReported = false
|
|
}
|
|
}
|
|
|
|
function zoomX (factor, mousePosition, container) {
|
|
let mouseAtCointainer = unTransformThisPoint(mousePosition, container)
|
|
let leftDistance = mouseAtCointainer.x
|
|
let rightDistance = container.frame.width - mouseAtCointainer.x
|
|
let diff = thisObject.max.x - thisObject.min.x
|
|
let min = thisObject.min.x + diff * factor * leftDistance / (container.frame.width / 2)
|
|
let max = thisObject.max.x - diff * factor * rightDistance / (container.frame.width / 2)
|
|
|
|
if (min < max) {
|
|
thisObject.min.x = min
|
|
thisObject.max.x = max
|
|
thisObject.recalculateScale()
|
|
}
|
|
}
|
|
|
|
function zoomY (factor, mousePosition, container) {
|
|
let mouseAtCointainer = unTransformThisPoint(mousePosition, container)
|
|
let topDistance = mouseAtCointainer.y
|
|
let bottomDistance = container.frame.height - mouseAtCointainer.y
|
|
let diff = thisObject.max.y - thisObject.min.y
|
|
let min = thisObject.min.y + diff * factor * bottomDistance / (container.frame.height / 2)
|
|
let max = thisObject.max.y - diff * factor * topDistance / (container.frame.height / 2)
|
|
|
|
if (min < max) {
|
|
thisObject.min.y = min
|
|
thisObject.max.y = max
|
|
thisObject.recalculateScale()
|
|
}
|
|
}
|
|
|
|
function recalculateScale (event) {
|
|
thisObject.scale.x = thisObject.maxWidth / (thisObject.max.x - thisObject.min.x)
|
|
thisObject.scale.y = thisObject.maxHeight / (thisObject.max.y - thisObject.min.y)
|
|
|
|
thisObject.eventHandler.raiseEvent('Scale Changed', event)
|
|
}
|
|
|
|
function initializeX (minValue, maxValue, maxWidth) {
|
|
thisObject.min.x = minValue.x // * 0.999; // 0.1% less
|
|
thisObject.max.x = maxValue.x // * 1.001; // 0.1% more
|
|
|
|
thisObject.scale.x = thisObject.maxWidth / (thisObject.max.x - thisObject.min.x)
|
|
}
|
|
|
|
function initializeY (minValue, maxValue, pMaxHeight) {
|
|
thisObject.min.y = minValue.y // * 0.999; // 0.1% less
|
|
thisObject.max.y = maxValue.y // * 1.001; // 0.1% more
|
|
|
|
thisObject.scale.y = pMaxHeight / (thisObject.max.y - thisObject.min.y)
|
|
|
|
thisObject.maxHeight = pMaxHeight
|
|
}
|
|
|
|
function transformThisPoint (point) {
|
|
/*
|
|
|
|
This is the straigh fordward transformation this object provides. The input is a point on the data set that wants to be plotted in a timeline.
|
|
The x value of the point must be a datetime.valueOf() and is going to be transformed into an x value on the coordinate system of the timeline.
|
|
The y value should be a value in the dataset at that moment in time. Depending on how this coordinate system was initialized then are the
|
|
possible values y can have. For sure it must be in the range between the min and max y declared at initialization. The y value has a special
|
|
treatment since the browser canvas object used, has a zero y value at the top, while the timeline we use has a zero y value at the botton,
|
|
for that reason y is flipped.
|
|
|
|
Besides this, what we do is to multiply by the scale, which in turn is calculated at initialization time depending on the values provided.
|
|
|
|
*/
|
|
|
|
point = {
|
|
x: (point.x - thisObject.min.x) * thisObject.scale.x,
|
|
y: thisObject.maxHeight - (point.y - thisObject.min.y) * thisObject.scale.y
|
|
}
|
|
|
|
return point
|
|
}
|
|
|
|
function transformThisPoint2 (point) {
|
|
point = {
|
|
x: (point.x - thisObject.min.x) * thisObject.scale.x,
|
|
y: (thisObject.maxHeight - point.y - thisObject.min.y) * thisObject.scale.y
|
|
}
|
|
|
|
return point
|
|
}
|
|
|
|
function unInverseTransform (point, inverseY) {
|
|
point = {
|
|
x: (point.x / thisObject.scale.x) + thisObject.min.x,
|
|
y: (inverseY - point.y) / thisObject.scale.y + thisObject.min.y
|
|
}
|
|
|
|
return point
|
|
}
|
|
|
|
function inverseTransformUncappedY (point, inverseY) {
|
|
point = {
|
|
x: (point.x - thisObject.min.x) * thisObject.scale.x,
|
|
y: (inverseY - point.y) * thisObject.scale.y
|
|
}
|
|
|
|
return point
|
|
}
|
|
}
|