-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
297 additions
and
1 deletion.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
import { createViews } from '../../src/view/view'; | ||
import { createRenderer } from '../../src/renderer/renderer'; | ||
import { mount, createDiv } from '../utils'; | ||
|
||
function renderViews(views, width = 640, height = 480) { | ||
const renderer = createRenderer(width, height); | ||
mount(createDiv(), renderer.node()); | ||
return views.map(([{ | ||
x, y, width, height, | ||
}]) => renderer.rect({ | ||
x, y, width, height, stroke: 'black', fill: 'none', | ||
})); | ||
} | ||
|
||
describe('createViews', () => { | ||
test('basic container', () => { | ||
const views = createViews({}); | ||
renderViews(views); | ||
|
||
expect(views.length).toBe(1); | ||
const [[view, [node]]] = views; | ||
expect(view).toEqual({ | ||
x: 0, y: 0, width: 640, height: 480, | ||
}); | ||
expect(node).toEqual({}); | ||
}); | ||
|
||
test('layer container', () => { | ||
const views = createViews({ | ||
type: 'layer', | ||
children: [{}, {}], | ||
}); | ||
renderViews(views); | ||
|
||
expect(views.length).toBe(1); | ||
const [[view, nodes]] = views; | ||
expect(view).toEqual({ | ||
x: 0, y: 0, width: 640, height: 480, | ||
}); | ||
expect(nodes.length).toBe(3); | ||
}); | ||
|
||
test('row container', () => { | ||
const views = createViews({ | ||
type: 'row', | ||
children: [{}, {}], | ||
}); | ||
renderViews(views); | ||
expect(views.length).toBe(3); | ||
|
||
const [, [view, [node]]] = views; | ||
expect(view).toEqual({ | ||
height: 480, width: 300, x: 0, y: 0, | ||
}); | ||
expect(node).toEqual({}); | ||
}); | ||
|
||
test('col container', () => { | ||
const views = createViews({ | ||
type: 'col', | ||
padding: 20, | ||
flex: [1, 2, 1], | ||
children: [ | ||
{}, {}, {}, | ||
], | ||
}); | ||
renderViews(views); | ||
|
||
expect(views.length).toBe(4); | ||
|
||
const [, [view, [node]]] = views; | ||
expect(view).toEqual({ | ||
height: 110, width: 640, x: 0, y: 0, | ||
}); | ||
expect(node).toEqual({}); | ||
}); | ||
|
||
test('flex container', () => { | ||
const views = createViews({ | ||
type: 'row', | ||
children: [ | ||
{}, | ||
{ type: 'col', children: [{}, {}] }, | ||
], | ||
}); | ||
renderViews(views); | ||
|
||
expect(views.length).toBe(5); | ||
const [, , , [view, [node]]] = views; | ||
expect(view).toEqual({ | ||
height: 220, width: 300, x: 340, y: 0, | ||
}); | ||
expect(node).toEqual({}); | ||
}); | ||
|
||
test('facet container with specified x', () => { | ||
const data = [ | ||
{ sex: 'male', skin: 'white' }, | ||
{ sex: 'male', skin: 'black' }, | ||
{ sex: 'female', skin: 'white' }, | ||
{ sex: 'female', skin: 'yellow' }, | ||
]; | ||
const views = createViews({ | ||
type: 'facet', | ||
encodings: { | ||
x: 'sex', | ||
}, | ||
data, | ||
children: [{}], | ||
}); | ||
renderViews(views); | ||
|
||
expect(views.length).toBe(3); | ||
const [, [view, [node]]] = views; | ||
const { transform, ...rest } = view; | ||
expect(rest).toEqual({ | ||
height: 375, width: 275, x: 45, y: 45, | ||
}); | ||
expect(node).toEqual({}); | ||
expect(transform(data)).toEqual([ | ||
{ sex: 'male', skin: 'white' }, | ||
{ sex: 'male', skin: 'black' }, | ||
]); | ||
}); | ||
|
||
test('facet container with specified y', () => { | ||
const data = [ | ||
{ sex: 'male', skin: 'white' }, | ||
{ sex: 'male', skin: 'black' }, | ||
{ sex: 'female', skin: 'white' }, | ||
{ sex: 'female', skin: 'yellow' }, | ||
]; | ||
const views = createViews({ | ||
type: 'facet', | ||
encodings: { | ||
y: 'skin', | ||
}, | ||
data, | ||
children: [{}, {}], | ||
}); | ||
renderViews(views); | ||
|
||
expect(views.length).toBe(4); | ||
const [, [view, [node]]] = views; | ||
const { transform, ...rest } = view; | ||
expect(rest).toEqual({ | ||
height: 125, width: 550, x: 45, y: 45, | ||
}); | ||
expect(node).toEqual({}); | ||
expect(transform(data)).toEqual([ | ||
{ sex: 'male', skin: 'white' }, | ||
{ sex: 'female', skin: 'white' }, | ||
]); | ||
}); | ||
|
||
test('facet container', () => { | ||
const data = [ | ||
{ sex: 'male', skin: 'white' }, | ||
{ sex: 'male', skin: 'black' }, | ||
{ sex: 'female', skin: 'white' }, | ||
{ sex: 'female', skin: 'yellow' }, | ||
]; | ||
const views = createViews({ | ||
type: 'facet', | ||
encodings: { | ||
x: 'sex', | ||
y: 'skin', | ||
}, | ||
data, | ||
padding: 20, | ||
children: [{}, {}], | ||
}); | ||
renderViews(views); | ||
|
||
expect(views.length).toBe(7); | ||
const [, [view, [node]]] = views; | ||
const { transform, ...rest } = view; | ||
expect(rest).toEqual({ | ||
height: 111.66666666666667, width: 265, x: 45, y: 45, | ||
}); | ||
expect(node).toEqual({}); | ||
expect(transform(data)).toEqual([ | ||
{ sex: 'male', skin: 'white' }, | ||
]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { group } from '../utils'; | ||
|
||
export function computeFacetViews(box, { | ||
data, encodings = {}, padding = 0, | ||
paddingLeft = 45, paddingRight = 45, paddingBottom = 45, paddingTop = 60, | ||
}) { | ||
const { x, y } = encodings; | ||
const cols = x ? Array.from(group(data, (d) => d[x]).keys()) : [undefined]; | ||
const rows = y ? Array.from(group(data, (d) => d[y]).keys()) : [undefined]; | ||
const n = cols.length; | ||
const m = rows.length; | ||
const views = []; | ||
const width = box.width - paddingLeft - paddingRight; | ||
const height = box.height - paddingTop - paddingBottom; | ||
const boxWidth = (width - padding * (n - 1)) / n; | ||
const boxHeight = (height - padding * (m - 1)) / m; | ||
for (let i = 0; i < n; i += 1) { | ||
for (let j = 0; j < m; j += 1) { | ||
const transform = (data) => { | ||
const inRow = (d) => d[x] === cols[i] || cols[i] === undefined; | ||
const inCol = (d) => d[y] === rows[j] || rows[j] === undefined; | ||
return data.filter((d) => inRow(d) && inCol(d)); | ||
}; | ||
views.push({ | ||
x: paddingLeft + box.x + padding * i + i * boxWidth, | ||
y: paddingRight + box.y + padding * j + j * boxHeight, | ||
width: boxWidth, | ||
height: boxHeight, | ||
transform, | ||
}); | ||
} | ||
} | ||
return views; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
export function computeFlexViews(box, node) { | ||
const { | ||
type, children, flex = children.map(() => 1), padding = 40, | ||
} = node; | ||
const [mainStart, mainSize, crossSize, crossStart] = type === 'col' | ||
? ['y', 'height', 'width', 'x'] | ||
: ['x', 'width', 'height', 'y']; | ||
|
||
const sum = flex.reduce((total, value) => total + value); | ||
const totalSize = box[mainSize] - padding * (children.length - 1); | ||
const sizes = flex.map((value) => totalSize * (value / sum)); | ||
|
||
const childrenViews = []; | ||
for (let next = box[mainStart], i = 0; i < sizes.length; next += sizes[i] + padding, i += 1) { | ||
childrenViews.push({ | ||
[mainStart]: next, | ||
[mainSize]: sizes[i], | ||
[crossStart]: box[crossStart], | ||
[crossSize]: box[crossSize], | ||
}); | ||
} | ||
return childrenViews; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { createViews } from './view'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export function computeLayerViews(box, node) { | ||
const { children = [] } = node; | ||
return new Array(children.length).fill(0).map(() => ({ ...box })); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { computeFlexViews } from './flex'; | ||
import { computeFacetViews } from './facet'; | ||
import { computeLayerViews } from './layer'; | ||
import { descendants, group } from '../utils'; | ||
|
||
export function createViews(root, computes = { | ||
layer: computeLayerViews, | ||
col: computeFlexViews, | ||
row: computeFlexViews, | ||
facet: computeFacetViews, | ||
}) { | ||
const nodes = descendants(root); | ||
const { | ||
width = 640, height = 480, x = 0, y = 0, | ||
} = root; | ||
const rootView = { | ||
width, height, x, y, | ||
}; | ||
const nodeView = new Map([[root, rootView]]); | ||
|
||
for (const node of nodes) { | ||
const view = nodeView.get(node); | ||
const { children = [], type } = node; | ||
const computeChildrenViews = computes[type]; | ||
if (computeChildrenViews) { | ||
const childrenViews = computeChildrenViews(view, node); | ||
if (computeChildrenViews !== computeFacetViews) { | ||
for (const [i, child] of Object.entries(children)) { | ||
nodeView.set(child, childrenViews[i]); | ||
} | ||
} else { | ||
for (const child of children) { | ||
for (const view of childrenViews) { | ||
nodeView.set({ ...child }, view); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
const key = (d) => `${d.x}-${d.y}-${d.width}-${d.height}`; | ||
const keyViews = group(Array.from(nodeView.entries()), ([, view]) => key(view)); | ||
return Array.from(keyViews.values()).map((views) => { | ||
const view = views[0][1]; | ||
const nodes = views.map((d) => d[0]); | ||
return [view, nodes]; | ||
}); | ||
} |