Published
Edited
Apr 20, 2021
Fork of Weight Chart
Insert cell
md`# Food Chart`
Insert cell
renderWeightChart()
Insert cell
d3 = require('d3')
Insert cell
import { range } from '@nuuuwan/list-utils'
Insert cell
import { getSVG, drawLine, drawText } from '@nuuuwan/svg-utils'
Insert cell
import { DAY_LIST, MONTH_LIST } from '@nuuuwan/time-utils'
Insert cell
CHART_WIDTH = 1000
Insert cell
CHART_HEIGHT = 773
Insert cell
PADDING = 72
Insert cell
MIN_WEIGHT = 0
Insert cell
WEIGHT_RANGE = 6
Insert cell
WEIGHT_INCR = 0.25
Insert cell
TIME_RANGE_DAYS = 4
Insert cell
TIME_INCR = 1 / 7
Insert cell
PERSON_NAME = 'Nuwan'
Insert cell
function renderWeightChart() {
const svg = getSVG({ width: CHART_WIDTH, height: CHART_HEIGHT });
const INNER_WIDTH = CHART_WIDTH - PADDING * 2;
const INNER_HEIGHT = CHART_HEIGHT - PADDING * 2;
const MAX_WEIGHT = MIN_WEIGHT + WEIGHT_RANGE;
const TICK_DIM = 12;
const STROKE_WIDTH_NORMAL = 0.1;
const STROKE_WIDTH_THICK = 1;

const FONT_SIZE_NORMAL = 24;
const FONT_SIZE_SMALL = 12;
const FONT_SIZE_LARGE = 36;

const nIncrWeight = parseInt(WEIGHT_RANGE / WEIGHT_INCR);
const nDarkWeight = parseInt(1 / WEIGHT_INCR);
let weightLbSet = new Set();
let bmiSet = new Set();
range(0, nIncrWeight + 1).forEach(function(i) {
const [x1, x2] = [PADDING - TICK_DIM, PADDING + INNER_WIDTH + TICK_DIM];
const y = PADDING + (INNER_HEIGHT * i) / nIncrWeight;
const isDark = i % nDarkWeight === 0;

const strokeWidth = isDark ? STROKE_WIDTH_THICK : STROKE_WIDTH_NORMAL;
drawLine(svg, [x1, y], [x2, y], { stroke: 'black', strokeWidth });

if (isDark) {
const weight = MAX_WEIGHT - i * WEIGHT_INCR;
const dayTime = (24 - weight * (24 / MAX_WEIGHT)) % 24;

let dayTimeStr;
if (dayTime === 0) {
dayTimeStr = '12am';
} else if (dayTime === 12) {
dayTimeStr = '12pm';
} else if (dayTime > 12) {
dayTimeStr = `${dayTime - 12}pm`;
} else {
dayTimeStr = `${dayTime}am`;
}

drawText(svg, [PADDING * 0.75, y], dayTimeStr, {
textAnchor: 'end',
fill: 'black',
fontSize: FONT_SIZE_NORMAL
});
}
});

const nIncrTime = parseInt(TIME_RANGE_DAYS / TIME_INCR);
const nDarkTime = parseInt(1 / TIME_INCR);
const dateNow = new Date();
const startTimeUnixTime =
parseInt(dateNow.getTime() / (7 * 86400 * 1000)) * 7 * 86400 * 1000 +
3 * 86400 * 1000;

range(0, nIncrTime + 1).forEach(function(i) {
const [y1, y2] = [PADDING - TICK_DIM, PADDING + INNER_HEIGHT + TICK_DIM];
const x = PADDING + (INNER_WIDTH * i) / nIncrTime;
const isDark = i % nDarkTime === 0;

const strokeWidth = isDark ? STROKE_WIDTH_THICK : STROKE_WIDTH_NORMAL;
drawLine(svg, [x, y1], [x, y2], { stroke: 'black', strokeWidth });

drawText(
svg,
[x + INNER_WIDTH / nIncrTime / 2, PADDING * 0.75],
DAY_LIST[i % 7].substring(0, 1),
{
textAnchor: 'middle',
fill: 'black',
fontSize: FONT_SIZE_SMALL
}
);

if (isDark) {
const date = new Date(startTimeUnixTime + i * 86400 * 1000);
const dateStr = `${MONTH_LIST[date.getMonth()].substring(
0,
3
)} ${date.getDate()}`;
drawText(svg, [x, PADDING * 0.5], dateStr, {
textAnchor: 'middle',
fill: 'black',
fontSize: FONT_SIZE_NORMAL
});
}
});

drawText(
svg,
[CHART_WIDTH / 2, CHART_HEIGHT - PADDING / 2],
`${PERSON_NAME}'s Food Chart`,
{
textAnchor: 'middle',
fill: 'black'
}
);

return svg.node();
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more