How I build a tool to visualize todo lists
This the technical part of The 1 tool you'll ever need to improve your results (opens new window). This just my take on how I went thorugh implementing hill chart for me to use freely in my projects.
I love this game (Hill climb racing) BTW, but I'll choose another way to implement hill charts using D3.js (opens new window) a javascript data visualization library.
Since we'll deal with charts and math Let's start by this equation.
const hillFn = (point) =>
50 * Math.sin((Math.PI / 50) * point - (1 / 2) * Math.PI) + 50;
Woh! that's looks complicated I know, this function is responsible for produce hill-like curve and, correctly position and drag points on the curve.
I used Desmos (opens new window) to visualize above equation on the curve.
Now we have ourselves a hole not a hill but we'll fix that.
// Set X and Y axis scale values, it used to determine the center of the chart
// when calling this.xScale(50), it also flip the y axis to start from the
// lowest point and scale up like claiming a hill from the ground.
this.xScale = scaleLinear().domain([0, 100]).range([0, this.chartWidth]);
this.yScale = scaleLinear().domain([0, 100]).range([this.chartHeight, 0]);
this.chartWidth
could be any width.
One of the most powerful features in d3js is scaleLinear
domains. In this implementation d3 will represent ranges from 0 to chart width in domain (scale) from 0 to 100 where 0 is the beginning of the chart and 100 is the end and 50 is the hightest point, no matter the width or the hight of the chart.
in the second line we flipped the range so that the y axis to start from the lowest point and scale up like claiming a hill from the ground. it's like adding minus sign before the equation.
const hillFn = (point) =>
-1 * (50 * Math.sin((Math.PI / 50) * point - (1 / 2) * Math.PI) + 50);
# Normalizing points
Because every point on the hill chart can be represented as value from 0 to 100 on the X axis, end-user may enter a point x coordinates on the chart and the hillfn
will take care of converting it to actual (x, y) pair on the chart.
// Example of point
{
id: '3' // (optional)
color: 'red',
description: 'Late af task',
size: 10,
x: 45,
link: '/fired.html',
}
# Points can climb hills
While implementing dragging points, I faced problem of converting new coordinates (x, y) pair to scale values from 0 to 100.
x axis value on the chart which represented by data.x
is taking the mouse x axis event value, while it can't move freely on y axis according to the mouse coordinates, so in order to get y axis value we need to get the invert of scale x value and pass it to hillfn
which will get the scale y. For example
In this example x coordinate of the mouse equals the yellow point x coordinate on the chart, to get y yScale(hillFn(invertedX))
// Convert current point coordinates back to the original scale
// between 0 and 100 to set it in the data attribute
const invertedX = that.xScale.invert(x);
// move point on x axis
data.x = x;
// Explained above
data.y = that.yScale(hillFn(invertedX));
After climbing that hill (dragging), a user may want to know new point coordantes that's when I faced another problem of converting y point to scale Y point so user can save it to database.
I had to find the reverse of hillfn
// The inverse of hillfn() convert values from chart coordinates
// back to the original scale hillFn() value. mainly used
// in dragging event and setting the setting new values.
const hillFnInverse = (point) =>
(25 * (2 * Math.asin((point - 50) / 50) + Math.PI)) / Math.PI;
Now we can convert point coordinates on chart to scale pair
const invertedY = hillFnInverse(that.yScale.invert(data.y));
const newInvertedCoordinates = {
x: invertedX,
y: invertedY,
};