- added a 2D collision detection for new added systems, closed #570

This commit is contained in:
Mark Friedrich
2017-12-23 15:04:14 +01:00
parent 230c5f6cdb
commit fd5b397671
10 changed files with 770 additions and 29 deletions

341
js/app/map/layout.js Normal file
View File

@@ -0,0 +1,341 @@
define(() => {
'use strict';
class Position {
constructor(config) {
this._defaultConfig = {
container: null, // parent DOM container element
center: null, // DOM elements that works as center
elementClass: 'pf-system', // class for all elements
defaultGapX: 50,
defaultGapY: 50,
gapX: 50, // leave gap between elements (x-axis)
gapY: 50, // leave gap between elements (y-axis)
loops: 2, // max loops around "center" for search
grid: false, // set to [20, 20] to force grid snapping
newElementWidth: 100, // width for new element
newElementHeight: 22, // height for new element
debug: false, // render debug elements
debugElementClass: 'pf-system-debug' // class for debug elements
};
this._config = Object.assign({}, this._defaultConfig, config);
/**
* convert degree into radial unit
* @param deg
* @returns {number}
* @private
*/
this._degToRad = (deg) => {
return deg * Math.PI / 180;
};
/**
* get element dimension/position of a DOM element
* @param element
* @returns {*}
* @private
*/
this._getElementDimension = (element) => {
let dim = null;
if(element){
// add "gap" to a and b in order to have some space around elements
dim = {
left: element.style.left ? parseInt(element.style.left, 10) : 0,
top: element.style.top ? parseInt(element.style.top, 10) : 0,
a: parseInt((element.offsetWidth / 2).toString(), 10) + this._config.gapX,
b: parseInt((element.offsetHeight / 2).toString(), 10) + this._config.gapY,
width: element.offsetWidth,
height: element.offsetHeight
};
}
return dim;
};
/**
* get x/y coordinate on an eclipse around a 2D area by a given radial angle
* @param dim
* @param angle
* @returns {*}
* @private
*/
this._getEllipseCoordinates = (dim, angle) => {
let coordinates = null;
if(dim){
angle = this._degToRad(angle);
coordinates = {
x: Math.round((dim.a * dim.b) / Math.sqrt(Math.pow(dim.b, 2) + Math.pow(dim.a, 2) * Math.pow(Math.tan(angle), 2) )),
y: Math.round((dim.a * dim.b) / Math.sqrt(Math.pow(dim.a, 2) + Math.pow(dim.b, 2) / Math.pow(Math.tan(angle), 2) ))
};
// invert coordinate based on quadrant ------------------------------------------------------------
if( angle > (Math.PI / 2) && angle < (3 * Math.PI / 2) ){
coordinates.x = coordinates.x * -1;
}
if( angle > Math.PI && angle < (2 * Math.PI) ){
coordinates.y = coordinates.y * -1;
}
}
return coordinates;
};
/**
* get dimensions of all surrounding elements
* @returns {Array}
* @private
*/
this._getAllElementDimensions = () => {
let dimensions = [];
let surroundingElements = this._getContainer().getElementsByClassName(this._config.elementClass);
for(let element of surroundingElements){
dimensions.push(this._getElementDimension(element));
}
return dimensions;
};
/**
* Transform a x/y coordinate into a 2D element with width/height
* @param centerDimension
* @param coordinate
* @returns {*}
* @private
*/
this._transformCoordinate = (centerDimension, coordinate) => {
let dim = null;
if(centerDimension && coordinate){
let left = 0;
let top = 0;
// calculate left/top based on coordinate system quadrant -----------------------------------------
// -> flip horizontal in Q2 and Q3
if(coordinate.x >= 0 && coordinate.y > 0){
// 1. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x;
top = centerDimension.top + 2 * (centerDimension.b - this._config.gapY) - Math.abs(coordinate.y) - this._config.newElementHeight;
}else if(coordinate.x < 0 && coordinate.y > 0){
// 2. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x - this._config.newElementWidth;
top = centerDimension.top + 2 * (centerDimension.b - this._config.gapY) - Math.abs(coordinate.y) - this._config.newElementHeight;
}else if(coordinate.x < 0 && coordinate.y <= 0){
// 3. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x - this._config.newElementWidth;
top = centerDimension.top + Math.abs(coordinate.y);
}else{
// 4. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x;
top = centerDimension.top + Math.abs(coordinate.y);
}
// center horizontal for x = 0 coordinate (top and bottom element) --------------------------------
if(coordinate.x === 0){
left -= Math.round(this._config.newElementWidth / 2);
}
// transform to grid coordinates (if grid snapping is enabled) ------------------------------------
if(this._config.grid){
left = Math.floor(left / this._config.grid[0]) * this._config.grid[0];
top = Math.floor(top / this._config.grid[1]) * this._config.grid[1];
}
dim = {
left: left,
top: top,
width: this._config.newElementWidth,
height: this._config.newElementHeight
};
}
return dim;
};
/**
* calc overlapping percent of two given dimensions
* @param dim1
* @param dim2
* @returns {number}
* @private
*/
this._percentCovered = (dim1, dim2) => {
let percent = 0;
if (
(dim1.left <= dim2.left) &&
(dim1.top <= dim2.top) &&
((dim1.left + dim1.width) >= (dim2.left + dim2.width)) &&
((dim1.top + dim1.height) > (dim2.top + dim2.height))
) {
// The whole thing is covering the whole other thing
percent = 100;
}else{
// Only parts may be covered, calculate percentage
dim1.right = dim1.left + dim1.width;
dim1.bottom = dim1.top + dim1.height;
dim2.right = dim2.left + dim2.width;
dim2.bottom = dim2.top + dim2.height;
let l = Math.max(dim1.left, dim2.left);
let r = Math.min(dim1.right, dim2.right);
let t = Math.max(dim1.top, dim2.top);
let b = Math.min(dim1.bottom, dim2.bottom);
if (b >= t && r >= l) {
percent = (((r - l) * (b - t)) / (dim2.width * dim2.height)) * 100;
}
}
return percent;
};
/**
* checks whether dim1 is partially overlapped by any other element
* @param dim1
* @param dimensionContainer
* @param allDimensions
* @returns {boolean}
* @private
*/
this._isOverlapping = (dim1, dimensionContainer, allDimensions) => {
let isOverlapping = false;
if(dim1){
if(this._percentCovered(dimensionContainer, dim1 ) === 100){
// element is within parent container
for(let dim2 of allDimensions){
let percentCovered = this._percentCovered(dim1, dim2);
if(percentCovered){
isOverlapping = true;
// render debug element
this._showDebugElement(dim1, percentCovered);
break;
}
}
}else{
isOverlapping = true;
this._showDebugElement(dim1, 100);
}
}else{
isOverlapping = true;
}
return isOverlapping;
};
/**
* find all dimensions around a centerDimension that are not overlapped by other elements
* @param maxResults
* @param steps
* @param allDimensions
* @param loops
* @returns {Array}
* @private
*/
this._findDimensions = (maxResults, steps, allDimensions, loops) => {
let dimensions = [];
let start = 0;
let end = 360;
let angle = end / steps;
let dimensionContainer = this._getElementDimension(this._getContainer());
steps = steps || 1;
loops = loops || 1;
// increase the "gab" between center element and potential found dimensions...
// ... for each recursive loop call, to get an elliptical cycle beyond
// -> defaultGab for 1. loop, ..., defaultGab + 3 * defaultGab/ 2 for 3. loop
this._config.gapX = this._config.defaultGapX + loops * Math.round(this._config.defaultGapX / 2);
this._config.gapY = this._config.defaultGapY + loops * Math.round(this._config.defaultGapY / 2);
let centerDimension = this._getElementDimension(this._config.center);
do {
// get all potential coordinates on an eclipse around a given "centerElementDimension"
let coordinate = this._getEllipseCoordinates(centerDimension, end);
// transform relative x/y coordinate into a absolute 2D area
let checkDimension = this._transformCoordinate(centerDimension, coordinate);
if (!this._isOverlapping(checkDimension, dimensionContainer, allDimensions)) {
dimensions.push({
left: checkDimension.left,
top: checkDimension.top,
width: checkDimension.width,
height: checkDimension.height
});
// render debug element
this._showDebugElement(checkDimension, 0);
maxResults--;
}
end -= angle;
} while (maxResults > 0 && start < end);
if(maxResults > 0 && loops < this._config.loops){
loops++;
steps *= 2;
dimensions = dimensions.concat(this._findDimensions(maxResults, steps, allDimensions, loops));
}
return dimensions;
};
/**
* get container (parent) element
* @returns {*}
* @private
*/
this._getContainer = () => {
return this._config.container ? this._config.container : document.body;
};
/**
* render debug element into parent container
* -> checks overlapping dimension with other elements
* @param dimension
* @param percentCovered
* @private
*/
this._showDebugElement = (dimension, percentCovered) => {
if(this._config.debug){
let element = document.createElement('div');
element.style.left = dimension.left + 'px';
element.style.top = dimension.top + 'px';
element.style.width = dimension.width + 'px';
element.style.height = dimension.height + 'px';
element.style.backgroundColor = Boolean(percentCovered) ? 'rgba(255,0,0,0.1)' : 'rgba(0,255,0,0.1)';
element.innerHTML = Math.round(percentCovered * 100) / 100 + '%';
element.classList.add(this._config.debugElementClass);
this._getContainer().appendChild(element);
}
};
/**
* hide all debug elements
* @private
*/
this._hideDebugElements = () => {
let debugElements = this._getContainer().getElementsByClassName(this._config.debugElementClass);
while(debugElements.length > 0){
debugElements[0].parentNode.removeChild(debugElements[0]);
}
};
// public functions ---------------------------------------------------------------------------------------
/**
* search for surrounding, non overlapping dimensions
* @param maxResults
* @param steps
* @returns {Array}
*/
this.findNonOverlappingDimensions = (maxResults, steps) => {
this._hideDebugElements();
// element dimensions that exist and should be checked for overlapping
let allDimensions = this._getAllElementDimensions();
return this._findDimensions(maxResults, steps, allDimensions);
};
}
}
return {
Position: Position
};
});

