diff options
| -rw-r--r-- | src/components/Tile.vue | 8 | ||||
| -rw-r--r-- | src/components/TileTree.vue | 14 | ||||
| -rw-r--r-- | src/layout.ts | 201 | ||||
| -rw-r--r-- | src/types.ts | 82 |
4 files changed, 158 insertions, 147 deletions
diff --git a/src/components/Tile.vue b/src/components/Tile.vue index 19c9838..2c349c1 100644 --- a/src/components/Tile.vue +++ b/src/components/Tile.vue @@ -46,7 +46,7 @@ export default defineComponent({ <template> <div :style="{position: 'absolute', - left: tree.x+'px', top: tree.y+'px', width: tree.w+'px', height: tree.h+'px', + left: tree.pos.x+'px', top: tree.pos.y+'px', width: tree.dims.w+'px', height: tree.dims.h+'px', zIndex: zIdx, overflow: overFlow, transitionDuration: transitionDuration+'ms'}" class="transition-[left,top,width,height] ease-out border border-stone-900 bg-white"> <div v-if="tree.children.length == 0" @@ -61,9 +61,9 @@ export default defineComponent({ {{tree.tolNode.name}} </div> <div v-if="tree.sideArea" - :style="{position: 'absolute', left: tree.sideArea.x+'px', top: tree.sideArea.y+'px', - width: (tree.sideArea.w + (tree.sideArea.sweptLeft ? tree.sideArea.extraSz : 0))+'px', - height: (tree.sideArea.h + (tree.sideArea.sweptLeft ? 0 : tree.sideArea.extraSz))+'px', + :style="{position: 'absolute', left: tree.sideArea.pos.x+'px', top: tree.sideArea.pos.y+'px', + width: (tree.sideArea.dims.w + (tree.sideArea.sweptLeft ? tree.sideArea.extraSz : 0))+'px', + height: (tree.sideArea.dims.h + (tree.sideArea.sweptLeft ? 0 : tree.sideArea.extraSz))+'px', borderRightColor: (tree.sideArea.sweptLeft ? 'white' : 'currentColor'), borderBottomColor: (tree.sideArea.sweptLeft ? 'currentColor' : 'white'), transitionDuration: transitionDuration+'ms'}" diff --git a/src/components/TileTree.vue b/src/components/TileTree.vue index ce2688e..9fc053a 100644 --- a/src/components/TileTree.vue +++ b/src/components/TileTree.vue @@ -2,7 +2,7 @@ import {defineComponent} from 'vue'; import Tile from './Tile.vue'; -import {TolNode, TreeNode, LayoutNode} from '../types'; +import {TolNode, TreeNode, LayoutNode, Coord, Dims} from '../types'; import {genLayout, layoutInfoHooks} from '../layout'; //regarding importing a file f1.ts: //using 'import f1.ts' makes vue-tsc complain, and 'import f1.js' makes vite complain @@ -72,7 +72,7 @@ export default defineComponent({ nodeList[0].children = children; }, tryLayout(){ - let layout = genLayout(this.tree, 0, 0, this.width, this.height, true); + let layout = genLayout(this.tree, new Coord(), new Dims(this.width, this.height), true); if (layout == null){ console.log('Unable to layout tree'); return false; @@ -82,10 +82,8 @@ export default defineComponent({ } }, applyLayout(layout: LayoutNode, tree: TreeNode){ - tree.x = layout.x; - tree.y = layout.y; - tree.w = layout.w; - tree.h = layout.h; + tree.pos = layout.pos; + tree.dims = layout.dims; tree.headerSz = layout.headerSz; layout.children.forEach((n,i) => this.applyLayout(n, tree.children[i])); //handle case where leaf nodes placed in leftover space from parent-sweep @@ -94,8 +92,8 @@ export default defineComponent({ tree.sideArea = layout.sideArea; //move leaf node children to parent area tree.children.filter(n => n.children.length == 0).map(n => { - n.x += layout.sideArea!.x; - n.y += layout.sideArea!.y; + n.pos.x += layout.sideArea!.pos.x; + n.pos.y += layout.sideArea!.pos.y; }); } else { tree.sideArea = null; diff --git a/src/layout.ts b/src/layout.ts index e82cb78..de77a69 100644 --- a/src/layout.ts +++ b/src/layout.ts @@ -1,7 +1,7 @@ -import {TolNode, TreeNode, LayoutNode, SideArea, LeftoverArea} from './types'; +import {TolNode, TreeNode, LayoutNode, Coord, Dims, SideArea, LeftoverArea} from './types'; export {genLayout, layoutInfoHooks}; -type LayoutFn = (node: TreeNode, x: number, y: number, w: number, h: number, hideHeader: boolean, +type LayoutFn = (node: TreeNode, pos: Coord, dims: Dims, hideHeader?: boolean, options?: {subLayoutFn?: LayoutFn, extraArea?: LeftoverArea | null}) => LayoutNode | null; let TILE_SPACING = 5; @@ -43,24 +43,24 @@ const layoutInfoHooks = { //made common-across-layout-types for layout inter-usa } //lays out nodes as squares in a rectangle, with spacing -let sqrLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader){ +let sqrLayoutFn: LayoutFn = function (node, pos, dims, hideHeader=false){ //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; - if (availW*availH <= 0) + let availDims = new Dims(dims.w - TILE_SPACING, dims.h - headerSz - TILE_SPACING); + if (availDims.area() <= 0) return null; - let numChildren = node.children.length, ar = availW/availH; + let numChildren = node.children.length, ar = availDims.aspectRatio(); let lowestEmp = Number.POSITIVE_INFINITY, numCols = 0, numRows = 0, tileSize = 0; 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; - let tileSz = ar > ar2 ? availH/nr-TILE_SPACING : availW/nc-TILE_SPACING; + let tileSz = ar > ar2 ? availDims.h/nr-TILE_SPACING : availDims.w/nc-TILE_SPACING; if (tileSz < MIN_TILE_SZ) continue; else if (tileSz > MAX_TILE_SZ) tileSz = MAX_TILE_SZ; - let empSpc = (1-frac)*availW*availH + (nc*nr-numChildren)*(tileSz - TILE_SPACING)**2; + let empSpc = (1-frac)*availDims.area() + (nc*nr-numChildren)*(tileSz - TILE_SPACING)**2; if (empSpc < lowestEmp){ lowestEmp = empSpc; numCols = nc; @@ -72,33 +72,33 @@ let sqrLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader){ return null; let childLayouts = arrayOf(0, numChildren); for (let i = 0; i < numChildren; i++){ - let child = node.children[i]; let childX = TILE_SPACING + (i % numCols)*(tileSize + TILE_SPACING); let childY = TILE_SPACING + headerSz + Math.floor(i / numCols)*(tileSize + TILE_SPACING); - if (child.children.length == 0){ - childLayouts[i] = new LayoutNode(child.tolNode.name, [], childX, childY, tileSize, tileSize, - {headerSz: 0, contentW: tileSize, contentH: tileSize, empSpc: 0}); + if (node.children[i].children.length == 0){ + childLayouts[i] = new LayoutNode(node.children[i].tolNode.name, [], + new Coord(childX, childY), new Dims(tileSize), 0, {contentDims: new Dims(tileSize), empSpc: 0}); } else { - childLayouts[i] = sqrLayoutFn(child, childX, childY, tileSize, tileSize, false); + childLayouts[i] = sqrLayoutFn(node.children[i], new Coord(childX, childY), new Dims(tileSize)); if (childLayouts[i] == null) return null; lowestEmp += childLayouts[i].empSpc; } } - return new LayoutNode(node.tolNode.name, childLayouts, x, y, w, h, { - headerSz, - contentW: numCols * (tileSize + TILE_SPACING) + TILE_SPACING, - contentH: numRows * (tileSize + TILE_SPACING) + TILE_SPACING + headerSz, + return new LayoutNode(node.tolNode.name, childLayouts, pos, dims, headerSz, { + contentDims: new Dims( + numCols * (tileSize + TILE_SPACING) + TILE_SPACING, + numRows * (tileSize + TILE_SPACING) + TILE_SPACING + headerSz + ), empSpc: lowestEmp, }); } //lays out nodes as rectangles organised into rows, partially using other layouts for children -let rectLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, options={subLayoutFn: rectLayoutFn}){ +let rectLayoutFn: LayoutFn = function (node, pos, dims, hideHeader=false, options={subLayoutFn: rectLayoutFn}){ if (node.children.every(n => n.children.length == 0)) - return sqrLayoutFn(node, x, y, w, h, hideHeader); + return sqrLayoutFn(node, pos, dims, 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 availDims = new Dims(dims.w - TILE_SPACING, dims.h - TILE_SPACING - headerSz); let numChildren = node.children.length; let rowBrks: number[]|null = null; //will holds node indices at which each row starts let lowestEmp = Number.POSITIVE_INFINITY, rowBreaks = null, childLayouts = null; @@ -147,12 +147,12 @@ let rectLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, options={su } //get cell dims 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 cellHs = rowsOfCnts.map(row => row.reduce((x,y) => x+y) / totalTileCount * availDims.h); let cellWs = arrayOf(0, numChildren); for (let r = 0; r < rowsOfCnts.length; r++){ let rowCount = rowsOfCnts[r].reduce((x,y) => x+y); for (let c = 0; c < rowsOfCnts[r].length; c++){ - cellWs[rowBrks[r]+c] = rowsOfCnts[r][c] / rowCount * availW; + cellWs[rowBrks[r]+c] = rowsOfCnts[r][c] / rowCount * availDims.w; } } //impose min-tile-size @@ -190,21 +190,22 @@ let rectLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, options={su childW = cellWs[nodeIdx] - TILE_SPACING, childH = cellHs[r] - TILE_SPACING; if (child.children.length == 0){ let contentSz = Math.min(childW, childH); - childLyts[nodeIdx] = new LayoutNode(child.tolNode.name, [], childX, childY, childW, childH, - {headerSz: 0, contentW: contentSz, contentH: contentSz, empSpc: childW*childH - contentSz**2}); + childLyts[nodeIdx] = new LayoutNode( + child.tolNode.name, [], new Coord(childX, childY), new Dims(childW, childH), 0, + {contentDims: new Dims(contentSz), empSpc: childW*childH - contentSz**2}); } else if (child.children.every(n => n.children.length == 0)){ - childLyts[nodeIdx] = sqrLayoutFn(child, childX, childY, childW, childH, false); + childLyts[nodeIdx] = sqrLayoutFn(child, new Coord(childX, childY), new Dims(childW, childH)); } else { let layoutFn = (options && options.subLayoutFn) || rectLayoutFn; - childLyts[nodeIdx] = layoutFn(child, childX, childY, childW, childH, false); + childLyts[nodeIdx] = layoutFn(child, new Coord(childX, childY), new Dims(childW, childH)); } if (childLyts[nodeIdx] == null) continue rowBrksLoop; //handle horizontal empty-space-shifting if (RECT_SPC_SHIFTING){ - let empHorz = childLyts[nodeIdx].w - childLyts[nodeIdx].contentW; - childLyts[nodeIdx].w -= empHorz; - childLyts[nodeIdx].empSpc -= empHorz * childLyts[nodeIdx].h; + let empHorz = childLyts[nodeIdx].dims.w - childLyts[nodeIdx].contentDims.w; + childLyts[nodeIdx].dims.w -= empHorz; + childLyts[nodeIdx].empSpc -= empHorz * childLyts[nodeIdx].dims.h; if (c < rowsOfCnts[r].length-1){ cellXs[nodeIdx+1] -= empHorz; cellWs[nodeIdx+1] += empHorz; @@ -216,11 +217,11 @@ let rectLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, options={su //handle vertical empty-space-shifting if (RECT_SPC_SHIFTING){ let nodeIdxs = seq(rowsOfCnts[r].length).map(i => rowBrks![r]+i); - let empVerts = nodeIdxs.map(idx => childLyts[idx].h - childLyts[idx].contentH); + let empVerts = nodeIdxs.map(idx => childLyts[idx].dims.h - childLyts[idx].contentDims.h); let minEmpVert = Math.min(...empVerts); nodeIdxs.forEach(idx => { - childLyts[idx].h -= minEmpVert; - childLyts[idx].empSpc -= minEmpVert * childLyts[idx].w; + childLyts[idx].dims.h -= minEmpVert; + childLyts[idx].empSpc -= minEmpVert * childLyts[idx].dims.w; }); if (r < rowBrks.length-1){ cellYs[r+1] -= minEmpVert; @@ -230,7 +231,7 @@ let rectLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, options={su } } //other updates - empSpc += empHorzTotal * childLyts[rowBrks[r]].h; + empSpc += empHorzTotal * childLyts[rowBrks[r]].dims.h; } //get empty-space for (let r = 0; r < rowBrks.length; r++){ @@ -238,7 +239,7 @@ let rectLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, options={su empSpc += childLyts[rowBrks[r]+c].empSpc; } } - empSpc += empVTotal * availW; + empSpc += empVTotal * availDims.w; //check with best-so-far if (empSpc < lowestEmp){ lowestEmp = empSpc; @@ -249,24 +250,26 @@ let rectLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, options={su if (rowBreaks == null || childLayouts == null) //redundant hinting for tsc return null; //make no-child tiles have width/height fitting their content - childLayouts.filter(l => l.children.length == 0).forEach(l => {l.w = l.contentW; l.h = l.contentH;}); + childLayouts.filter(l => l.children.length == 0).forEach(l => { + l.dims.w = l.contentDims.w; + l.dims.h = l.contentDims.h; + }); //determine layout - return new LayoutNode(node.tolNode.name, childLayouts, x, y, w, h, - {headerSz, contentW: w, contentH: h, empSpc: lowestEmp}); - //trying to shrink contentW and contentH causes problems with swept-to-parent-area div-alignment + return new LayoutNode(node.tolNode.name, childLayouts, pos, dims, headerSz, {contentDims: dims, empSpc: lowestEmp}); + //trying to shrink contentDims causes problems with swept-to-parent-area div-alignment } //lays out nodes by pushing leaves to one side, partially using other layouts for children -let sweepLeavesLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, options={extraArea: null}){ +let sweepLeavesLayoutFn: LayoutFn = function (node, pos, dims, hideHeader=false, options={extraArea: null}){ //separate leaf and non-leaf nodes let leaves: TreeNode[] = [], nonLeaves: TreeNode[] = []; node.children.forEach(n => (n.children.length == 0 ? leaves : nonLeaves).push(n)); //determine layout let tempTree: TreeNode; if (nonLeaves.length == 0){ //if all leaves, use squares-layout - return sqrLayoutFn(node, x, y, w, h, hideHeader); + return sqrLayoutFn(node, pos, dims, hideHeader); } else if (leaves.length == 0){ tempTree = new TreeNode(new TolNode('SWEEP_REM_' + node.tolNode.name), nonLeaves); - return rectLayoutFn(tempTree, x, y, w, h, hideHeader, {subLayoutFn: sweepLeavesLayoutFn}); + return rectLayoutFn(tempTree, pos, dims, hideHeader, {subLayoutFn: sweepLeavesLayoutFn}); } else { let ratio = leaves.length / (leaves.length + nonLeaves.map(e => e.tileCount).reduce((x,y) => x+y)); let headerSz = (hideHeader ? 0 : HEADER_SZ); @@ -276,52 +279,57 @@ let sweepLeavesLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, opti if (ALLOW_SWEEP_TO_PARENT && parentArea){ tempTree = new TreeNode(new TolNode('SWEEP_' + node.tolNode.name), leaves); sweptLeft = parentArea.sweptLeft; - sweptLayout = sqrLayoutFn(tempTree, 0, 0, parentArea.w, parentArea.h, sweptLeft); + sweptLayout = sqrLayoutFn(tempTree, new Coord(), parentArea.dims, sweptLeft); if (sweptLayout != null){ - let area = {x: x, y: y+headerSz, w: w, h: h-headerSz}; - if (!sweptLeft){ //no remaining-area header if swept-upward - area.y = y; area.h = h; - } + let newDims = new Dims(dims.w, dims.h-headerSz); + if (!sweptLeft) //no remaining-area header if swept-upward + newDims.h = dims.h; //get remaining-area layout tempTree = new TreeNode(new TolNode('SWEEP_REM_' + node.tolNode.name), nonLeaves); if (nonLeaves.length > 1){ - nonLeavesLayout = rectLayoutFn(tempTree, 0, 0, area.w, area.h, true, + nonLeavesLayout = rectLayoutFn(tempTree, new Coord(), newDims, true, {subLayoutFn: sweepLeavesLayoutFn}); } else { //get leftover swept-layout-area to propagate - let leftOverWidth = parentArea.w - sweptLayout.contentW; - let leftOverHeight = parentArea.h - sweptLayout.contentH; + let leftOverWidth = parentArea.dims.w - sweptLayout.contentDims.w; + let leftOverHeight = parentArea.dims.h - sweptLayout.contentDims.h; leftoverArea = sweptLeft ? - {...parentArea, parentY:parentArea.parentY+sweptLayout.contentH-TILE_SPACING-headerSz, - h:leftOverHeight-TILE_SPACING} : - {...parentArea, - parentX:parentArea.parentX+sweptLayout.contentW-TILE_SPACING, - parentY:parentArea.parentY + headerSz, - w:leftOverWidth-TILE_SPACING, h:parentArea.h - headerSz}; + new LeftoverArea( + new Coord(parentArea.parentPos.x, + parentArea.parentPos.y+sweptLayout.contentDims.h-TILE_SPACING-headerSz), + new Dims(parentArea.dims.w, leftOverHeight-TILE_SPACING), + sweptLeft + ) : + new LeftoverArea( + new Coord(parentArea.parentPos.x+sweptLayout.contentDims.w-TILE_SPACING, + parentArea.parentPos.y + headerSz), + new Dims(leftOverWidth-TILE_SPACING, parentArea.dims.h - headerSz), + sweptLeft + ); //call genLayout nonLeavesLayout = rectLayoutFn( - tempTree, 0, 0, area.w, area.h, true, - {subLayoutFn: (n,x,y,w,h,hh) => sweepLeavesLayoutFn(n,x,y,w,h,hh,{extraArea: leftoverArea})}); + tempTree, new Coord(), newDims, true, + {subLayoutFn: (n,p,d,h) => sweepLeavesLayoutFn(n,p,d,h,{extraArea:leftoverArea})}); } if (nonLeavesLayout != null){ - nonLeavesLayout.children.forEach(layout => {layout.y += (sweptLeft ? headerSz : 0)}); + nonLeavesLayout.children.forEach(layout => {layout.pos.y += (sweptLeft ? headerSz : 0)}); usingParentArea = true; } } } if (!usingParentArea){ - let area = {x: x, y: y+headerSz, w: w, h: h-headerSz}; + let newDims = new Dims(dims.w, dims.h-headerSz); tempTree = new TreeNode(new TolNode('SWEEP_' + node.tolNode.name), leaves); - let xyChg: [number, number]; + let xyChg = new Coord(); //get swept-area layout let leftLayout = null, topLayout = null; let documentAR = document.documentElement.clientWidth / document.documentElement.clientHeight; if (SWEEP_MODE == 'left' || (SWEEP_MODE == 'shorter' && documentAR >= 1) || SWEEP_MODE == 'auto'){ - leftLayout = sqrLayoutFn(tempTree, 0, 0, - Math.max(area.w*ratio, MIN_TILE_SZ+TILE_SPACING*2), area.h, true); + leftLayout = sqrLayoutFn(tempTree, new Coord(), + new Dims(Math.max(newDims.w*ratio, MIN_TILE_SZ+TILE_SPACING*2), newDims.h), true); } else if (SWEEP_MODE == 'top' || (SWEEP_MODE == 'shorter' && documentAR < 1) || SWEEP_MODE == 'auto'){ - topLayout = sqrLayoutFn(tempTree, 0, 0, - area.w, Math.max(area.h*ratio, MIN_TILE_SZ+TILE_SPACING*2), true); + topLayout = sqrLayoutFn(tempTree, new Coord(), + new Dims(newDims.w, Math.max(newDims.h*ratio, MIN_TILE_SZ+TILE_SPACING*2)), true); } if (SWEEP_MODE == 'auto'){ sweptLayout = @@ -333,43 +341,46 @@ let sweepLeavesLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, opti sweptLeft = (sweptLayout == leftLayout); if (sweptLayout == null) return null; - sweptLayout.children.forEach(layout => {layout.y += headerSz}); + sweptLayout.children.forEach(layout => {layout.pos.y += headerSz}); //get remaining-area layout if (sweptLeft){ - xyChg = [sweptLayout.contentW - TILE_SPACING, 0]; - area.w += -sweptLayout.contentW + TILE_SPACING; + xyChg.x = sweptLayout.contentDims.w - TILE_SPACING; + newDims.w += -sweptLayout.contentDims.w + TILE_SPACING; } else { - xyChg = [0, sweptLayout.contentH - TILE_SPACING]; - area.h += -sweptLayout.contentH + TILE_SPACING; + xyChg.y = sweptLayout.contentDims.h - TILE_SPACING; + newDims.h += -sweptLayout.contentDims.h + TILE_SPACING; } tempTree = new TreeNode(new TolNode('SWEEP_REM_' + node.tolNode.name), nonLeaves); if (nonLeaves.length > 1){ - nonLeavesLayout = rectLayoutFn(tempTree, 0, 0, area.w, area.h, true, {subLayoutFn: sweepLeavesLayoutFn}); + nonLeavesLayout = rectLayoutFn(tempTree, new Coord(), newDims, true, + {subLayoutFn: sweepLeavesLayoutFn}); } else { //get leftover swept-layout-area to propagate if (sweptLeft){ - leftoverArea = new LeftoverArea( //parentX and parentY are relative to the non-leaves-area - -sweptLayout.contentW + TILE_SPACING, sweptLayout.contentH - TILE_SPACING, - sweptLayout.contentW - TILE_SPACING*2, area.h-sweptLayout.contentH - TILE_SPACING, + leftoverArea = new LeftoverArea( + new Coord(-sweptLayout.contentDims.w + TILE_SPACING, sweptLayout.contentDims.h - TILE_SPACING), + new Dims(sweptLayout.contentDims.w - TILE_SPACING*2, + newDims.h-sweptLayout.contentDims.h - TILE_SPACING), sweptLeft ); } else { leftoverArea = new LeftoverArea( - sweptLayout.contentW - TILE_SPACING, -sweptLayout.contentH + TILE_SPACING, - area.w-sweptLayout.contentW - TILE_SPACING, sweptLayout.contentH - TILE_SPACING*2, + new Coord(sweptLayout.contentDims.w-TILE_SPACING, -sweptLayout.contentDims.h + TILE_SPACING), + new Dims(newDims.w-sweptLayout.contentDims.w-TILE_SPACING, + sweptLayout.contentDims.h-TILE_SPACING*2), sweptLeft ); } - leftoverArea.w = Math.max(0, leftoverArea.w); - leftoverArea.h = Math.max(0, leftoverArea.h); + leftoverArea.dims.w = Math.max(0, leftoverArea.dims.w); + leftoverArea.dims.h = Math.max(0, leftoverArea.dims.h); //call genLayout nonLeavesLayout = rectLayoutFn( - tempTree, 0, 0, area.w, area.h, true, - {subLayoutFn: (n,x,y,w,h,hh) => sweepLeavesLayoutFn(n,x,y,w,h,hh,{extraArea: leftoverArea})}); + tempTree, new Coord(), newDims, true, + {subLayoutFn: (n,p,d,h) => sweepLeavesLayoutFn(n,p,d,h,{extraArea: leftoverArea})}); } if (nonLeavesLayout == null) return null; - nonLeavesLayout.children.forEach(layout => {layout.x += xyChg[0]; layout.y += xyChg[1] + headerSz;}); + nonLeavesLayout.children.forEach(layout => {layout.pos.x += xyChg.x; layout.pos.y += xyChg.y + headerSz;}); } if (sweptLayout == null || nonLeavesLayout == null) //hint for tsc return null; @@ -379,28 +390,26 @@ let sweepLeavesLayoutFn: LayoutFn = function (node, x, y, w, h, hideHeader, opti let layoutsInOldOrder = seq(node.children.length) .map(i => children.findIndex(n => n == node.children[i])) .map(i => layouts[i]); - return new LayoutNode(node.tolNode.name, layoutsInOldOrder, x, y, w, h, { - headerSz, - contentW: usingParentArea ? nonLeavesLayout.contentW : (sweptLeft ? - sweptLayout.contentW + nonLeavesLayout.contentW - TILE_SPACING : - Math.max(sweptLayout.contentW, nonLeavesLayout.contentW)), - contentH: usingParentArea ? nonLeavesLayout.contentH + headerSz : (sweptLeft ? - Math.max(sweptLayout.contentH, nonLeavesLayout.contentH) + headerSz : - sweptLayout.contentH + nonLeavesLayout.contentH - TILE_SPACING + headerSz), + return new LayoutNode(node.tolNode.name, layoutsInOldOrder, pos, dims, headerSz, { + contentDims: new Dims( + usingParentArea ? nonLeavesLayout.contentDims.w : (sweptLeft ? + sweptLayout.contentDims.w + nonLeavesLayout.contentDims.w - TILE_SPACING : + Math.max(sweptLayout.contentDims.w, nonLeavesLayout.contentDims.w)), + usingParentArea ? nonLeavesLayout.contentDims.h + headerSz : (sweptLeft ? + Math.max(sweptLayout.contentDims.h, nonLeavesLayout.contentDims.h) + headerSz : + sweptLayout.contentDims.h + nonLeavesLayout.contentDims.h - TILE_SPACING + headerSz), + ), empSpc: sweptLayout.empSpc + nonLeavesLayout.empSpc, sideArea: (usingParentArea && parentArea != null) ? - { - x: parentArea.parentX, y: parentArea.parentY, - w: parentArea.w, h: parentArea.h, - sweptLeft: sweptLeft, extraSz: TILE_SPACING+1, - }: - null, + new SideArea(new Coord(parentArea.parentPos.x, parentArea.parentPos.y), parentArea.dims, + sweptLeft, TILE_SPACING+1) + : null, }); } } //default layout function -let genLayout: LayoutFn = function (node, x, y, w, h, hideHeader){ - return sweepLeavesLayoutFn(node, x, y, w, h, hideHeader); +let genLayout: LayoutFn = function (node, pos, dims, hideHeader){ + return sweepLeavesLayoutFn(node, pos, dims, hideHeader); } //clips values in array to within [min,max], and redistributes to compensate, returning null if unable diff --git a/src/types.ts b/src/types.ts index 82a8bb9..527a8c8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,21 +9,17 @@ export class TolNode { export class TreeNode { tolNode: TolNode; children: TreeNode[]; - x: number; - y: number; - w: number; - h: number; + pos: Coord; + dims: Dims; headerSz: number; sideArea: SideArea | null; tileCount: number; - constructor(tolNode: TolNode, children: TreeNode[], x=0, y=0, w=0, h=0, - {headerSz=0, sideArea=null, tileCount=1} = {}){ + constructor(tolNode: TolNode, children: TreeNode[], pos=new Coord(0,0), dims=new Dims(0,0), headerSz=0, + {sideArea=null, tileCount=1} = {}){ this.tolNode = tolNode; this.children = children; - this.x = x; - this.y = y; - this.w = w; - this.h = h; + this.pos = pos; + this.dims = dims; this.headerSz = headerSz; this.sideArea = sideArea; this.tileCount = tileCount; @@ -32,57 +28,65 @@ export class TreeNode { export class LayoutNode { name: string; children: LayoutNode[]; - x: number; - y: number; - w: number; - h: number; + pos: Coord; + dims: Dims; headerSz: number; - contentW: number; - contentH: number; + contentDims: Dims; empSpc: number; sideArea: SideArea | null; - constructor(name: string, children: LayoutNode[], x=0, y=0, w=0, h=0, - {headerSz=0, contentW=0, contentH=0, empSpc=0, sideArea=null as SideArea|null} = {}){ + constructor(name: string, children: LayoutNode[], pos=new Coord(0,0), dims=new Dims(0,0), headerSz=0, + {contentDims=new Dims(0,0), empSpc=0, sideArea=null as SideArea|null} = {}){ this.name = name; - this.x = x; - this.y = y; - this.w = w; - this.h = h; + this.pos = pos; + this.dims = dims; this.headerSz = headerSz; this.children = children; - this.contentW = contentW; - this.contentH = contentH; + this.contentDims = contentDims; this.empSpc = empSpc; this.sideArea = sideArea; } } -export class SideArea { +export class Coord { x: number; y: number; + constructor(x=0, y=0){ + this.x = x; + this.y = y; + } +} +export class Dims { w: number; h: number; + constructor(w: number, h?: number){ + this.w = w; + this.h = h === undefined ? w : h; + } + area(){ + return this.w * this.h; + } + aspectRatio(){ + return this.w / this.h; + } +} +export class SideArea { + pos: Coord; + dims: Dims; sweptLeft: boolean; extraSz: number; - constructor(x: number, y: number, w: number, h: number, sweptLeft: boolean, extraSz: number){ - this.x = x; - this.y = y; - this.w = w; - this.h = h; + constructor(pos: Coord, dims: Dims, sweptLeft: boolean, extraSz: number){ + this.pos = pos; + this.dims = dims; this.sweptLeft = sweptLeft; this.extraSz = extraSz; } } export class LeftoverArea { - parentX: number; - parentY: number; - w: number; - h: number; + parentPos: Coord; + dims: Dims; sweptLeft: boolean; - constructor(parentX: number, parentY: number, w: number, h: number, sweptLeft: boolean){ - this.parentX = parentX; - this.parentY = parentY; - this.w = w; - this.h = h; + constructor(parentPos: Coord, dims: Dims, sweptLeft: boolean){ + this.parentPos = parentPos; + this.dims = dims; this.sweptLeft = sweptLeft; } } |
