aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/components/Tile.vue21
-rw-r--r--src/components/TileTree.vue78
-rw-r--r--src/layout.js368
3 files changed, 230 insertions, 237 deletions
diff --git a/src/components/Tile.vue b/src/components/Tile.vue
index 1b5c2b7..9830a69 100644
--- a/src/components/Tile.vue
+++ b/src/components/Tile.vue
@@ -1,27 +1,13 @@
<script>
-import {defaultLayout} from '/src/layout.js';
export default {
name: 'tile',
data(){
return {
zIdx: 0,
- layoutSys: defaultLayout,
}
},
props: {
tree: Object,
- x: Number,
- y: Number,
- width: Number,
- height: Number,
- hideHeader: Boolean,
- },
- computed: {
- layout(){
- if (this.tree.children.length == 0)
- return {};
- return this.layoutSys.genLayout(this.tree.children, 0, 0, this.width, this.height, this.hideHeader);
- }
},
methods: {
onImgClick(){
@@ -48,20 +34,19 @@ export default {
<template>
<div
- :style="{position: 'absolute', left: x+'px', top: y+'px', width: width+'px', height: height+'px', zIndex: zIdx}"
+ :style="{position: 'absolute', left: tree.x+'px', top: tree.y+'px',
+ width: tree.w+'px', height: tree.h+'px', zIndex: zIdx}"
class="transition-[left,top,width,height] duration-300 ease-out border border-stone-900 bg-white overflow-hidden">
<div v-if="tree.children.length == 0"
:style="{backgroundImage: 'url(\'/src/assets/' + tree.tolNode.name + '.jpg\')'}"
class="hover:cursor-pointer w-full h-full bg-cover" @click="onImgClick"
/>
<div v-else>
- <div v-if="!hideHeader" :style="{height: this.layoutSys.HEADER_SZ + 'px'}"
+ <div v-if="tree.headerSz > 0" :style="{height: tree.headerSz+'px'}"
class="text-center hover:cursor-pointer bg-stone-300" @click="onHeaderClick">
{{tree.tolNode.name}}
</div>
<tile v-for="child in tree.children" :key="child.tolNode.name" :tree="child"
- :x="layout.coords[child.tolNode.name].x" :y="layout.coords[child.tolNode.name].y"
- :width="layout.coords[child.tolNode.name].w" :height="layout.coords[child.tolNode.name].h"
@tile-clicked="onInnerTileClicked" @header-clicked="onInnerHeaderClicked"
></tile>
</div>
diff --git a/src/components/TileTree.vue b/src/components/TileTree.vue
index b1fb3fd..5c3d08e 100644
--- a/src/components/TileTree.vue
+++ b/src/components/TileTree.vue
@@ -1,29 +1,49 @@
<script>
-import tol from '/src/tol.json';
-import {defaultLayout, initTree} from '/src/layout.js';
import Tile from './Tile.vue';
+import tol from '/src/tol.json';
+function preprocessTol(tree){
+ if (!tree.children){
+ tree.children = [];
+ } else {
+ tree.children.forEach(preprocessTol);
+ }
+}
+preprocessTol(tol);
+
+import {staticSqrLayout, staticRectLayout, sweepToSideLayout} from '/src/layout.js';
+let LAYOUT_SYS = sweepToSideLayout;
+
export default {
data(){
return {
- tree: initTree(this.preprocessTol(tol), 1),
+ tree: this.initTree(tol, 1),
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
- layoutSys: defaultLayout,
}
},
methods: {
- preprocessTol(tree){
- if (!tree.children){
- tree.children = [];
- } else {
- tree.children.forEach(this.preprocessTol);
+ initTree(tol, lvl){
+ let tree = {
+ tolNode:tol, children:[],
+ x:0, y:0, w:0, h:0, headerSz:0,
+ };
+ function initTreeRec(tree, lvl){
+ if (lvl > 0)
+ tree.children = tree.tolNode.children.map(tNode => initTreeRec({
+ tolNode: tNode, children: [],
+ x:0, y:0, w:0, h:0, headerSz:0,
+ }, lvl-1));
+ return tree;
}
+ initTreeRec(tree, lvl);
+ LAYOUT_SYS.initLayoutInfo(tree)
return tree;
},
onResize(){
this.width = document.documentElement.clientWidth;
this.height = document.documentElement.clientHeight;
+ this.tryLayout(); //use best-effort collapsing-layout?
},
onInnerTileClicked(nodeList){
//nodeList holds an array of tree-objects, from the clicked-on-tile's tree-object upward
@@ -33,20 +53,43 @@ export default {
return;
}
//add children
- nodeList[0].children = nodeList[0].tolNode.children.map(e => ({
- tolNode: e,
- children: [],
+ nodeList[0].children = nodeList[0].tolNode.children.map(tNode => ({
+ tolNode: tNode, children: [],
+ x:0, y:0, w:0, h:0, headerSz:0,
}));
- this.layoutSys.updateLayoutInfoOnExpand(nodeList);
+ LAYOUT_SYS.updateLayoutInfoOnExpand(nodeList);
+ //try to layout tree
+ if (!this.tryLayout())
+ nodeList[0].children = [];
},
- onInnerHeaderClicked(nodeList){
- //nodeList will hold an array of tree-objects, from the clicked-on-tile's tree-object upward
+ onInnerHeaderClicked(nodeList){ //nodeList is array of tree-objects, from clicked-on-tile's tree-object upward
nodeList[0].children = [];
- this.layoutSys.updateLayoutInfoOnCollapse(nodeList);
+ LAYOUT_SYS.updateLayoutInfoOnCollapse(nodeList);
+ this.tryLayout();
},
+ tryLayout(){
+ let layout = LAYOUT_SYS.genLayout(this.tree, 0, 0, this.width, this.height, true);
+ if (layout == null){
+ console.log('Unable to layout tree');
+ return false;
+ } else {
+ this.applyLayout(layout, this.tree);
+ return true;
+ }
+ },
+ applyLayout(layout, tree){
+ //layout format: {x, y, w, h, headerSz, children:[layout1, ...], contentW, contentH, empSpc}
+ tree.x = layout.x;
+ tree.y = layout.y;
+ tree.w = layout.w;
+ tree.h = layout.h;
+ tree.headerSz = layout.headerSz;
+ layout.children.forEach((n,i) => this.applyLayout(n, tree.children[i]));
+ }
},
created(){
window.addEventListener('resize', this.onResize);
+ this.tryLayout();
},
unmounted(){
window.removeEventListener('resize', this.onResize);
@@ -59,8 +102,7 @@ export default {
<template>
<div class="h-[100vh]">
- <tile :tree="tree" :x="0" :y="0" :width="width" :height="height" hideHeader
- @tile-clicked="onInnerTileClicked" @header-clicked="onInnerHeaderClicked"></tile>
+ <tile :tree="tree" @tile-clicked="onInnerTileClicked" @header-clicked="onInnerHeaderClicked"></tile>
</div>
</template>
diff --git a/src/layout.js b/src/layout.js
index b713e29..1fa1f2d 100644
--- a/src/layout.js
+++ b/src/layout.js
@@ -1,45 +1,50 @@
-export {defaultLayout, initTree};
+export {staticSqrLayout, staticRectLayout, sweepToSideLayout};
-const DEFAULT_TILE_SPACING = 5;
-const DEFAULT_HEADER_SZ = 20;
+const TILE_SPACING = 5;
+const HEADER_SZ = 20;
-const staticSqrLayout = { //determines layout for squares in a specified rectangle, with spacing
- TILE_SPACING: DEFAULT_TILE_SPACING,
- HEADER_SZ: DEFAULT_HEADER_SZ,
- genLayout(nodes, x0, y0, w, h, hideHeader){
- //get number-of-columns with highest occupied-fraction of rectangles with aspect-ratio w/h
- //account for tile-spacing?, account for parent-box-border?, cache results?,
- let hOffset = (hideHeader ? 0 : this.HEADER_SZ);
- let availW = w - this.TILE_SPACING, availH = h - hOffset - this.TILE_SPACING;
- let numTiles = nodes.length, ar = availW/availH;
- let numCols, numRows, bestFrac = 0, tileSz, empSpc;
- for (let nc = 1; nc <= numTiles; nc++){
- let nr = Math.ceil(numTiles/nc);
+const staticSqrLayout = { //lays out nodes as squares in a rectangle, with spacing
+ genLayout(node, x, y, w, h, hideHeader){
+ //get number-of-columns with lowest leftover empty space
+ let headerSz = (hideHeader ? 0 : HEADER_SZ);
+ let availW = w - TILE_SPACING, availH = h - headerSz - TILE_SPACING;
+ let numChildren = node.children.length, ar = availW/availH;
+ let lowestEmp = Number.POSITIVE_INFINITY, numCols, numRows, tileSize;
+ for (let nc = 1; nc <= numChildren; nc++){
+ let nr = Math.ceil(numChildren/nc);
let ar2 = nc/nr;
let frac = ar > ar2 ? ar2/ar : ar/ar2;
- if (frac > bestFrac){
- bestFrac = frac;
+ let tileSz = ar > ar2 ? availH/nr-TILE_SPACING : availW/nc-TILE_SPACING;
+ let empSpc = (1-frac)*availW*availH + (nc*nr-numChildren)*(tileSz - TILE_SPACING)**2;
+ if (empSpc < lowestEmp){
+ lowestEmp = empSpc;
numCols = nc;
numRows = nr;
- tileSz = ar > ar2 ? availH/numRows-this.TILE_SPACING : availW/numCols-this.TILE_SPACING;
- empSpc = (1 - frac) * availW * availH;
- empSpc += (nc*nr - numTiles) * tileSz**2;
+ tileSize = tileSz;
+ }
+ }
+ let childLayouts = Array(numChildren).fill();
+ for (let i = 0; i < numChildren; i++){
+ let childX = TILE_SPACING + (i % numCols)*(tileSize + TILE_SPACING);
+ let childY = TILE_SPACING + headerSz + Math.floor(i / numCols)*(tileSize + TILE_SPACING);
+ if (node.children[i].children.length == 0){
+ childLayouts[i] = {
+ x: childX, y: childY, w: tileSize, h: tileSize, headerSz: 0,
+ children: [],
+ contentW: tileSize, contentH: tileSize, empSpc: 0,
+ }
+ } else {
+ childLayouts[i] = this.genLayout(node.children[i], childX, childY, tileSize, tileSize, false);
+ lowestEmp += childLayouts[i].empSpc;
}
}
- //determine layout
return {
- coords: Object.fromEntries(
- nodes.map((el, idx) => [el.tolNode.name, {
- x: x0 + this.TILE_SPACING + (idx % numCols)*(tileSz + this.TILE_SPACING),
- y: y0 + this.TILE_SPACING + hOffset + Math.floor(idx / numCols)*(tileSz + this.TILE_SPACING),
- w: tileSz,
- h: tileSz
- }])
- ),
- contentW: numCols * (tileSz + this.TILE_SPACING) + this.TILE_SPACING,
- contentH: numRows * (tileSz + this.TILE_SPACING) + this.TILE_SPACING + hOffset,
- empSpc: empSpc
- };
+ x: x, y: y, w: w, h: h, headerSz: headerSz,
+ children: childLayouts,
+ contentW: numCols * (tileSize + TILE_SPACING) + TILE_SPACING,
+ contentH: numRows * (tileSize + TILE_SPACING) + TILE_SPACING + headerSz,
+ empSpc: lowestEmp,
+ }
},
initLayoutInfo(tree){
return;
@@ -52,28 +57,47 @@ const staticSqrLayout = { //determines layout for squares in a specified rectang
}
};
const staticRectLayout = {
- TILE_SPACING: DEFAULT_TILE_SPACING,
- HEADER_SZ: DEFAULT_HEADER_SZ,
- genLayout(nodes, x0, y0, w, h, hideHeader){
- if (nodes.every(e => e.children.length == 0)){
- return staticSqrLayout.genLayout(nodes, x0, y0, w, h, hideHeader);
- }
- //if a node has children, find 'best' grid-arrangement
- let hOffset = (hideHeader ? 0 : this.HEADER_SZ);
- let availW = w - this.TILE_SPACING, availH = h - this.TILE_SPACING - hOffset;
- let numChildren = nodes.length;
- let rowBrks = [0]; //holds node indices at which each row starts
- let rowBreaks, lowestEmp = Number.POSITIVE_INFINITY, rowsOfCounts, cellWidths, cellHeights, nodeDims;
+ genLayout(node, x, y, w, h, hideHeader, subLayout = staticRectLayout){
+ if (node.children.every(n => n.children.length == 0))
+ return staticSqrLayout.genLayout(node, x, y, w, h, hideHeader);
+ //find grid-arrangement with lowest leftover empty space
+ let headerSz = (hideHeader ? 0 : HEADER_SZ);
+ let availW = w - TILE_SPACING, availH = h - TILE_SPACING - headerSz;
+ let numChildren = node.children.length;
+ let rowBrks = null; //will holds node indices at which each row starts
+ let lowestEmp = Number.POSITIVE_INFINITY, rowBreaks, rowsOfCounts, childLayouts;
+ rowBrksLoop:
while (true){
+ //update rowBrks or exit loop
+ if (rowBrks == null){
+ rowBrks = [0];
+ } else {
+ let i = rowBrks.length-1;
+ while (true){
+ if (i > 0 && rowBrks[i] < numChildren-1 - (rowBrks.length-1 - i)){
+ rowBrks[i]++;
+ break;
+ } else if (i > 0){
+ i--;
+ } else {
+ if (rowBrks.length < numChildren){
+ rowBrks = Array.from({length: rowBrks.length+1}, (x,i) => i);
+ } else {
+ break rowBrksLoop;
+ }
+ break;
+ }
+ }
+ }
//create list-of-lists representing each row's cells' tileCounts
let rowsOfCnts = Array(rowBrks.length).fill();
for (let r = 0; r < rowBrks.length; r++){
let numNodes = (r == rowBrks.length-1) ? numChildren-rowBrks[r] : rowBrks[r+1]-rowBrks[r];
let rowNodeIdxs = Array.from({length: numNodes}, (x,i) => i+rowBrks[r]);
- rowsOfCnts[r] = rowNodeIdxs.map(idx => nodes[idx].tileCount);
+ rowsOfCnts[r] = rowNodeIdxs.map(idx => node.children[idx].tileCount);
}
//get cell dims
- let totalTileCount = nodes.map(e => e.tileCount).reduce((x,y) => x+y);
+ let totalTileCount = node.children.map(n => n.tileCount).reduce((x,y) => x+y);
let cellHs = rowsOfCnts.map(row => row.reduce((x, y) => x+y) / totalTileCount * availH);
let cellWs = Array(numChildren).fill();
for (let r = 0; r < rowsOfCnts.length; r++){
@@ -82,56 +106,61 @@ const staticRectLayout = {
cellWs[rowBrks[r]+c] = rowsOfCnts[r][c] / rowCount * availW;
}
}
- //get node dims and empty-space
- let emptySpace = 0;
- let nodeDs = Array(numChildren).fill();
+ //get cell x/y coords
+ let cellXs = Array(cellWs.length).fill(0);
for (let r = 0; r < rowBrks.length; r++){
- for (let c = 0; c < rowsOfCnts[r].length; c++){
+ for (let c = 1; c < rowsOfCnts[r].length; c++){
let nodeIdx = rowBrks[r]+c;
- let res = nodes[nodeIdx].layoutRes(cellWs[nodeIdx], cellHs[r]);
- emptySpace += res.empSpc;
- nodeDs[nodeIdx] = [res.contentW, res.contentH];
+ cellXs[nodeIdx] = cellXs[nodeIdx-1] + cellWs[nodeIdx-1];
}
}
- if (emptySpace < lowestEmp){
- lowestEmp = emptySpace;
- rowBreaks = [...rowBrks];
- rowsOfCounts = rowsOfCnts;
- cellWidths = cellWs;
- cellHeights = cellHs;
- nodeDims = nodeDs;
+ let cellYs = Array(cellHs.length).fill(0);
+ for (let r = 1; r < rowBrks.length; r++){
+ cellYs[r] = cellYs[r-1] + cellHs[r-1];
}
- //update rowBrks or exit loop
- let i = rowBrks.length-1, exitLoop = false;
- while (true){
- if (i > 0 && rowBrks[i] < numChildren-1 - (rowBrks.length-1 - i)){
- rowBrks[i]++;
- break;
- } else if (i > 0){
- i--;
- } else {
- if (rowBrks.length < numChildren){
- rowBrks = Array.from({length: rowBrks.length+1}, (x,i) => i);
+ //get child layouts and empty-space
+ let childLyts = Array(numChildren).fill(), empSpc = 0;
+ for (let r = 0; r < rowBrks.length; r++){
+ for (let c = 0; c < rowsOfCnts[r].length; c++){
+ let nodeIdx = rowBrks[r]+c;
+ let child = node.children[nodeIdx];
+ let childX = cellXs[nodeIdx] + TILE_SPACING, childY = cellYs[r] + TILE_SPACING + headerSz,
+ childW = cellWs[nodeIdx] - TILE_SPACING, childH = cellHs[r] - TILE_SPACING;
+ if (child.children.length == 0){
+ let contentSz = Math.min(childW, childH);
+ childLyts[nodeIdx] = {
+ x: childX, y: childY, w: contentSz, h: contentSz, headerSz: 0,
+ children: [],
+ contentW: contentSz, contentH: contentSz, empSpc: childW*childH - contentSz**2,
+ };
+ } else if (child.children.every(n => n.children.length == 0)){
+ childLyts[nodeIdx] = staticSqrLayout.genLayout(child, childX, childY, childW, childH, false);
} else {
- exitLoop = true;
+ childLyts[nodeIdx] = subLayout.genLayout(child, childX, childY, childW, childH, false);
}
- break;
+ empSpc += childLyts[nodeIdx].empSpc;
}
}
- if (exitLoop)
- break;
+ //check with best-so-far
+ if (empSpc < lowestEmp){
+ lowestEmp = empSpc;
+ rowBreaks = [...rowBrks];
+ rowsOfCounts = rowsOfCnts;
+ childLayouts = childLyts;
+ }
}
//for each row, shift empty right-space to rightmost cell
let minEmpHorzTotal = Number.POSITIVE_INFINITY;
for (let r = 0; r < rowBreaks.length; r++){
- let empHorzTotal = 0;
+ let empHorzTotal = 0, leftShiftTotal = 0;
for (let c = 0; c < rowsOfCounts[r].length - 1; c++){
let nodeIdx = rowBreaks[r] + c;
- let empHorz = cellWidths[nodeIdx] - nodeDims[nodeIdx][0];
- cellWidths[nodeIdx] -= empHorz;
+ let empHorz = childLayouts[nodeIdx].w - childLayouts[nodeIdx].contentW;
+ childLayouts[nodeIdx].w -= empHorz;
empHorzTotal += empHorz;
+ childLayouts[nodeIdx+1].x -= empHorzTotal;
}
- cellWidths[rowBreaks[r] + rowsOfCounts[r].length - 1] += empHorzTotal;
+ childLayouts[rowBreaks[r] + rowsOfCounts[r].length - 1].w += empHorzTotal;
if (empHorzTotal < minEmpHorzTotal)
minEmpHorzTotal = empHorzTotal;
}
@@ -139,43 +168,24 @@ const staticRectLayout = {
let empVertTotal = 0;
for (let r = 0; r < rowBreaks.length - 1; r++){
let nodeIdxs = Array.from({length: rowsOfCounts[r].length}, (x,i) => rowBreaks[r] + i);
- let empVerts = nodeIdxs.map(idx => cellHeights[r] - nodeDims[idx][1]);
+ nodeIdxs.forEach(idx => childLayouts[idx].y -= empVertTotal);
+ let empVerts = nodeIdxs.map(idx => childLayouts[idx].h - childLayouts[idx].contentH);
let minEmpVert = Math.min(...empVerts);
- cellHeights[r] -= minEmpVert;
+ nodeIdxs.forEach(idx => childLayouts[idx].h -= minEmpVert);
empVertTotal += minEmpVert;
}
- cellHeights[rowBreaks.length - 1] += empVertTotal;
+ let lastRowIdx = rowBreaks.length-1;
+ let lastNodeIdxs = Array.from({length: rowsOfCounts[lastRowIdx].length}, (x,i) => rowBreaks[lastRowIdx] + i);
+ lastNodeIdxs.forEach(idx => childLayouts[idx].y -= empVertTotal);
+ lastNodeIdxs.map(idx => childLayouts[idx].h += empVertTotal);
//determine layout
- let cellHorzPoints = Array(cellWidths.length).fill(0);
- for (let r = 0; r < rowBreaks.length; r++){
- for (let c = 1; c < rowsOfCounts[r].length; c++){
- let nodeIdx = rowBreaks[r]+c;
- cellHorzPoints[nodeIdx] = cellHorzPoints[nodeIdx-1] + cellWidths[nodeIdx-1];
- }
- }
- let cellVertPoints = Array(cellHeights.length).fill(0);
- for (let r = 1; r < rowBreaks.length; r++){
- cellVertPoints[r] = cellVertPoints[r-1] + cellHeights[r-1];
- }
return {
- coords: Object.fromEntries(
- nodes.map((el, idx) => {
- let cellW = cellWidths[idx];
- let rowIdx = rowBreaks.findIndex((e,i) => i==rowBreaks.length-1 || rowBreaks[i+1] > idx);
- let cellH = cellHeights[rowIdx];
- let cellAR = cellW / cellH;
- return [el.tolNode.name, {
- x: x0 + this.TILE_SPACING + cellHorzPoints[idx],
- y: y0 + this.TILE_SPACING + cellVertPoints[rowIdx] + hOffset,
- w: (el.children.length == 0 ? (cellAR>1 ? cellW * 1/cellAR : cellW) : cellW) - this.TILE_SPACING,
- h: (el.children.length == 0 ? (cellAR>1 ? cellH : cellH * cellAR) : cellH) - this.TILE_SPACING
- }];
- })
- ),
+ x: x, y: y, w: w, h: h, headerSz: headerSz,
+ children: childLayouts,
contentW: w - minEmpHorzTotal,
contentH: h - empVertTotal,
- empSpc: lowestEmp
- };
+ empSpc: lowestEmp,
+ }
},
initLayoutInfo(tree){
if (tree.children.length > 0){
@@ -197,75 +207,65 @@ const staticRectLayout = {
updateLayoutInfo(tree){
if (tree.children.length == 0){
tree.tileCount = 1;
- tree.layoutRes = (w, h) => {
- let ar = w / h;
- let ar2 = 1;
- let frac = ar > ar2 ? ar2/ar : ar/ar2;
- return {
- empSpc: (1 - frac) * w * h,
- contentW: ar > ar2 ? frac * w : w,
- contentH: ar > ar2 ? h : frac * h
- };
- };
} else {
tree.tileCount = tree.children.map(e => e.tileCount).reduce((x,y) => x+y);
- if (tree.children.every(e => e.children.length == 0)){
- tree.layoutRes = (w, h) => {
- let layout = staticSqrLayout.genLayout(tree.children, 0, 0, w, h, tree.hideHeader);
- return {empSpc: layout.empSpc, contentW: layout.contentW, contentH: layout.contentH};
- };
- } else {
- tree.layoutRes = (w, h) => {
- let layout = staticRectLayout.genLayout(tree.children, 0, 0, w, h, tree.hideHeader);
- return {empSpc: layout.empSpc, contentW: layout.contentW, contentH: layout.contentH};
- };
- }
}
}
};
const sweepToSideLayout = {
- TILE_SPACING: DEFAULT_TILE_SPACING,
- HEADER_SZ: DEFAULT_HEADER_SZ,
- genLayout(nodes, x0, y0, w, h, hideHeader){
+ genLayout(node, x, y, w, h, hideHeader){
//separate leaf and non-leaf nodes
let leaves = [], nonLeaves = [];
- nodes.forEach(e => (e.children.length == 0 ? leaves : nonLeaves).push(e));
+ node.children.forEach(n => (n.children.length == 0 ? leaves : nonLeaves).push(n));
//determine layout
+ let tempTree;
if (nonLeaves.length == 0){ //if all leaves, use squares-layout
- return staticSqrLayout.genLayout(nodes, x0, y0, w, h, hideHeader);
- } else { //if some non-leaves, use rect-layout
- if (leaves.length == 0){
- return staticRectLayout.genLayout(nonLeaves, x0, y0, w, h, hideHeader);
+ return staticSqrLayout.genLayout(node, x, y, w, h, hideHeader);
+ } else if (leaves.length == 0){
+ tempTree = {tolNode: null, children: nonLeaves};
+ return staticRectLayout.genLayout(tempTree, x, y, w, h, hideHeader);
+ } else {
+ let ratio = leaves.length / (leaves.length + nonLeaves.map(e => e.tileCount).reduce((x,y) => x+y));
+ let headerSz = (hideHeader ? 0 : HEADER_SZ);
+ //get swept-area layout
+ let area = {x: x, y: y+headerSz, w: w, h: h-headerSz};
+ tempTree = {tolNode: null, children: leaves};
+ let leftLayout = staticSqrLayout.genLayout(tempTree, area.x, area.y, area.w*ratio, area.h, true);
+ let topLayout = staticSqrLayout.genLayout(tempTree, area.x, area.y, area.w, area.h*ratio, true);
+ //let sweptLayout = leftLayout;
+ let sweptLayout = (leftLayout.empSpc < topLayout.empSpc) ? leftLayout : topLayout;
+ sweptLayout.children.forEach(layout => {layout.y += headerSz});
+ //get remaining-area layout
+ let xyChg;
+ if (sweptLayout == leftLayout){
+ xyChg = [sweptLayout.contentW - TILE_SPACING, 0];
+ area.w += -sweptLayout.contentW + TILE_SPACING;
} else {
- let ratio = leaves.length / (leaves.length + nonLeaves.map(e => e.tileCount).reduce((x,y) => x+y));
- let hOffset = (hideHeader ? 0 : this.HEADER_SZ);
- //get swept-area layout
- let area = {x: x0, y: y0+hOffset, w: w, h: h-hOffset};
- let leftLayout = staticSqrLayout.genLayout(leaves, area.x, area.y, area.w*ratio, area.h, true);
- let topLayout = staticSqrLayout.genLayout(leaves, area.x, area.y, area.w, area.h*ratio, true);
- //let sweptLayout = leftLayout;
- let sweptLayout = (leftLayout.empSpc < topLayout.empSpc) ? leftLayout : topLayout;
- //get remaining-area layout
- if (sweptLayout == leftLayout){
- area.x += sweptLayout.contentW - this.TILE_SPACING;
- area.w += -sweptLayout.contentW + this.TILE_SPACING;
- } else {
- area.y += sweptLayout.contentH - this.TILE_SPACING;
- area.h += -sweptLayout.contentH + this.TILE_SPACING;
- }
- let nonLeavesLayout = staticRectLayout.genLayout(nonLeaves, area.x, area.y, area.w, area.h, true);
- //return combined layout
- return {
- coords: {...sweptLayout.coords, ...nonLeavesLayout.coords},
- contentW: (sweptLayout == leftLayout) ?
- sweptLayout.contentW + nonLeavesLayout.contentW - this.TILE_SPACING :
- Math.max(sweptLayout.contentW, nonLeavesLayout.contentW),
- contentH: (sweptLayout == leftLayout) ?
- Math.max(sweptLayout.contentH, nonLeavesLayout.contentH) :
- sweptLayout.contentH + nonLeavesLayout.contentH - this.TILE_SPACING,
- empSpc: sweptLayout.empSpc + nonLeavesLayout.empSpc
- };
+ xyChg = [0, sweptLayout.contentH - TILE_SPACING];
+ area.h += -sweptLayout.contentH + TILE_SPACING;
}
+ tempTree = {tolNode: null, children: nonLeaves}
+ let nonLeavesLayout = staticRectLayout.genLayout(
+ tempTree, area.x, area.y, area.w, area.h, true, sweepToSideLayout);
+ nonLeavesLayout.children.forEach(layout => {layout.x += xyChg[0]; layout.y += xyChg[1] + headerSz;});
+ //return combined layouts
+ let children = leaves.concat(nonLeaves);
+ let layouts = sweptLayout.children.concat(nonLeavesLayout.children);
+ let layoutsInOldOrder = [...Array(node.children.length).keys()]
+ .map(i => children.findIndex(n => n == node.children[i]))
+ .map(i => layouts[i]);
+ return {
+ x: x, y: y, w: w, h: h, headerSz: headerSz,
+ //children: [...sweptLayout.children, ...nonLeavesLayout.children],
+ children: layoutsInOldOrder,
+ contentW: (sweptLayout == leftLayout) ?
+ sweptLayout.contentW + nonLeavesLayout.contentW - TILE_SPACING :
+ Math.max(sweptLayout.contentW, nonLeavesLayout.contentW),
+ contentH: (sweptLayout == leftLayout) ?
+ Math.max(sweptLayout.contentH, nonLeavesLayout.contentH) :
+ sweptLayout.contentH + nonLeavesLayout.contentH - TILE_SPACING,
+ empSpc: sweptLayout.empSpc + nonLeavesLayout.empSpc
+ };
}
},
initLayoutInfo(tree){
@@ -288,43 +288,9 @@ const sweepToSideLayout = {
updateLayoutInfo(tree){
if (tree.children.length == 0){
tree.tileCount = 1;
- tree.layoutRes = (w, h) => {
- let ar = w / h;
- let ar2 = 1;
- let frac = ar > ar2 ? ar2/ar : ar/ar2;
- return {
- empSpc: (1 - frac) * w * h,
- contentW: ar > ar2 ? frac * w : w,
- contentH: ar > ar2 ? h : frac * h
- };
- };
} else {
tree.tileCount = tree.children.map(e => e.tileCount).reduce((x,y) => x+y);
- if (tree.children.every(e => e.children.length == 0)){
- tree.layoutRes = (w, h) => {
- let layout = staticSqrLayout.genLayout(tree.children, 0, 0, w, h, tree.hideHeader);
- return {empSpc: layout.empSpc, contentW: layout.contentW, contentH: layout.contentH};
- }
- } else {
- tree.layoutRes = (w, h) => {
- let layout = sweepToSideLayout.genLayout(tree.children, 0, 0, w, h, tree.hideHeader);
- return {empSpc: layout.empSpc, contentW: layout.contentW, contentH: layout.contentH};
- }
- }
}
}
};
-let defaultLayout = sweepToSideLayout;
-function initTree(tol, lvl, layout = defaultLayout){
- let tree = {tolNode: tol, children: []};
- initTreeRec(tree, lvl);
- layout.initLayoutInfo(tree)
- return tree;
-}
-function initTreeRec(tree, lvl){
- if (lvl > 0){
- tree.children = tree.tolNode.children.map(e => initTreeRec({tolNode: e, children: []}, lvl-1));
- }
- return tree;
-}