View File

@@ -25,7 +25,6 @@ define([
zIndexCounter: 110,
maxActiveConnections: 8,
mapSnapToGrid: false, // "Snap to Grid" feature for drag&drop systems on map (optional)
mapWrapperClass: 'pf-map-wrapper', // wrapper div (scrollable)
mapClass: 'pf-map', // class for all maps
@@ -1569,6 +1568,7 @@ define([
// get new position
newPosition = System.calculateNewSystemPosition(sourceSystem);
}else{
// check mouse cursor position (add system to map)
newPosition = {
@@ -2062,7 +2062,7 @@ define([
let mapContainer = $( map.getContainer() );
let systemHeadExpand = $( system.find('.' + config.systemHeadExpandClass) );
let systemBody = $( system.find('.' + config.systemBodyClass) );
let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
// map overlay will be set on "drag" start
let mapOverlayTimer = null;
@@ -2083,7 +2083,7 @@ define([
// check if grid-snap is enable -> this enables napping for !CURRENT! Element
if( mapContainer.hasClass(MapUtil.config.mapGridClass) ){
params.drag.params.grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
params.drag.params.grid = grid;
}else{
delete( params.drag.params.grid );
}

View File

@@ -8,8 +8,9 @@ define([
'app/init',
'app/util',
'bootbox',
'app/map/util'
], ($, Init, Util, bootbox, MapUtil) => {
'app/map/util',
'app/map/layout'
], ($, Init, Util, bootbox, MapUtil, Layout) => {
'use strict';
let config = {
@@ -298,21 +299,45 @@ define([
/**
* calculate the x/y coordinates for a new system - relativ to a source system
* @param sourceSystem
* @param grid
* @returns {{x: *, y: *}}
*/
let calculateNewSystemPosition = function(sourceSystem){
let mapContainer = sourceSystem.parent();
let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
// related system is available
let currentX = sourceSystem.css('left');
let currentY = sourceSystem.css('top');
let x = 0;
let y = 0;
// remove "px"
currentX = parseInt( currentX.substring(0, currentX.length - 2) );
currentY = parseInt( currentY.substring(0, currentY.length - 2) );
let position = new Layout.Position({
container: mapContainer[0],
center: sourceSystem[0],
loops: 4,
grid: mapContainer.hasClass(MapUtil.config.mapGridClass) ? grid : false,
debug: false
});
let dimensions = position.findNonOverlappingDimensions(1, 16);
if(dimensions.length){
//... empty map space found
x = dimensions[0].left;
y = dimensions[0].top;
}else{
//... fallback
// related system is available
let currentX = sourceSystem.css('left');
let currentY = sourceSystem.css('top');
// remove "px"
currentX = parseInt( currentX.substring(0, currentX.length - 2) );
currentY = parseInt( currentY.substring(0, currentY.length - 2) );
x = currentX + config.newSystemOffset.x;
y = currentY + config.newSystemOffset.y;
}
let newPosition = {
x: currentX + config.newSystemOffset.x,
y: currentY + config.newSystemOffset.y
x: x,
y: y
};
return newPosition;

View File

@@ -269,7 +269,6 @@ $.fn.dragToSelect = function (conf) {
// remove element from "deselected" elements (e.g on add -> remove -> add scenario)
deselectedItems = deselectedItems.not(el);
}else {
if(el.hasClass(config.selectedClass)){
el.removeClass(config.selectedClass);
deselectedItems = deselectedItems.add(el);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,341 @@
define(() => {
'use strict';
class Position {
constructor(config) {
this._defaultConfig = {
container: null, // parent DOM container element
center: null, // DOM elements that works as center
elementClass: 'pf-system', // class for all elements
defaultGapX: 50,
defaultGapY: 50,
gapX: 50, // leave gap between elements (x-axis)
gapY: 50, // leave gap between elements (y-axis)
loops: 2, // max loops around "center" for search
grid: false, // set to [20, 20] to force grid snapping
newElementWidth: 100, // width for new element
newElementHeight: 22, // height for new element
debug: false, // render debug elements
debugElementClass: 'pf-system-debug' // class for debug elements
};
this._config = Object.assign({}, this._defaultConfig, config);
/**
* convert degree into radial unit
* @param deg
* @returns {number}
* @private
*/
this._degToRad = (deg) => {
return deg * Math.PI / 180;
};
/**
* get element dimension/position of a DOM element
* @param element
* @returns {*}
* @private
*/
this._getElementDimension = (element) => {
let dim = null;
if(element){
// add "gap" to a and b in order to have some space around elements
dim = {
left: element.style.left ? parseInt(element.style.left, 10) : 0,
top: element.style.top ? parseInt(element.style.top, 10) : 0,
a: parseInt((element.offsetWidth / 2).toString(), 10) + this._config.gapX,
b: parseInt((element.offsetHeight / 2).toString(), 10) + this._config.gapY,
width: element.offsetWidth,
height: element.offsetHeight
};
}
return dim;
};
/**
* get x/y coordinate on an eclipse around a 2D area by a given radial angle
* @param dim
* @param angle
* @returns {*}
* @private
*/
this._getEllipseCoordinates = (dim, angle) => {
let coordinates = null;
if(dim){
angle = this._degToRad(angle);
coordinates = {
x: Math.round((dim.a * dim.b) / Math.sqrt(Math.pow(dim.b, 2) + Math.pow(dim.a, 2) * Math.pow(Math.tan(angle), 2) )),
y: Math.round((dim.a * dim.b) / Math.sqrt(Math.pow(dim.a, 2) + Math.pow(dim.b, 2) / Math.pow(Math.tan(angle), 2) ))
};
// invert coordinate based on quadrant ------------------------------------------------------------
if( angle > (Math.PI / 2) && angle < (3 * Math.PI / 2) ){
coordinates.x = coordinates.x * -1;
}
if( angle > Math.PI && angle < (2 * Math.PI) ){
coordinates.y = coordinates.y * -1;
}
}
return coordinates;
};
/**
* get dimensions of all surrounding elements
* @returns {Array}
* @private
*/
this._getAllElementDimensions = () => {
let dimensions = [];
let surroundingElements = this._getContainer().getElementsByClassName(this._config.elementClass);
for(let element of surroundingElements){
dimensions.push(this._getElementDimension(element));
}
return dimensions;
};
/**
* Transform a x/y coordinate into a 2D element with width/height
* @param centerDimension
* @param coordinate
* @returns {*}
* @private
*/
this._transformCoordinate = (centerDimension, coordinate) => {
let dim = null;
if(centerDimension && coordinate){
let left = 0;
let top = 0;
// calculate left/top based on coordinate system quadrant -----------------------------------------
// -> flip horizontal in Q2 and Q3
if(coordinate.x >= 0 && coordinate.y > 0){
// 1. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x;
top = centerDimension.top + 2 * (centerDimension.b - this._config.gapY) - Math.abs(coordinate.y) - this._config.newElementHeight;
}else if(coordinate.x < 0 && coordinate.y > 0){
// 2. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x - this._config.newElementWidth;
top = centerDimension.top + 2 * (centerDimension.b - this._config.gapY) - Math.abs(coordinate.y) - this._config.newElementHeight;
}else if(coordinate.x < 0 && coordinate.y <= 0){
// 3. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x - this._config.newElementWidth;
top = centerDimension.top + Math.abs(coordinate.y);
}else{
// 4. quadrant
left = centerDimension.left + centerDimension.a - this._config.gapX + coordinate.x;
top = centerDimension.top + Math.abs(coordinate.y);
}
// center horizontal for x = 0 coordinate (top and bottom element) --------------------------------
if(coordinate.x === 0){
left -= Math.round(this._config.newElementWidth / 2);
}
// transform to grid coordinates (if grid snapping is enabled) ------------------------------------
if(this._config.grid){
left = Math.floor(left / this._config.grid[0]) * this._config.grid[0];
top = Math.floor(top / this._config.grid[1]) * this._config.grid[1];
}
dim = {
left: left,
top: top,
width: this._config.newElementWidth,
height: this._config.newElementHeight
};
}
return dim;
};
/**
* calc overlapping percent of two given dimensions
* @param dim1
* @param dim2
* @returns {number}
* @private
*/
this._percentCovered = (dim1, dim2) => {
let percent = 0;
if (
(dim1.left <= dim2.left) &&
(dim1.top <= dim2.top) &&
((dim1.left + dim1.width) >= (dim2.left + dim2.width)) &&
((dim1.top + dim1.height) > (dim2.top + dim2.height))
) {
// The whole thing is covering the whole other thing
percent = 100;
}else{
// Only parts may be covered, calculate percentage
dim1.right = dim1.left + dim1.width;
dim1.bottom = dim1.top + dim1.height;
dim2.right = dim2.left + dim2.width;
dim2.bottom = dim2.top + dim2.height;
let l = Math.max(dim1.left, dim2.left);
let r = Math.min(dim1.right, dim2.right);
let t = Math.max(dim1.top, dim2.top);
let b = Math.min(dim1.bottom, dim2.bottom);
if (b >= t && r >= l) {
percent = (((r - l) * (b - t)) / (dim2.width * dim2.height)) * 100;
}
}
return percent;
};
/**
* checks whether dim1 is partially overlapped by any other element
* @param dim1
* @param dimensionContainer
* @param allDimensions
* @returns {boolean}
* @private
*/
this._isOverlapping = (dim1, dimensionContainer, allDimensions) => {
let isOverlapping = false;
if(dim1){
if(this._percentCovered(dimensionContainer, dim1 ) === 100){
// element is within parent container
for(let dim2 of allDimensions){
let percentCovered = this._percentCovered(dim1, dim2);
if(percentCovered){
isOverlapping = true;
// render debug element
this._showDebugElement(dim1, percentCovered);
break;
}
}
}else{
isOverlapping = true;
this._showDebugElement(dim1, 100);
}
}else{
isOverlapping = true;
}
return isOverlapping;
};
/**
* find all dimensions around a centerDimension that are not overlapped by other elements
* @param maxResults
* @param steps
* @param allDimensions
* @param loops
* @returns {Array}
* @private
*/
this._findDimensions = (maxResults, steps, allDimensions, loops) => {
let dimensions = [];
let start = 0;
let end = 360;
let angle = end / steps;
let dimensionContainer = this._getElementDimension(this._getContainer());
steps = steps || 1;
loops = loops || 1;
// increase the "gab" between center element and potential found dimensions...
// ... for each recursive loop call, to get an elliptical cycle beyond
// -> defaultGab for 1. loop, ..., defaultGab + 3 * defaultGab/ 2 for 3. loop
this._config.gapX = this._config.defaultGapX + loops * Math.round(this._config.defaultGapX / 2);
this._config.gapY = this._config.defaultGapY + loops * Math.round(this._config.defaultGapY / 2);
let centerDimension = this._getElementDimension(this._config.center);
do {
// get all potential coordinates on an eclipse around a given "centerElementDimension"
let coordinate = this._getEllipseCoordinates(centerDimension, end);
// transform relative x/y coordinate into a absolute 2D area
let checkDimension = this._transformCoordinate(centerDimension, coordinate);
if (!this._isOverlapping(checkDimension, dimensionContainer, allDimensions)) {
dimensions.push({
left: checkDimension.left,
top: checkDimension.top,
width: checkDimension.width,
height: checkDimension.height
});
// render debug element
this._showDebugElement(checkDimension, 0);
maxResults--;
}
end -= angle;
} while (maxResults > 0 && start < end);
if(maxResults > 0 && loops < this._config.loops){
loops++;
steps *= 2;
dimensions = dimensions.concat(this._findDimensions(maxResults, steps, allDimensions, loops));
}
return dimensions;
};
/**
* get container (parent) element
* @returns {*}
* @private
*/
this._getContainer = () => {
return this._config.container ? this._config.container : document.body;
};
/**
* render debug element into parent container
* -> checks overlapping dimension with other elements
* @param dimension
* @param percentCovered
* @private
*/
this._showDebugElement = (dimension, percentCovered) => {
if(this._config.debug){
let element = document.createElement('div');
element.style.left = dimension.left + 'px';
element.style.top = dimension.top + 'px';
element.style.width = dimension.width + 'px';
element.style.height = dimension.height + 'px';
element.style.backgroundColor = Boolean(percentCovered) ? 'rgba(255,0,0,0.1)' : 'rgba(0,255,0,0.1)';
element.innerHTML = Math.round(percentCovered * 100) / 100 + '%';
element.classList.add(this._config.debugElementClass);
this._getContainer().appendChild(element);
}
};
/**
* hide all debug elements
* @private
*/
this._hideDebugElements = () => {
let debugElements = this._getContainer().getElementsByClassName(this._config.debugElementClass);
while(debugElements.length > 0){
debugElements[0].parentNode.removeChild(debugElements[0]);
}
};
// public functions ---------------------------------------------------------------------------------------
/**
* search for surrounding, non overlapping dimensions
* @param maxResults
* @param steps
* @returns {Array}
*/
this.findNonOverlappingDimensions = (maxResults, steps) => {
this._hideDebugElements();
// element dimensions that exist and should be checked for overlapping
let allDimensions = this._getAllElementDimensions();
return this._findDimensions(maxResults, steps, allDimensions);
};
}
}
return {
Position: Position
};
});

View File

@@ -25,7 +25,6 @@ define([
zIndexCounter: 110,
maxActiveConnections: 8,
mapSnapToGrid: false, // "Snap to Grid" feature for drag&drop systems on map (optional)
mapWrapperClass: 'pf-map-wrapper', // wrapper div (scrollable)
mapClass: 'pf-map', // class for all maps
@@ -1569,6 +1568,7 @@ define([
// get new position
newPosition = System.calculateNewSystemPosition(sourceSystem);
}else{
// check mouse cursor position (add system to map)
newPosition = {
@@ -2062,7 +2062,7 @@ define([
let mapContainer = $( map.getContainer() );
let systemHeadExpand = $( system.find('.' + config.systemHeadExpandClass) );
let systemBody = $( system.find('.' + config.systemBodyClass) );
let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
// map overlay will be set on "drag" start
let mapOverlayTimer = null;
@@ -2083,7 +2083,7 @@ define([
// check if grid-snap is enable -> this enables napping for !CURRENT! Element
if( mapContainer.hasClass(MapUtil.config.mapGridClass) ){
params.drag.params.grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
params.drag.params.grid = grid;
}else{
delete( params.drag.params.grid );
}

View File

@@ -8,8 +8,9 @@ define([
'app/init',
'app/util',
'bootbox',
'app/map/util'
], ($, Init, Util, bootbox, MapUtil) => {
'app/map/util',
'app/map/layout'
], ($, Init, Util, bootbox, MapUtil, Layout) => {
'use strict';
let config = {
@@ -298,21 +299,45 @@ define([
/**
* calculate the x/y coordinates for a new system - relativ to a source system
* @param sourceSystem
* @param grid
* @returns {{x: *, y: *}}
*/
let calculateNewSystemPosition = function(sourceSystem){
let mapContainer = sourceSystem.parent();
let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
// related system is available
let currentX = sourceSystem.css('left');
let currentY = sourceSystem.css('top');
let x = 0;
let y = 0;
// remove "px"
currentX = parseInt( currentX.substring(0, currentX.length - 2) );
currentY = parseInt( currentY.substring(0, currentY.length - 2) );
let position = new Layout.Position({
container: mapContainer[0],
center: sourceSystem[0],
loops: 4,
grid: mapContainer.hasClass(MapUtil.config.mapGridClass) ? grid : false,
debug: false
});
let dimensions = position.findNonOverlappingDimensions(1, 16);
if(dimensions.length){
//... empty map space found
x = dimensions[0].left;
y = dimensions[0].top;
}else{
//... fallback
// related system is available
let currentX = sourceSystem.css('left');
let currentY = sourceSystem.css('top');
// remove "px"
currentX = parseInt( currentX.substring(0, currentX.length - 2) );
currentY = parseInt( currentY.substring(0, currentY.length - 2) );
x = currentX + config.newSystemOffset.x;
y = currentY + config.newSystemOffset.y;
}
let newPosition = {
x: currentX + config.newSystemOffset.x,
y: currentY + config.newSystemOffset.y
x: x,
y: y
};
return newPosition;

View File

@@ -269,7 +269,6 @@ $.fn.dragToSelect = function (conf) {
// remove element from "deselected" elements (e.g on add -> remove -> add scenario)
deselectedItems = deselectedItems.not(el);
}else {
if(el.hasClass(config.selectedClass)){
el.removeClass(config.selectedClass);
deselectedItems = deselectedItems.add(el);

View File

@@ -406,6 +406,17 @@ $mapWidth: 2500px ;
}
}
// debug system class
.pf-system-debug{
position: absolute;
color: $white;
font-size: 10px;
line-height: 22px;
text-align: center;
pointer-events: none;
z-index: 500;
}
// Endpoints ================================================================
.pf-map-endpoint-source, .pf-map-endpoint-target{
z-index: 90;