diff --git a/Canvas.js b/Canvas.js index f41ffc738..19a14f60a 100644 --- a/Canvas.js +++ b/Canvas.js @@ -60,16 +60,15 @@ function newCanvas () { browserCanvas.removeEventListener('click', onMouseClick, false) browserCanvas.removeEventListener('mouseout', onMouseOut, false) - /* Mouse wheel events. */ - if (browserCanvas.removeEventListener) { - // IE9, Chrome, Safari, Opera - browserCanvas.removeEventListener('mousewheel', onMouseWheel, false) - // Firefox - browserCanvas.removeEventListener('DOMMouseScroll', onMouseWheel, false) - } - // IE 6/7/8 - else browserCanvas.detachEvent('onmousewheel', onMouseWheel) + browserCanvas.removeEventListener('mousewheel', onMouseWheel, false)// IE9, Chrome, Safari, Opera + browserCanvas.removeEventListener('DOMMouseScroll', onMouseWheel, false) // Firefox + } else browserCanvas.detachEvent('onmousewheel', onMouseWheel) // IE 6/7/8 + + browserCanvas.removeEventListener('dragenter', onDragEnter, false) + browserCanvas.removeEventListener('dragleave', onDragLeave, false) + browserCanvas.removeEventListener('dragover', onDragOver, false) + browserCanvas.removeEventListener('drop', onDragDrop, false) } catch (err) { if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack) } } @@ -110,9 +109,6 @@ function newCanvas () { if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onCharSpaceInitialized -> err = ' + err.stack) } } } - - /* Splash Screen */ - splashScreen = newSplashScreen() splashScreen.initialize() @@ -121,8 +117,6 @@ function newCanvas () { thisObject.animation = animation - /* Here we add all the functions that will be called during the animation cycle. */ - animation.addCallBackFunction('Floating Space Draw', thisObject.floatingSpace.draw) animation.addCallBackFunction('Floating Space Physics', thisObject.floatingSpace.physics) animation.addCallBackFunction('Chart Space Background', thisObject.chartSpace.drawBackground) @@ -144,7 +138,6 @@ function newCanvas () { function initializeBrowserCanvas () { try { browserCanvasContext = browserCanvas.getContext('2d') - // browserCanvasContext.font = 'italic small-caps bold 12px Saira' browserCanvasContext.font = 'Saira' viewPort.initialize() @@ -155,24 +148,23 @@ function newCanvas () { function addCanvasEvents () { try { - /* Mouse down and up events to control the drag of the canvas. */ - browserCanvas.addEventListener('mousedown', onMouseDown, false) browserCanvas.addEventListener('mouseup', onMouseUp, false) browserCanvas.addEventListener('mousemove', onMouseMove, false) browserCanvas.addEventListener('click', onMouseClick, false) browserCanvas.addEventListener('mouseout', onMouseOut, false) - /* Mouse wheel events. */ - if (browserCanvas.addEventListener) { - // IE9, Chrome, Safari, Opera - browserCanvas.addEventListener('mousewheel', onMouseWheel, false) - // Firefox - browserCanvas.addEventListener('DOMMouseScroll', onMouseWheel, false) - } - // IE 6/7/8 - else browserCanvas.attachEvent('onmousewheel', onMouseWheel) + browserCanvas.addEventListener('mousewheel', onMouseWheel, false) // IE9, Chrome, Safari, Opera + browserCanvas.addEventListener('DOMMouseScroll', onMouseWheel, false) // Firefox + } else browserCanvas.attachEvent('onmousewheel', onMouseWheel)// IE 6/7/8 + + /* Dragging Files Over the Canvas */ + + browserCanvas.addEventListener('dragenter', onDragEnter, false) + browserCanvas.addEventListener('dragleave', onDragLeave, false) + browserCanvas.addEventListener('dragover', onDragOver, false) + browserCanvas.addEventListener('drop', onDragDrop, false) // Disables the context menu when you right mouse click the canvas. browserCanvas.oncontextmenu = function (e) { @@ -183,6 +175,62 @@ function newCanvas () { } } + function onDragEnter (event) { + try { + event.preventDefault() + event.stopPropagation() + thisObject.cockpitSpace.toTop() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragEnter -> err = ' + err.stack) } + } + } + + function onDragLeave (event) { + try { + event.preventDefault() + event.stopPropagation() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragLeave -> err = ' + err.stack) } + } + } + + function onDragOver (event) { + try { + event.preventDefault() + event.stopPropagation() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragOver -> err = ' + err.stack) } + } + } + + function onDragDrop (event) { + try { + event.preventDefault() + event.stopPropagation() + + let files = event.dataTransfer.files + + handleFiles(files) + function handleFiles (files) { + ([...files]).forEach(loadFileData) + } + + function loadFileData (file) { + let reader = new FileReader() + reader.readAsText(file) + reader.onloadend = function () { + let mousePosition = { + x: event.x, + y: event.y + } + thisObject.strategySpace.workspace.spawn(reader.result, mousePosition) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragDrop -> err = ' + err.stack) } + } + } + function onMouseDown (event) { try { let point = { @@ -202,6 +250,7 @@ function newCanvas () { if (container !== undefined && container.isDraggeable === true) { containerBeingDragged = container containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } @@ -216,6 +265,7 @@ function newCanvas () { if (container !== undefined && container.isDraggeable === true) { containerBeingDragged = container containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } @@ -226,6 +276,7 @@ function newCanvas () { if (container !== undefined && container.isDraggeable === true) { containerBeingDragged = container containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } @@ -241,6 +292,7 @@ function newCanvas () { if (container !== undefined && container.isDraggeable === true && event.button === 2) { containerBeingDragged = container containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } @@ -257,6 +309,7 @@ function newCanvas () { if (container.isDraggeable === true) { containerBeingDragged = container containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } else { viewPortBeingDragged = true @@ -270,6 +323,7 @@ function newCanvas () { containerBeingDragged = container containerDragStarted = true floatingObjectDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } @@ -367,33 +421,6 @@ function newCanvas () { deactivateDragging(event) } - function deactivateDragging (event) { - try { - if (containerDragStarted || viewPortBeingDragged || floatingObjectDragStarted) { - thisObject.eventHandler.raiseEvent('Drag Finished', undefined) - } - - if ( - containerDragStarted || - floatingObjectDragStarted || - viewPortBeingDragged - ) { - ignoreNextClick = true - } - /* Turn off all the possible things that can be dragged. */ - - containerDragStarted = false - floatingObjectDragStarted = false - viewPortBeingDragged = false - - containerBeingDragged = undefined - - browserCanvas.style.cursor = 'auto' - } catch (err) { - if (ERROR_LOG === true) { logger.write('[ERROR] onMouseUp -> err = ' + err.stack) } - } - } - function onMouseMove (event) { try { let point = { @@ -409,6 +436,7 @@ function newCanvas () { let targetContainer = thisObject.floatingSpace.getContainer(point) if (targetContainer !== undefined) { if (targetContainer.id !== containerBeingDragged.id) { + containerBeingDragged.eventHandler.raiseEvent('onDragFinished', point) containerBeingDragged = undefined containerDragStarted = false floatingObjectDragStarted = false @@ -417,6 +445,7 @@ function newCanvas () { return } } else { + containerBeingDragged.eventHandler.raiseEvent('onDragFinished', point) containerDragStarted = false containerBeingDragged = undefined floatingObjectDragStarted = false @@ -588,6 +617,36 @@ function newCanvas () { } } + function deactivateDragging (event) { + try { + if (containerDragStarted || viewPortBeingDragged || floatingObjectDragStarted) { + thisObject.eventHandler.raiseEvent('Drag Finished', undefined) + } + + if ( + containerDragStarted || + floatingObjectDragStarted || + viewPortBeingDragged + ) { + ignoreNextClick = true + } + /* Turn off all the possible things that can be dragged. */ + + containerDragStarted = false + floatingObjectDragStarted = false + viewPortBeingDragged = false + + if (containerBeingDragged !== undefined) { + containerBeingDragged.eventHandler.raiseEvent('onDragFinished', event) + containerBeingDragged = undefined + } + + browserCanvas.style.cursor = 'auto' + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] deactivateDragging -> err = ' + err.stack) } + } + } + function checkDrag (event) { try { if (containerDragStarted === true || floatingObjectDragStarted === true || viewPortBeingDragged === true) { @@ -646,3 +705,4 @@ function newCanvas () { } } } + diff --git a/Container.js b/Container.js index 2b467470f..2753e96d9 100644 --- a/Container.js +++ b/Container.js @@ -33,6 +33,8 @@ function newContainer () { let onFocusParentEvent = false let onNotFocusParentEvent = false let onDisplaceParentEvent = false + let onDragStartedParentEvent = false + let onDragFinishedParentEvent = false let isConnectedToParent = false @@ -42,6 +44,8 @@ function newContainer () { let onFocusEventSubscriptionId let onNotFocusEventSubscriptionId let onDisplaceEventSubscriptionId + let onDragStartedEventSubscriptionId + let onDragFinishedEventSubscriptionId return thisObject @@ -53,6 +57,8 @@ function newContainer () { thisObject.parentContainer.eventHandler.stopListening(onFocusEventSubscriptionId) thisObject.parentContainer.eventHandler.stopListening(onNotFocusEventSubscriptionId) thisObject.parentContainer.eventHandler.stopListening(onDisplaceEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onDragStartedEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onDragFinishedEventSubscriptionId) thisObject.parentContainer = undefined thisObject.eventHandler.finalize() @@ -86,7 +92,9 @@ function newContainer () { onMouseNotOverEvent, onFocusEvent, onNotFocusEvent, - onDisplaceEvent + onDisplaceEvent, + onDragStartedEvent, + onDragFinishedEvent ) { connectedToParentWidth = onWidth connectedToParentHeight = onHeight @@ -98,6 +106,8 @@ function newContainer () { onFocusParentEvent = onFocusEvent onNotFocusParentEvent = onNotFocusEvent onDisplaceParentEvent = onDisplaceEvent + onDragStartedParentEvent = onDragStartedEvent + onDragFinishedParentEvent = onDragFinishedEvent isConnectedToParent = true @@ -123,6 +133,12 @@ function newContainer () { if (onDisplaceParentEvent === true) { onDisplaceEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onDisplace', onDisplace) } + if (onDragStartedParentEvent === true) { + onDragStartedEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onDragStarted', onDragStarted) + } + if (onDragFinishedParentEvent === true) { + onDragFinishedEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onDragFinished', onDragFinished) + } if (connectedToParentWidth) { thisObject.frame.width = thisObject.parentContainer.frame.width @@ -175,6 +191,14 @@ function newContainer () { thisObject.eventHandler.raiseEvent('onDisplace', event) } + function onDragStarted (event) { + thisObject.eventHandler.raiseEvent('onDragStarted', event) + } + + function onDragFinished (event) { + thisObject.eventHandler.raiseEvent('onDragFinished', event) + } + function isForThisPurpose (purpose) { if (purpose !== undefined) { switch (purpose) { diff --git a/FloatingSpace/FloatingLayer.js b/FloatingSpace/FloatingLayer.js index f3889b6f1..a366a4b40 100644 --- a/FloatingSpace/FloatingLayer.js +++ b/FloatingSpace/FloatingLayer.js @@ -15,7 +15,6 @@ function newFloatingLayer () { const MODULE_NAME = 'Floating Layer' - const INFO_LOG = false const ERROR_LOG = true const logger = newWebDebugLog() logger.fileName = MODULE_NAME @@ -121,8 +120,6 @@ function newFloatingLayer () { */ try { - if (INFO_LOG === true) { logger.write('[INFO] getFloatingObject -> Entering function.') } - if (pFloatingObjectHandle !== undefined) { for (let i = 0; i < invisibleFloatingObjects.length; i++) { let floatingObject = invisibleFloatingObjects[i] @@ -397,6 +394,8 @@ function newFloatingLayer () { targetRepulsionForce(i) gravityForce(floatingObject, payload) + + proximityBetweenFloatingObjects(i) } } catch (err) { if (ERROR_LOG === true) { logger.write('[ERROR] physics -> applyPhysics -> err= ' + err.stack) } @@ -436,36 +435,36 @@ function newFloatingLayer () { const coulomb = 0.00001 const minForce = 0.01 - var d = Math.sqrt(Math.pow(payload.targetPosition.x - floatingObject.container.frame.position.x, 2) + Math.pow(payload.targetPosition.y - floatingObject.container.frame.position.y, 2)) // ... we calculate the distance ... + let d = Math.sqrt(Math.pow(payload.targetPosition.x - floatingObject.container.frame.position.x, 2) + Math.pow(payload.targetPosition.y - floatingObject.container.frame.position.y, 2)) // ... we calculate the distance ... - var force = coulomb * d * d / floatingObject.currentMass // In this case the mass of the floatingObject affects the gravity force that it receives, that gives priority to target position to bigger floatingObjects. + let force = coulomb * d * d / floatingObject.currentMass // In this case the mass of the floatingObject affects the gravity force that it receives, that gives priority to target position to bigger floatingObjects. if (force < minForce) { // We need this attraction force to overcome the friction we imposed to the system. force = minForce } - var pos1 = { + let pos1 = { x: floatingObject.container.frame.position.x, y: floatingObject.container.frame.position.y } - var pos2 = { + let pos2 = { x: payload.targetPosition.x, y: payload.targetPosition.y } - var posDiff = { // Next we need the vector resulting from the 2 positions. + let posDiff = { // Next we need the vector resulting from the 2 positions. x: pos2.x - pos1.x, y: pos2.y - pos1.y } if (d !== 0) { - var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. + let unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. x: posDiff.x / d, y: posDiff.y / d } - var forceVector = { + let forceVector = { x: unitVector.x * force, y: unitVector.y * force } @@ -486,19 +485,19 @@ function newFloatingLayer () { const coulomb = 2 - var floatingObject1 = visibleFloatingObjects[currentFloatingObject] + let floatingObject1 = visibleFloatingObjects[currentFloatingObject] - for (var i = 0; i < visibleFloatingObjects.length; i++) { + for (let i = 0; i < visibleFloatingObjects.length; i++) { // The force to be applied is considering all other floatingObjects... if (i !== currentFloatingObject) { // ... except for the current one. - var floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... + let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... - var d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... + let d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... - var force = coulomb * floatingObject2.currentMass / (d * d) // ... and with it the repulsion force. + let force = coulomb * floatingObject2.currentMass / (d * d) // ... and with it the repulsion force. /* We need to put a hard limit to this force, in order to to eject very little floatingObjects to the infinite and beyond. */ @@ -506,27 +505,27 @@ function newFloatingLayer () { force = 1 } - var pos1 = { + let pos1 = { x: floatingObject1.container.frame.position.x, y: floatingObject1.container.frame.position.y } - var pos2 = { + let pos2 = { x: floatingObject2.container.frame.position.x, y: floatingObject2.container.frame.position.y } - var posDiff = { // Next we need the vector resulting from the 2 positions. + let posDiff = { // Next we need the vector resulting from the 2 positions. x: pos2.x - pos1.x, y: pos2.y - pos1.y } - var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. + let unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. x: posDiff.x / d, y: posDiff.y / d } - var forceVector = { + let forceVector = { x: unitVector.x * force, y: unitVector.y * force } @@ -542,18 +541,52 @@ function newFloatingLayer () { } } + function proximityBetweenFloatingObjects (currentFloatingObject) { + try { + let floatingObject1 = visibleFloatingObjects[currentFloatingObject] + floatingObject1.nearbyFloatingObjects = [] + for (let i = 0; i < visibleFloatingObjects.length; i++) { + if (i !== currentFloatingObject) { + let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... + + let d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... + + if (floatingObject1.nearbyFloatingObjects.length === 0) { + floatingObject1.nearbyFloatingObjects.push([d, floatingObject2]) + } else { + let recordAdded = false + for (let j = 0; j < floatingObject1.nearbyFloatingObjects.length; j++) { + let recordedDistance = floatingObject1.nearbyFloatingObjects[j][0] + if (d < recordedDistance) { + floatingObject1.nearbyFloatingObjects.splice(j, 0, [d, floatingObject2]) + recordAdded = true + break + } + } + if (recordAdded === false) { + floatingObject1.nearbyFloatingObjects.push([d, floatingObject2]) + } + } + floatingObject1.nearbyFloatingObjects.splice(10, floatingObject1.nearbyFloatingObjects.length) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] proximityBetweenFloatingObjects -> err= ' + err.stack) } + } + } + function targetRepulsionForce (currentFloatingObject) { try { /* We generate a repulsion force between floatingObjects, that prevents them to be collisioning so often. */ const coulomb = 2 - var floatingObject1 = visibleFloatingObjects[currentFloatingObject] + let floatingObject1 = visibleFloatingObjects[currentFloatingObject] - for (var i = 0; i < visibleFloatingObjects.length; i++) { + for (let i = 0; i < visibleFloatingObjects.length; i++) { // The force to be applied is considering all other floatingObjects... - var floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... + let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... let payload = { position: undefined @@ -578,9 +611,9 @@ function newFloatingLayer () { } } - var d = Math.sqrt(Math.pow(payload.targetPosition.x - floatingObject1.container.frame.position.x, 2) + Math.pow(payload.targetPosition.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... + let d = Math.sqrt(Math.pow(payload.targetPosition.x - floatingObject1.container.frame.position.x, 2) + Math.pow(payload.targetPosition.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... - var force = coulomb * floatingObject2.currentMass / (d * d) // ... and with it the repulsion force. + let force = coulomb * floatingObject2.currentMass / (d * d) // ... and with it the repulsion force. /* We need to put a hard limit to this force, in order to to eject very little floatingObjects to the infinite and beyond. */ @@ -588,28 +621,28 @@ function newFloatingLayer () { force = maxTargetRepulsionForce } - var pos1 = { + let pos1 = { x: floatingObject1.container.frame.position.x, y: floatingObject1.container.frame.position.y } - var pos2 = { + let pos2 = { x: payload.targetPosition.x, y: payload.targetPosition.y } - var posDiff = { // Next we need the vector resulting from the 2 positions. + let posDiff = { // Next we need the vector resulting from the 2 positions. x: pos2.x - pos1.x, y: pos2.y - pos1.y } if (d !== 0) { - var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. + let unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. x: posDiff.x / d, y: posDiff.y / d } - var forceVector = { + let forceVector = { x: unitVector.x * force, y: unitVector.y * force } @@ -647,10 +680,10 @@ function newFloatingLayer () { try { /* This function detects weather 2 floatingObjects collide with each other. */ - var r1 = floatingObject1.container.frame.radius - var r2 = floatingObject2.container.frame.radius + let r1 = floatingObject1.container.frame.radius + let r2 = floatingObject2.container.frame.radius - var distance = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) + let distance = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) if (distance > (r1 + r2)) { // No solutions, the circles are too far apart. @@ -675,26 +708,26 @@ function newFloatingLayer () { try { /* This function changes speed and position of floatingObjects that are in collision */ - var collisionision_angle = Math.atan2((floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y), (floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x)) + let collisionision_angle = Math.atan2((floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y), (floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x)) - var speed1 = Math.sqrt(floatingObject1.currentSpeed.x * floatingObject1.currentSpeed.x + floatingObject1.currentSpeed.y * floatingObject1.currentSpeed.y) // Magnitude of Speed Vector for floatingObject 1 - var speed2 = Math.sqrt(floatingObject2.currentSpeed.x * floatingObject2.currentSpeed.x + floatingObject2.currentSpeed.y * floatingObject2.currentSpeed.y) // Magnitude of Speed Vector for floatingObject 2 + let speed1 = Math.sqrt(floatingObject1.currentSpeed.x * floatingObject1.currentSpeed.x + floatingObject1.currentSpeed.y * floatingObject1.currentSpeed.y) // Magnitude of Speed Vector for floatingObject 1 + let speed2 = Math.sqrt(floatingObject2.currentSpeed.x * floatingObject2.currentSpeed.x + floatingObject2.currentSpeed.y * floatingObject2.currentSpeed.y) // Magnitude of Speed Vector for floatingObject 2 - var direction_1 = Math.atan2(floatingObject1.currentSpeed.y, floatingObject1.currentSpeed.x) - var direction_2 = Math.atan2(floatingObject2.currentSpeed.y, floatingObject2.currentSpeed.x) + let direction_1 = Math.atan2(floatingObject1.currentSpeed.y, floatingObject1.currentSpeed.x) + let direction_2 = Math.atan2(floatingObject2.currentSpeed.y, floatingObject2.currentSpeed.x) - var new_xspeed_1 = speed1 * Math.cos(direction_1 - collisionision_angle) - var new_yspeed_1 = speed1 * Math.sin(direction_1 - collisionision_angle) - var new_xspeed_2 = speed2 * Math.cos(direction_2 - collisionision_angle) - var new_yspeed_2 = speed2 * Math.sin(direction_2 - collisionision_angle) + let new_xspeed_1 = speed1 * Math.cos(direction_1 - collisionision_angle) + let new_yspeed_1 = speed1 * Math.sin(direction_1 - collisionision_angle) + let new_xspeed_2 = speed2 * Math.cos(direction_2 - collisionision_angle) + let new_yspeed_2 = speed2 * Math.sin(direction_2 - collisionision_angle) - var final_xspeed_1 = ((floatingObject1.currentMass - floatingObject2.currentMass) * new_xspeed_1 + (floatingObject2.currentMass + floatingObject2.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass) - var final_xspeed_2 = ((floatingObject1.currentMass + floatingObject1.currentMass) * new_xspeed_1 + (floatingObject2.currentMass - floatingObject1.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass) - var final_yspeed_1 = new_yspeed_1 - var final_yspeed_2 = new_yspeed_2 + let final_xspeed_1 = ((floatingObject1.currentMass - floatingObject2.currentMass) * new_xspeed_1 + (floatingObject2.currentMass + floatingObject2.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass) + let final_xspeed_2 = ((floatingObject1.currentMass + floatingObject1.currentMass) * new_xspeed_1 + (floatingObject2.currentMass - floatingObject1.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass) + let final_yspeed_1 = new_yspeed_1 + let final_yspeed_2 = new_yspeed_2 - var cosAngle = Math.cos(collisionision_angle) - var sinAngle = Math.sin(collisionision_angle) + let cosAngle = Math.cos(collisionision_angle) + let sinAngle = Math.sin(collisionision_angle) floatingObject1.currentSpeed.x = cosAngle * final_xspeed_1 - sinAngle * final_yspeed_1 floatingObject1.currentSpeed.y = sinAngle * final_xspeed_1 + cosAngle * final_yspeed_1 @@ -702,36 +735,36 @@ function newFloatingLayer () { floatingObject2.currentSpeed.x = cosAngle * final_xspeed_2 - sinAngle * final_yspeed_2 floatingObject2.currentSpeed.y = sinAngle * final_xspeed_2 + cosAngle * final_yspeed_2 - var pos1 = { + let pos1 = { x: floatingObject1.container.frame.position.x, y: floatingObject1.container.frame.position.y } - var pos2 = { + let pos2 = { x: floatingObject2.container.frame.position.x, y: floatingObject2.container.frame.position.y } // get the mtd - var posDiff = { + let posDiff = { x: pos1.x - pos2.x, y: pos1.y - pos2.y } - var d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) + let d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) // minimum translation distance to push floatingObjects apart after intersecting - var scalar = (((floatingObject1.container.frame.radius + floatingObject2.container.frame.radius) - d) / d) + let scalar = (((floatingObject1.container.frame.radius + floatingObject2.container.frame.radius) - d) / d) - var minTD = { + let minTD = { x: posDiff.x * scalar, y: posDiff.y * scalar } // resolve intersection -- // computing inverse mass quantities - var im1 = 1 / floatingObject1.currentMass - var im2 = 1 / floatingObject2.currentMass + let im1 = 1 / floatingObject1.currentMass + let im2 = 1 / floatingObject2.currentMass // push-pull them apart based off their mass diff --git a/FloatingSpace/FloatingObject.js b/FloatingSpace/FloatingObject.js index a6a8c9bef..266a7cd0c 100644 --- a/FloatingSpace/FloatingObject.js +++ b/FloatingSpace/FloatingObject.js @@ -20,6 +20,7 @@ function newFloatingObject () { rawRadius: 0, // This is the radius of this floating object without zoom. targetRadius: 0, // This is the target radius of the floating object with zoom applied. It should be animated until reaching this value. isPinned: false, + nearbyFloatingObjects: [], pinToggle: pinToggle, physics: physics, initializeMass: initializeMass, @@ -65,6 +66,7 @@ function newFloatingObject () { thisObject.container = undefined thisObject.payload = undefined thisObject.fitFunction = undefined + thisObject.nearbyFloatingObjects = undefined } function initialize (type, payload) { diff --git a/FloatingSpace/StrategyPart.js b/FloatingSpace/StrategyPart.js index dfeae626b..664ec9fc8 100644 --- a/FloatingSpace/StrategyPart.js +++ b/FloatingSpace/StrategyPart.js @@ -17,6 +17,8 @@ function newStrategyPart () { codeEditor: undefined, partTitle: undefined, isExecuting: undefined, + getReadyToAttach: getReadyToAttach, + showAvailabilityToAttach: showAvailabilityToAttach, highlight: highlight, unHighlight: unHighlight, physics: physics, @@ -43,16 +45,34 @@ function newStrategyPart () { let selfFocusEventSubscriptionId let selfNotFocuskEventSubscriptionId let selfDisplaceEventSubscriptionId + let selfDragStartedEventSubscriptionId + let selfDragFinishedEventSubscriptionId let isHighlighted let highlightCounter = 0 + let previousDistance + + let readyToAttachDisplayCounter = 5 + let readyToAttachDisplayIncrement = 0.1 + + let readyToAttachCounter = 0 + let isReadyToAttach + let availableToAttachCounter = 0 + let isAvailableToAttach + + let isAttaching = false + let isDragging = false + let attachToNode + return thisObject function finalize () { thisObject.container.eventHandler.stopListening(selfFocusEventSubscriptionId) thisObject.container.eventHandler.stopListening(selfNotFocuskEventSubscriptionId) thisObject.container.eventHandler.stopListening(selfDisplaceEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfDragStartedEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfDragFinishedEventSubscriptionId) thisObject.container.finalize() thisObject.container = undefined @@ -70,6 +90,7 @@ function newStrategyPart () { } icon = undefined + attachToNode = undefined } function initialize (payload, menuItemsInitialValues) { @@ -95,22 +116,26 @@ function newStrategyPart () { selfFocusEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onFocus', onFocus) selfNotFocuskEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onNotFocus', onNotFocus) selfDisplaceEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDisplace', onDisplace) + selfDragStartedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDragStarted', onDragStarted) + selfDragFinishedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDragFinished', onDragFinished) } function getContainer (point) { let container - if (thisObject.codeEditor !== undefined) { - container = thisObject.codeEditor.getContainer(point) + if (isDragging === false && thisObject.isOnFocus === true) { + if (thisObject.codeEditor !== undefined) { + container = thisObject.codeEditor.getContainer(point) + if (container !== undefined) { return container } + } + + container = thisObject.partTitle.getContainer(point) + if (container !== undefined) { return container } + + container = thisObject.menu.getContainer(point) if (container !== undefined) { return container } } - container = thisObject.partTitle.getContainer(point) - if (container !== undefined) { return container } - - container = thisObject.menu.getContainer(point) - if (container !== undefined) { return container } - if (thisObject.container.frame.isThisPointHere(point, true) === true) { return thisObject.container } else { @@ -127,8 +152,8 @@ function newStrategyPart () { } if (thisObject.payload.chainParent === undefined) { - thisObject.payload.targetPosition.x = canvas.floatingSpace.container.frame.width / 2, - thisObject.payload.targetPosition.y = canvas.floatingSpace.container.frame.height / 2 + thisObject.payload.targetPosition.x = thisObject.payload.position.x, + thisObject.payload.targetPosition.y = thisObject.payload.position.y } else { thisObject.payload.targetPosition.x = thisObject.payload.chainParent.payload.position.x thisObject.payload.targetPosition.y = thisObject.payload.chainParent.payload.position.y @@ -136,6 +161,117 @@ function newStrategyPart () { iconPhysics() highlightPhisycs() + detachingPhysics() + attachingPhysics() + } + + function attachingPhysics () { + attacchingCounters() + + if (thisObject.isOnFocus !== true) { return } + if (isDragging !== true) { return } + if (thisObject.payload.chainParent !== undefined) { return } + + let nearbyFloatingObjects = thisObject.payload.floatingObject.nearbyFloatingObjects + let compatibleType + let compatibleSubType + switch (thisObject.payload.node.type) { + case 'Strategy': + compatibleType = 'Trading System' + compatibleSubType = undefined + break + case 'Phase': + compatibleType = 'Stop' + '.' + 'Take Profit' + '.' + 'Phase' + compatibleSubType = undefined + break + case 'Situation': + compatibleType = 'Phase' + '.' + 'Take Position Event' + '.' + 'Trigger On Event' + '.' + 'Trigger Off Event' + compatibleSubType = undefined + break + case 'Condition': + compatibleType = 'Situation' + compatibleSubType = undefined + break + default: + return + } + let foundCompatible = false + attachToNode = undefined + isAttaching = false + + for (let i = 0; i < nearbyFloatingObjects.length; i++) { + let nearby = nearbyFloatingObjects[i] + let distance = nearby[0] + let floatingObject = nearby[1] + let nearbyNode = floatingObject.payload.node + if (compatibleType.indexOf(nearbyNode.type) >= 0) { + if (thisObject.payload.node.type === 'Phase' && nearbyNode.type === 'Phase' && nearbyNode.payload.parentNode === undefined) { continue } + if (foundCompatible === false) { + if (distance < thisObject.container.frame.radius * 1.5 + floatingObject.container.frame.radius * 1.5) { + nearbyNode.payload.uiObject.getReadyToAttach() + isAttaching = true + attachToNode = nearbyNode + foundCompatible = true + } + } + nearbyNode.payload.uiObject.showAvailabilityToAttach() + } + } + } + + function attacchingCounters () { + if (readyToAttachDisplayCounter > 15) { + readyToAttachDisplayIncrement = -0.25 + } + + if (readyToAttachDisplayCounter < 5) { + readyToAttachDisplayIncrement = 0.25 + } + + readyToAttachDisplayCounter = readyToAttachDisplayCounter + readyToAttachDisplayIncrement + + readyToAttachCounter-- + if (readyToAttachCounter <= 0) { + readyToAttachCounter = 0 + isReadyToAttach = false + } else { + isReadyToAttach = true + } + + availableToAttachCounter-- + if (availableToAttachCounter <= 0) { + availableToAttachCounter = 0 + isAvailableToAttach = false + } else { + isAvailableToAttach = true + } + } + + function getReadyToAttach () { + readyToAttachCounter = 10 + } + + function showAvailabilityToAttach () { + availableToAttachCounter = 10 + } + + function detachingPhysics () { + if (isDragging !== true) { return } + + let distanceToChainParent = Math.sqrt(Math.pow(thisObject.payload.position.x - thisObject.payload.targetPosition.x, 2) + Math.pow(thisObject.payload.position.y - thisObject.payload.targetPosition.y, 2)) + let ratio = distanceToChainParent / previousDistance + previousDistance = distanceToChainParent + if (thisObject.isOnFocus !== true) { return } + if (thisObject.payload.chainParent === undefined) { return } + if (thisObject.payload.parentNode === undefined) { return } + + let THRESHOLD = 1.15 + + if (previousDistance !== undefined) { + if (ratio > THRESHOLD) { + canvas.strategySpace.workspace.detachNode(thisObject.payload.node) + } + } } function highlightPhisycs () { @@ -176,10 +312,28 @@ function newStrategyPart () { thisObject.partTitle.exitEditMode() } + function onDragStarted (event) { + thisObject.partTitle.exitEditMode() + if (thisObject.codeEditor !== undefined) { + thisObject.codeEditor.deactivate() + } + isDragging = true + } + + function onDragFinished (event) { + if (isAttaching === true) { + canvas.strategySpace.workspace.attachNode(thisObject.payload.node, attachToNode) + } + isDragging = false + } + function drawBackground () { if (thisObject.isOnFocus === false) { drawConnectingLine() - thisObject.menu.drawBackground() + + if (isDragging === false && thisObject.isOnFocus === true) { + thisObject.menu.drawBackground() + } } } @@ -193,7 +347,9 @@ function newStrategyPart () { function drawForeground () { if (thisObject.isOnFocus === false) { drawBodyAndPicture() - thisObject.menu.drawForeground() + if (isDragging === false) { + thisObject.menu.drawForeground() + } } } @@ -212,13 +368,17 @@ function newStrategyPart () { if (thisObject.codeEditor !== undefined) { if (thisObject.codeEditor.visible === false) { drawBodyAndPicture() - thisObject.menu.drawBackground() - thisObject.menu.drawForeground() + if (isDragging === false) { + thisObject.menu.drawBackground() + thisObject.menu.drawForeground() + } } } else { drawBodyAndPicture() - thisObject.menu.drawBackground() - thisObject.menu.drawForeground() + if (isDragging === false) { + thisObject.menu.drawBackground() + thisObject.menu.drawForeground() + } } } } @@ -307,35 +467,50 @@ function newStrategyPart () { switch (thisObject.payload.node.type) { case 'Phase': { let parent = thisObject.payload.parentNode - for (let i = 0; i < parent.phases.length; i++) { - let phase = parent.phases[i] - if (phase.id === thisObject.payload.node.id) { - label = label + ' #' + (i + 1) - return label + if (parent !== undefined) { + for (let i = 0; i < parent.phases.length; i++) { + let phase = parent.phases[i] + if (phase.id === thisObject.payload.node.id) { + label = label + ' #' + (i + 1) + return label + } } + } else { + return label } + break } case 'Situation': { let parent = thisObject.payload.parentNode - for (let i = 0; i < parent.situations.length; i++) { - let situation = parent.situations[i] - if (situation.id === thisObject.payload.node.id) { - label = label + ' #' + (i + 1) - return label + if (parent !== undefined) { + for (let i = 0; i < parent.situations.length; i++) { + let situation = parent.situations[i] + if (situation.id === thisObject.payload.node.id) { + label = label + ' #' + (i + 1) + return label + } } + } else { + return label } + break } case 'Condition': { let parent = thisObject.payload.parentNode - for (let i = 0; i < parent.conditions.length; i++) { - let condition = parent.conditions[i] - if (condition.id === thisObject.payload.node.id) { - label = label + ' #' + (i + 1) - return label + if (parent !== undefined) { + for (let i = 0; i < parent.conditions.length; i++) { + let condition = parent.conditions[i] + if (condition.id === thisObject.payload.node.id) { + label = label + ' #' + (i + 1) + return label + } } + } else { + return label } + break } default: { @@ -395,6 +570,36 @@ function newStrategyPart () { browserCanvasContext.setLineDash([4, 20]) browserCanvasContext.stroke() } + + if (isReadyToAttach === true) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 2 + readyToAttachDisplayCounter * 2 + let OPACITY = readyToAttachCounter / 10 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([readyToAttachDisplayCounter, readyToAttachDisplayCounter * 2]) + browserCanvasContext.stroke() + } + + if (isAvailableToAttach === true && isReadyToAttach === false) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 1.5 + let OPACITY = availableToAttachCounter / 10 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TURQUOISE + ', ' + OPACITY + ')' + + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([8, 20]) + browserCanvasContext.stroke() + } } /* Image */ @@ -428,4 +633,3 @@ function newStrategyPart () { } } } - diff --git a/FloatingSpace/StrategyPartConstructor.js b/FloatingSpace/StrategyPartConstructor.js index 687a01332..bbe0151e8 100644 --- a/FloatingSpace/StrategyPartConstructor.js +++ b/FloatingSpace/StrategyPartConstructor.js @@ -41,7 +41,7 @@ function newStrategyPartConstructor () { strategyPart.isVisibleFunction = canvas.floatingSpace.isThisPointVisible let menuItemsInitialValues = getMenuItemsInitialValues(strategyPart, floatingObject, payload) strategyPart.initialize(payload, menuItemsInitialValues) - strategyPart.container.connectToParent(floatingObject.container, false, false, true, true, false, false, true, true, true) + strategyPart.container.connectToParent(floatingObject.container, false, false, true, true, false, false, true, true, true, true, true) payload.uiObject = strategyPart setFloatingObjectBasicProperties(floatingObject, payload) @@ -154,7 +154,7 @@ function newStrategyPartConstructor () { }] break } - case 'Strategy Entry Event': { + case 'Trigger On Event': { menuItemsInitialValues = [ { action: 'Pin / Unpin', @@ -196,7 +196,7 @@ function newStrategyPartConstructor () { }] break } - case 'Strategy Exit Event': { + case 'Trigger Off Event': { menuItemsInitialValues = [ { action: 'Pin / Unpin', @@ -238,7 +238,7 @@ function newStrategyPartConstructor () { }] break } - case 'Trade Entry Event': { + case 'Take Position Event': { menuItemsInitialValues = [ { action: 'Pin / Unpin', @@ -569,15 +569,15 @@ function newStrategyPartConstructor () { level_1() break } - case 'Strategy Entry Event': { + case 'Trigger On Event': { level_2() break } - case 'Strategy Exit Event': { + case 'Trigger Off Event': { level_2() break } - case 'Trade Entry Event': { + case 'Take Position Event': { level_2() break } diff --git a/FloatingSpace/StrategyPartTitle.js b/FloatingSpace/StrategyPartTitle.js index 0af02da79..296b05772 100644 --- a/FloatingSpace/StrategyPartTitle.js +++ b/FloatingSpace/StrategyPartTitle.js @@ -55,13 +55,13 @@ function newStrategyPartTitle () { thisObject.allwaysVisible = true break } - case 'Strategy Entry Event': { + case 'Trigger On Event': { break } - case 'Strategy Exit Event': { + case 'Trigger Off Event': { break } - case 'Trade Entry Event': { + case 'Take Position Event': { break } case 'Stop': { @@ -110,6 +110,7 @@ function newStrategyPartTitle () { } function physics () { + if (thisObject.payload.title === undefined) { return } let title = trimTitle(thisObject.payload.title) const FRAME_HEIGHT = 25 @@ -252,7 +253,8 @@ function newStrategyPartTitle () { } function trimTitle (title) { - const MAX_LABEL_LENGTH = 15 + if (title === undefined) { return } + const MAX_LABEL_LENGTH = 35 if (title.length > MAX_LABEL_LENGTH) { title = title.substring(0, MAX_LABEL_LENGTH) + '...' } diff --git a/Spaces/CockpitSpace.js b/Spaces/CockpitSpace.js index afff50bad..92cb0e922 100644 --- a/Spaces/CockpitSpace.js +++ b/Spaces/CockpitSpace.js @@ -8,6 +8,8 @@ assetBalances: undefined, restartSimulation: undefined, fullscreen: undefined, + toTop: toTop, + toBottom: toBottom, draw: draw, physics: physics, getContainer: getContainer, @@ -76,6 +78,14 @@ thisObject.container.frame.height = COCKPIT_SPACE_HEIGHT } + function toTop () { + thisObject.container.frame.position.y = 0 + } + + function toBottom () { + thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT + } + function physics () { thisObjectPhysics() childrenPhysics() diff --git a/Spaces/StrategySpace.js b/Spaces/StrategySpace.js index c4b646d3a..c24ffadbc 100644 --- a/Spaces/StrategySpace.js +++ b/Spaces/StrategySpace.js @@ -46,9 +46,9 @@ function newStrategySpace () { const relationshipArray = [ ['Trading System', 'analysis'], ['Strategy', 'quality'], - ['Strategy Entry Event', 'startup'], - ['Strategy Exit Event', 'support'], - ['Trade Entry Event', 'compass'], + ['Trigger On Event', 'startup'], + ['Trigger Off Event', 'support'], + ['Take Position Event', 'compass'], ['Stop', 'pixel'], ['Take Profit', 'competition'], ['Phase', 'placeholder'], @@ -180,4 +180,3 @@ function newStrategySpace () { } } } - diff --git a/StrategySpace/Workspace.js b/StrategySpace/Workspace.js index c118b191a..ba9d5be83 100644 --- a/StrategySpace/Workspace.js +++ b/StrategySpace/Workspace.js @@ -5,6 +5,9 @@ function newWorkspace () { let thisObject = { tradingSystem: undefined, container: undefined, + spawn: spawn, + detachNode: detachNode, + attachNode: attachNode, getContainer: getContainer, initialize: initialize, finalize: finalize @@ -22,10 +25,14 @@ function newWorkspace () { thisObject.container.frame.width = 0 thisObject.container.frame.height = 0 + spawnPosition = { + x: canvas.floatingSpace.container.frame.width / 2, + y: canvas.floatingSpace.container.frame.height / 2 + } + return thisObject function finalize () { - destroyStrategyParts() thisObject.tradingSystem = undefined thisObject.container.finalize() thisObject.container = undefined @@ -43,12 +50,284 @@ function newWorkspace () { } + function spawn (nodeText, point) { + point = canvas.floatingSpace.container.frameThisPoint(point) + spawnPosition.x = point.x + spawnPosition.y = point.y + + let dirtyNode = JSON.parse(nodeText) + let rootNode = getProtocolNode(dirtyNode) + createPartFromNode(rootNode, undefined, undefined) + } + + function createPartFromNode (node, parentNode, chainParent) { + switch (node.type) { + case 'Condition': + { + let condition = node + createPart('Condition', condition.name, condition, parentNode, chainParent, 'Condition') + return + } + case 'Situation': { + let situation = node + createPart('Situation', situation.name, situation, parentNode, chainParent, 'Situation') + for (let m = 0; m < node.conditions.length; m++) { + let condition = node.conditions[m] + createPartFromNode(condition, situation, situation) + } + return + } + case 'Phase': { + let phase = node + createPart('Phase', phase.name, phase, parentNode, chainParent, phase.subType) + for (let m = 0; m < node.situations.length; m++) { + let situation = node.situations[m] + createPartFromNode(situation, phase, phase) + } + return + } + case 'Stop': { + let lastPhase + let stop = node + createPart('Stop', stop.name, stop, parentNode, chainParent, 'Stop') + for (let m = 0; m < node.phases.length; m++) { + let phase = node.phases[m] + let thisChainParent + if (m === 0) { + thisChainParent = node + } else { + thisChainParent = lastPhase + } + lastPhase = phase + createPartFromNode(phase, stop, thisChainParent) + } + return + } + case 'Take Profit': { + let lastPhase + let takeProfit = node + createPart('Take Profit', takeProfit.name, takeProfit, parentNode, chainParent, 'Take Profit') + for (let m = 0; m < node.phases.length; m++) { + let phase = node.phases[m] + let thisChainParent + if (m === 0) { + thisChainParent = node + } else { + thisChainParent = lastPhase + } + lastPhase = phase + createPartFromNode(phase, takeProfit, thisChainParent) + } + return + } + case 'Take Position Event': { + let event = node + createPart('Take Position Event', event.name, event, parentNode, chainParent, 'Take Position Event') + for (let m = 0; m < node.situations.length; m++) { + let situation = node.situations[m] + createPartFromNode(situation, event, event) + } + return + } + case 'Trigger On Event': { + let event = node + createPart('Trigger On Event', event.name, event, parentNode, chainParent, 'Trigger On Event') + for (let m = 0; m < node.situations.length; m++) { + let situation = node.situations[m] + createPartFromNode(situation, event, event) + } + return + } + case 'Trigger Off Event': { + let event = node + createPart('Trigger Off Event', event.name, event, parentNode, chainParent, 'Trigger Off Event') + for (let m = 0; m < node.situations.length; m++) { + let situation = node.situations[m] + createPartFromNode(situation, event, event) + } + return + } + case 'Strategy': { + let strategy = node + createPart('Strategy', strategy.name, strategy, parentNode, chainParent, 'Strategy') + createPartFromNode(node.entryPoint, strategy, strategy) + createPartFromNode(node.exitPoint, strategy, strategy) + createPartFromNode(node.sellPoint, strategy, strategy) + createPartFromNode(node.stopLoss, strategy, strategy) + createPartFromNode(node.buyOrder, strategy, strategy) + return + } + case 'Trading System': { + let tradingSystem = node + createPart('Trading System', tradingSystem.name, tradingSystem, parentNode, chainParent, 'Trading System') + for (let m = 0; m < node.strategies.length; m++) { + let strategy = node.strategies[m] + createPartFromNode(strategy, tradingSystem, tradingSystem) + } + return + } + } + } + + function detachNode (node) { + switch (node.type) { + case 'Trading System': { + return + } + case 'Strategy': { + let payload = node.payload + for (let i = 0; i < payload.parentNode.strategies.length; i++) { + let strategy = payload.parentNode.strategies[i] + if (strategy.id === node.id) { + payload.parentNode.strategies.splice(i, 1) + } + payload.parentNode = undefined + payload.chainParent = undefined + } + } + break + case 'Trigger On Event': { + return + } + break + case 'Trigger Off Event': { + return + } + break + case 'Take Position Event': { + return + } + break + case 'Stop': { + return + } + break + case 'Take Profit': { + return + } + break + case 'Condition': { + let payload = node.payload + for (let i = 0; i < payload.parentNode.conditions.length; i++) { + let condition = payload.parentNode.conditions[i] + if (condition.id === node.id) { + payload.parentNode.conditions.splice(i, 1) + } + } + node.payload.chainParent = undefined + node.payload.parentNode = undefined + } + break + case 'Situation': { + let payload = node.payload + for (let i = 0; i < payload.parentNode.situations.length; i++) { + let situation = payload.parentNode.situations[i] + if (situation.id === node.id) { + payload.parentNode.situations.splice(i, 1) + } + } + node.payload.chainParent = undefined + node.payload.parentNode = undefined + } + break + case 'Phase': { + let payload = node.payload + for (let i = 0; i < payload.parentNode.phases.length; i++) { + let phase = payload.parentNode.phases[i] + if (phase.id === node.id) { + if (i < payload.parentNode.phases.length - 1) { + let nextPhase = payload.parentNode.phases[i + 1] + if (i > 0) { + let previousPhase = payload.parentNode.phases[i - 1] + nextPhase.payload.chainParent = previousPhase + } else { + nextPhase.payload.chainParent = payload.parentNode + } + } + payload.parentNode.phases.splice(i, 1) + payload.parentNode = undefined + payload.chainParent = undefined + return + } + } + } + } + } + + function attachNode (node, attachToNode) { + switch (node.type) { + case 'Strategy': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.strategies.push(node) + } + break + case 'Phase': { + switch (attachToNode.type) { + case 'Stop': { + node.payload.parentNode = attachToNode + if (attachToNode.phases.length > 0) { + let phase = attachToNode.phases[attachToNode.phases.length - 1] + node.payload.chainParent = phase + } else { + node.payload.chainParent = attachToNode + } + attachToNode.phases.push(node) + } + break + case 'Take Profit': { + node.payload.parentNode = attachToNode + if (attachToNode.phases.length > 0) { + let phase = attachToNode.phases[attachToNode.phases.length - 1] + node.payload.chainParent = phase + } else { + node.payload.chainParent = attachToNode + } + attachToNode.phases.push(node) + } + break + case 'Phase': { + node.payload.parentNode = attachToNode.payload.parentNode + for (let i = 0; i < node.payload.parentNode.phases.length; i++) { + let phase = node.payload.parentNode.phases[i] + if (attachToNode.id === phase.id) { + if (i === node.payload.parentNode.phases.length - 1) { + node.payload.chainParent = attachToNode + node.payload.parentNode.phases.push(node) + } else { + node.payload.chainParent = attachToNode + let nextPhase = node.payload.parentNode.phases[i + 1] + nextPhase.payload.chainParent = node + node.payload.parentNode.phases.splice(i + 1, 0, node) + return + } + } + } + } + } + } + break + case 'Situation': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.situations.push(node) + } + break + case 'Condition': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.conditions.push(node) + } + break + } + } + function createPart (partType, name, node, parentNode, chainParent, title) { let payload = {} if (chainParent === undefined) { payload.targetPosition = { - x: canvas.floatingSpace.container.frame.width / 2, - y: canvas.floatingSpace.container.frame.height / 2 + x: spawnPosition.x, + y: spawnPosition.y } } else { payload.targetPosition = { @@ -92,10 +371,10 @@ function newWorkspace () { let strategy = tradingSystem.strategies[m] createPart('Strategy', strategy.name, strategy, tradingSystem, tradingSystem) - createPart('Strategy Entry Event', '', strategy.entryPoint, strategy, strategy) + createPart('Trigger On Event', '', strategy.entryPoint, strategy, strategy) for (let k = 0; k < strategy.entryPoint.situations.length; k++) { let situation = strategy.entryPoint.situations[k] - createPart('Situation', situation.name, situation, strategy.entryPoint, strategy.entryPoint, 'Strategy Entry' + ' ' + 'Situation') + createPart('Situation', situation.name, situation, strategy.entryPoint, strategy.entryPoint, 'Trigger On' + ' ' + 'Situation') for (let m = 0; m < situation.conditions.length; m++) { let condition = situation.conditions[m] @@ -103,10 +382,10 @@ function newWorkspace () { } } - createPart('Strategy Exit Event', '', strategy.exitPoint, strategy, strategy) + createPart('Trigger Off Event', '', strategy.exitPoint, strategy, strategy) for (let k = 0; k < strategy.exitPoint.situations.length; k++) { let situation = strategy.exitPoint.situations[k] - createPart('Situation', situation.name, situation, strategy.exitPoint, strategy.exitPoint, 'Strategy Exit' + ' ' + 'Situation') + createPart('Situation', situation.name, situation, strategy.exitPoint, strategy.exitPoint, 'Trigger Off' + ' ' + 'Situation') for (let m = 0; m < situation.conditions.length; m++) { let condition = situation.conditions[m] @@ -114,10 +393,10 @@ function newWorkspace () { } } - createPart('Trade Entry Event', '', strategy.sellPoint, strategy, strategy) + createPart('Take Position Event', '', strategy.sellPoint, strategy, strategy) for (let k = 0; k < strategy.sellPoint.situations.length; k++) { let situation = strategy.sellPoint.situations[k] - createPart('Situation', situation.name, situation, strategy.sellPoint, strategy.sellPoint, 'Trade Entry' + ' ' + 'Situation') + createPart('Situation', situation.name, situation, strategy.sellPoint, strategy.sellPoint, 'Take Position' + ' ' + 'Situation') for (let m = 0; m < situation.conditions.length; m++) { let condition = situation.conditions[m] @@ -174,10 +453,6 @@ function newWorkspace () { } } - function destroyStrategyParts () { - - } - async function onMenuItemClick (payload, action) { switch (action) { case 'Save Trading System': @@ -230,9 +505,9 @@ function newWorkspace () { strategyParent.strategies.push(strategy) createPart('Strategy', strategy.name, strategy, strategyParent, strategyParent, 'Strategy') - createPart('Strategy Entry Event', '', strategy.entryPoint, strategy, strategy) - createPart('Strategy Exit Event', '', strategy.exitPoint, strategy, strategy) - createPart('Trade Entry Event', '', strategy.sellPoint, strategy, strategy) + createPart('Trigger On Event', '', strategy.entryPoint, strategy, strategy) + createPart('Trigger Off Event', '', strategy.exitPoint, strategy, strategy) + createPart('Take Position Event', '', strategy.sellPoint, strategy, strategy) createPart('Stop', '', strategy.stopLoss, strategy, strategy) createPart('Take Profit', '', strategy.buyOrder, strategy, strategy) } @@ -303,19 +578,29 @@ function newWorkspace () { function deleteStrategy (node) { let payload = node.payload - for (let j = 0; j < payload.parentNode.strategies.length; j++) { - let strategy = payload.parentNode.strategies[j] - if (strategy.id === node.id) { - deleteEvent(strategy.entryPoint) - deleteEvent(strategy.exitPoint) - deleteEvent(strategy.sellPoint) - deleteManagedItem(strategy.stopLoss) - deleteManagedItem(strategy.buyOrder) - destroyPart(strategy) - payload.parentNode.strategies.splice(j, 1) - cleanNode(strategy) - return + if (payload.parentNode !== undefined) { + for (let j = 0; j < payload.parentNode.strategies.length; j++) { + let strategy = payload.parentNode.strategies[j] + if (strategy.id === node.id) { + deleteEvent(strategy.entryPoint) + deleteEvent(strategy.exitPoint) + deleteEvent(strategy.sellPoint) + deleteManagedItem(strategy.stopLoss) + deleteManagedItem(strategy.buyOrder) + payload.parentNode.strategies.splice(j, 1) + destroyPart(strategy) + cleanNode(strategy) + return + } } + } else { + deleteEvent(node.entryPoint) + deleteEvent(node.exitPoint) + deleteEvent(node.sellPoint) + deleteManagedItem(node.stopLoss) + deleteManagedItem(node.buyOrder) + destroyPart(node) + cleanNode(node) } } @@ -337,55 +622,80 @@ function newWorkspace () { function deletePhase (node) { let payload = node.payload - for (let k = 0; k < payload.parentNode.phases.length; k++) { - let phase = payload.parentNode.phases[k] - if (phase.id === node.id) { - while (phase.situations.length > 0) { - let situation = phase.situations[0] - deleteSituation(situation) - } - phase.situations = [] + if (payload.parentNode !== undefined) { + for (let k = 0; k < payload.parentNode.phases.length; k++) { + let phase = payload.parentNode.phases[k] + if (phase.id === node.id) { + while (phase.situations.length > 0) { + let situation = phase.situations[0] + deleteSituation(situation) + } + phase.situations = [] /* Before deleting this phase we need to give its chainParent to the next phase down the chain */ - if (k < payload.parentNode.phases.length - 1) { - payload.parentNode.phases[k + 1].payload.chainParent = payload.chainParent - } + if (k < payload.parentNode.phases.length - 1) { + payload.parentNode.phases[k + 1].payload.chainParent = payload.chainParent + } /* Continue destroying this phase */ - destroyPart(phase) - payload.parentNode.phases.splice(k, 1) - cleanNode(phase) - return + payload.parentNode.phases.splice(k, 1) + destroyPart(phase) + cleanNode(phase) + return + } } + } else { + while (node.situations.length > 0) { + let situation = node.situations[0] + deleteSituation(situation) + } + node.situations = [] + destroyPart(node) + cleanNode(node) } } function deleteSituation (node) { let payload = node.payload - for (let j = 0; j < payload.parentNode.situations.length; j++) { - let situation = payload.parentNode.situations[j] - if (situation.id === node.id) { - while (situation.conditions.length > 0) { - let condition = situation.conditions[0] - deleteCondition(condition) + if (payload.parentNode !== undefined) { + for (let j = 0; j < payload.parentNode.situations.length; j++) { + let situation = payload.parentNode.situations[j] + if (situation.id === node.id) { + while (situation.conditions.length > 0) { + let condition = situation.conditions[0] + deleteCondition(condition) + } + situation.conditions = [] + payload.parentNode.situations.splice(j, 1) + destroyPart(situation) + cleanNode(situation) + return } - situation.conditions = [] - destroyPart(situation) - payload.parentNode.situations.splice(j, 1) - cleanNode(situation) - return } + } else { + while (node.conditions.length > 0) { + let condition = node.conditions[0] + deleteCondition(condition) + } + node.conditions = [] + destroyPart(node) + cleanNode(node) } } function deleteCondition (node) { let payload = node.payload - for (let i = 0; i < payload.parentNode.conditions.length; i++) { - let condition = payload.parentNode.conditions[i] - if (condition.id === node.id) { - destroyPart(node) - payload.parentNode.conditions.splice(i, 1) - cleanNode(condition) - return + if (payload.parentNode !== undefined) { + for (let i = 0; i < payload.parentNode.conditions.length; i++) { + let condition = payload.parentNode.conditions[i] + if (condition.id === node.id) { + payload.parentNode.conditions.splice(i, 1) + destroyPart(node) + cleanNode(condition) + return + } } + } else { + destroyPart(node) + cleanNode(node) } } @@ -486,7 +796,7 @@ function newWorkspace () { return takeProfit break } - case 'Trade Entry Event': { + case 'Take Position Event': { let event = { type: node.type, subType: node.subType, @@ -501,7 +811,7 @@ function newWorkspace () { return event break } - case 'Strategy Entry Event': { + case 'Trigger On Event': { let event = { type: node.type, subType: node.subType, @@ -516,7 +826,7 @@ function newWorkspace () { return event break } - case 'Strategy Exit Event': { + case 'Trigger Off Event': { let event = { type: node.type, subType: node.subType,