From 2292d50c2b83bb30fff5a7c91d97f4556763a3f8 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 7 Mar 2020 19:39:56 +0100 Subject: [PATCH] double click --- CanvasApp/ChartingSpace/EdgeEditor.js | 220 ++++++++++++++++++++----- CanvasApp/ChartingSpace/TimeMachine.js | 6 +- CanvasApp/ChartingSpace/Viewport.js | 72 ++++---- CanvasApp/Globals.js | 5 +- CanvasApp/Panels/LayersPanel.js | 2 + 5 files changed, 230 insertions(+), 75 deletions(-) diff --git a/CanvasApp/ChartingSpace/EdgeEditor.js b/CanvasApp/ChartingSpace/EdgeEditor.js index 402418a3c..fa46141a4 100644 --- a/CanvasApp/ChartingSpace/EdgeEditor.js +++ b/CanvasApp/ChartingSpace/EdgeEditor.js @@ -28,6 +28,7 @@ function newEdgeEditor () { let whereIsMouseOver = 'outside' let coordinateSystem + let coordinateSystemWhenDragStarted let onMouseOverEventSubscriptionId let onMouseNotOverEventSubscriptionId @@ -39,6 +40,25 @@ function newEdgeEditor () { y: 0 } } + + mouseWhenDragStarted = { + position: { + x: 0, + y: 0 + } + } + + parentFrameWhenDragStarted = { + position: { + x: 0, + y: 0 + }, + width: 0, + height: 0 + } + + let doubleClickCounter = 0 + return thisObject function finalize () { @@ -50,6 +70,16 @@ function newEdgeEditor () { thisObject.container = undefined thisObject.fitFunction = undefined mouse = undefined + mouseWhenDragStarted = undefined + parentFrameWhenDragStarted = undefined + + coordinateSystem = undefined + + if (coordinateSystemWhenDragStarted !== undefined) { + coordinateSystemWhenDragStarted.finalize() + } + + coordinateSystemWhenDragStarted = undefined } function initialize (pCoordinateSystem) { @@ -74,7 +104,52 @@ function newEdgeEditor () { thisObject.isMouseOver = false } + function onDoubleClick () { + doubleClickCounter = 0 + if (canvas.chartingSpace.viewport.zoomLevel === DOUBLE_CLICK_ZOOM_OUT_LEVEL) { + canvas.chartingSpace.viewport.displaceToContainer(thisObject.container.parentContainer) + canvas.chartingSpace.viewport.zoomAtCenter(DOUBLE_CLICK_ZOOM_IN_LEVEL) + } + } + function onDragStarted (event) { + if (doubleClickCounter > 0) { + onDoubleClick() + return + } else { + doubleClickCounter = 50 + } + + mouseWhenDragStarted = { + position: { + x: event.x, + y: event.y + } + } + + parentFrameWhenDragStarted = { + position: { + x: thisObject.container.parentContainer.frame.position.x, + y: thisObject.container.parentContainer.frame.position.y + }, + width: thisObject.container.parentContainer.frame.width, + height: thisObject.container.parentContainer.frame.height + } + + coordinateSystemWhenDragStarted = newCoordinateSystem() + + let minValue = { + x: coordinateSystem.min.x, + y: coordinateSystem.min.y + } + + let maxValue = { + x: coordinateSystem.max.x, + y: coordinateSystem.max.y + } + + coordinateSystemWhenDragStarted.initialize(minValue, maxValue, coordinateSystem.maxWidth, coordinateSystem.maxHeight) + switch (event.buttons) { case 1: { whatHappened = 'left mouse button' @@ -147,8 +222,28 @@ function newEdgeEditor () { } function physics () { + draggingPhysics() + doubleClickPhysics() + } + + function doubleClickPhysics () { + doubleClickCounter-- + if (doubleClickCounter < 0) { + doubleClickCounter = 0 + } + } + + function draggingPhysics () { if (thisObject.container.frame.position.x === 0 && thisObject.container.frame.position.y === 0) { return } + let mouseNoZoom = canvas.chartingSpace.viewport.unTransformThisPoint(mouse.position) + let mouseWhenDragStartedNoZoom = canvas.chartingSpace.viewport.unTransformThisPoint(mouseWhenDragStarted.position) + + let dragVectorWhenDragStarted = { + x: mouseNoZoom.x - mouseWhenDragStartedNoZoom.x, + y: mouseNoZoom.y - mouseWhenDragStartedNoZoom.y + } + let dragVector = { x: thisObject.container.frame.position.x, y: thisObject.container.frame.position.y @@ -157,28 +252,31 @@ function newEdgeEditor () { thisObject.container.frame.position.x = 0 thisObject.container.frame.position.y = 0 - const MIN_WIDTH = 100 - const MIN_HEIGHT = 50 + const MIN_WIDTH = 250 + const MIN_HEIGHT = 120 switch (whereIsMouseOver) { case 'center': { switch (whatHappened) { case 'left mouse button + SHIFT': { /* This is equivalent to drag the whole Time Machine, so we will apply the translation received onto the Time Machine container. */ - thisObject.container.parentContainer.frame.position.x = thisObject.container.parentContainer.frame.position.x + dragVector.x - thisObject.container.parentContainer.frame.position.y = thisObject.container.parentContainer.frame.position.y + dragVector.y + thisObject.container.parentContainer.frame.position.x = parentFrameWhenDragStarted.position.x + dragVectorWhenDragStarted.x + thisObject.container.parentContainer.frame.position.y = parentFrameWhenDragStarted.position.y + dragVectorWhenDragStarted.y + snapPointToGrid(thisObject.container.parentContainer.frame.position) thisObject.container.parentContainer.eventHandler.raiseEvent('onDisplace') break } case 'left mouse button': { let point = { - x: -dragVector.x, - y: -dragVector.y + x: (-dragVectorWhenDragStarted.x), + y: (-dragVectorWhenDragStarted.y) } - let newMinDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) - let newMaxRate = getRateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) - let xDifferenceMaxMin = coordinateSystem.max.x - coordinateSystem.min.x - let yDifferenceMaxMin = coordinateSystem.max.y - coordinateSystem.min.y + + let newMinDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystemWhenDragStarted) + let newMaxRate = getRateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystemWhenDragStarted) + + let xDifferenceMaxMin = coordinateSystemWhenDragStarted.max.x - coordinateSystemWhenDragStarted.min.x + let yDifferenceMaxMin = coordinateSystemWhenDragStarted.max.y - coordinateSystemWhenDragStarted.min.y coordinateSystem.min.x = newMinDate.valueOf() coordinateSystem.max.x = newMinDate.valueOf() + xDifferenceMaxMin coordinateSystem.min.y = newMaxRate - yDifferenceMaxMin @@ -194,6 +292,7 @@ function newEdgeEditor () { x: -dragVector.x, y: 0 } + let newMinDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) let xDifferenceMaxMin = coordinateSystem.max.x - coordinateSystem.min.x coordinateSystem.min.x = newMinDate.valueOf() @@ -209,18 +308,25 @@ function newEdgeEditor () { break } case 'top' : { - if (thisObject.container.parentContainer.frame.height - dragVector.y < MIN_HEIGHT && dragVector.y > 0) { - return + dragVectorWhenDragStarted.x = 0 + + let newHight = parentFrameWhenDragStarted.height - dragVectorWhenDragStarted.y + if (newHight < MIN_HEIGHT && dragVectorWhenDragStarted.y > 0) { + dragVectorWhenDragStarted.y = parentFrameWhenDragStarted.height - MIN_HEIGHT } + thisObject.container.parentContainer.frame.position.y = parentFrameWhenDragStarted.position.y + dragVectorWhenDragStarted.y + snapPointToGrid(thisObject.container.parentContainer.frame.position) + thisObject.container.parentContainer.frame.height = parentFrameWhenDragStarted.height + (parentFrameWhenDragStarted.position.y - thisObject.container.parentContainer.frame.position.y) + let point = { - x: dragVector.x, - y: dragVector.y + x: 0, + y: (thisObject.container.parentContainer.frame.position.y - parentFrameWhenDragStarted.position.y) } - let newMaxRate = getRateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) - thisObject.container.parentContainer.frame.position.y = thisObject.container.parentContainer.frame.position.y + dragVector.y - thisObject.container.parentContainer.frame.height = thisObject.container.parentContainer.frame.height - dragVector.y + /* This is equivalent to getRateFromPointAtContainer, but as we do not have a container anymore because we already change it, we do it like this. */ + point = coordinateSystemWhenDragStarted.unInverseTransform(point, parentFrameWhenDragStarted.height) + let newMaxRate = point.y switch (whatHappened) { case 'left mouse button': { @@ -243,17 +349,25 @@ function newEdgeEditor () { break } case 'bottom' : { - if (thisObject.container.parentContainer.frame.height + dragVector.y < MIN_HEIGHT && dragVector.y < 0) { - return + dragVectorWhenDragStarted.x = 0 + + let newHight = dragVectorWhenDragStarted.y + parentFrameWhenDragStarted.height + if (newHight < MIN_HEIGHT && dragVectorWhenDragStarted.y < 0) { + dragVectorWhenDragStarted.y = MIN_HEIGHT - parentFrameWhenDragStarted.height } let point = { - x: dragVector.x, - y: dragVector.y + thisObject.container.frame.height + x: 0, + y: dragVectorWhenDragStarted.y + parentFrameWhenDragStarted.height + parentFrameWhenDragStarted.position.y } - let newMinRate = getRateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) + snapPointToGrid(point) + point.y = point.y - parentFrameWhenDragStarted.position.y - thisObject.container.parentContainer.frame.height = thisObject.container.parentContainer.frame.height + dragVector.y + thisObject.container.parentContainer.frame.height = point.y + + /* This is equivalent to getRateFromPointAtContainer, but as we do not have a container anymore because we already change it, we do it like this. */ + point = coordinateSystemWhenDragStarted.unInverseTransform(point, parentFrameWhenDragStarted.height) + let newMinRate = point.y switch (whatHappened) { case 'left mouse button': { @@ -276,17 +390,25 @@ function newEdgeEditor () { break } case 'left' : { - if (thisObject.container.parentContainer.frame.width - dragVector.x < MIN_WIDTH && dragVector.x > 0) { - return - } - let point = { - x: dragVector.x, - y: dragVector.y - } - let newMinDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) + dragVectorWhenDragStarted.y = 0 - thisObject.container.parentContainer.frame.position.x = thisObject.container.parentContainer.frame.position.x + dragVector.x - thisObject.container.parentContainer.frame.width = thisObject.container.parentContainer.frame.width - dragVector.x + let newWidth = parentFrameWhenDragStarted.width - dragVectorWhenDragStarted.x + if (newWidth < MIN_WIDTH && dragVectorWhenDragStarted.x > 0) { + dragVectorWhenDragStarted.x = parentFrameWhenDragStarted.width - MIN_WIDTH + } + + thisObject.container.parentContainer.frame.position.x = parentFrameWhenDragStarted.position.x + dragVectorWhenDragStarted.x + snapPointToGrid(thisObject.container.parentContainer.frame.position) + thisObject.container.parentContainer.frame.width = parentFrameWhenDragStarted.width + (parentFrameWhenDragStarted.position.x - thisObject.container.parentContainer.frame.position.x) + + let point = { + x: (thisObject.container.parentContainer.frame.position.x - parentFrameWhenDragStarted.position.x), + y: 0 + } + + /* This is equivalent to getDateFromPointAtContainer, but as we do not have a container anymore because we already change it, we do it like this. */ + point = coordinateSystemWhenDragStarted.unInverseTransform(point, parentFrameWhenDragStarted.width) + let newMinDate = point.x switch (whatHappened) { case 'left mouse button': { @@ -305,17 +427,25 @@ function newEdgeEditor () { break } case 'right' : { - if (thisObject.container.parentContainer.frame.width + dragVector.x < MIN_WIDTH && dragVector.x < 0) { - return + dragVectorWhenDragStarted.y = 0 + + let newWidth = dragVectorWhenDragStarted.x + parentFrameWhenDragStarted.width + if (newWidth < MIN_WIDTH && dragVectorWhenDragStarted.x < 0) { + dragVectorWhenDragStarted.x = MIN_WIDTH - parentFrameWhenDragStarted.width } let point = { - x: dragVector.x + thisObject.container.frame.width, - y: dragVector.y + x: dragVectorWhenDragStarted.x + parentFrameWhenDragStarted.width + parentFrameWhenDragStarted.position.x, + y: 0 } - let newMaxDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) + snapPointToGrid(point) + point.x = point.x - parentFrameWhenDragStarted.position.x - thisObject.container.parentContainer.frame.width = thisObject.container.parentContainer.frame.width + dragVector.x + thisObject.container.parentContainer.frame.width = point.x + + /* This is equivalent to getRateFromPointAtContainer, but as we do not have a container anymore because we already change it, we do it like this. */ + point = coordinateSystemWhenDragStarted.unInverseTransform(point, parentFrameWhenDragStarted.width) + let newMaxDate = point.x switch (whatHappened) { case 'left mouse button': { @@ -334,6 +464,16 @@ function newEdgeEditor () { break } } + + function snapPointToGrid (point) { + point.x = snapScalarToGrid(point.x) + point.y = snapScalarToGrid(point.y) + } + + function snapScalarToGrid (scalar) { + const GRID_SIZE = 10 + return Math.trunc(scalar / GRID_SIZE) * GRID_SIZE + GRID_SIZE / 2 + } } function drawForeground () { @@ -342,7 +482,7 @@ function newEdgeEditor () { let lineWidth if (whereIsMouseOver === 'outside' || thisObject.isMouseOver === false) { - edgeSize = EDGE_SIZE / 2 + edgeSize = 2 lineWidth = 0.1 } else { edgeSize = EDGE_SIZE diff --git a/CanvasApp/ChartingSpace/TimeMachine.js b/CanvasApp/ChartingSpace/TimeMachine.js index e21cb3466..a93192eb5 100644 --- a/CanvasApp/ChartingSpace/TimeMachine.js +++ b/CanvasApp/ChartingSpace/TimeMachine.js @@ -393,13 +393,15 @@ function newTimeMachine () { if (tabAnimationStatus === 'Closing') { tabAnimationCounter = tabAnimationCounter - STEP - if (tabAnimationCounter === 0) { + if (tabAnimationCounter <= 0) { + tabAnimationCounter = 0 tabAnimationStatus = 'Closed' } } if (tabAnimationStatus === 'Opening') { tabAnimationCounter = tabAnimationCounter + STEP - if (tabAnimationCounter === MAX_COUNTER_VALUE) { + if (tabAnimationCounter >= MAX_COUNTER_VALUE) { + tabAnimationCounter = MAX_COUNTER_VALUE tabAnimationStatus = 'Open' } } diff --git a/CanvasApp/ChartingSpace/Viewport.js b/CanvasApp/ChartingSpace/Viewport.js index 76b6c6528..2f5dad3e7 100644 --- a/CanvasApp/ChartingSpace/Viewport.js +++ b/CanvasApp/ChartingSpace/Viewport.js @@ -30,6 +30,7 @@ function newViewport () { mousePosition: undefined, margins: MARGINS, payload: undefined, + zoomAtCenter: zoomAtCenter, changeZoom: changeZoom, onMouseWheel: onMouseWheel, transformThisPoint: transformThisPoint, @@ -39,6 +40,7 @@ function newViewport () { fitIntoVisibleArea: fitIntoVisibleArea, fitIntoViewport: fitIntoViewport, displace: displace, + displaceToContainer: displaceToContainer, physics: physics, raiseEvents: raiseEvents, resize: resize, @@ -50,14 +52,7 @@ function newViewport () { x: 0, y: 0 } - let targetPosition = { - x: 0, - y: 0 - } - let positionIncrement = { - x: 0, - y: 0 - } + thisObject.mousePosition = { x: 0, y: 0 @@ -69,6 +64,8 @@ function newViewport () { thisObject.zoomTargetLevel = MIN_ZOOM_LEVEL INITIAL_TIME_PERIOD = recalculatePeriod(thisObject.zoomLevel) + let overrideMousePositionCounter = 0 + return thisObject function finalize () { @@ -137,8 +134,36 @@ function newViewport () { thisObject.eventHandler.raiseEvent('Position Changed', event) } + function displaceToContainer (container) { + let targetPoint = { + x: -container.frame.position.x - container.frame.width / 2, + y: -container.frame.position.y - container.frame.height / 2 + TOP_SPACE_HEIGHT + } + position.x = targetPoint.x + thisObject.width / 2 + position.y = targetPoint.y + thisObject.height / 2 + } + + function zoomAtCenter (level) { + thisObject.zoomTargetLevel = level + overrideMousePositionCounter = 50 + ANIMATION_INCREMENT = 0.5 + } + + function mousePositionPhysics () { + if (overrideMousePositionCounter > 0) { + thisObject.mousePosition.x = browserCanvas.width / 2 + thisObject.mousePosition.y = (browserCanvas.height - TOP_SPACE_HEIGHT) / 2 - TOP_SPACE_HEIGHT / 2 + + overrideMousePositionCounter-- + if (overrideMousePositionCounter < 0) { + overrideMousePositionCounter = 0 + } + } + } + function physics () { if (thisObject.visible === false) { return } + mousePositionPhysics() animationPhysics() positioningPhysics() readObjectState() @@ -156,27 +181,19 @@ function newViewport () { function animationPhysics () { if (thisObject.zoomLevel < thisObject.zoomTargetLevel) { if (thisObject.zoomTargetLevel - thisObject.zoomLevel < ANIMATION_INCREMENT) { - ANIMATION_INCREMENT = Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel) + ANIMATION_INCREMENT = Math.round(Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel) * 100) / 100 } - thisObject.zoomLevel = thisObject.zoomLevel + ANIMATION_INCREMENT + thisObject.zoomLevel = Math.round((thisObject.zoomLevel + ANIMATION_INCREMENT) * 100) / 100 changeZoom(thisObject.zoomLevel - ANIMATION_INCREMENT, thisObject.zoomLevel) } if (thisObject.zoomLevel > thisObject.zoomTargetLevel) { if (thisObject.zoomLevel - thisObject.zoomTargetLevel < ANIMATION_INCREMENT) { - ANIMATION_INCREMENT = Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel) + ANIMATION_INCREMENT = Math.round(Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel) * 100) / 100 } - thisObject.zoomLevel = thisObject.zoomLevel - ANIMATION_INCREMENT + thisObject.zoomLevel = Math.round((thisObject.zoomLevel - ANIMATION_INCREMENT) * 100) / 100 changeZoom(thisObject.zoomLevel + ANIMATION_INCREMENT, thisObject.zoomLevel) } - - if (positionIncrement.y !== 0) { - if (Math.trunc(Math.abs(targetPosition.y - position.y) * 1000) >= Math.trunc(Math.abs(positionIncrement.y) * 1000)) { - position.y = position.y + positionIncrement.y - } else { - positionIncrement.y = 0 - } - } } function onMouseWheel (event) { @@ -205,9 +222,9 @@ function newViewport () { if (thisObject.zoomTargetLevel + amount * morePower < MIN_ZOOM_LEVEL) { return false } - thisObject.zoomTargetLevel = thisObject.zoomTargetLevel + amount * morePower + thisObject.zoomTargetLevel = Math.round(thisObject.zoomTargetLevel + amount * morePower) - ANIMATION_INCREMENT = Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel) / ANIMATION_STEPS + ANIMATION_INCREMENT = Math.round(Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel) / ANIMATION_STEPS * 100) / 100 let newEvent = { newLevel: thisObject.zoomTargetLevel, @@ -286,18 +303,8 @@ function newViewport () { position.x = position.x - newMouse.x + thisObject.mousePosition.x position.y = position.y - newMouse.y + thisObject.mousePosition.y - let testPoint = unTransformThisPoint(thisObject.mousePosition, newLevel) - saveObjectState() - targetPosition.x = position.x - targetPosition.y = position.y - - positionIncrement = { - x: 0, - y: 0 - } - thisObject.eventHandler.raiseEvent('Zoom Changed') } @@ -408,3 +415,4 @@ function newViewport () { } } } + diff --git a/CanvasApp/Globals.js b/CanvasApp/Globals.js index 1a1c76a6b..f9f463746 100644 --- a/CanvasApp/Globals.js +++ b/CanvasApp/Globals.js @@ -168,6 +168,9 @@ const _3_MINUTES_IN_MILISECONDS = 3 * 60 * 1000; const _2_MINUTES_IN_MILISECONDS = 2 * 60 * 1000; const _1_MINUTE_IN_MILISECONDS = 1 * 60 * 1000; +const DOUBLE_CLICK_ZOOM_OUT_LEVEL = 0 +const DOUBLE_CLICK_ZOOM_IN_LEVEL = 8 + let NEW_SESSION_INITIAL_DATE = new Date(); // This value will be overwritten at the canvas.chartingSpace.viewport.initialize if the user had a prevous session with this same browser. let INITIAL_ZOOM_LEVEL = -28.25 // This is the zoom level at the view port in which the APP starts. let INITIAL_TIME_PERIOD = ONE_DAY_IN_MILISECONDS // This value will be overwritten at the canvas.chartingSpace.viewport.initialize if the user had a prevous session with this same browser. @@ -213,7 +216,7 @@ const PERIOD_01_MIN = "01-min"; We define here the size of the chartingSpace. It has to bee enough big in order to accomodate all the charts we expect to display in this space. */ -const TIME_MACHINE_WIDTH = 320; +const TIME_MACHINE_WIDTH = 250; const TIME_MACHINE_HEIGHT = 120; let mediaRecorder // to downloadText canvas animation as a mediaRecorder diff --git a/CanvasApp/Panels/LayersPanel.js b/CanvasApp/Panels/LayersPanel.js index b5d89fbc7..c3c04f019 100644 --- a/CanvasApp/Panels/LayersPanel.js +++ b/CanvasApp/Panels/LayersPanel.js @@ -314,6 +314,7 @@ function newLayersPanel () { function physics () { if (isInitialized === false) { return } + if (thisObject.payload.node === undefined) { return } mouseOverPhysics() thisObject.panelTabButton.physics() @@ -344,6 +345,7 @@ function newLayersPanel () { calculateVisbleLayers() function syncWithDesignerLayers () { + if (thisObject.payload.node === undefined) { return } let layerManager = thisObject.payload.node for (let p = 0; p < layerManager.layers.length; p++) { let layerNode = layerManager.layers[p]