Files
Superalgos/ChartingSpace/CoordinateSystem.js
T

222 lines
6.7 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,
autoMinScale: true,
autoMaxScale: true,
physics: physics,
reportValue: reportValue,
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 newMax = -VERY_LARGE_NUMBER
let newMin = VERY_LARGE_NUMBER
let notValueReported = 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 () {
if ((thisObject.autoMinScale === true || thisObject.autoMaxScale === true) && notValueReported === false) {
let mustRecalculate = false
if (thisObject.autoMaxScale === true && thisObject.max.y !== newMax) {
thisObject.max.y = newMax
mustRecalculate = true
}
if (thisObject.autoMaxScale === true) {
newMax = -VERY_LARGE_NUMBER
}
if (thisObject.autoMinScale === true && thisObject.min.y !== newMin) {
thisObject.min.y = newMin
newMin = VERY_LARGE_NUMBER
mustRecalculate = true
}
if (thisObject.autoMinScale === true) {
newMin = VERY_LARGE_NUMBER
}
if (mustRecalculate === true) {
recalculateScale()
}
/* Reseting this to start over at each cycle. */
notValueReported = true
}
}
function reportValue (value) {
if (thisObject.autoMinScale === true) {
if (value < newMin) { newMin = value }
notValueReported = false
}
if (thisObject.autoMaxScale === true) {
if (value > newMax) { newMax = value }
notValueReported = 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
}
}