734 lines
29 KiB
JavaScript
734 lines
29 KiB
JavaScript
/*
|
|
This module represent one layer of many possible layers that could be at the Floating Space. Objects in the same layer are subject to
|
|
the same physics, and by being at the same level, they interact with each other. For example, they can bounce when one hit the other.
|
|
This module posesses its own physics engine. There are different types of floating objects, but this layer takes care of each of them.
|
|
|
|
Floating Objects Types:
|
|
|
|
1. Profile Balls: These are small balls with a profile picture, and two levels, one above and one below the ball. They are usually used
|
|
to specify the location of a bot instance.
|
|
|
|
2. Notes: Notes are a rectangular area where some text is posted. It has a subject and a body.
|
|
|
|
3. UI Objects: These are small balls that represent parts of an strategy.
|
|
*/
|
|
|
|
function newFloatingLayer () {
|
|
const MODULE_NAME = 'Floating Layer'
|
|
const ERROR_LOG = true
|
|
const logger = newWebDebugLog()
|
|
logger.fileName = MODULE_NAME
|
|
|
|
let thisObject = {
|
|
addFloatingObject: addFloatingObject,
|
|
removeFloatingObject: removeFloatingObject,
|
|
getFloatingObject: getFloatingObject,
|
|
physics: physics,
|
|
changeTargetRepulsion: changeTargetRepulsion,
|
|
draw: draw,
|
|
getContainer: getContainer,
|
|
initialize: initialize,
|
|
finalize: finalize
|
|
}
|
|
|
|
/*
|
|
When objects are added to this layer they are added to the invisibleFloatingObjects array and they stay there because they are invisible.
|
|
Visibility of objects is not decided at this layer, but outside. The ones who decides which object is visible or not are plotters, because
|
|
they know the position of objects and the coordinate system which these positions belong to.
|
|
|
|
To save resources, the physics engine only apply its calculations to visible floating objects.
|
|
*/
|
|
|
|
let invisibleFloatingObjects = []
|
|
|
|
/*
|
|
Floating objects are accesible to plotters because they carry a "payload" object that was creted by them. This object includes the position
|
|
and visible property. When visible turns to true, this layer module will move the reference of the floating object from the invisible array
|
|
to the visibleFloatingObjects array. If the plotter decides that the object is to far from the what the user is seeing, it can eventually
|
|
turn the visible property off again, forcing the layer to remove the object from the visibleFloatingObjects and place it again at the invisible
|
|
array.
|
|
*/
|
|
|
|
let visibleFloatingObjects = []
|
|
|
|
let maxTargetRepulsionForce = 0.001
|
|
let currentHandle = 0
|
|
|
|
return thisObject
|
|
|
|
function finalize () {
|
|
invisibleFloatingObjects = []
|
|
visibleFloatingObjects = []
|
|
}
|
|
|
|
function initialize () {
|
|
|
|
/* We dont need to initialize anything right now. */
|
|
}
|
|
|
|
function getContainer (point) {
|
|
let container
|
|
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
let floatingObject = visibleFloatingObjects[i]
|
|
container = floatingObject.getContainer(point)
|
|
if (container !== undefined) { return container }
|
|
}
|
|
|
|
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
|
let floatingObject = invisibleFloatingObjects[i]
|
|
container = floatingObject.getContainer(point)
|
|
if (container !== undefined) { return container }
|
|
}
|
|
|
|
return container
|
|
}
|
|
|
|
function addFloatingObject (pFloatingObject) {
|
|
currentHandle++
|
|
pFloatingObject.handle = currentHandle // Math.floor((Math.random() * 10000000) + 1);
|
|
visibleFloatingObjects.push(pFloatingObject)
|
|
}
|
|
|
|
function removeFloatingObject (pFloatingObjectHandle) {
|
|
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
|
let floatingObject = invisibleFloatingObjects[i]
|
|
|
|
if (floatingObject.handle === pFloatingObjectHandle) {
|
|
invisibleFloatingObjects.splice(i, 1) // Delete item from array.
|
|
return
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
let floatingObject = visibleFloatingObjects[i]
|
|
|
|
if (floatingObject.handle === pFloatingObjectHandle) {
|
|
visibleFloatingObjects.splice(i, 1) // Delete item from array.
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
function getFloatingObject (pFloatingObjectHandle, pFloatingObjectIndex) {
|
|
/*
|
|
There are two ways to look for a floating object: by handle or by index. The search by handle is done on the visible and invisible array.
|
|
The search by index is done only on the visible ones. The search by index is usually needed when mouse events occurs. In those cases
|
|
only objects visible to the end usser matters.
|
|
*/
|
|
|
|
try {
|
|
if (pFloatingObjectHandle !== undefined) {
|
|
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
|
let floatingObject = invisibleFloatingObjects[i]
|
|
|
|
if (floatingObject.handle === pFloatingObjectHandle) {
|
|
return floatingObject
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
let floatingObject = visibleFloatingObjects[i]
|
|
|
|
if (floatingObject.handle === pFloatingObjectHandle) {
|
|
return floatingObject
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFloatingObjectIndex !== undefined) {
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
let floatingObject = visibleFloatingObjects[i]
|
|
|
|
if (i === pFloatingObjectIndex) {
|
|
return floatingObject
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
if (ERROR_LOG === true) { logger.write('[ERROR] getFloatingObject -> err= ' + err.stack) }
|
|
}
|
|
}
|
|
|
|
function draw () {
|
|
drawInvisibleObjects()
|
|
drawVisibleObjects()
|
|
}
|
|
|
|
function drawInvisibleObjects () {
|
|
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
|
let floatingObject = invisibleFloatingObjects[i]
|
|
floatingObject.drawBackground()
|
|
}
|
|
|
|
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
|
let floatingObject = invisibleFloatingObjects[i]
|
|
floatingObject.drawMiddleground()
|
|
}
|
|
|
|
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
|
let floatingObject = invisibleFloatingObjects[invisibleFloatingObjects.length - i - 1]
|
|
floatingObject.drawForeground()
|
|
}
|
|
}
|
|
|
|
function drawVisibleObjects () {
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
let floatingObject = visibleFloatingObjects[i]
|
|
floatingObject.drawBackground()
|
|
}
|
|
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
let floatingObject = visibleFloatingObjects[i]
|
|
floatingObject.drawMiddleground()
|
|
}
|
|
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
let floatingObject = visibleFloatingObjects[visibleFloatingObjects.length - i - 1]
|
|
floatingObject.drawForeground()
|
|
}
|
|
|
|
/* Invisible objects on focus (freezed) should still have some priority */
|
|
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
|
let floatingObject = invisibleFloatingObjects[i]
|
|
floatingObject.drawOnFocus()
|
|
}
|
|
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
let floatingObject = visibleFloatingObjects[i]
|
|
floatingObject.drawOnFocus()
|
|
}
|
|
}
|
|
|
|
function makeVisible () {
|
|
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
|
let floatingObject = invisibleFloatingObjects[i]
|
|
|
|
if (floatingObject.isFrozen === false && floatingObject.isParentCollapsed === false) {
|
|
visibleFloatingObjects.push(floatingObject)
|
|
invisibleFloatingObjects.splice(i, 1) // Delete item from array.
|
|
makeVisible()
|
|
return // Only one at the time.
|
|
}
|
|
}
|
|
}
|
|
|
|
function makeInvisible () {
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
let floatingObject = visibleFloatingObjects[i]
|
|
|
|
if (floatingObject.isFrozen === true || floatingObject.isParentCollapsed === true) {
|
|
invisibleFloatingObjects.push(floatingObject)
|
|
visibleFloatingObjects.splice(i, 1) // Delete item from array.
|
|
makeInvisible()
|
|
return // Only one at the time.
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************/
|
|
/* */
|
|
/* Physics Engine Follows */
|
|
/* */
|
|
/******************************************/
|
|
|
|
function physics () {
|
|
/*
|
|
The Physics engine is hooked at the animation loop, so it executes very very often. According to this Physics engine, each
|
|
floating object has a position and a speed. In fact, each floating object has two positions at the same time:
|
|
|
|
1. The current position: is the position of the ball, floating around.
|
|
2. The anchor position: is the position of where the ball is achored on the plot. This one is inside the payload object controlled by the
|
|
plotter.
|
|
|
|
The anchor position influences the current position, since the Physics engine will apply a gravity force to the ball's position in order
|
|
to make it gravitate towards the anchor position. At the same time there will be a repulsion force that will prevent the ball reaching
|
|
the anchor position, so that the anchor point keeps visible to the user.
|
|
|
|
*/
|
|
|
|
try {
|
|
makeVisible()
|
|
makeInvisible()
|
|
applyPhysics()
|
|
|
|
function applyPhysics () {
|
|
/* This function makes all the calculations to apply phisycs on all visible floatingObjects in this layer. */
|
|
|
|
try {
|
|
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
|
let floatingObject = invisibleFloatingObjects[i]
|
|
floatingObject.physics()
|
|
}
|
|
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
let floatingObject = visibleFloatingObjects[i]
|
|
floatingObject.physics()
|
|
|
|
/* From here on, only if they are not too far. */
|
|
if (canvas.floatingSpace.isItFar(floatingObject.payload)) { continue }
|
|
|
|
if (floatingObject.positionLocked === false) {
|
|
floatingObject.container.frame.position.x = floatingObject.container.frame.position.x + floatingObject.currentSpeed.x
|
|
floatingObject.container.frame.position.y = floatingObject.container.frame.position.y + floatingObject.currentSpeed.y
|
|
}
|
|
|
|
if (floatingObject.friction < floatingObject.targetFriction) {
|
|
floatingObject.friction = floatingObject.friction + 0.00001
|
|
} else {
|
|
floatingObject.friction = floatingObject.friction - 0.00001
|
|
}
|
|
|
|
floatingObject.currentSpeed.x = floatingObject.currentSpeed.x * floatingObject.friction // Desaceleration factor.
|
|
floatingObject.currentSpeed.y = floatingObject.currentSpeed.y * floatingObject.friction // Desaceleration factor.
|
|
|
|
let payload = {
|
|
position: undefined,
|
|
visible: false
|
|
}
|
|
|
|
switch (floatingObject.type) {
|
|
case 'UI Object': {
|
|
payload.targetPosition = floatingObject.payload.targetPosition
|
|
payload.visible = floatingObject.payload.visible
|
|
break
|
|
}
|
|
default: {
|
|
break
|
|
}
|
|
}
|
|
|
|
if (floatingObject.container.frame.position.x < payload.targetPosition.x) {
|
|
floatingObject.currentSpeed.x = floatingObject.currentSpeed.x + 0.005
|
|
} else {
|
|
floatingObject.currentSpeed.x = floatingObject.currentSpeed.x - 0.005
|
|
}
|
|
|
|
if (floatingObject.container.frame.position.y < payload.targetPosition.y) {
|
|
floatingObject.currentSpeed.y = floatingObject.currentSpeed.y + 0.005
|
|
} else {
|
|
floatingObject.currentSpeed.y = floatingObject.currentSpeed.y - 0.005
|
|
}
|
|
|
|
const MAX_SPEED = 10
|
|
|
|
if (floatingObject.currentSpeed.x > MAX_SPEED) {
|
|
floatingObject.currentSpeed.x = MAX_SPEED
|
|
}
|
|
|
|
if (floatingObject.currentSpeed.y > MAX_SPEED) {
|
|
floatingObject.currentSpeed.y = MAX_SPEED
|
|
}
|
|
|
|
if (floatingObject.currentSpeed.x < -MAX_SPEED) {
|
|
floatingObject.currentSpeed.x = -MAX_SPEED
|
|
}
|
|
|
|
if (floatingObject.currentSpeed.y < -MAX_SPEED) {
|
|
floatingObject.currentSpeed.y = -MAX_SPEED
|
|
}
|
|
|
|
// We let the Floating Object animate the physics loops by itself.
|
|
checkBoundaries(floatingObject)
|
|
|
|
/* Collision Control */
|
|
|
|
for (let k = i + 1; k < visibleFloatingObjects.length; k++) {
|
|
if (colliding(visibleFloatingObjects[i], visibleFloatingObjects[k])) {
|
|
resolveCollision(visibleFloatingObjects[k], visibleFloatingObjects[i])
|
|
}
|
|
}
|
|
|
|
/* Calculate repulsion force produced by all other floatingObjects */
|
|
|
|
repulsionForceBetweenFloatingObjects(i)
|
|
|
|
targetRepulsionForce(i)
|
|
|
|
gravityForce(floatingObject, payload)
|
|
|
|
proximityBetweenFloatingObjects(i)
|
|
}
|
|
} catch (err) {
|
|
if (ERROR_LOG === true) { logger.write('[ERROR] physics -> applyPhysics -> err= ' + err.stack) }
|
|
}
|
|
}
|
|
} catch (err) {
|
|
if (ERROR_LOG === true) { logger.write('[ERROR] physics -> err= ' + err.stack) }
|
|
}
|
|
}
|
|
|
|
function checkBoundaries (floatingObject) {
|
|
if (floatingObject.container.frame.position.x - floatingObject.container.frame.radius < 0) {
|
|
floatingObject.container.frame.position.x = floatingObject.container.frame.radius
|
|
floatingObject.currentSpeed.x = -floatingObject.currentSpeed.x
|
|
}
|
|
|
|
if (floatingObject.container.frame.position.x + floatingObject.container.frame.radius > canvas.floatingSpace.container.frame.width) {
|
|
floatingObject.container.frame.position.x = canvas.floatingSpace.container.frame.width - floatingObject.container.frame.radius
|
|
floatingObject.currentSpeed.x = -floatingObject.currentSpeed.x
|
|
}
|
|
|
|
if (floatingObject.container.frame.position.y - floatingObject.container.frame.radius < 0) {
|
|
floatingObject.container.frame.position.y = floatingObject.container.frame.radius
|
|
floatingObject.currentSpeed.y = -floatingObject.currentSpeed.y
|
|
}
|
|
|
|
if (floatingObject.container.frame.position.y + floatingObject.container.frame.radius > canvas.floatingSpace.container.frame.height) {
|
|
floatingObject.container.frame.position.y = canvas.floatingSpace.container.frame.height - floatingObject.container.frame.radius
|
|
floatingObject.currentSpeed.y = -floatingObject.currentSpeed.y
|
|
}
|
|
}
|
|
|
|
function gravityForce (floatingObject, payload) {
|
|
try {
|
|
/* We simulate a kind of gravity towards the target point of each floatingObject. This force will make the floatingObject to keep pushing to reach that point. */
|
|
|
|
const coulomb = 0.00001
|
|
const minForce = 0.01
|
|
|
|
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 ...
|
|
|
|
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
|
|
}
|
|
|
|
let pos1 = {
|
|
x: floatingObject.container.frame.position.x,
|
|
y: floatingObject.container.frame.position.y
|
|
}
|
|
|
|
let pos2 = {
|
|
x: payload.targetPosition.x,
|
|
y: payload.targetPosition.y
|
|
}
|
|
|
|
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) {
|
|
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
|
|
}
|
|
|
|
let forceVector = {
|
|
x: unitVector.x * force,
|
|
y: unitVector.y * force
|
|
}
|
|
|
|
/* We add the force vector to the speed vector */
|
|
if (isNaN(forceVector.x) === false && isNaN(forceVector.y) === false) {
|
|
floatingObject.currentSpeed.x = floatingObject.currentSpeed.x + forceVector.x
|
|
floatingObject.currentSpeed.y = floatingObject.currentSpeed.y + forceVector.y
|
|
}
|
|
}
|
|
} catch (err) {
|
|
if (ERROR_LOG === true) { logger.write('[ERROR] gravityForce -> err= ' + err.stack) }
|
|
}
|
|
}
|
|
|
|
function repulsionForceBetweenFloatingObjects (currentFloatingObject) {
|
|
try {
|
|
/* We generate a repulsion force between floatingObjects, that prevents them to be collisioning so often. */
|
|
|
|
const coulomb = 2
|
|
|
|
let floatingObject1 = visibleFloatingObjects[currentFloatingObject]
|
|
|
|
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.
|
|
|
|
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 ...
|
|
|
|
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. */
|
|
|
|
if (force > 1) {
|
|
force = 1
|
|
}
|
|
|
|
let pos1 = {
|
|
x: floatingObject1.container.frame.position.x,
|
|
y: floatingObject1.container.frame.position.y
|
|
}
|
|
|
|
let pos2 = {
|
|
x: floatingObject2.container.frame.position.x,
|
|
y: floatingObject2.container.frame.position.y
|
|
}
|
|
|
|
let posDiff = { // Next we need the vector resulting from the 2 positions.
|
|
x: pos2.x - pos1.x,
|
|
y: pos2.y - pos1.y
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
let forceVector = {
|
|
x: unitVector.x * force,
|
|
y: unitVector.y * force
|
|
}
|
|
|
|
/* We substract the force vector to the speed vector of the current floatingObject */
|
|
if (isNaN(forceVector.x) === false && isNaN(forceVector.y) === false) {
|
|
floatingObject1.currentSpeed.x = floatingObject1.currentSpeed.x - forceVector.x
|
|
floatingObject1.currentSpeed.y = floatingObject1.currentSpeed.y - forceVector.y
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
if (ERROR_LOG === true) { logger.write('[ERROR] repulsionForceBetweenFloatingObjects -> err= ' + err.stack) }
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
let floatingObject1 = visibleFloatingObjects[currentFloatingObject]
|
|
|
|
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
|
// The force to be applied is considering all other floatingObjects...
|
|
|
|
let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject...
|
|
|
|
let payload = {
|
|
position: undefined
|
|
}
|
|
|
|
switch (floatingObject2.type) {
|
|
case 'UI Object': {
|
|
payload.targetPosition = floatingObject2.payload.targetPosition
|
|
break
|
|
}
|
|
default: {
|
|
break
|
|
}
|
|
}
|
|
|
|
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 ...
|
|
|
|
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. */
|
|
|
|
if (force > maxTargetRepulsionForce) {
|
|
force = maxTargetRepulsionForce
|
|
}
|
|
|
|
let pos1 = {
|
|
x: floatingObject1.container.frame.position.x,
|
|
y: floatingObject1.container.frame.position.y
|
|
}
|
|
|
|
let pos2 = {
|
|
x: payload.targetPosition.x,
|
|
y: payload.targetPosition.y
|
|
}
|
|
|
|
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) {
|
|
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
|
|
}
|
|
|
|
let forceVector = {
|
|
x: unitVector.x * force,
|
|
y: unitVector.y * force
|
|
}
|
|
|
|
/* We substract the force vector to the speed vector of the current floatingObject */
|
|
|
|
if (isNaN(forceVector.x) === false && isNaN(forceVector.y) === false) {
|
|
floatingObject1.currentSpeed.x = floatingObject1.currentSpeed.x - forceVector.x
|
|
floatingObject1.currentSpeed.y = floatingObject1.currentSpeed.y - forceVector.y
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
if (ERROR_LOG === true) { logger.write('[ERROR] targetRepulsionForce -> err= ' + err.stack) }
|
|
}
|
|
}
|
|
|
|
function changeTargetRepulsion (pDelta) {
|
|
try {
|
|
if (pDelta > 0) {
|
|
pDelta = 1
|
|
} else {
|
|
pDelta = -1
|
|
}
|
|
|
|
maxTargetRepulsionForce = maxTargetRepulsionForce + pDelta / 1000
|
|
|
|
if (maxTargetRepulsionForce < 0.0000000001) {
|
|
maxTargetRepulsionForce = 0.0000000001
|
|
}
|
|
} catch (err) {
|
|
if (ERROR_LOG === true) { logger.write('[ERROR] changeTargetRepulsion -> err= ' + err.stack) }
|
|
}
|
|
}
|
|
|
|
function colliding (floatingObject1, floatingObject2) {
|
|
try {
|
|
/* This function detects weather 2 floatingObjects collide with each other. */
|
|
|
|
let r1 = floatingObject1.container.frame.radius
|
|
let r2 = floatingObject2.container.frame.radius
|
|
|
|
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.
|
|
return false
|
|
} else if (distance <= r1 + r2) {
|
|
// One circle contains the other.
|
|
return true
|
|
} else if ((distance === 0) && (r1 === r2)) {
|
|
// the circles coincide.
|
|
return true
|
|
} else return true
|
|
} catch (err) {
|
|
if (ERROR_LOG === true) { logger.write('[ERROR] colliding -> err= ' + err.stack) }
|
|
}
|
|
}
|
|
|
|
function distance (x1, y1, x2, y2) {
|
|
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
|
|
}
|
|
|
|
function resolveCollision (floatingObject1, floatingObject2) {
|
|
try {
|
|
/* This function changes speed and position of floatingObjects that are in collision */
|
|
|
|
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))
|
|
|
|
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
|
|
|
|
let direction_1 = Math.atan2(floatingObject1.currentSpeed.y, floatingObject1.currentSpeed.x)
|
|
let direction_2 = Math.atan2(floatingObject2.currentSpeed.y, floatingObject2.currentSpeed.x)
|
|
|
|
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)
|
|
|
|
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
|
|
|
|
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
|
|
|
|
floatingObject2.currentSpeed.x = cosAngle * final_xspeed_2 - sinAngle * final_yspeed_2
|
|
floatingObject2.currentSpeed.y = sinAngle * final_xspeed_2 + cosAngle * final_yspeed_2
|
|
|
|
let pos1 = {
|
|
x: floatingObject1.container.frame.position.x,
|
|
y: floatingObject1.container.frame.position.y
|
|
}
|
|
|
|
let pos2 = {
|
|
x: floatingObject2.container.frame.position.x,
|
|
y: floatingObject2.container.frame.position.y
|
|
}
|
|
|
|
// get the mtd
|
|
let posDiff = {
|
|
x: pos1.x - pos2.x,
|
|
y: pos1.y - pos2.y
|
|
}
|
|
|
|
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
|
|
let scalar = (((floatingObject1.container.frame.radius + floatingObject2.container.frame.radius) - d) / d)
|
|
|
|
let minTD = {
|
|
x: posDiff.x * scalar,
|
|
y: posDiff.y * scalar
|
|
}
|
|
|
|
// resolve intersection --
|
|
// computing inverse mass quantities
|
|
let im1 = 1 / floatingObject1.currentMass
|
|
let im2 = 1 / floatingObject2.currentMass
|
|
|
|
// push-pull them apart based off their mass
|
|
|
|
pos1.x = pos1.x + minTD.x * (im1 / (im1 + im2))
|
|
pos1.y = pos1.y + minTD.y * (im1 / (im1 + im2))
|
|
pos2.x = pos2.x - minTD.x * (im2 / (im1 + im2))
|
|
pos2.y = pos2.y - minTD.y * (im2 / (im1 + im2))
|
|
|
|
if (floatingObject1.positionLocked === false && isNaN(pos1.x) === false && isNaN(pos1.y) === false) {
|
|
floatingObject1.container.frame.position = pos1
|
|
}
|
|
if (floatingObject2.positionLocked === false && isNaN(pos2.x) === false && isNaN(pos2.y) === false) {
|
|
floatingObject2.container.frame.position = pos2
|
|
}
|
|
} catch (err) {
|
|
if (ERROR_LOG === true) { logger.write('[ERROR] resolveCollision -> err= ' + err.stack) }
|
|
}
|
|
}
|
|
}
|