basicprimitives
Version:
Basic Primitives Diagrams for JavaScript - data visualization components library that implements organizational chart and multi-parent dependency diagrams, contains implementations of JavaScript Controls and PDF rendering plugins.
299 lines (260 loc) • 11.6 kB
JavaScript
import Rect from '../../graphics/structs/Rect';
import Size from '../../graphics/structs/Size';
import { OrientationType, PageFitMode, Visibility } from '../../enums';
function LevelVisibility(level, visibility) {
this.level = level;
this.visibility = visibility;
};
export default function ItemsPositionsTask(currentControlSizeTask, scaleOptionTask, orientationOptionTask, itemsSizesOptionTask, connectorsOptionTask, normalizeOptionTask,
createLayoutsTreeTask,
itemTemplateParamsTask,
cursorItemTask, combinedNormalVisibilityItemsTask) {
var _data = {
treeItemsPositions: {}, // TreeItemPosition();
size: null // Rect();
};
function process() {
var { verticalAlignment, pageFitMode, minimalVisibility, padding,
normalLevelShift, dotLevelShift, lineLevelShift, normalItemsInterval, dotItemsInterval, lineItemsInterval,
checkBoxPanelSize, buttonsPanelSize, groupTitlePanelSize, groupTitlePlacementType, cousinsIntervalMultiplier } = itemsSizesOptionTask.getOptions();
var { arrowsDirection, linesWidth } = connectorsOptionTask.getOptions();
var { maximumColumnsInMatrix, horizontalAlignment } = normalizeOptionTask.getOptions();
var { orientationType } = orientationOptionTask.getOptions();
var isItemSelected = combinedNormalVisibilityItemsTask.isItemSelected;
var cursorItemId = cursorItemTask.getCursorTreeItem();
var getTemplateParams = itemTemplateParamsTask.getTemplateParams;
/* calculate panel size */
var { optimalPanelSize } = currentControlSizeTask.getOptions();
var panelSize = new Size(optimalPanelSize);
var { scale } = scaleOptionTask.getOptions();
panelSize.scale(1.0 / scale);
var layoutsTree = createLayoutsTreeTask.getLayoutsTree();
/* find root layout id */
var rootLayoutId = null;
layoutsTree.loopLevels(this, function(nodeId, node, levelIndex) {
rootLayoutId = nodeId;
return layoutsTree.BREAK;
})
/* enumerate items level indexes */
var levelIndexes = {};
var maximumLevelIndex = 0;
var hasNodes = false;
layoutsTree.loopLevels(this, function(layoutId, layout, levelIndex) {
if(layout.loop != null) {
var parentId = layoutsTree.parentid(layoutId);
var parentLevelIndex = levelIndexes[parentId] || 0;
layout.loop(this, function(treeItem, levelIndex) {
var itemLevelIndex = parentLevelIndex + levelIndex;
levelIndexes[treeItem.id] = itemLevelIndex;
maximumLevelIndex = Math.max(maximumLevelIndex, itemLevelIndex);
hasNodes = true;
})
}
})
var options = {
verticalAlignment,
pageFitMode,
padding,
minimalVisibility,
orientationType,
arrowsDirection,
linesWidth,
checkBoxPanelSize,
buttonsPanelSize,
groupTitlePanelSize,
groupTitlePlacementType,
maximumColumnsInMatrix,
horizontalAlignment,
shifts: getShifts(normalLevelShift, dotLevelShift, lineLevelShift, lineLevelShift),
intervals: getIntervals(normalItemsInterval, dotItemsInterval, lineItemsInterval, lineItemsInterval),
cousinsIntervalMultiplier
};
/* find optimal panel size */
_data.treeItemsPositions = {};
_data.size = panelSize;
if(hasNodes) {
var {treeItemsPositions, size} = autoFitDiagramToPageSize(panelSize, maximumLevelIndex, rootLayoutId, layoutsTree, levelIndexes, cursorItemId, isItemSelected, getTemplateParams, options);
_data.treeItemsPositions = treeItemsPositions;
_data.size = size;
/* arrange items positions */
var treeItemPosition = _data.treeItemsPositions[rootLayoutId];
treeItemPosition.actualPosition = new Rect(0, 0, _data.size.width, _data.size.height);
var layoutsDirections = {};
layoutsTree.loopPreOrder(this, function(childLayoutId, childLayout, parentLayoutId, parentLayout) {
var treeItemPosition = _data.treeItemsPositions[childLayoutId];
var layoutDirection = layoutsDirections[childLayoutId];
childLayout.arrange(this, treeItemPosition.actualPosition, layoutDirection, _data.treeItemsPositions, options, function (treeItemId, treeItemPosition, layoutDirection) {
_data.treeItemsPositions[treeItemId] = treeItemPosition;
layoutsDirections[treeItemId] = layoutDirection;
});
});
}
return true;
}
function autoFitDiagramToPageSize(panelSize, maximumLevelIndex, rootLayoutId, layoutsTree, levelIndexes, cursorItemId, isItemSelected, getTemplateParams, options) {
var result,
possibleLevelVisibilities,
enabledLevelVisibilities;
var { orientationType, pageFitMode, minimalVisibility } = options;
switch (orientationType) {
case OrientationType.Left:
case OrientationType.Right:
panelSize.invert();
break;
}
switch (pageFitMode) {
case PageFitMode.None:
case PageFitMode.AutoSize:
possibleLevelVisibilities = [new LevelVisibility(0, Visibility.Normal)];
enabledLevelVisibilities = getLevelVisibilities(maximumLevelIndex, possibleLevelVisibilities, 0);
result = measureLayout(rootLayoutId, layoutsTree, enabledLevelVisibilities, levelIndexes, cursorItemId, isItemSelected, getTemplateParams, options);
break;
default:
possibleLevelVisibilities = getPossibleLevelVisibilities(maximumLevelIndex, minimalVisibility);
enabledLevelVisibilities = getLevelVisibilities(maximumLevelIndex, possibleLevelVisibilities, possibleLevelVisibilities.length - 1);
// Find minimal placeholder size to hold completely folded diagram
result = measureLayout(rootLayoutId, layoutsTree, enabledLevelVisibilities, levelIndexes, cursorItemId, isItemSelected, getTemplateParams, options);
if (checkDiagramSize(result.size, panelSize, pageFitMode)) {
/* maximum compression fits to page */
var minimalPlaceholderSize = new Rect(0, 0, result.size.width, result.size.height);
minimalPlaceholderSize.addRect(0, 0, panelSize.width, panelSize.height);
minimalPlaceholderSize.offset(0, 0, 5, 5);
/* Find optimal diagram size */
findOptimalSize(this, possibleLevelVisibilities.length - 1, function (index) {
enabledLevelVisibilities = getLevelVisibilities(maximumLevelIndex, possibleLevelVisibilities, index);
result = measureLayout(rootLayoutId, layoutsTree, enabledLevelVisibilities, levelIndexes, cursorItemId, isItemSelected, getTemplateParams, options);
/* compare root layout to the available panel space */
return checkDiagramSize(result.size, minimalPlaceholderSize, options.pageFitMode);
});
}
break;
}
return result;
}
function findOptimalSize(thisArg, maximum, funcCheckSize) {
var minimum = 0,
cursorIndex;
if (!funcCheckSize.call(thisArg, minimum)) {
/* minimum compression does not fit to page */
cursorIndex = maximum;
while (maximum - minimum > 1) {
cursorIndex = Math.floor((maximum + minimum) / 2.0);
if (funcCheckSize.call(thisArg, cursorIndex)) {
/* middle point size fit to page */
maximum = cursorIndex;
}
else {
minimum = cursorIndex;
}
}
if (maximum !== cursorIndex) {
funcCheckSize.call(thisArg, maximum);
}
}
};
function measureLayout(rootLayoutId, layoutsTree, levelVisibilities, levelIndexes, cursorItemId, isItemSelected, getTemplateParams, options ) {
var treeItemsPositions = {};
/* measure individual nodes from bottom to up */
layoutsTree.loopPostOrder(this, function(childLayoutId, childLayout, parentLayoutId, parentLayout) {
var levelIndex = levelIndexes[childLayoutId];
var levelVisibility = levelVisibilities[levelIndex];
var isCursor = (cursorItemId == childLayoutId);
var isSelected = isItemSelected(childLayoutId);
var treeItemTemplate = getTemplateParams(childLayoutId);
treeItemsPositions[childLayoutId] = childLayout.measure(levelVisibility, isCursor, isSelected, treeItemTemplate, treeItemsPositions, options);
});
return {
treeItemsPositions,
size: treeItemsPositions[rootLayoutId].actualSize
}
}
function getLevelVisibilities(maximumLevelIndex, possibleLevelVisibilities, cursorIndex) {
var index,
levelVisibility;
var result = [];
for(var levelIndex = 0; levelIndex <= maximumLevelIndex; levelIndex+=1 ) {
result.push(Visibility.Normal);
};
/* set levels visibility */
for (index = 0; index <= cursorIndex; index += 1) {
levelVisibility = possibleLevelVisibilities[index];
result[levelVisibility.level] = levelVisibility.visibility;
}
return result;
}
function getPossibleLevelVisibilities(maximumLevelIndex, minimalVisibility) {
var result = [new LevelVisibility(0, Visibility.Normal)];
var visibilities = [];
switch (minimalVisibility) {
case Visibility.Normal:
break;
case Visibility.Dot:
visibilities.push(Visibility.Dot);
break;
case Visibility.Auto:
case Visibility.Line:
case Visibility.Invisible:
visibilities.push(Visibility.Dot);
visibilities.push(Visibility.Line);
break;
}
for(var levelIndex = maximumLevelIndex; levelIndex >= 0; levelIndex-=1) {
for (var index = 0; index < visibilities.length; index += 1) {
result.push(new LevelVisibility(levelIndex, visibilities[index]));
}
};
return result;
};
function checkDiagramSize(diagramSize, panelSize, pageFitMode) {
var result = false;
switch (pageFitMode) {
case PageFitMode.PageWidth:
if (panelSize.width >= diagramSize.width) {
result = true;
}
break;
case PageFitMode.PageHeight:
if (panelSize.height >= diagramSize.height) {
result = true;
}
break;
case PageFitMode.FitToPage:
if (panelSize.height >= diagramSize.height && panelSize.width >= diagramSize.width) {
result = true;
}
break;
}
return result;
};
function getShifts(normalLevelShift, dotLevelShift, lineLevelShift, invisibleLineLevelShift) {
var result = [];
result[Visibility.Normal] = normalLevelShift;
result[Visibility.Dot] = dotLevelShift;
result[Visibility.Line] = lineLevelShift;
result[Visibility.Invisible] = invisibleLineLevelShift;
return result;
};
function getIntervals(normalItemsInterval, dotItemsInterval, lineItemsInterval, invisibleLineItemsInterval) {
var result = [];
result[Visibility.Normal] = normalItemsInterval;
result[Visibility.Dot] = dotItemsInterval;
result[Visibility.Line] = lineItemsInterval;
result[Visibility.Invisible] = invisibleLineItemsInterval;
return result;
};
function getItemPosition(itemid) {
return _data.treeItemsPositions[itemid];
}
function getItemsPositions() {
return _data.treeItemsPositions;
}
function getContentSize() {
return _data.size;
}
return {
process: process,
getItemsPositions: getItemsPositions,
getItemPosition: getItemPosition,
getContentSize: getContentSize
};
};