Skip to content
Snippets Groups Projects
Commit a2ca1b5c authored by Emmanuel Raviart's avatar Emmanuel Raviart
Browse files

Add very preliminary Waterfall.

parent d73cff52
No related branches found
No related tags found
No related merge requests found
<script>
import { getContext } from 'svelte';
const { width, height, xScale, yRange } = getContext('LayerCake');
export let gridlines = true;
export let tickMarks = false;
export let formatTick = d => d;
export let baseline = false;
export let snapTicks = false;
export let ticks = undefined;
export let xTick = undefined;
export let yTick = 16;
export let dxTick = 0;
export let dyTick = 0;
$: isBandwidth = typeof $xScale.bandwidth === 'function';
$: tickVals = Array.isArray(ticks) ? ticks :
isBandwidth ?
$xScale.domain() :
typeof ticks === 'function' ?
ticks($xScale.ticks()) :
$xScale.ticks(ticks);
function textAnchor(i) {
if (snapTicks === true) {
if (i === 0) {
return 'start';
}
if (i === tickVals.length - 1) {
return 'end';
}
}
return 'middle';
}
</script>
<g class='axis x-axis' class:snapTicks>
{#each tickVals as tick, i}
<g class='tick tick-{ i }' transform='translate({$xScale(tick)},{$yRange[0]})'>
{#if gridlines !== false}
<line class="gridline" y1='{$height * -1}' y2='0' x1='0' x2='0'></line>
{/if}
{#if tickMarks === true}
<line class="tick-mark" y1='{0}' y2='{6}' x1='{xTick || isBandwidth ? $xScale.bandwidth() / 2 : 0}' x2='{xTick || isBandwidth ? $xScale.bandwidth() / 2 : 0}'></line>
{/if}
<text
x="{xTick || isBandwidth ? $xScale.bandwidth() / 2 : 0}"
y='{yTick}'
dx='{dxTick}'
dy='{dyTick}'
text-anchor='{textAnchor(i)}'>{formatTick(tick)}</text>
</g>
{/each}
{#if baseline === true}
<line class="baseline" y1='{$height + 0.5}' y2='{$height + 0.5}' x1='0' x2='{$width}'></line>
{/if}
</g>
<style>
.tick {
font-size: .725em;
font-weight: 200;
}
line,
.tick line {
stroke: #aaa;
stroke-dasharray: 2;
}
.tick text {
fill: #666;
}
.tick .tick-mark,
.baseline {
stroke-dasharray: 0;
}
/* This looks slightly better */
.axis.snapTicks .tick:last-child text {
transform: translateX(3px);
}
.axis.snapTicks .tick.tick-0 text {
transform: translateX(-3px);
}
</style>
<script>
import { getContext } from 'svelte';
const { padding, xRange, yScale } = getContext('LayerCake');
export let ticks = 4;
export let tickMarks = false;
export let gridlines = true;
export let formatTick = d => d;
export let xTick = 0;
export let yTick = 0;
export let dxTick = 0;
export let dyTick = -4;
export let textAnchor = 'start';
$: isBandwidth = typeof $yScale.bandwidth === 'function';
$: tickVals = Array.isArray(ticks) ? ticks :
isBandwidth ?
$yScale.domain() :
typeof ticks === 'function' ?
ticks($yScale.ticks()) :
$yScale.ticks(ticks);
</script>
<g class='axis y-axis' transform='translate({-$padding.left}, 0)'>
{#each tickVals as tick, i}
<g class='tick tick-{tick}' transform='translate({$xRange[0] + (isBandwidth ? $padding.left : 0)}, {$yScale(tick)})'>
{#if gridlines !== false}
<line
class="gridline"
x2='100%'
y1={yTick + (isBandwidth ? ($yScale.bandwidth() / 2) : 0)}
y2={yTick + (isBandwidth ? ($yScale.bandwidth() / 2) : 0)}
></line>
{/if}
{#if tickMarks === true}
<line
class='tick-mark'
x1='0'
x2='{isBandwidth ? -6 : 6}'
y1={yTick + (isBandwidth ? ($yScale.bandwidth() / 2) : 0)}
y2={yTick + (isBandwidth ? ($yScale.bandwidth() / 2) : 0)}
></line>
{/if}
<text
x='{xTick}'
y='{yTick + (isBandwidth ? $yScale.bandwidth() / 2 : 0)}'
dx='{isBandwidth ? -9 : dxTick}'
dy='{isBandwidth ? 4 : dyTick}'
style="text-anchor:{isBandwidth ? 'end' : textAnchor};"
>{formatTick(tick)}</text>
</g>
{/each}
</g>
<style>
.tick {
font-size: .725em;
font-weight: 200;
}
.tick line {
stroke: #aaa;
}
.tick .gridline {
stroke-dasharray: 2;
}
.tick text {
fill: #666;
}
.tick.tick-0 line {
stroke-dasharray: 0;
}
</style>
<script>
import { getContext } from "svelte"
const { data, xGet, yGet, yRange, xScale } = getContext("LayerCake")
$: columnWidth = (d) => {
const vals = $xGet(d)
return Math.max(0, vals[1] - vals[0])
}
$: columnHeight = (d) => {
return $yRange[0] - $yGet(d)
}
/* --------------------------------------------
* Default styles
*/
export let fill = "#00e047"
export let stroke = ""
export let strokeWidth = 0
</script>
<g class="column-group">
{#each $data as d, i}
<rect
class="group-rect"
data-id={i}
x={$xScale.bandwidth ? $xGet(d) : $xGet(d)[0]}
y={$yGet(d)}
width={$xScale.bandwidth ? $xScale.bandwidth() : columnWidth(d)}
height={columnHeight(d)}
{fill}
{stroke}
stroke-width={strokeWidth}
/>
{/each}
</g>
<script lang="ts">
import { scaleBand } from "d3-scale"
import type { Decomposition } from "$lib/decompositions"
import { walkDecomposition } from "$lib/decompositions"
import { LayerCake, Svg } from "$lib/layercake"
import AxisX from "./AxisX.svelte"
import AxisY from "./AxisY.svelte"
import Column from "./Column.svelte"
export let decomposition: Decomposition
$: data = [...walkDecomposition(decomposition)].filter(
({ value }) => value !== 0,
)
</script>
<div class="h-64 w-full">
<LayerCake
{data}
x="short_name"
xScale={scaleBand().paddingInner([0.02]).round(true)}
xDomain={[...walkDecomposition(decomposition)]
.filter(({ value }) => value !== 0)
.map((node) => node.short_name)}
y="value"
>
<Svg>
<Column />
<AxisX />
<AxisY />
</Svg>
</LayerCake>
</div>
export { default } from "./index.svelte"
interface Decomposition { export interface Decomposition {
code: string code: string
short_name: string short_name: string
children?: Decomposition[] children?: Decomposition[]
value?: number
} }
export const decomposition: Decomposition = { export const decomposition: Decomposition = {
......
export { default as LayerCake } from './LayerCake.svelte'; export { default as LayerCake } from "./LayerCake.svelte"
export { default as Html } from './layouts/Html.svelte'; export { default as Html } from "./layouts/Html.svelte"
export { default as Svg } from './layouts/Svg.svelte'; export { default as Svg } from "./layouts/Svg.svelte"
export { default as ScaledSvg } from './layouts/ScaledSvg.svelte'; export { default as ScaledSvg } from "./layouts/ScaledSvg.svelte"
export { default as Canvas } from './layouts/Canvas.svelte'; export { default as Canvas } from "./layouts/Canvas.svelte"
export { default as WebGL } from './layouts/Webgl.svelte'; export { default as WebGL } from "./layouts/Webgl.svelte"
export { default as scaleCanvas } from './lib/scaleCanvas.js'; export { default as scaleCanvas } from "./lib/scaleCanvas.js"
export { default as flatten } from './lib/flatten.js'; export { default as flatten } from "./lib/flatten.js"
export { default as uniques } from './lib/uniques.js'; export { default as uniques } from "./lib/uniques.js"
export { default as calcExtents } from './lib/calcExtents.js'; export { default as calcExtents } from "./lib/calcExtents.js"
export { default as raise } from './lib/raise.js'; export { default as raise } from "./lib/raise.js"
...@@ -110,13 +110,19 @@ ...@@ -110,13 +110,19 @@
import { browser } from "$app/env" import { browser } from "$app/env"
import { session } from "$app/stores" import { session } from "$app/stores"
import { decomposition } from "$lib/decompositions" import type { Decomposition } from "$lib/decompositions"
import { decomposition as decompositionWithoutValue } from "$lib/decompositions"
// import type { Simulation } from "$lib/simulations" // import type { Simulation } from "$lib/simulations"
import type { Situation } from "$lib/situations" import type { Situation } from "$lib/situations"
import Waterfall from "$lib/Waterfall"
// export let simulation: Simulation // export let simulation: Simulation
let results: { code: string; value: number[] }[] = [] let valueByCode: { [code: string]: number } = {}
let decomposition = updateDecompositionValues(
decompositionWithoutValue as Decomposition,
valueByCode,
)
const year = 2017 const year = 2017
const situation: Situation = { const situation: Situation = {
individus: { individus: {
...@@ -165,12 +171,21 @@ ...@@ -165,12 +171,21 @@
{ {
// maxAttempts: 10, // maxAttempts: 10,
onmessage: (event) => { onmessage: (event) => {
console.log("WebSocket message received:", event)
const result = JSON.parse(event.data) const result = JSON.parse(event.data)
if (result.error !== undefined) { if (result.error !== undefined) {
console.log("Error:", result) console.log("Error:", result)
} else { } else {
results = [...results, result] valueByCode = {
...valueByCode,
[result.code]: result.value.reduce((sum, cell) => {
sum += cell
return sum
}, 0),
}
decomposition = updateDecompositionValues(
decomposition,
valueByCode,
)
} }
}, },
// onopen: (event) => console.log("[WebSocket] Connected!", event), // onopen: (event) => console.log("[WebSocket] Connected!", event),
...@@ -186,7 +201,7 @@ ...@@ -186,7 +201,7 @@
} }
function submit() { function submit() {
results = [] valueByCode = {}
webSocket.send( webSocket.send(
JSON.stringify({ JSON.stringify({
decomposition, decomposition,
...@@ -199,13 +214,43 @@ ...@@ -199,13 +214,43 @@
}), }),
) )
} }
function updateDecompositionValues(
node: Decomposition,
valueByCode: { [code: string]: number },
): Decomposition {
let value = valueByCode[node.code]
if (value === undefined) {
if (node.children === undefined) {
if (node.value !== 0) {
node = {
...node,
value: 0,
}
}
} else {
const children = node.children.map((child) =>
updateDecompositionValues(child, valueByCode),
)
node = {
...node,
children,
value: children.reduce((sum, child) => sum + child.value, 0),
}
}
} else if (node.value !== value) {
node = {
...node,
value,
}
}
return node
}
</script> </script>
<pre>{JSON.stringify(situation)}</pre> <pre>{JSON.stringify(situation)}</pre>
<button on:click={submit}>Simuler</button> <button on:click={submit}>Simuler</button>
{#each results as result} <Waterfall {decomposition} />
<pre>{JSON.stringify(result)}</pre>
{/each}
<!-- <pre>{JSON.stringify(simulation, null, 2)}</pre> --> <!-- <pre>{JSON.stringify(simulation, null, 2)}</pre> -->
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment