From 0442b3c5c861c2096fb3d7f74fb77073660651f1 Mon Sep 17 00:00:00 2001 From: Terry Truong Date: Fri, 4 Mar 2022 01:34:30 +1100 Subject: Improve sweep-to-side layout --- src/App.vue | 5 +-- src/components/TileTree.vue | 86 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 71 insertions(+), 20 deletions(-) diff --git a/src/App.vue b/src/App.vue index b1efd4b..26ae610 100644 --- a/src/App.vue +++ b/src/App.vue @@ -14,8 +14,9 @@ import TileTree from "./components/TileTree.vue"; export default { data() { return { - //tree: tol.map(e => ({...e, children:[]})), - tree: tol, + //tree: {...tol, children:[]}, + tree: {...tol, children:tol.children.map(e => ({...e, children:[]}))}, + //tree: tol, width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, } diff --git a/src/components/TileTree.vue b/src/components/TileTree.vue index 46da28e..8170ad7 100644 --- a/src/components/TileTree.vue +++ b/src/components/TileTree.vue @@ -26,13 +26,27 @@ export default { } }, methods: { + //determines layout for squares in a specified rectangle, with spacing basicSquaresLayout(nodes, x0, y0, w, h){ - //determine layout for squares in a specified rectangle, with spacing - let numCols = this.pickNumCols(nodes.length, w/h); - let numRows = Math.ceil(nodes.length / numCols); + //get number-of-columns with highest occupied-fraction of rectangles with aspect-ratio w/h + //account for tile-spacing? + let numTiles = nodes.length, ar = w/h; + let numCols, numRows, bestFrac = 0; + for (let nc = 1; nc <= numTiles; nc++){ + let nr = Math.ceil(numTiles/nc); + let ar2 = nc/nr; + let frac = ar > ar2 ? ar2/ar : ar/ar2; + if (frac > bestFrac){ + bestFrac = frac; + numCols = nc; + numRows = nr; + } + } + //compute other parameters let tileSz = Math.min( ((w - this.TILE_SPACING) / numCols) - this.TILE_SPACING, ((h - this.TILE_SPACING) / numRows) - this.TILE_SPACING); + //determine layout return Object.fromEntries( nodes.map((el, idx) => [el.name, { x: x0 + (idx % numCols)*(tileSz + this.TILE_SPACING) + this.TILE_SPACING, @@ -42,19 +56,54 @@ export default { }]) ); }, - pickNumCols(numTiles, aspectRatio){ //account for tile-spacing? - //look for number of columns with highest occupied-fraction of rectangles with aspectRatio - let bestNum, bestFrac = 0; - for (let numCols = 1; numCols <= numTiles; numCols++){ - let numRows = Math.ceil(numTiles/numCols); - let ar = numCols/numRows; - let frac = aspectRatio > ar ? ar/aspectRatio : aspectRatio/ar; - if (frac > bestFrac){ - bestFrac = frac; - bestNum = numCols; + basicRectsLayout(nodes, x0, y0, w, h){ + //get number-of-columns with highest tileCount-proportions-alignment + let numTiles = nodes.length; + let tileCounts = nodes.map(e => e.tileCount); + let tileCountTotal = tileCounts.reduce((x, y) => x+y); + let numCols, bestScore = Number.NEGATIVE_INFINITY, rowProportions, colProportions; + for (let nc = 1; nc <= numTiles; nc++){ + let nr = Math.ceil(numTiles/nc); + //create grid representing each tile's tileCount (0 for no tile) + let grid = Array(nr).fill().map(e => Array(nc).fill(0)); + for (let i = 0; i < tileCounts.length; i++){ + grid[Math.floor(i / nc)][i % nc] = tileCounts[i]; } + //get totals across each row/column divided by tileCountTotal + let rowProp = grid.map(e => e.reduce((x, y) => x+y) / tileCountTotal); + let colProp = [...Array(nc).keys()].map(c => + [...Array(nr).keys()].map(r => grid[r][c]).reduce((x,y) => x+y) / tileCountTotal); + //get score + let score = 0; + for (let r = 0; r < nr; r++){ + for (let c = 0; c < nc; c++){ + score -= Math.abs(grid[r][c] - (rowProp[r] * colProp[c])); + } + } + if (score > bestScore){ + bestScore = score; + numCols = nc; + rowProportions = rowProp; + colProportions = colProp; + } + } + //determine layout + let rowNetProps = [0]; + for (let i = 0; i < rowProportions.length-1; i++){ + rowNetProps.push(rowNetProps[i] + rowProportions[i]); + } + let colNetProps = [0]; + for (let i = 0; i < colProportions.length-1; i++){ + colNetProps.push(colNetProps[i] + colProportions[i]); } - return bestNum; + let retVal = Object.fromEntries( + nodes.map((el, idx) => [el.name, { + x: x0 + colNetProps[idx % numCols]*(w - this.TILE_SPACING) + this.TILE_SPACING, + y: y0 + rowNetProps[Math.floor(idx / numCols)]*(h - this.TILE_SPACING) + this.TILE_SPACING, + w: colProportions[idx % numCols]*(w - this.TILE_SPACING) - this.TILE_SPACING, + h: rowProportions[Math.floor(idx / numCols)]*(h - this.TILE_SPACING) - this.TILE_SPACING + }])); + return retVal; }, sweepToSideLayout(nodes, x0, y0, w, h){ //separate leaf and non-leaf nodes @@ -68,10 +117,11 @@ export default { if (leaves.length > 0){ let ratio = leaves.length / this.tree.tileCount; retVal = this.basicSquaresLayout(leaves, x0, y0, w*ratio, h); - x0 += w*ratio; - w -= w*ratio; + x0 += w*ratio - this.TILE_SPACING; + w -= (w*ratio - this.TILE_SPACING); } - return {...retVal, ...this.basicSquaresLayout(nonLeaves, x0, y0, w, h)}; + //return {...retVal, ...this.basicSquaresLayout(nonLeaves, x0, y0, w, h)}; + return {...retVal, ...this.basicRectsLayout(nonLeaves, x0, y0, w, h)}; } } } @@ -82,7 +132,7 @@ export default {
{{tree.name}}
-
-- cgit v1.2.3